From 069c938eb6ebfd77f6a415207331c66f72270e5f Mon Sep 17 00:00:00 2001 From: "coyot@coyot-sager-PC" Date: Tue, 28 Feb 2017 22:35:01 +0000 Subject: pull from rev d22beb597e52ecbf1c98f25d4489ea0425eda4b0 of sl-321 --- indra/viewer_components/manager/InstallerError.py | 49 - .../manager/InstallerUserMessage.py | 316 ------ indra/viewer_components/manager/SL_Launcher | 199 ---- indra/viewer_components/manager/apply_update.py | 277 ----- indra/viewer_components/manager/download_update.py | 105 -- .../manager/tests/data/settings.xml | 1184 -------------------- indra/viewer_components/manager/tests/summary.json | 1 - .../manager/tests/test_InstallerError.py | 45 - .../tests/test_check_for_completed_download.py | 59 - .../tests/test_convert_version_file_style.py | 61 - .../manager/tests/test_get_filename.py | 66 -- .../manager/tests/test_get_log_file_handle.py | 68 -- .../manager/tests/test_get_parent_path.py | 83 -- .../manager/tests/test_get_platform_key.py | 44 - .../manager/tests/test_get_settings.py | 87 -- .../manager/tests/test_make_VVM_UUID_hash.py | 47 - .../manager/tests/test_make_download_dir.py | 47 - .../manager/tests/test_query_vvm.py | 73 -- .../manager/tests/test_silent_write.py | 49 - .../manager/tests/test_summary.py | 45 - .../manager/tests/with_setup_args.py | 68 -- indra/viewer_components/manager/update_manager.py | 547 --------- 22 files changed, 3520 deletions(-) delete mode 100644 indra/viewer_components/manager/InstallerError.py delete mode 100644 indra/viewer_components/manager/InstallerUserMessage.py delete mode 100755 indra/viewer_components/manager/SL_Launcher delete mode 100755 indra/viewer_components/manager/apply_update.py delete mode 100755 indra/viewer_components/manager/download_update.py delete mode 100644 indra/viewer_components/manager/tests/data/settings.xml delete mode 100644 indra/viewer_components/manager/tests/summary.json delete mode 100644 indra/viewer_components/manager/tests/test_InstallerError.py delete mode 100644 indra/viewer_components/manager/tests/test_check_for_completed_download.py delete mode 100644 indra/viewer_components/manager/tests/test_convert_version_file_style.py delete mode 100644 indra/viewer_components/manager/tests/test_get_filename.py delete mode 100644 indra/viewer_components/manager/tests/test_get_log_file_handle.py delete mode 100644 indra/viewer_components/manager/tests/test_get_parent_path.py delete mode 100644 indra/viewer_components/manager/tests/test_get_platform_key.py delete mode 100644 indra/viewer_components/manager/tests/test_get_settings.py delete mode 100644 indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py delete mode 100644 indra/viewer_components/manager/tests/test_make_download_dir.py delete mode 100644 indra/viewer_components/manager/tests/test_query_vvm.py delete mode 100644 indra/viewer_components/manager/tests/test_silent_write.py delete mode 100644 indra/viewer_components/manager/tests/test_summary.py delete mode 100644 indra/viewer_components/manager/tests/with_setup_args.py delete mode 100755 indra/viewer_components/manager/update_manager.py (limited to 'indra/viewer_components') diff --git a/indra/viewer_components/manager/InstallerError.py b/indra/viewer_components/manager/InstallerError.py deleted file mode 100644 index 3b199ea231..0000000000 --- a/indra/viewer_components/manager/InstallerError.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file InstallerError.py -@author coyot -@date 2016-05-16 -@brief custom exception class for VMP - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -""" -usage: - ->>> import InstallerError ->>> import os ->>> try: -... os.mkdir('/tmp') -... except OSError, oe: -... ie = InstallerError.InstallerError(oe, "foo") -... raise ie - -Traceback (most recent call last): - File "", line 5, in -InstallerError.InstallerError: [Errno [Errno 17] File exists: '/tmp'] foo -""" - -class InstallerError(OSError): - def __init___(self, message): - Exception.__init__(self, message) diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py deleted file mode 100644 index 8002399659..0000000000 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python - -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2013&license=viewerlgpl$ -# Copyright (c) 2013, Linden Research, Inc. -# $/LicenseInfo$ - -""" -@file InstallerUserMessage.py -@author coyot -@date 2016-05-16 -""" - -""" -This does everything the old updater/scripts/darwin/messageframe.py script did and some more bits. -Pushed up the manager directory to be multiplatform. -""" - -import os -import Queue -import threading -import time -import Tkinter as tk -import ttk - - -class InstallerUserMessage(tk.Tk): - #Goals for this class: - # Provide a uniform look and feel - # Provide an easy to use convenience class for other scripts - # Provide windows that automatically disappear when done (for differing notions of done) - # Provide a progress bar that isn't a glorified spinner, but based on download progress - #Non-goals: - # No claim to threadsafety is made or warranted. Your mileage may vary. - # Please consult a doctor if you experience thread pain. - - #Linden standard green color, from Marketing - linden_green = "#487A7B" - - def __init__(self, text="", title="", width=500, height=200, wraplength = 400, icon_name = None, icon_path = None): - tk.Tk.__init__(self) - self.grid() - self.title(title) - self.choice = tk.BooleanVar() - self.config(background = 'black') - # background="..." doesn't work on MacOS for radiobuttons or progress bars - # http://tinyurl.com/tkmacbuttons - ttk.Style().configure('Linden.TLabel', foreground=InstallerUserMessage.linden_green, background='black') - ttk.Style().configure('Linden.TButton', foreground=InstallerUserMessage.linden_green, background='black') - ttk.Style().configure("black.Horizontal.TProgressbar", foreground=InstallerUserMessage.linden_green, background='black') - - #This bit of configuration centers the window on the screen - # The constants below are to adjust for typical overhead from the - # frame borders. - self.xp = (self.winfo_screenwidth() / 2) - (width / 2) - 8 - self.yp = (self.winfo_screenheight() / 2) - (height / 2) - 20 - self.geometry('{0}x{1}+{2}+{3}'.format(width, height, self.xp, self.yp)) - - #find a few things - self.script_dir = os.path.dirname(os.path.realpath(__file__)) - self.contents_dir = os.path.dirname(self.script_dir) - self.icon_dir = os.path.abspath(os.path.join(self.contents_dir, 'Resources/vmp_icons')) - - #finds the icon and creates the widget - self.find_icon(icon_path, icon_name) - - #defines what to do when window is closed - self.protocol("WM_DELETE_WINDOW", self._delete_window) - - #callback id - self.id = -1 - - def _delete_window(self): - #capture and discard all destroy events before the choice is set - if not ((self.choice == None) or (self.choice == "")): - try: - #initialized value. If we have an outstanding callback, kill it before killing ourselves - if self.id != -1: - self.after_cancel(self.id) - self.destroy() - except: - #tk may try to destroy the same object twice - pass - - def set_colors(self, widget): - # #487A7B is "Linden Green" - widget.config(foreground = InstallerUserMessage.linden_green) - widget.config(background='black') - - def find_icon(self, icon_path = None, icon_name = None): - #we do this in each message, let's do it just once instead. - if not icon_path: - icon_path = self.icon_dir - icon_path = os.path.join(icon_path, icon_name) - print icon_path - if os.path.exists(icon_path): - icon = tk.PhotoImage(file=icon_path) - self.image_label = tk.Label(image = icon) - self.image_label.image = icon - else: - #default to text if image not available - self.image_label = tk.Label(text = "Second Life") - - def auto_resize(self, row_count = 0, column_count = 0, heavy_row = None, heavy_column = None): - #auto resize window to fit all rows and columns - #"heavy" gets extra weight - for x in range(column_count): - if x == heavy_column: - self.columnconfigure(x, weight = 2) - else: - self.columnconfigure(x, weight=1) - - for y in range(row_count): - if y == heavy_row: - self.rowconfigure(y, weight = 2) - else: - self.rowconfigure(x, weight=1) - - def basic_message(self, message): - #message: text to be displayed - #icon_path: directory holding the icon, defaults to icons subdir of script dir - #icon_name: filename of icon to be displayed - self.choice.set(True) - self.text_label = tk.Label(text = message) - self.set_colors(self.text_label) - self.set_colors(self.image_label) - #pad, direction and weight are all experimentally derived by retrying various values - self.image_label.grid(row = 1, column = 1, sticky = 'W') - self.text_label.grid(row = 1, column = 2, sticky = 'W', padx =100) - self.auto_resize(row_count = 1, column_count = 2) - self.mainloop() - - def binary_choice_message(self, message, true = 'Yes', false = 'No'): - #true: first option, returns True - #false: second option, returns False - #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice - #usage: - # frame = InstallerUserMessage.InstallerUserMessage( ... ) - # frame = frame.binary_choice_message( ... ) - # (wait for user to click) - # value = frame.choice.get() - - self.text_label = tk.Label(text = message) - #command registers the callback to the method named. We want the frame to go away once clicked. - #button 1 returns True/1, button 2 returns False/0 - self.button_one = ttk.Radiobutton(text = true, variable = self.choice, value = True, - command = self._delete_window, style = 'Linden.TButton') - self.button_two = ttk.Radiobutton(text = false, variable = self.choice, value = False, - command = self._delete_window, style = 'Linden.TButton') - self.set_colors(self.text_label) - self.set_colors(self.image_label) - #pad, direction and weight are all experimentally derived by retrying various values - self.image_label.grid(row = 1, column = 1, rowspan = 3, sticky = 'W') - self.text_label.grid(row = 1, column = 2, rowspan = 3) - self.button_one.grid(row = 1, column = 3, sticky = 'W', pady = 40) - self.button_two.grid(row = 2, column = 3, sticky = 'W', pady = 0) - self.auto_resize(row_count = 2, column_count = 3, heavy_column = 3) - #self.button_two.deselect() - self.update() - self.mainloop() - - def trinary_choice_message(self, message, one = 1, two = 2, three = 3): - #one: first option, returns 1 - #two: second option, returns 2 - #three: third option, returns 3 - #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice - #usage: - # frame = InstallerUserMessage.InstallerUserMessage( ... ) - # frame = frame.binary_choice_message( ... ) - # (wait for user to click) - # value = frame.choice.get() - - self.text_label = tk.Label(text = message) - #command registers the callback to the method named. We want the frame to go away once clicked. - self.button_one = ttk.Radiobutton(text = one, variable = self.choice, value = 1, - command = self._delete_window, style = 'Linden.TButton') - self.button_two = ttk.Radiobutton(text = two, variable = self.choice, value = 2, - command = self._delete_window, style = 'Linden.TButton') - self.button_three = ttk.Radiobutton(text = three, variable = self.choice, value = 3, - command = self._delete_window, style = 'Linden.TButton') - self.set_colors(self.text_label) - self.set_colors(self.image_label) - #pad, direction and weight are all experimentally derived by retrying various values - self.image_label.grid(row = 1, column = 1, rowspan = 4, sticky = 'W') - self.text_label.grid(row = 1, column = 2, rowspan = 4, padx = 5) - self.button_one.grid(row = 1, column = 3, sticky = 'W', pady = 5) - self.button_two.grid(row = 2, column = 3, sticky = 'W', pady = 5) - self.button_three.grid(row = 3, column = 3, sticky = 'W', pady = 5) - self.auto_resize(row_count = 3, column_count = 3, heavy_column = 3) - #self.button_two.deselect() - self.update() - self.mainloop() - - def progress_bar(self, message = None, size = 0, interval = 100, pb_queue = None): - #Best effort attempt at a real progress bar - # This is what Tk calls "determinate mode" rather than "indeterminate mode" - #size: denominator of percent complete - #interval: frequency, in ms, of how often to poll the file for progress - #pb_queue: queue object used to send updates to the bar - self.text_label = tk.Label(text = message) - self.set_colors(self.text_label) - self.set_colors(self.image_label) - self.image_label.grid(row = 1, column = 1, sticky = 'NSEW') - self.text_label.grid(row = 2, column = 1, sticky = 'NSEW') - self.progress = ttk.Progressbar(self, style = 'black.Horizontal.TProgressbar', orient="horizontal", length=100, mode="determinate") - self.progress.grid(row = 3, column = 1, sticky = 'NSEW') - self.value = 0 - self.progress["maximum"] = size - self.auto_resize(row_count = 1, column_count = 3) - self.queue = pb_queue - self.check_scheduler() - - def check_scheduler(self): - try: - if self.value < self.progress["maximum"]: - self.check_queue() - self.id = self.after(100, self.check_scheduler) - else: - #prevent a race condition between polling and the widget destruction - self.after_cancel(self.id) - except tk.TclError: - #we're already dead, just die quietly - pass - - def check_queue(self): - while self.queue.qsize(): - try: - msg = float(self.queue.get(0)) - #custom signal, time to tear down - if msg == -1: - self.choice.set(True) - self.destroy() - else: - self.progress.step(msg) - self.value = msg - except Queue.Empty: - #nothing to do - return - -class ThreadedClient(threading.Thread): - #for test only, not part of the functional code - def __init__(self, queue): - threading.Thread.__init__(self) - self.queue = queue - - def run(self): - for x in range(1, 90, 10): - time.sleep(1) - print "run " + str(x) - self.queue.put(10) - #tkk progress bars wrap at exactly 100 percent, look full at 99% - print "leftovers" - self.queue.put(9) - time.sleep(5) - # -1 is a custom signal to the progress_bar to quit - self.queue.put(-1) - -if __name__ == "__main__": - #When run as a script, just test the InstallUserMessage. - #To proceed with the test, close the first window, select on the second and fourth. The third will close by itself. - import sys - import tempfile - - def set_and_check(frame, value): - print "value: " + str(value) - frame.progress.step(value) - if frame.progress["value"] < frame.progress["maximum"]: - print "In Progress" - else: - print "Over now" - - #basic message window test - frame2 = InstallerUserMessage(text = "Something in the way she moves....", title = "Beatles Quotes for 100", icon_name="head-sl-logo.gif") - print frame2.contents_dir - print frame2.icon_dir - frame2.basic_message(message = "...attracts me like no other.") - print "Destroyed!" - sys.stdout.flush() - - #binary choice test. User destroys window when they select. - frame3 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") - frame3.binary_choice_message(message = "And all I have to do is think of her.", - true = "Don't want to leave her now", false = 'You know I believe and how') - print frame3.choice.get() - sys.stdout.flush() - - #progress bar - queue = Queue.Queue() - thread = ThreadedClient(queue) - thread.start() - print "thread started" - - frame4 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 300", icon_name="head-sl-logo.gif") - frame4.progress_bar(message = "You're asking me will my love grow", size = 100, pb_queue = queue) - print "frame defined" - frame4.mainloop() - - #trinary choice test. User destroys window when they select. - frame3a = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") - frame3a.trinary_choice_message(message = "And all I have to do is think of her.", - one = "Don't want to leave her now", two = 'You know I believe and how', three = 'John is Dead') - print frame3a.choice.get() - sys.stdout.flush() diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher deleted file mode 100755 index 257543187c..0000000000 --- a/indra/viewer_components/manager/SL_Launcher +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python - -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ -# Copyright (c) 2013, Linden Research, Inc. - -import os -import sys - -#module globals -log_file_handle = None -cwd = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, os.path.join(cwd, 'llbase')) - -import argparse -import collections -import InstallerUserMessage -#NOTA BENE: -# For POSIX platforms, llsd.py will be imported from the same directory. -# For Windows, llsd.py will be compiled into the executable by pyinstaller -try: - from llbase import llsd -except: - #if Windows, this is expected, if not, we're dead - if os.name == 'nt': - pass -import platform -import subprocess -import update_manager - - -def silent_write(log_file_handle, text): - #if we have a log file, write. If not, do nothing. - #this is so we don't have to keep trapping for an exception with a None handle - #oh and because it is best effort, it is also a holey_write ;) - if (log_file_handle): - #prepend text for easy grepping - timestamp = datetime.utcnow().strftime("%Y-%m-%D %H:%M:%S") - log_file_handle.write(timestamp + " LAUNCHER: " + text + "\n") - -def get_cmd_line(): - platform_name = platform.system() - #find the parent of the logs and user_settings directories - if (platform_name == 'Darwin'): - settings_file = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'Resources/app_settings/cmd_line.xml') - elif (platform_name == 'Linux'): - settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') - #using list format of join is important here because the Windows pathsep in a string escapes the next char - elif (platform_name == 'Windows'): - settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') - else: - settings_file = None - - try: - cmd_line = llsd.parse((open(settings_file)).read()) - except: - silent_write(log_file_handle, "Could not parse settings file %s" % settings_file) - cmd_line = None - - return cmd_line - -def get_settings(): - #return the settings file parsed into a dict - try: - settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) - settings = llsd.parse((open(settings_file)).read()) - except llsd.LLSDParseError as lpe: - silent_write(log_file_handle, "Could not parse settings file %s" % lpe) - return None - return settings - -def capture_vmp_args(arg_list = None, cmd_line = None): - #expected input format: arg_list = ['--set', 'foo', 'bar', '-X', '-Y', 'qux'] - #take a copy of the viewer parameters that are of interest to VMP. - #the regex for a parameter is -- {opt1} {opt2} - cli_overrides = {} - cmd_line = get_cmd_line() - - vmp_params = {'--channel':'channel', '--settings':'settings', '--update-service':'update-service', '--set':'set'} - #the settings set with --set. All such settings have only one argument. - vmp_setters = ('UpdaterMaximumBandwidth', 'UpdaterServiceCheckPeriod', 'UpdaterServicePath', 'UpdaterServiceSetting', 'UpdaterServiceURL', 'UpdaterWillingToTest') - - #Here turn the list into a queue, popping off the left as we go. Note that deque() makes a copy by value, not by reference - #Because of the complexity introduced by the uncertainty of how many options a parameter can take, this is far less complicated code than the more - #pythonic (x,y) = since we will sometimes have (x), sometimes (x,y) and sometimes (x,y,z) - #also, because the pop is destructive, we prevent ourselves from iterating back over list elements that iterator methods would peek ahead at - vmp_queue = collections.deque(arg_list) - while (len(vmp_queue)): - param = vmp_queue.popleft() - #if it is not one of ours, pop through args until we get to the next parameter - if param in vmp_params.keys(): - if param == '--set': - setting_name = vmp_queue.popleft() - setting_value = vmp_queue.popleft() - if setting_name in vmp_setters: - cli_overrides[vmp_params[param]] = (setting_name, setting_value) - else: - #find out how many args this parameter has - no_dashes = vmp_params[param] - count = cmd_line[no_dashes]['count'] - param_args = [] - if count > 0: - for argh in range(0,count): - param_args.append(vmp_queue.popleft()) - #the parameter name is the key, the (possibly empty) list of args is the value - cli_overrides[vmp_params[param]] = param_args - - #to prevent KeyErrors on missing keys, set the remainder to None - for key in vmp_params: - if key != '--set': - try: - cli_overrides[key] - except KeyError: - cli_overrides[key] = None - else: - cli_overrides["--set"] = {} - for arg in vmp_setters: - try: - cli_overrides[key][arg] - except KeyError: - cli_overrides[key][arg] = None - return cli_overrides - -#main entry point -#this and a few other update manager methods really should be refactored into a util lib -parent_dir = update_manager.get_parent_path(update_manager.get_platform_key()) -log_file_handle = update_manager.get_log_file_handle(parent_dir, 'launcher.log') - -executable_name = "" -if sys.platform.startswith('darwin'): - executable_name = "Second Life" -elif sys.platform.startswith("win") or sys.platform.startswith("cyg"): - if os.path.isfile(os.path.join(cwd,"SecondLifeViewer.exe")): - executable_name = "SecondLifeViewer.exe" - elif os.path.isfile(os.path.join(cwd,"SecondLifeTest.exe")): - executable_name = "SecondLifeTest.exe" - else: - sys.exit("Can't find Windows viewer binary") -elif sys.platform.startswith("linux"): - executable_name = "secondlife" -else: - #SL doesn't run on VMS or punch cards - sys.exit("Unsupported platform") - -#find the viewer to be lauched -viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),executable_name) - -parser = argparse.ArgumentParser() -args = parser.parse_known_args(sys.argv) -#args[1] looks like ['./SL_Launcher', '--set', 'foo', 'bar', '-X', '-Y', 'qux'], dump the progname -args_list_to_pass = args[1][1:] -vmp_args = capture_vmp_args(args_list_to_pass) -#make a copy by value, not by reference -command = list(args_list_to_pass) - -(success, state, condition) = update_manager.update_manager(vmp_args) -# From update_manager: -# (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) -# (False, 'download', version): we failed to download the new version -# (False, 'apply', version): we failed to apply the new version -# (True, None, None): No update found -# (True, 'in place', True): update applied in place -# (True, 'in place', path_to_new_launcher): Update applied by a new install to a new location -# (True, 'background', True): background download initiated -#These boil down three cases: -# Success is False, then pop up a message and launch the current viewer -# No update, update succeeded in place in foreground, or background update started: silently launch the current viewer channel -# Updated succeed to a different channel, launch that viewer and exit -if not success: - msg = 'Update failed in the %s process. Please check logs. Viewer will launch starting momentarily.' % state - update_manager.after_frame(msg) - command.insert(0,viewer_binary) - viewer_process = subprocess.Popen(command) - #at the moment, we just exit here. Later, the crash monitor will be launched at this point -elif (success == True and - (state == None - or (state == 'background' and condition == True) - or (state == 'in_place' and condition == True))): - command.insert(0,viewer_binary) - viewer_process = subprocess.Popen(command) - #at the moment, we just exit here. Later, the crash monitor will be launched at this point -else: - #'condition' is the path to the new launcher. - command.insert(0,condition) - viewer_process = subprocess.Popen(command) - sys.exit(0) diff --git a/indra/viewer_components/manager/apply_update.py b/indra/viewer_components/manager/apply_update.py deleted file mode 100755 index 643e4ad2bc..0000000000 --- a/indra/viewer_components/manager/apply_update.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2016&license=viewerlgpl$ -# Copyright (c) 2016, Linden Research, Inc. -# $/LicenseInfo$ - -""" -@file apply_update.py -@author coyot -@date 2016-06-28 -""" - -""" -Applies an already downloaded update. -""" - -import argparse -import errno -import fnmatch -import InstallerUserMessage as IUM -import os -import os.path -import plistlib -import re -import shutil -import subprocess -import sys -import tarfile -import tempfile - -#Module level variables - -#fnmatch expressions -LNX_REGEX = '*' + '.bz2' -MAC_REGEX = '*' + '.dmg' -MAC_APP_REGEX = '*' + '.app' -WIN_REGEX = '*' + '.exe' - -#which install the updater is run from -INSTALL_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - -#whether the update is to the INSTALL_DIR or not. Most of the time this is the case. -IN_PLACE = True - -BUNDLE_IDENTIFIER = "com.secondlife.indra.viewer" -# Magic OS directory name that causes Cocoa viewer to crash on OS X 10.7.5 -# (see MAINT-3331) -STATE_DIR = os.path.join(os.environ["HOME"], "Library", "Saved Application State", - BUNDLE_IDENTIFIER + ".savedState") - -def silent_write(log_file_handle, text): - #if we have a log file, write. If not, do nothing. - if (log_file_handle): - #prepend text for easy grepping - log_file_handle.write("APPLY UPDATE: " + text + "\n") - -def get_filename(download_dir = None): - #given a directory that supposedly has the download, find the installable - #if you are on platform X and you give the updater a directory with an installable - #for platform Y, you are either trying something fancy or get what you deserve - #or both - for filename in os.listdir(download_dir): - if (fnmatch.fnmatch(filename, LNX_REGEX) - or fnmatch.fnmatch(filename, MAC_REGEX) - or fnmatch.fnmatch(filename, WIN_REGEX)): - return os.path.join(download_dir, filename) - #someone gave us a bad directory - return None - -def try_dismount(log_file_handle = None, installable = None, tmpdir = None): - #best effort cleanup try to dismount the dmg file if we have mounted one - #the French judge gave it a 5.8 - try: - #use the df command to find the device name - #Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on - #/dev/disk1s2 2047936 643280 1404656 32% 80408 175582 31% /private/tmp/mnt/Second Life Installer - command = ["df", os.path.join(tmpdir, "Second Life Installer")] - output = subprocess.check_output(command) - #first word of second line of df output is the device name - mnt_dev = output.split('\n')[1].split()[0] - #do the dismount - command = ["hdiutil", "detach", "-force", mnt_dev] - output = subprocess.check_output(command) - silent_write(log_file_handle, "hdiutil detach succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not detach dmg file %s. Error messages: %s" % (installable, e.message)) - -def apply_update(download_dir = None, platform_key = None, log_file_handle = None, in_place = True): - #for lnx and mac, returns path to newly installed viewer - #for win, return the name of the executable - #returns None on failure for all three - #throws an exception if it can't find an installable at all - - IN_PLACE = in_place - - installable = get_filename(download_dir) - if not installable: - #could not find the download - raise ValueError("Could not find installable in " + download_dir) - - #apply update using the platform specific tools - if platform_key == 'lnx': - installed = apply_linux_update(installable, log_file_handle) - elif platform_key == 'mac': - installed = apply_mac_update(installable, log_file_handle) - elif platform_key == 'win': - installed = apply_windows_update(installable, log_file_handle) - else: - #wtf? - raise ValueError("Unknown Platform: " + platform_key) - - if not installed: - #only mark the download as done when everything is done - done_filename = os.path.join(os.path.dirname(installable), ".done") - open(done_filename, 'w+').close() - - return installed - -def apply_linux_update(installable = None, log_file_handle = None): - try: - #untar to tmpdir - tmpdir = tempfile.mkdtemp() - tar = tarfile.open(name = installable, mode="r:bz2") - tar.extractall(path = tmpdir) - if IN_PLACE: - #rename current install dir - shutil.move(INSTALL_DIR,install_dir + ".bak") - #mv new to current - shutil.move(tmpdir, INSTALL_DIR) - #delete tarball on success - os.remove(installable) - except Exception, e: - silent_write(log_file_handle, "Update failed due to " + repr(e)) - return None - return INSTALL_DIR - -def apply_mac_update(installable = None, log_file_handle = None): - #INSTALL_DIR is something like /Applications/Second Life Viewer.app/Contents/MacOS, need to jump up two levels for the install base - install_base = os.path.dirname(INSTALL_DIR) - install_base = os.path.dirname(install_base) - - #verify dmg file - try: - output = subprocess.check_output(["hdiutil", "verify", installable], stderr=subprocess.STDOUT) - silent_write(log_file_handle, "dmg verification succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not verify dmg file %s. Error messages: %s" % (installable, e.message)) - return None - #make temp dir and mount & attach dmg - tmpdir = tempfile.mkdtemp() - try: - output = subprocess.check_output(["hdiutil", "attach", installable, "-mountroot", tmpdir]) - silent_write(log_file_handle, "hdiutil attach succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not attach dmg file %s. Error messages: %s" % (installable, e.message)) - return None - #verify plist - mounted_appdir = None - for top_dir in os.listdir(tmpdir): - for appdir in os.listdir(os.path.join(tmpdir, top_dir)): - appdir = os.path.join(os.path.join(tmpdir, top_dir), appdir) - if fnmatch.fnmatch(appdir, MAC_APP_REGEX): - try: - plist = os.path.join(appdir, "Contents", "Info.plist") - CFBundleIdentifier = plistlib.readPlist(plist)["CFBundleIdentifier"] - mounted_appdir = appdir - except: - #there is no except for this try because there are multiple directories that legimately don't have what we are looking for - pass - if not mounted_appdir: - silent_write(log_file_handle, "Could not find app bundle in dmg %s." % (installable,)) - return None - if CFBundleIdentifier != BUNDLE_IDENTIFIER: - silent_write(log_file_handle, "Wrong or null bundle identifier for dmg %s. Bundle identifier: %s" % (installable, CFBundleIdentifier)) - try_dismount(log_file_handle, installable, tmpdir) - return None - #do the install, finally - if IN_PLACE: - # swap out old install directory - bundlename = os.path.basename(mounted_appdir) - silent_write(log_file_handle, "Updating %s" % bundlename) - swapped_out = os.path.join(tmpdir, INSTALL_DIR.lstrip('/')) - shutil.move(install_base, swapped_out) - else: - silent_write(log_file_handle, "Installing %s" % install_base) - - # copy over the new bits - try: - shutil.copytree(mounted_appdir, install_base, symlinks=True) - retcode = 0 - except Exception, e: - # try to restore previous viewer - if os.path.exists(swapped_out): - silent_write(log_file_handle, "Install of %s failed, rolling back to previous viewer." % installable) - shutil.move(swapped_out, installed_test) - retcode = 1 - finally: - try_dismount(log_file_handle, installable, tmpdir) - if retcode: - return None - - #see MAINT-3331 - try: - shutil.rmtree(STATE_DIR) - except Exception, e: - #if we fail to delete something that isn't there, that's okay - if e[0] == errno.ENOENT: - pass - else: - raise e - - os.remove(installable) - return install_base - -def apply_windows_update(installable = None, log_file_handle = None): - #the windows install is just running the NSIS installer executable - #from VMP's perspective, it is a black box - try: - output = subprocess.check_output(installable, stderr=subprocess.STDOUT) - silent_write(log_file_handle, "Install of %s succeeded." % installable) - silent_write(log_file_handle, output) - except subprocess.CalledProcessError, cpe: - silent_write(log_file_handle, "%s failed with return code %s. Error messages: %s." % - (cpe.cmd, cpe.returncode, cpe.message)) - return None - #Due to the black box nature of the install, we have to derive the application path from the - #name of the installable. This is essentially reverse-engineering app_name()/app_name_oneword() - #in viewer_manifest.py - #the format of the filename is: Second_Life_{Project Name}_A-B-C-XXXXXX_i686_Setup.exe - #which deploys to C:\Program Files (x86)\SecondLifeProjectName\ - #so we want all but the last four phrases and tack on Viewer if there is no project - if re.search('Project', installable): - winstall = os.path.join("C:\\Program Files (x86)\\", "".join(installable.split("_")[:-3])) - else: - winstall = os.path.join("C:\\Program Files (x86)\\", "".join(installable.split("_")[:-3])+"Viewer") - return winstall - -def main(): - parser = argparse.ArgumentParser("Apply Downloaded Update") - parser.add_argument('--dir', dest = 'download_dir', help = 'directory to find installable', required = True) - parser.add_argument('--pkey', dest = 'platform_key', help =' OS: lnx|mac|win', required = True) - parser.add_argument('--in_place', action = 'store_false', help = 'This upgrade is for a different channel', default = True) - parser.add_argument('--log_file', dest = 'log_file', default = None, help = 'file to write messages to') - args = parser.parse_args() - - if args.log_file: - try: - f = open(args.log_file,'w+') - except: - print "%s could not be found or opened" % args.log_file - sys.exit(1) - - IN_PLACE = args.in_place - result = apply_update(download_dir = args.download_dir, platform_key = args.platform_key, log_file_handle = f) - if not result: - sys.exit("Update failed") - else: - sys.exit(0) - - -if __name__ == "__main__": - main() diff --git a/indra/viewer_components/manager/download_update.py b/indra/viewer_components/manager/download_update.py deleted file mode 100755 index a5e365fa37..0000000000 --- a/indra/viewer_components/manager/download_update.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2016&license=viewerlgpl$ -# Copyright (c) 2016, Linden Research, Inc. -# $/LicenseInfo$ - -""" -@file download_update.py -@author coyot -@date 2016-06-23 -""" - -""" -Performs a download of an update. In a separate script from update_manager so that we can -call it with subprocess. -""" - -import argparse -import InstallerUserMessage as IUM -import os -import Queue -import requests -import threading - -#module default -CHUNK_SIZE = 1024 - -def download_update(url = None, download_dir = None, size = None, progressbar = False, chunk_size = CHUNK_SIZE): - #url to download from - #download_dir to download to - #total size (for progressbar) of download - #progressbar: whether to display one (not used for background downloads) - #chunk_size is in bytes, amount to download at once - - queue = Queue.Queue() - if not os.path.exists(download_dir): - os.makedirs(download_dir) - #the url split provides the basename of the filename - filename = os.path.join(download_dir, url.split('/')[-1]) - req = requests.get(url, stream=True) - down_thread = ThreadedDownload(req, filename, chunk_size, progressbar, queue) - down_thread.start() - - if progressbar: - frame = IUM.InstallerUserMessage(title = "Second Life Downloader", icon_name="head-sl-logo.gif") - frame.progress_bar(message = "Download Progress", size = size, pb_queue = queue) - frame.mainloop() - else: - #nothing for the main thread to do - down_thread.join() - -class ThreadedDownload(threading.Thread): - def __init__(self, req, filename, chunk_size, progressbar, in_queue): - #req is a python request object - #target filename to download to - #chunk_size is in bytes, amount to download at once - #progressbar: whether to display one (not used for background downloads) - #in_queue mediates communication between this thread and the progressbar - threading.Thread.__init__(self) - self.req = req - self.filename = filename - self.chunk_size = int(chunk_size) - self.progressbar = progressbar - self.in_queue = in_queue - - def run(self): - with open(self.filename, 'wb') as fd: - #keep downloading until we run out of chunks, then download the last bit - for chunk in self.req.iter_content(self.chunk_size): - fd.write(chunk) - if self.progressbar: - #this will increment the progress bar by len(chunk)/size units - self.in_queue.put(len(chunk)) - #signal value saying to the progress bar that it is done and can destroy itself - #if len(chunk) is ever -1, we get to file a bug against Python - self.in_queue.put(-1) - -def main(): - #main method is for standalone use such as support and QA - #VMP will import this module and run download_update directly - parser = argparse.ArgumentParser("Download URI to directory") - parser.add_argument('--url', dest='url', help='URL of file to be downloaded', required=True) - parser.add_argument('--dir', dest='download_dir', help='directory to be downloaded to', required=True) - parser.add_argument('--pb', dest='progressbar', help='whether or not to show a progressbar', action="store_true", default = False) - parser.add_argument('--size', dest='size', help='size of download for progressbar') - parser.add_argument('--chunk_size', dest='chunk_size', default=CHUNK_SIZE, help='max portion size of download to be loaded in memory in bytes.') - args = parser.parse_args() - - download_update(url = args.url, download_dir = args.download_dir, size = args.size, progressbar = args.progressbar, chunk_size = args.chunk_size) - - -if __name__ == "__main__": - main() diff --git a/indra/viewer_components/manager/tests/data/settings.xml b/indra/viewer_components/manager/tests/data/settings.xml deleted file mode 100644 index 07e420dcb3..0000000000 --- a/indra/viewer_components/manager/tests/data/settings.xml +++ /dev/null @@ -1,1184 +0,0 @@ - - - AllowMultipleViewers - - Comment - Allow multiple viewers. - Type - Boolean - Value - 1 - - AllowTapTapHoldRun - - Comment - Tapping a direction key twice and holding it down makes avatar run - Type - Boolean - Value - 0 - - AppearanceCameraMovement - - Comment - When entering appearance editing mode, camera zooms in on currently selected portion of avatar - Type - Boolean - Value - 0 - - AudioLevelMedia - - Comment - Audio level of Quicktime movies - Type - F32 - Value - 0.699999988079071044921875 - - AudioLevelMic - - Comment - Audio level of microphone input - Type - F32 - Value - 0.1749999970197677612304688 - - AudioLevelMusic - - Comment - Audio level of streaming music - Type - F32 - Value - 0 - - AudioLevelSFX - - Comment - Audio level of in-world sound effects - Type - F32 - Value - 0.699999988079071044921875 - - AudioLevelVoice - - Comment - Audio level of voice chat - Type - F32 - Value - 1 - - AudioStreamingMedia - - Comment - Enable streaming - Type - Boolean - Value - 0 - - AvatarAxisDeadZone0 - - Comment - Avatar axis 0 dead zone. - Type - F32 - Value - 0.1000000014901161193847656 - - AvatarAxisDeadZone1 - - Comment - Avatar axis 1 dead zone. - Type - F32 - Value - 0.1000000014901161193847656 - - AvatarAxisDeadZone2 - - Comment - Avatar axis 2 dead zone. - Type - F32 - Value - 0.1000000014901161193847656 - - AvatarAxisDeadZone3 - - Comment - Avatar axis 3 dead zone. - Type - F32 - Value - 1 - - AvatarAxisDeadZone4 - - Comment - Avatar axis 4 dead zone. - Type - F32 - Value - 0.01999999955296516418457031 - - AvatarAxisDeadZone5 - - Comment - Avatar axis 5 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - AvatarAxisScale3 - - Comment - Avatar axis 3 scaler. - Type - F32 - Value - 0 - - AvatarAxisScale4 - - Comment - Avatar axis 4 scaler. - Type - F32 - Value - 2 - - AvatarAxisScale5 - - Comment - Avatar axis 5 scaler. - Type - F32 - Value - 2 - - AvatarFeathering - - Comment - Avatar feathering (less is softer) - Type - F32 - Value - 6 - - AvatarFileName - - Comment - Alternative avatar file name - Type - String - Value - avatar_lad_tentacles.xml - - BuildAxisDeadZone0 - - Comment - Build axis 0 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone1 - - Comment - Build axis 1 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone2 - - Comment - Build axis 2 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone3 - - Comment - Build axis 3 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone4 - - Comment - Build axis 4 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone5 - - Comment - Build axis 5 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisScale0 - - Comment - Build axis 0 scaler. - Type - F32 - Value - 6 - - BuildAxisScale1 - - Comment - Build axis 1 scaler. - Type - F32 - Value - 6 - - BuildAxisScale2 - - Comment - Build axis 2 scaler. - Type - F32 - Value - 6 - - BuildAxisScale3 - - Comment - Build axis 3 scaler. - Type - F32 - Value - 6 - - BuildAxisScale4 - - Comment - Build axis 4 scaler. - Type - F32 - Value - 6 - - BuildAxisScale5 - - Comment - Build axis 5 scaler. - Type - F32 - Value - 6 - - BuildFeathering - - Comment - Build feathering (less is softer) - Type - F32 - Value - 12 - - BulkChangeEveryoneCopy - - Comment - Bulk changed objects can be copied by everyone - Type - Boolean - Value - 1 - - BulkChangeNextOwnerCopy - - Comment - Bulk changed objects can be copied by next owner - Type - Boolean - Value - 1 - - BulkChangeNextOwnerModify - - Comment - Bulk changed objects can be modified by next owner - Type - Boolean - Value - 1 - - BulkChangeShareWithGroup - - Comment - Bulk changed objects are shared with the currently active group - Type - Boolean - Value - 1 - - CacheValidateCounter - - Comment - Used to distribute cache validation - Type - U32 - Value - 122 - - CameraPosOnLogout - - Comment - Camera position when last logged out (global coordinates) - Type - Vector3D - Value - - 288290.4477181434631347656 - 275988.5277819633483886719 - 49.10921102762222290039062 - - - ClickToWalk - - Comment - Click in world to walk to location - Type - Boolean - Value - 0 - - ConversationSortOrder - - Comment - Specifies sort key for conversations - Type - U32 - Value - 0 - - CurrentGrid - - Comment - Currently Selected Grid - Type - String - Value - util.agni.lindenlab.com - - Cursor3D - - Comment - Treat Joystick values as absolute positions (not deltas). - Type - Boolean - Value - 0 - - FirstLoginThisInstall - - Comment - Specifies that you have not logged in with the viewer since you performed a clean install - Type - Boolean - Value - 0 - - FirstRunThisInstall - - Comment - Specifies that you have not run the viewer since you performed a clean install - Type - Boolean - Value - 0 - - FlycamAxisDeadZone0 - - Comment - Flycam axis 0 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone1 - - Comment - Flycam axis 1 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone2 - - Comment - Flycam axis 2 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone3 - - Comment - Flycam axis 3 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone4 - - Comment - Flycam axis 4 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone5 - - Comment - Flycam axis 5 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone6 - - Comment - Flycam axis 6 dead zone. - Type - F32 - Value - 1 - - FlycamAxisScale0 - - Comment - Flycam axis 0 scaler. - Type - F32 - Value - 42 - - FlycamAxisScale1 - - Comment - Flycam axis 1 scaler. - Type - F32 - Value - 40 - - FlycamAxisScale2 - - Comment - Flycam axis 2 scaler. - Type - F32 - Value - 40 - - FlycamAxisScale3 - - Comment - Flycam axis 3 scaler. - Type - F32 - Value - 0 - - FlycamAxisScale4 - - Comment - Flycam axis 4 scaler. - Type - F32 - Value - 2 - - FlycamAxisScale5 - - Comment - Flycam axis 5 scaler. - Type - F32 - Value - 3 - - FlycamAxisScale6 - - Comment - Flycam axis 6 scaler. - Type - F32 - Value - 0 - - FlycamFeathering - - Comment - Flycam feathering (less is softer) - Type - F32 - Value - 5 - - FocusPosOnLogout - - Comment - Camera focus point when last logged out (global coordinates) - Type - Vector3D - Value - - 288287.8830481640761718154 - 275991.5973855691263452172 - 47.96361158013021963597566 - - - ForceShowGrid - - Comment - Always show grid dropdown on login screen - Type - Boolean - Value - 1 - - HttpProxyType - - Comment - Proxy type to use for HTTP operations - Type - String - Value - None - - JoystickInitialized - - Comment - Whether or not a joystick has been detected and initiailized. - Type - String - Value - UnknownDevice - - LSLFindCaseInsensitivity - - Comment - Use case insensitivity when searching in LSL editor - Type - Boolean - Value - 1 - - LastFeatureVersion - - Comment - [DO NOT MODIFY] Feature Table Version number for tracking rendering system changes - Type - S32 - Value - 37 - - LastGPUString - - Comment - [DO NOT MODIFY] previous GPU id string for tracking hardware changes - Type - String - Value - NVIDIA Corporation NVIDIA GeForce GT 750M OpenGL Engine - - LastPrefTab - - Comment - Last selected tab in preferences window - Type - S32 - Value - 1 - - LastRunVersion - - Comment - Version number of last instance of the viewer that you ran - Type - String - Value - Second Life Project Bento 5.0.0.315657 - - LocalCacheVersion - - Comment - Version number of cache - Type - S32 - Value - 7 - - LoginLocation - - Comment - Default Login location ('last', 'home') preference - Type - String - Value - home - - MapScale - - Comment - World map zoom level (pixels per region) - Type - F32 - Value - 256 - - MaxJointsPerMeshObject - - Comment - Maximum joints per rigged mesh object - Type - U32 - Value - 51 - - MediaEnablePopups - - Comment - If true, enable targeted links and javascript in media to open new media browser windows without a prompt. - Type - Boolean - Value - 1 - - MediaShowOnOthers - - Comment - Whether or not to show media on other avatars - Type - Boolean - Value - 1 - - MigrateCacheDirectory - - Comment - Check for old version of disk cache to migrate to current location - Type - Boolean - Value - 0 - - NavBarShowParcelProperties - - Comment - Show parcel property icons in navigation bar - Type - Boolean - Value - 0 - - NextLoginLocation - - Comment - Location to log into for this session - set from command line or the login panel, cleared following a successfull login. - Type - String - Value - home - - NotificationConferenceIMOptions - - Comment - - Specifies how the UI responds to Conference IM Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - none - - NotificationFriendIMOptions - - Comment - - Specifies how the UI responds to Friend IM Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - openconversations - - NotificationGroupChatOptions - - Comment - - Specifies how the UI responds to Group Chat Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - none - - NotificationNearbyChatOptions - - Comment - - Specifies how the UI responds to Nearby Chat Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - none - - NotificationNonFriendIMOptions - - Comment - - Specifies how the UI responds to Non Friend IM Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - openconversations - - NumSessions - - Comment - Number of successful logins to Second Life - Type - S32 - Value - 1674 - - PlayTypingAnim - - Comment - Your avatar plays the typing animation whenever you type in the chat bar - Type - Boolean - Value - 0 - - PoolSizeExpCache - - Comment - Coroutine Pool size for ExpCache - Type - U32 - Value - 5 - - PreferredBrowserBehavior - - Comment - Use system browser for any links (0), use builtin browser for SL links and system one for others (1) or use builtin browser only (2). - Type - U32 - Value - 0 - - PreferredMaturity - - Comment - Setting for the user's preferred maturity level (consts in indra_constants.h) - Type - U32 - Value - 42 - - PresetGraphicActive - - Comment - Name of currently selected preference - Type - String - Value - Default - - ProbeHardwareOnStartup - - Comment - Query current hardware configuration on application startup - Type - Boolean - Value - 0 - - QAMode - - Comment - Enable Testing Features. - Type - Boolean - Value - 1 - - RenderAnisotropic - - Comment - Render textures using anisotropic filtering - Type - Boolean - Value - 1 - - RenderAvatarCloth - - Comment - Controls if avatars use wavy cloth - Type - Boolean - Value - 0 - - RenderAvatarLODFactor - - Comment - Controls level of detail of avatars (multiplier for current screen area when calculated level of detail) - Type - F32 - Value - 1 - - RenderFSAASamples - - Comment - Number of samples to use for FSAA (0 = no AA). - Type - U32 - Value - 2 - - RenderFarClip - - Comment - Distance of far clip plane from camera (meters) - Type - F32 - Value - 128 - - RenderMaxPartCount - - Comment - Maximum number of particles to display on screen - Type - S32 - Value - 2048 - - RenderQualityPerformance - - Comment - Which graphics settings you've chosen - Type - U32 - Value - 4 - - RenderReflectionDetail - - Comment - Detail of reflection render pass. - Type - S32 - Value - 0 - - RenderTerrainLODFactor - - Comment - Controls level of detail of terrain (multiplier for current screen area when calculated level of detail) - Type - F32 - Value - 2 - - RenderVBOEnable - - Comment - Use GL Vertex Buffer Objects - Type - Boolean - Value - 0 - - RenderVolumeLODFactor - - Comment - Controls level of detail of primitives (multiplier for current screen area when calculated level of detail) - Type - F32 - Value - 1.125 - - ShowAdvancedGraphicsSettings - - Comment - Show advanced graphics settings - Type - Boolean - Value - 1 - - ShowBanLines - - Comment - Show in-world ban/access borders - Type - Boolean - Value - 0 - - ShowStartLocation - - Comment - Display starting location menu on login screen - Type - Boolean - Value - 1 - - SkeletonFileName - - Comment - Alternative skeleton file name - Type - String - Value - avatar_skeleton_tentacles.xml - - SkyPresetName - - Comment - Sky preset to use. May be superseded by region settings or by a day cycle (see DayCycleName). - Type - String - Value - Sunset - - SnapshotConfigURL - - Comment - URL to fetch Snapshot Sharing configuration data from. - Type - String - Value - http://photos.apps.avatarsunited.com/viewer_config - - SnapshotFormat - - Comment - Save snapshots in this format (0 = PNG, 1 = JPEG, 2 = BMP) - Type - S32 - Value - 1 - - SnapshotQuality - - Comment - Quality setting of postcard JPEGs (0 = worst, 100 = best) - Type - S32 - Value - 100 - - SpellCheck - - Comment - Enable spellchecking on line and text editors - Type - Boolean - Value - 0 - - SpellCheckDictionary - - Comment - Current primary and secondary dictionaries used for spell checking - Type - String - Value - English (United States) - - TextureMemory - - Comment - Amount of memory to use for textures in MB (0 = autodetect) - Type - S32 - Value - 256 - - UseDayCycle - - Comment - Whether to use use a day cycle or a fixed sky. - Type - Boolean - Value - 0 - - UseDebugMenus - - Comment - Turns on "Debug" menu - Type - Boolean - Value - 1 - - UseEnvironmentFromRegion - - Comment - Choose whether to use the region's environment settings, or override them with the local settings. - Type - Boolean - Value - 0 - - VFSOldSize - - Comment - [DO NOT MODIFY] Controls resizing of local file cache - Type - U32 - Value - 102 - - VFSSalt - - Comment - [DO NOT MODIFY] Controls local file caching behavior - Type - U32 - Value - 260093998 - - VersionChannelName - - Comment - Version information generated by running the viewer - Type - String - Value - Second Life Release - - VertexShaderEnable - - Comment - Enable/disable all GLSL shaders (debug) - Type - Boolean - Value - 1 - - VoiceInputAudioDevice - - Comment - Audio input device to use for voice - Type - String - Value - C-Media USB Audio Device - - VoiceOutputAudioDevice - - Comment - Audio output device to use for voice - Type - String - Value - C-Media USB Audio Device - - WLSkyDetail - - Comment - Controls vertex detail on the WindLight sky. Lower numbers will give better performance and uglier skies. - Type - U32 - Value - 48 - - WebProfileFloaterRect - - Comment - Web profile floater dimensions - Type - Rect - Value - - 1189 - 957 - 1674 - 277 - - - WindowHeight - - Comment - SL viewer window height - Type - U32 - Value - 1000 - - WindowWidth - - Comment - SL viewer window width - Type - U32 - Value - 1626 - - WindowX - - Comment - X coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) - Type - S32 - Value - 50 - - WindowY - - Comment - Y coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) - Type - S32 - Value - 50 - - - diff --git a/indra/viewer_components/manager/tests/summary.json b/indra/viewer_components/manager/tests/summary.json deleted file mode 100644 index b78859d427..0000000000 --- a/indra/viewer_components/manager/tests/summary.json +++ /dev/null @@ -1 +0,0 @@ -{"Type":"viewer","Version":"4.0.5.315117","Channel":"Second Life Release"} diff --git a/indra/viewer_components/manager/tests/test_InstallerError.py b/indra/viewer_components/manager/tests/test_InstallerError.py deleted file mode 100644 index 9139447932..0000000000 --- a/indra/viewer_components/manager/tests/test_InstallerError.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file test_InstallerError.py -@author coyot -@date 2016-06-01 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - - -from nose.tools import assert_equal - -import InstallerError -import os - -def test_InstallerError(): - try: - #try to make our own homedir, this will fail on all three platforms - homedir = os.path.abspath(os.path.expanduser('~')) - os.mkdir(homedir) - except OSError, oe: - ie = InstallerError.InstallerError(oe, "Installer failed to create a homedir that already exists.") - - assert_equal( str(ie), - "[Errno [Errno 17] File exists: '%s'] Installer failed to create a homedir that already exists." % homedir) diff --git a/indra/viewer_components/manager/tests/test_check_for_completed_download.py b/indra/viewer_components/manager/tests/test_check_for_completed_download.py deleted file mode 100644 index bcaaef4c3f..0000000000 --- a/indra/viewer_components/manager/tests/test_check_for_completed_download.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_check_for_completed_download.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import tempfile -import update_manager -import with_setup_args - -def check_for_completed_download_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'test1') - tmpdir2 = tempfile.mkdtemp(prefix = 'test2') - tempfile.mkstemp(suffix = '.done', dir = tmpdir1) - - return [tmpdir1,tmpdir2], {} - -def check_for_completed_download_teardown(tmpdir1,tmpdir2): - shutil.rmtree(tmpdir1, ignore_errors = True) - shutil.rmtree(tmpdir2, ignore_errors = True) - -@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown) -def test_completed_check_for_completed_download(tmpdir1,tmpdir2): - assert_equal(update_manager.check_for_completed_download(tmpdir1), 'done'), "Failed to find completion marker" - -@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown) -def test_incomplete_check_for_completed_download(tmpdir1,tmpdir2): - #should return False - incomplete = not update_manager.check_for_completed_download(tmpdir2) - assert incomplete, "False positive, should not mark complete without a marker" diff --git a/indra/viewer_components/manager/tests/test_convert_version_file_style.py b/indra/viewer_components/manager/tests/test_convert_version_file_style.py deleted file mode 100644 index 244c22c458..0000000000 --- a/indra/viewer_components/manager/tests/test_convert_version_file_style.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file test_convert_version_file_style.py -@author coyot -@date 2016-06-01 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - - -from nose.tools import assert_equal - -import update_manager - -def test_normal_form(): - version = '1.2.3.456789' - golden = '1_2_3_456789' - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) - -def test_short_form(): - version = '1.23' - golden = '1_23' - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) - -def test_idempotent(): - version = '123' - golden = '123' - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) - -def test_none(): - version = None - golden = None - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) diff --git a/indra/viewer_components/manager/tests/test_get_filename.py b/indra/viewer_components/manager/tests/test_get_filename.py deleted file mode 100644 index efb6349374..0000000000 --- a/indra/viewer_components/manager/tests/test_get_filename.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_get_filename.py -@author coyot -@date 2016-06-30 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import tempfile -import apply_update -import with_setup_args - -def get_filename_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'lnx') - tmpdir2 = tempfile.mkdtemp(prefix = 'mac') - tmpdir3 = tempfile.mkdtemp(prefix = 'win') - tmpdir4 = tempfile.mkdtemp(prefix = 'bad') - tempfile.mkstemp(suffix = '.bz2', dir = tmpdir1) - tempfile.mkstemp(suffix = '.dmg', dir = tmpdir2) - tempfile.mkstemp(suffix = '.exe', dir = tmpdir3) - - return [tmpdir1,tmpdir2, tmpdir3, tmpdir4], {} - -def get_filename_teardown(tmpdir1,tmpdir2, tmpdir3, tmpdir4): - shutil.rmtree(tmpdir1, ignore_errors = True) - shutil.rmtree(tmpdir2, ignore_errors = True) - shutil.rmtree(tmpdir3, ignore_errors = True) - shutil.rmtree(tmpdir4, ignore_errors = True) - -@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown) -def test_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): - assert_is_not_none(apply_update.get_filename(tmpdir1)), "Failed to find installable" - assert_is_not_none(apply_update.get_filename(tmpdir2)), "Failed to find installable" - assert_is_not_none(apply_update.get_filename(tmpdir3)), "Failed to find installable" - -@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown) -def test_missing_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): - not_found = not apply_update.get_filename(tmpdir4) - assert not_found, "False positive, should not find an installable in an empty dir" diff --git a/indra/viewer_components/manager/tests/test_get_log_file_handle.py b/indra/viewer_components/manager/tests/test_get_log_file_handle.py deleted file mode 100644 index 5ea821acf7..0000000000 --- a/indra/viewer_components/manager/tests/test_get_log_file_handle.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python - -""" -@file test_get_log_file_handle.py -@author coyot -@date 2016-06-08 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * - -import os -import shutil -import tempfile -import update_manager -import with_setup_args - -def get_log_file_handle_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'test1') - tmpdir2 = tempfile.mkdtemp(prefix = 'test2') - log_file_path = os.path.abspath(os.path.join(tmpdir1,"update_manager.log")) - #not using tempfile because we want a particular filename - open(log_file_path, 'w+').close - - return [tmpdir1,tmpdir2,log_file_path], {} - -def get_log_file_handle_teardown(tmpdir1,tmpdir2,log_file_path): - shutil.rmtree(tmpdir1, ignore_errors = True) - shutil.rmtree(tmpdir2, ignore_errors = True) - -@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown) -def test_existing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path): - handle = update_manager.get_log_file_handle(tmpdir1) - if not handle: - print "Failed to find existing log file" - assert False - elif not os.path.exists(os.path.abspath(log_file_path+".old")): - print "Failed to rotate update manager log" - assert False - assert True - -@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown) -def test_missing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path): - handle = update_manager.get_log_file_handle(tmpdir2) - if not os.path.exists(log_file_path): - print "Failed to touch new log file" - assert False - assert True diff --git a/indra/viewer_components/manager/tests/test_get_parent_path.py b/indra/viewer_components/manager/tests/test_get_parent_path.py deleted file mode 100644 index 6e4b9dfaff..0000000000 --- a/indra/viewer_components/manager/tests/test_get_parent_path.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_get_parent_path.py -@author coyot -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import update_manager -import with_setup_args - -def get_parent_path_setup(): - key = update_manager.get_platform_key() - try: - if key == 'mac': - settings_dir = os.path.join(os.path.expanduser('~'),'Library','Application Support','SecondLife') - elif key == 'lnx': - settings_dir = os.path.join(os.path.expanduser('~'),'.secondlife') - elif key == 'win': - settings_dir = os.path.join(os.path.expanduser('~'),'AppData','Roaming','SecondLife') - else: - raise Exception("Invalid Platform Key") - - #preserve existing settings dir if any - if os.path.exists(settings_dir): - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - shutil.rmtree(old_dir, ignore_errors = True) - os.rename(settings_dir, old_dir) - os.makedirs(settings_dir) - except Exception, e: - print "get_parent_path_setup failed due to: %s" % str(e) - assert False - - #this is we don't have to rediscover settings_dir for test and teardown - return [settings_dir], {} - -def get_parent_path_teardown(settings_dir): - try: - shutil.rmtree(settings_dir, ignore_errors = True) - #restore previous settings dir if any - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - os.rename(old_dir, settings_dir) - except: - #cleanup is best effort - pass - -@with_setup_args.with_setup_args(get_parent_path_setup, get_parent_path_teardown) -def test_get_parent_path(settings_dir): - key = update_manager.get_platform_key() - got_settings_dir = update_manager.get_parent_path(key) - - assert settings_dir, "test_get_parent_path failed to obtain parent path" - - assert_equal(settings_dir, got_settings_dir) diff --git a/indra/viewer_components/manager/tests/test_get_platform_key.py b/indra/viewer_components/manager/tests/test_get_platform_key.py deleted file mode 100644 index eeaca1dff7..0000000000 --- a/indra/viewer_components/manager/tests/test_get_platform_key.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python - -""" -@file test_get_platform_key.py -@author coyot -@date 2016-06-01 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import assert_equal - -import platform -import update_manager - -def test_get_platform_key(): - key = update_manager.get_platform_key() - if key == 'mac': - assert_equal(platform.system(),'Darwin') - elif key == 'lnx': - assert_equal(platform.system(),'Linux') - elif key == 'win': - assert_equal(platform.system(),'Windows') - else: - assert_equal(key, None) diff --git a/indra/viewer_components/manager/tests/test_get_settings.py b/indra/viewer_components/manager/tests/test_get_settings.py deleted file mode 100644 index 7efcf62673..0000000000 --- a/indra/viewer_components/manager/tests/test_get_settings.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_get_settings.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import update_manager -import with_setup_args - -def get_settings_setup(): - try: - key = update_manager.get_platform_key() - settings_dir = os.path.join(update_manager.get_parent_path(key), "user_settings") - print settings_dir - - #preserve existing settings dir if any - if os.path.exists(settings_dir): - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - shutil.rmtree(old_dir, ignore_errors = True) - os.rename(settings_dir, old_dir) - os.makedirs(settings_dir) - - #the data subdir of the tests dir that this script is in - data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data") - #the test settings file - settings_file = os.path.join(data_dir, "settings.xml") - shutil.copyfile(settings_file, os.path.join(settings_dir, "settings.xml")) - - except Exception, e: - print "get_settings_setup failed due to: %s" % str(e) - assert False - - #this is we don't have to rediscover settings_dir for test and teardown - return [settings_dir], {} - -def get_settings_teardown(settings_dir): - try: - shutil.rmtree(settings_dir, ignore_errors = True) - #restore previous settings dir if any - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - os.rename(old_dir, settings_dir) - except: - #cleanup is best effort - pass - -@with_setup_args.with_setup_args(get_settings_setup, get_settings_teardown) -def test_get_settings(settings_dir): - key = update_manager.get_platform_key() - parent = update_manager.get_parent_path(key) - log_file = update_manager.get_log_file_handle(parent) - got_settings = update_manager.get_settings(log_file, parent) - - assert got_settings, "test_get_settings failed to find a settings.xml file" - - #test one key just to make sure it parsed - assert_equal(got_settings['CurrentGrid']['Value'], 'util.agni.lindenlab.com') diff --git a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py deleted file mode 100644 index 5939e5806a..0000000000 --- a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_make_VVM_UUID_hash.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * - -import update_manager - -def test_make_VVM_UUID_hash(): - #because the method returns different results on different hosts - #it is not easy to unit test it reliably. - #About the best we can do is check for the exception from subprocess - key = update_manager.get_platform_key() - try: - UUID_hash = update_manager.make_VVM_UUID_hash(key) - except Exception, e: - print "Test failed due to: %s" % str(e) - assert False - - #make_UUID_hash returned None - assert UUID_hash, "make_UUID_hash failed to make a hash." diff --git a/indra/viewer_components/manager/tests/test_make_download_dir.py b/indra/viewer_components/manager/tests/test_make_download_dir.py deleted file mode 100644 index e3f9cb83fe..0000000000 --- a/indra/viewer_components/manager/tests/test_make_download_dir.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python - -""" -@file test_make_download_dir.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * - -import update_manager - -def test_make_download_dir(): - key = update_manager.get_platform_key() - path = update_manager.get_parent_path(key) - version = '1.2.3.456789' - try: - download_dir = update_manager.make_download_dir(path, version) - except OSError, e: - print "make_download_dir failed to eat OSError %s" % str(e) - assert False - except Exception, e: - print "make_download_dir raised an unexpected exception %s" % str(e) - assert False - - assert download_dir, "make_download_dir returned None for path %s and version %s" % (path, version) diff --git a/indra/viewer_components/manager/tests/test_query_vvm.py b/indra/viewer_components/manager/tests/test_query_vvm.py deleted file mode 100644 index 79c8ede480..0000000000 --- a/indra/viewer_components/manager/tests/test_query_vvm.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_query_vvm.py -@author coyot -@date 2016-06-08 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * - -import os -import re -import shutil -import tempfile -import update_manager -import with_setup_args - -def query_vvm_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'test1') - handle = update_manager.get_log_file_handle(tmpdir1) - - return [tmpdir1,handle], {} - -def query_vvm_teardown(tmpdir1, handle): - shutil.rmtree(tmpdir1, ignore_errors = True) - -@with_setup_args.with_setup_args(query_vvm_setup, query_vvm_teardown) -def test_query_vvm(tmpdir1, handle): - key = update_manager.get_platform_key() - parent = update_manager.get_parent_path(key) - settings = update_manager.get_settings(handle, parent) - launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__)))) - summary = update_manager.get_summary(key, launcher_path) - - #for unit testing purposes, just testing a value from results. If no update, then None and it falls through - #for formal QA see: - # https://docs.google.com/document/d/1WNjOPdKlq0j_7s7gdNe_3QlyGnQDa3bFNvtyVM6Hx8M/edit - # https://wiki.lindenlab.com/wiki/Login_Test#Test_Viewer_Updater - #for test plans on all cases, as it requires setting up a fake VVM service - - try: - results = update_manager.query_vvm(handle, key, settings, summary) - except Exception, e: - print "query_vvm threw unexpected exception %s" % str(e) - assert False - - if results: - pattern = re.compile('Second Life') - assert pattern.search(results['channel']), "Bad results returned %s" % str(results) - - assert True diff --git a/indra/viewer_components/manager/tests/test_silent_write.py b/indra/viewer_components/manager/tests/test_silent_write.py deleted file mode 100644 index ad286787e2..0000000000 --- a/indra/viewer_components/manager/tests/test_silent_write.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_silent_write.py -@author coyot -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * - -import tempfile -import update_manager - -def test_silent_write_to_file(): - test_log = tempfile.TemporaryFile() - try: - update_manager.silent_write(test_log, "This is a test.") - except Exception, e: - print "Test failed due to: %s" % str(e) - assert False - -def test_silent_write_to_null(): - try: - update_manager.silent_write(None, "This is a test.") - except Exception, e: - print "Test failed due to: %s" % str(e) - assert False diff --git a/indra/viewer_components/manager/tests/test_summary.py b/indra/viewer_components/manager/tests/test_summary.py deleted file mode 100644 index f2f2af7347..0000000000 --- a/indra/viewer_components/manager/tests/test_summary.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_summary.py -@author coyot -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -from nose.tools import * - -import os.path -import tempfile -import update_manager - -def test_get_summary(): - key = update_manager.get_platform_key() - #launcher is one dir above tests - launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__)))) - summary_json = update_manager.get_summary(key, launcher_path) - - #we aren't testing the JSON library, one key pair is enough - #so we will use the one pair that is actually a constant - assert_equal(summary_json['Type'],'viewer') diff --git a/indra/viewer_components/manager/tests/with_setup_args.py b/indra/viewer_components/manager/tests/with_setup_args.py deleted file mode 100644 index 94d33aa5fe..0000000000 --- a/indra/viewer_components/manager/tests/with_setup_args.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python - -""" -@file with_setup_args.py -@author garyvdm -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -def with_setup_args(setup, teardown=None): - """Decorator to add setup and/or teardown methods to a test function:: - @with_setup_args(setup, teardown) - def test_something(): - " ... " - The setup function should return (args, kwargs) which will be passed to - test function, and teardown function. - Note that `with_setup_args` is useful *only* for test functions, not for test - methods or inside of TestCase subclasses. - """ - def decorate(func): - args = [] - kwargs = {} - - def test_wrapped(): - func(*args, **kwargs) - - test_wrapped.__name__ = func.__name__ - - def setup_wrapped(): - a, k = setup() - args.extend(a) - kwargs.update(k) - if hasattr(func, 'setup'): - func.setup() - test_wrapped.setup = setup_wrapped - - if teardown: - def teardown_wrapped(): - if hasattr(func, 'teardown'): - func.teardown() - teardown(*args, **kwargs) - - test_wrapped.teardown = teardown_wrapped - else: - if hasattr(func, 'teardown'): - test_wrapped.teardown = func.teardown() - return test_wrapped - return decorate diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py deleted file mode 100755 index 7962568119..0000000000 --- a/indra/viewer_components/manager/update_manager.py +++ /dev/null @@ -1,547 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file update_manager.py -@author coyot -@date 2016-05-16 -@brief executes viewer update checking and manages downloading and applying of updates - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -import os - -#NOTA BENE: -# For POSIX platforms, llbase will be imported from the same directory. -# For Windows, llbase will be compiled into the executable by pyinstaller -try: - from llbase import llrest - from llbase.llrest import RESTError - from llbase import llsd -except: - #if Windows, this is expected, if not, we're dead - if os.name == 'nt': - pass - -from copy import deepcopy -from datetime import datetime -from urlparse import urljoin - -import apply_update -import download_update -import errno -import fnmatch -import hashlib -import InstallerUserMessage -import json -import platform -import re -import shutil -import subprocess -import sys -import tempfile -import thread -import urllib - -def silent_write(log_file_handle, text): - #if we have a log file, write. If not, do nothing. - #this is so we don't have to keep trapping for an exception with a None handle - #oh and because it is best effort, it is also a holey_write ;) - if (log_file_handle): - #prepend text for easy grepping - timestamp = datetime.utcnow().strftime("%Y-%m-%D %H:%M:%S") - log_file_handle.write(timestamp + " UPDATE MANAGER: " + text + "\n") - -def after_frame(message, timeout = 10000): - #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds - #note that this blocks the caller for the duration of timeout - frame = InstallerUserMessage.InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") - #this is done before basic_message so that we aren't blocked by mainloop() - #frame.after(timeout, lambda: frame._delete_window) - frame.after(timeout, lambda: frame.destroy()) - frame.basic_message(message = message) - -def convert_version_file_style(version): - #converts a version string a.b.c.d to a_b_c_d as used in downloaded filenames - #re will throw a TypeError if it gets None, just return that. - try: - pattern = re.compile('\.') - return pattern.sub('_', version) - except TypeError, te: - return None - -def get_platform_key(): - #this is the name that is inserted into the VVM URI - #and carried forward through the rest of the updater to determine - #platform specific actions as appropriate - platform_dict = {'Darwin':'mac', 'Linux':'lnx', 'Windows':'win'} - platform_uname = platform.system() - try: - return platform_dict[platform_uname] - except KeyError: - return None - -def get_summary(platform_name, launcher_path): - #get the contents of the summary.json file. - #for linux and windows, this file is in the same directory as the script - #for mac, the script is in ../Contents/MacOS/ and the file is in ../Contents/Resources/ - script_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - if (platform_name == 'mac'): - summary_dir = os.path.abspath(os.path.join(script_dir, "../Resources")) - else: - summary_dir = script_dir - summary_file = os.path.join(summary_dir,"summary.json") - with open(summary_file) as summary_handle: - return json.load(summary_handle) - -def get_parent_path(platform_name): - #find the parent of the logs and user_settings directories - if (platform_name == 'mac'): - settings_dir = os.path.join(os.path.expanduser('~'),'Library','Application Support','SecondLife') - elif (platform_name == 'lnx'): - settings_dir = os.path.join(os.path.expanduser('~'),'.secondlife') - #using list format of join is important here because the Windows pathsep in a string escapes the next char - elif (platform_name == 'win'): - settings_dir = os.path.join(os.path.expanduser('~'),'AppData','Roaming','SecondLife') - else: - settings_dir = None - return settings_dir - -def make_download_dir(parent_dir, new_version): - #make a canonical download dir if it does not already exist - #format: ../user_settings/downloads/1.2.3.456789 - #we do this so that multiple viewers on the same host can update separately - #this also functions as a getter - try: - download_dir = os.path.join(parent_dir, "downloads", new_version) - os.makedirs(download_dir) - except OSError, hell: - #Directory already exists, that's okay. Other OSErrors are not okay. - if hell[0] == errno.EEXIST: - pass - else: - raise hell - return download_dir - -def check_for_completed_download(download_dir): - #there will be two files on completion, the download and a marker file called "".done"" - #for optional upgrades, there may also be a .skip file to skip this particular upgrade - #or .next to install on next run - completed = None - marker_regex = '*' + '.done' - skip_regex = '*' + '.skip' - next_regex = '*' + '.next' - for filename in os.listdir(download_dir): - if fnmatch.fnmatch(filename, marker_regex): - completed = 'done' - elif fnmatch.fnmatch(filename, skip_regex): - completed = 'skip' - elif fnmatch.fnmatch(filename, next_regex): - #so we don't skip infinitely - os.remove(filename) - completed = 'next' - if not completed: - #cleanup - shutil.rmtree(download_dir) - return completed - -def get_settings(log_file_handle, parent_dir): - #return the settings file parsed into a dict - try: - settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) - #this happens when the path to settings file happens on the command line - #we get a full path and don't need to munge it - if not os.path.exists(settings_file): - settings_file = parent_dir - settings = llsd.parse((open(settings_file)).read()) - except llsd.LLSDParseError as lpe: - silent_write(log_file_handle, "Could not parse settings file %s" % lpe) - return None - return settings - -def get_log_file_handle(parent_dir, filename = None): - #return a write handle on the log file - #plus log rotation and not dying on failure - if not filename: - return None - log_file = os.path.join(parent_dir, filename) - old_file = log_file + '.old' - #if someone's log files are present but not writable, they've screwed up their install. - if os.access(log_file, os.W_OK): - if os.access(old_file, os.W_OK): - os.unlink(old_file) - os.rename(log_file, old_file) - elif not os.path.exists(log_file): - #reimplement TOUCH(1) in Python - #perms default to 644 which is fine - open(log_file, 'w+').close() - try: - f = open(log_file,'w+') - except Exception as e: - #we don't have a log file to write to, make a best effort and sally onward - print "Could not open update manager log file %s" % log_file - f = None - return f - -def make_VVM_UUID_hash(platform_key): - #NOTE: There is no python library support for a persistent machine specific UUID - # AND all three platforms do this a different way, so exec'ing out is really the best we can do - #Lastly, this is a best effort service. If we fail, we should still carry on with the update - uuid = None - if (platform_key == 'lnx'): - uuid = subprocess.check_output(['/usr/bin/hostid']).rstrip() - elif (platform_key == 'mac'): - #this is absurdly baroque - #/usr/sbin/system_profiler SPHardwareDataType | fgrep 'Serial' | awk '{print $NF}' - uuid = subprocess.check_output(["/usr/sbin/system_profiler", "SPHardwareDataType"]) - #findall[0] does the grep for the value we are looking for: "Serial Number (system): XXXXXXXX" - #split(:)[1] gets us the XXXXXXX part - #lstrip shaves off the leading space that was after the colon - uuid = re.split(":", re.findall('Serial Number \(system\): \S*', uuid)[0])[1].lstrip() - elif (platform_key == 'win'): - # wmic csproduct get UUID | grep -v UUID - uuid = subprocess.check_output(['wmic','csproduct','get','UUID']) - #outputs in two rows: - #UUID - #XXXXXXX-XXXX... - uuid = re.split('\n',uuid)[1].rstrip() - if uuid is not None: - return hashlib.md5(uuid).hexdigest() - else: - #fake it - return hashlib.md5(str(uuid.uuid1())).hexdigest() - -def query_vvm(log_file_handle = None, platform_key = None, settings = None, summary_dict = None, UpdaterServiceURL = None, UpdaterWillingToTest = None): - result_data = None - baseURI = None - #URI template /update/v1.1/channelname/version/platformkey/platformversion/willing-to-test/uniqueid - #https://wiki.lindenlab.com/wiki/Viewer_Version_Manager_REST_API#Viewer_Update_Query - #note that the only two valid options are: - # # version-phx0.damballah.lindenlab.com - # # version-qa.secondlife-staging.com - print "updater service host: " + repr(UpdaterServiceURL) - if UpdaterServiceURL: - #we can't really expect the users to put the protocol or base dir on, they will give us a host - base_URI = urljoin('https://' + UpdaterServiceURL[0], '/update/') - else: - base_URI = 'https://update.secondlife.com/update/' - channelname = summary_dict['Channel'] - #this is kind of a mess because the settings value is a) in a map and b) is both the cohort and the version in one string - version = summary_dict['Version'] - #we need to use the dotted versions of the platform versions in order to be compatible with VVM rules and arithmetic - if platform_key == 'win': - platform_version = platform.win32_ver()[1] - elif platform_key == 'mac': - platform_version = platform.mac_ver()[0] - else: - platform_version = platform.release() - #this will always return something usable, error handling in method - UUID = str(make_VVM_UUID_hash(platform_key)) - #note that this will not normally be in a settings.xml file and is only here for test builds. - #for test builds, add this key to the ../user_settings/settings.xml - """ - test - - Comment - Tell update manager you aren't willing to test. - Type - String - Value - testno - - - """ - if UpdaterWillingToTest is not None: - if UpdaterWillingToTest: - test_ok = 'testok' - else: - test_ok = 'testno' - else: - try: - test_ok = settings['test']['Value'] - except KeyError: - #normal case, no testing key - test_ok = 'testok' - #because urljoin can't be arsed to take multiple elements - #channelname is a list because although it is only one string, it is a kind of argument and viewer args can take multiple keywords. - query_string = urllib.quote('v1.0/' + channelname[0] + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID) - silent_write(log_file_handle, "About to query VVM: %s" % base_URI + query_string) - VVMService = llrest.SimpleRESTService(name='VVM', baseurl=base_URI) - try: - result_data = VVMService.get(query_string) - except RESTError as re: - silent_write(log_file_handle, "Failed to query VVM using %s failed as %s" % (urljoin(base_URI,query_string), re)) - return None - return result_data - -def download(url = None, version = None, download_dir = None, size = 0, background = False, chunk_size = None, log_file_handle = None): - download_tries = 0 - download_success = False - if not chunk_size: - chunk_size = 1024 - #for background execution - path_to_downloader = os.path.join(os.path.dirname(os.path.realpath(__file__)), "download_update.py") - #three strikes and you're out - while download_tries < 3 and not download_success: - #323: Check for a partial update of the required update; in either event, display an alert that a download is required, initiate the download, and then install and launch - if download_tries == 0: - after_frame(message = "Downloading new version " + version + " Please wait.", timeout = 5000) - else: - after_frame(message = "Trying again to download new version " + version + " Please wait.", timeout = 5000) - if not background: - try: - download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True, chunk_size = chunk_size) - download_success = True - except Exception, e: - download_tries += 1 - silent_write(log_file_handle, "Failed to download new version " + version + ". Trying again.") - silent_write(log_file_handle, "Logging download exception: %s" % e.message) - else: - try: - #Python does not have a facility to multithread a method, so we make the method a standalone - #and subprocess that - subprocess.call(path_to_downloader, "--url = %s --dir = %s --pb --size = %s --chunk_size = %s" % (url, download_dir, size, chunk_size)) - download_success = True - except: - download_tries += 1 - silent_write(log_file_handle, "Failed to download new version " + version + ". Trying again.") - if not download_success: - silent_write(log_file_handle, "Failed to download new version " + version) - after_frame(message = "Failed to download new version " + version + " Please check connectivity.") - return False - return True - -def install(platform_key = None, download_dir = None, log_file_handle = None, in_place = None, downloaded = None): - #user said no to this one - if downloaded != 'skip': - after_frame(message = "New version downloaded. Installing now, please wait.") - success = apply_update.apply_update(download_dir, platform_key, log_file_handle, in_place) - version = download_dir.split('/')[-1] - if success: - silent_write(log_file_handle, "successfully updated to " + version) - shutil.rmtree(download_dir) - #this is either True for in place or the path to the new install for not in place - return success - else: - after_frame(message = "Failed to apply " + version) - silent_write(log_file_handle, "Failed to update viewer to " + version) - return False - -def download_and_install(downloaded = None, url = None, version = None, download_dir = None, size = None, - platform_key = None, log_file_handle = None, in_place = None, chunk_size = 1024): - #extracted to a method because we do it twice in update_manager() and this makes the logic clearer - if not downloaded: - #do the download, exit if we fail - if not download(url = url, version = version, download_dir = download_dir, size = size, chunk_size = chunk_size, log_file_handle = log_file_handle): - return (False, 'download', version) - #do the install - path_to_new_launcher = install(platform_key = platform_key, download_dir = download_dir, - log_file_handle = log_file_handle, in_place = in_place, downloaded = downloaded) - if path_to_new_launcher: - #if we succeed, propagate the success type upwards - if in_place: - return (True, 'in place', True) - else: - return (True, 'in place', path_to_new_launcher) - else: - #propagate failure - return (False, 'apply', version) - -def update_manager(cli_overrides = None): - #cli_overrides is a dict where the keys are specific parameters of interest and the values are the arguments to - #comments that begin with '323:' are steps taken from the algorithm in the description of SL-323. - # Note that in the interest of efficiency, such as determining download success once at the top - # The code does follow precisely the same order as the algorithm. - #return values rather than exit codes. All of them are to communicate with launcher - #we print just before we return so that __main__ outputs something - returns are swallowed - # (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) - # (False, 'download', version): we failed to download the new version - # (False, 'apply', version): we failed to apply the new version - # (True, None, None): No update found - # (True, 'in place, True): update applied in place - # (True, 'in place', path_to_new_launcher): Update applied by a new install to a new location - # (True, 'background', True): background download initiated - - #setup and getting initial parameters - platform_key = get_platform_key() - parent_dir = get_parent_path(platform_key) - log_file_handle = get_log_file_handle(parent_dir, 'update_manager.log') - settings = None - - #check to see if user has install rights - #get the owner of the install and the current user - script_owner_id = os.stat(os.path.realpath(__file__)).st_uid - user_id = os.geteuid() - #if we are on lnx or mac, we can pretty print the IDs as names using the pwd module - #win does not provide this support and Python will throw an ImportError there, so just use raw IDs - if script_owner_id != user_id: - if platform_key != 'win': - import pwd - script_owner_name = pwd.getpwuid(script_owner_id)[0] - username = pwd.getpwuid(user_id)[0] - else: - username = user_id - script_owner_name = script_owner_id - silent_write(log_file_handle, "Upgrade notification attempted by userid " + username) - frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") - frame.binary_choice_message(message = "Second Life was installed by userid " + script_owner_name - + ". Do you have privileges to install?", true = "Yes", false = 'No') - if not frame.choice.get(): - silent_write(log_file_handle, "Upgrade attempt declined by userid " + username) - after_frame(message = "Please find a system admin to upgrade Second Life") - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - if cli_overrides is not None: - if 'settings' in cli_overrides.keys(): - if cli_overrides['settings'] is not None: - settings = get_settings(log_file_handle, cli_overrides['settings'][0]) - else: - settings = get_settings(log_file_handle, parent_dir) - - if settings is None: - silent_write(log_file_handle, "Failed to load viewer settings") - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - #323: If a complete download of that update is found, check the update preference: - #settings['UpdaterServiceSetting'] = 0 is manual install - """ - UpdaterServiceSetting - - Comment - Configure updater service. - Type - U32 - Value - 0 - - """ - if cli_overrides is not None: - if 'set' in cli_overrides.keys(): - if 'UpdaterServiceSetting' in cli_overrides['set'].keys(): - install_automatically = cli_overrides['set']['UpdaterServiceSetting'] - else: - try: - install_automatically = settings['UpdaterServiceSetting']['Value'] - #because, for some godforsaken reason, we delete the setting rather than changing the value - except KeyError: - install_automatically = 1 - - #use default chunk size if none is given - if cli_overrides is not None: - if 'set' in cli_overrides.keys(): - if 'UpdaterMaximumBandwidth' in cli_overrides['set'].keys(): - chunk_size = cli_overrides['set']['UpdaterMaximumBandwidth'] - else: - chunk_size = 1024 - else: - chunk_size = 1024 - - #get channel and version - try: - summary_dict = get_summary(platform_key, os.path.abspath(os.path.realpath(__file__))) - #we send the override to the VVM, but retain the summary.json version for in_place computations - channel_override_summary = deepcopy(summary_dict) - if cli_overrides is not None: - if 'channel' in cli_overrides.keys(): - channel_override_summary['Channel'] = cli_overrides['channel'] - except Exception, e: - silent_write(log_file_handle, "Could not obtain channel and version, exiting.") - silent_write(log_file_handle, e.message) - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - #323: On launch, the Viewer Manager should query the Viewer Version Manager update api. - if cli_overrides is not None: - if 'update-service' in cli_overrides.keys(): - UpdaterServiceURL = cli_overrides['update-service'] - else: - #tells query_vvm to use the default - UpdaterServiceURL = None - else: - UpdaterServiceURL = None - result_data = query_vvm(log_file_handle, platform_key, settings, channel_override_summary, UpdaterServiceURL) - - #nothing to do or error - if not result_data: - silent_write(log_file_handle, "No update found.") - print "Update manager exited with (%s, %s, %s)" % (True, None, None) - return (True, None, None) - - #get download directory, if there are perm issues or similar problems, give up - try: - download_dir = make_download_dir(parent_dir, result_data['version']) - except Exception, e: - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - #if the channel name of the response is the same as the channel we are launched from, the update is "in place" - #and launcher will launch the viewer in this install location. Otherwise, it will launch the Launcher from - #the new location and kill itself. - in_place = (summary_dict['Channel'] == result_data['channel']) - print "summary %s, result %s, in_place %s" % (summary_dict['Channel'], result_data['channel'], in_place) - - #determine if we've tried this download before - downloaded = check_for_completed_download(download_dir) - - #323: If the response indicates that there is a required update: - if result_data['required'] or (not result_data['required'] and install_automatically): - #323: Check for a completed download of the required update; if found, display an alert, install the required update, and launch the newly installed viewer. - #323: If [optional download and] Install Automatically: display an alert, install the update and launch updated viewer. - return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, - size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size) - else: - #323: If the update response indicates that there is an optional update: - #323: Check to see if the optional update has already been downloaded. - #323: If a complete download of that update is found, check the update preference: - #note: automatic install handled above as the steps are the same as required upgrades - #323: If Install Manually: display a message with the update information and ask the user whether or not to install the update with three choices: - #323: Skip this update: create a marker that subsequent launches should not prompt for this update as long as it is optional, - # but leave the download in place so that if it becomes required it will be there. - #323: Install next time: create a marker that skips the prompt and installs on the next launch - #323: Install and launch now: do it. - if downloaded is not None and downloaded != 'skip': - frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") - #The choices are reordered slightly to encourage immediate install and slightly discourage skipping - frame.trinary_message(message = "Please make a selection", - one = "Install new version now.", two = 'Install the next time the viewer is launched.', three = 'Skip this update.') - choice = frame.choice.get() - if choice == 1: - return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, - size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size) - elif choice == 2: - tempfile.mkstmp(suffix = ".next", dir = download_dir) - return (True, None, None) - else: - tempfile.mkstmp(suffix = ".skip", dir = download_dir) - return (True, None, None) - else: - #multithread a download - download(url = result_data['url'], version = result_data['version'], download_dir = download_dir, size = result_data['size'], background = True, log_file_handle = log_file_handle) - print "Update manager exited with (%s, %s, %s)" % (True, 'background', True) - return (True, 'background', True) - - -if __name__ == '__main__': - #there is no argument parsing or other main() work to be done - update_manager() -- cgit v1.2.3