Example #1
0
    def update_via_zip(self, zip_file, interactive=True, test_port=8008, *args, **kwargs):
        if not os.path.exists(zip_file):
            raise CommandError("Zip file doesn't exist")
        if not self.kalite_is_installed():
            raise CommandError("KA Lite not yet installed; cannot update.  Please install KA Lite first, then update.\n")

        sys.stdout.write("Updating via zip file: %s\n" % zip_file)

        # current_dir === base dir for current installation
        self.current_dir = os.path.realpath(settings.PROJECT_PATH + "/../")

        # Prep
        self.print_header()
        self.get_dest_dir(
            zip_name=os.path.splitext(os.path.split(zip_file)[1])[0],
            interactive=interactive
        )
        self.get_move_videos(interactive)

        # Work
        self.extract_files(zip_file)
        self.copy_in_data()
        self.update_local_settings()
        self.move_video_files()

        # Validation & confirmation
        if is_windows():  # In Windows. serverstart is not async
            self.test_server_weak()
        else:
            self.test_server_full(test_port=test_port)
        self.move_to_final(interactive)
        self.start_server()

        self.print_footer()
Example #2
0
    def update_via_zip(self, zip_file, interactive=True, test_port=8008, *args, **kwargs):
        if not os.path.exists(zip_file):
            raise CommandError("Zip file doesn't exist")
        if not self.kalite_is_installed():
            raise CommandError("KA Lite not yet installed; cannot update.  Please install KA Lite first, then update.\n")

        sys.stdout.write("Updating via zip file: %s\n" % zip_file)

        # current_dir === base dir for current installation
        self.current_dir = os.path.realpath(settings.PROJECT_PATH + "/../")

        # Prep
        self.print_header()
        self.get_dest_dir(
            zip_name=os.path.splitext(os.path.split(zip_file)[1])[0],
            interactive=interactive
        )
        self.get_move_videos(interactive)

        # Work
        self.extract_files(zip_file)
        self.copy_in_data()
        self.update_local_settings()
        self.move_video_files()

        # Validation & confirmation
        if is_windows():  # In Windows. serverstart is not async
            self.test_server_weak()
        else:
            self.test_server_full(test_port=test_port)
        self.move_to_final(interactive)
        self.start_server()

        self.print_footer()
Example #3
0
    def handle(self, *args, **options):
        if not options["interactive"]:
            options["username"] = options["username"] or settings.INSTALL_ADMIN_USERNAME or getpass.getuser()
            options["hostname"] = options["hostname"] or get_host_name()

        sys.stdout.write("  _   __  ___    _     _ _        \n")
        sys.stdout.write(" | | / / / _ \  | |   (_) |       \n")
        sys.stdout.write(" | |/ / / /_\ \ | |    _| |_ ___  \n")
        sys.stdout.write(" |    \ |  _  | | |   | | __/ _ \ \n")
        sys.stdout.write(" | |\  \| | | | | |___| | ||  __/ \n")
        sys.stdout.write(" \_| \_/\_| |_/ \_____/_|\__\___| \n")
        sys.stdout.write("                                  \n")
        sys.stdout.write("http://kalite.learningequality.org\n")
        sys.stdout.write("                                  \n")
        sys.stdout.write("         version %s\n" % version.VERSION)
        sys.stdout.write("                                  \n")

        if sys.version_info >= (2,8) or sys.version_info < (2,6):
            raise CommandError("You must have Python version 2.6.x or 2.7.x installed. Your version is: %s\n" % sys.version_info)

        if options["interactive"]:
            sys.stdout.write("--------------------------------------------------------------------------------\n")
            sys.stdout.write("\n")
            sys.stdout.write("This script will configure the database and prepare it for use.\n")
            sys.stdout.write("\n")
            sys.stdout.write("--------------------------------------------------------------------------------\n")
            sys.stdout.write("\n")
            raw_input("Press [enter] to continue...")
            sys.stdout.write("\n")

        # Tried not to be os-specific, but ... hey. :-/
        if not is_windows() and hasattr(os, "getuid") and os.getuid() == 502:
            sys.stdout.write("-------------------------------------------------------------------\n")
            sys.stdout.write("WARNING: You are installing KA-Lite as root user!\n")
            sys.stdout.write("\tInstalling as root may cause some permission problems while running\n")
            sys.stdout.write("\tas a normal user in the future.\n")
            sys.stdout.write("-------------------------------------------------------------------\n")
            if options["interactive"]:
                if not raw_input_yn("Do you wish to continue and install it as root?"):
                    raise CommandError("Aborting script.\n")
                sys.stdout.write("\n")

        # Check to see if the current user is the owner of the install directory
        current_owner = find_owner(BASE_DIR)
        current_user = getpass.getuser()
        if current_owner != current_user:
            raise CommandError("""You are not the owner of this directory!
    Please copy all files to a directory that you own and then
    re-run this script.""")

        if not os.access(BASE_DIR, os.W_OK):
            raise CommandError("You do not have permission to write to this directory!")

        database_file = settings.DATABASES["default"]["NAME"]
        install_clean = True
        if os.path.exists(database_file):
            # We found an existing database file.  By default,
            #   we will upgrade it; users really need to work hard
            #   to delete the file (but it's possible, which is nice).
            sys.stdout.write("-------------------------------------------------------------------\n")
            sys.stdout.write("WARNING: Database file already exists! \n")
            sys.stdout.write("-------------------------------------------------------------------\n")
            if not options["interactive"] \
               or raw_input_yn("Keep database file and upgrade to KA Lite version %s? " % version.VERSION) \
               or not raw_input_yn("Remove database file '%s' now? " % database_file) \
               or not raw_input_yn("WARNING: all data will be lost!  Are you sure? "):
                install_clean = False
                sys.stdout.write("Upgrading database to KA Lite version %s\n" % version.VERSION)

            if install_clean:
                # After all, don't delete--just move.
                sys.stdout.write("OK.  We will run a clean install; \n")
                sys.stdout.write("the database file will be moved to a deletable location.\n")

        # Do all input at once, at the beginning
        if install_clean and options["interactive"]:
            if not options["username"] or not options["password"]:
                sys.stdout.write("\n")
                sys.stdout.write("Please choose a username and password for the admin account on this device.\n")
                sys.stdout.write("\tYou must remember this login information, as you will need\n")
                sys.stdout.write("\tto enter it to administer this installation of KA Lite.\n")
                sys.stdout.write("\n")
            (username, password) = get_username_password(options["username"], options["password"])
            (hostname, description) = get_hostname_and_description(options["hostname"], options["description"])
        else:
            username = options["username"] or settings.INSTALL_ADMIN_USERNAME
            password = options["password"] or settings.INSTALL_ADMIN_PASSWORD
            hostname = options["hostname"]
            description = options["description"]

        if username and not validate_username(username):
            raise CommandError("Username must contain only letters, digits, and underscores, and start with a letter.\n")


        ########################
        # Now do stuff
        ########################

        # Move database file (if exists)
        if install_clean and os.path.exists(database_file):
            # This is an overwrite install; destroy the old db
            dest_file = tempfile.mkstemp()[1]
            sys.stdout.write("(Re)moving database file to temp location, starting clean install.  Recovery location: %s\n" % dest_file)
            shutil.move(database_file, dest_file)

        # Got this far, it's OK to stop the server.
        import serverstop

        # Should clean_pyc for (clean) reinstall purposes
        call_command("clean_pyc", interactive=False, verbosity=options.get("verbosity"))

        # Migrate the database
        call_command("syncdb", interactive=False, verbosity=options.get("verbosity"))
        call_command("migrate", merge=True, verbosity=options.get("verbosity"))

        # Install data
        if install_clean:
            # Create device, load on any zone data
            call_command("generatekeys", verbosity=options.get("verbosity"))
            call_command("initdevice", hostname, description, verbosity=options.get("verbosity"))

        else:
            # Device exists; load data if required.
            #
            # Hackish, as this duplicates code from initdevice.
            #
            if os.path.exists(InitCommand.data_json_file):
                # This is a pathway to install zone-based data on a software upgrade.
                sys.stdout.write("Loading zone data from '%s'\n" % InitCommand.data_json_file)
                load_data_for_offline_install(in_file=InitCommand.data_json_file)

            confirm_or_generate_zone()
            initialize_facility()

        # Create the admin user
        if password:  # blank password (non-interactive) means don't create a superuser
            admin = get_object_or_None(User, username=username)
            if not admin:
                call_command("createsuperuser", username=username, email="*****@*****.**", interactive=False, verbosity=options.get("verbosity"))
                admin = User.objects.get(username=username)
            admin.set_password(password)
            admin.save()


        # Move scripts
        for script_name in ["start", "stop", "run_command"]:
            script_file = script_name + system_script_extension()
            dest_dir = os.path.join(settings.PROJECT_PATH, "..")
            src_dir = os.path.join(dest_dir, "scripts")
            shutil.copyfile(os.path.join(src_dir, script_file), os.path.join(dest_dir, script_file))
            shutil.copystat(os.path.join(src_dir, script_file), os.path.join(dest_dir, script_file))

        start_script_path = os.path.realpath(os.path.join(settings.PROJECT_PATH, "..", "start%s" % system_script_extension()))


        # Run videoscan, on the distributed server.
        if not settings.CENTRAL_SERVER:
            sys.stdout.write("Scanning for video files in the content directory (%s)\n" % settings.CONTENT_ROOT)
            call_command("videoscan")


        # done; notify the user.
        sys.stdout.write("\n")
        if install_clean:
            sys.stdout.write("CONGRATULATIONS! You've finished setting up the KA Lite server software.\n")
            sys.stdout.write("\tPlease run '%s' to start the server,\n" % start_script_path)
            sys.stdout.write("\tthen load 'http://127.0.0.1:%d/' in your browser to complete the device configuration.\n" % settings.user_facing_port())
        else:
            sys.stdout.write("CONGRATULATIONS! You've finished updating the KA Lite server software.\n")
            sys.stdout.write("\tPlease run '%s' to start the server.\n" % start_script_path)
        sys.stdout.write("\n")
    def move_to_final(self, interactive=True):
        """Confirm the move to the new location"""

        # Shut down the old server
        self.update_stage(stage_percent=0.5, notes="Stopping server.")
        stop_cmd = self.get_shell_script("stop*", location=self.current_dir)
        p = subprocess.Popen(stop_cmd, shell=False, cwd=os.path.split(stop_cmd)[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out = p.communicate()
        if out[1]:
            if  "No such process" not in out[1]:
                raise CommandError(out[1])

        # Make sure we know if the current_dir will be changed during process running
        in_place_move = (self.dest_dir == self.current_dir)

        # Double-check if destroying old install
        if in_place_move:
            ans = "" if interactive else "y"
            while ans.lower() not in ["y","n"]:
                ans = raw_input("* Server setup verified; complete by moving to the final destination? [y/n]: ").strip()
            if ans=="n":
                sys.stdout.write("**** Aborting update; new server (with content) can be found at %s\n" % self.working_dir)
                exit()

        # OK, don't actually kill it--just move it
        if os.path.exists(self.dest_dir):
            try:
                if is_windows() and in_place_move:
                    # We know this will fail, so rather than get in an intermediate state,
                    #   just move right to the compensatory mechanism.
                    raise Exception("Windows sucks.")

                tempdir = tempfile.mkdtemp()

                sys.stdout.write("* Moving old directory to a temporary location...\n")
                shutil.move(self.dest_dir, tempdir)

                # Move to the final destination
                sys.stdout.write("* Moving new installation to final position.\n")
                shutil.move(self.working_dir, self.dest_dir)

                if in_place_move:
                    self.current_dir = os.path.realpath(tempdir)


            except Exception as e:
                if unicode(e) == "Windows sucks.":
                    # We expect this error for Windows (sometimes, see above).
                    sys.stdout.write("Copying contents from temp directory to original directory.\n")
                else:

                    # If the move above fails, then we are in trouble.
                    #   The only way to try and save our asses is to
                    #   move each file from the new installation to the dest location,
                    #   one by one.
                    sys.stdout.write("***** Warning: failed to move: %s to %s:\n" % (self.dest_dir, tempdir))
                    sys.stdout.write("*****        '%s'\n" % e)
                    sys.stdout.write("***** Trying to copy contents into dest_dir\n")

                copy_success = 0  # count the # of files successfully moved over
                for root, dirs, files in os.walk(self.working_dir):
                    # Turn root into a relative path
                    assert root.startswith(self.working_dir), "Root from os.walk should be an absolute path."
                    relpath = root[len(self.working_dir)+1:]

                    # Loop over all directories to create destination directories
                    for d in dirs:
                        drelpath = os.path.join(relpath, d)
                        dabspath = os.path.join(self.dest_dir, drelpath)
                        if not os.path.exists(dabspath):
                            logging.debug("Created directory %s\n" % (dabspath))
                            os.mkdir(dabspath)

                    # Loop over all files, to move them over.
                    for f in files:
                        try:
                            frelpath = os.path.join(relpath, f)
                            shutil.copyfile(
                                os.path.join(self.working_dir, frelpath),
                                os.path.join(self.dest_dir, frelpath),
                            )
                            copy_success += 1
                        except:
                            sys.stderr.write("**** failed to copy %s\n" % os.path.join(self.working_dir, frelpath))
                sys.stdout.write("* Successfully copied %d files into final directory\n" % copy_success)
Example #5
0
    def move_to_final(self, interactive=True):
        """Confirm the move to the new location"""

        # Shut down the old server
        self.update_stage(stage_percent=0.5, notes="Stopping server.")
        stop_cmd = self.get_shell_script("stop*", location=self.current_dir)
        p = subprocess.Popen(stop_cmd,
                             shell=False,
                             cwd=os.path.split(stop_cmd)[0],
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        out = p.communicate()
        if out[1]:
            if "No such process" not in out[1]:
                raise CommandError(out[1])

        # Make sure we know if the current_dir will be changed during process running
        in_place_move = (self.dest_dir == self.current_dir)

        # Double-check if destroying old install
        if in_place_move:
            ans = "" if interactive else "y"
            while ans.lower() not in ["y", "n"]:
                ans = raw_input(
                    "* Server setup verified; complete by moving to the final destination? [y/n]: "
                ).strip()
            if ans == "n":
                sys.stdout.write(
                    "**** Aborting update; new server (with content) can be found at %s\n"
                    % self.working_dir)
                exit()

        # OK, don't actually kill it--just move it
        if os.path.exists(self.dest_dir):
            try:
                if is_windows() and in_place_move:
                    # We know this will fail, so rather than get in an intermediate state,
                    #   just move right to the compensatory mechanism.
                    raise Exception("Windows sucks.")

                tempdir = tempfile.mkdtemp()

                sys.stdout.write(
                    "* Moving old directory to a temporary location...\n")
                shutil.move(self.dest_dir, tempdir)

                # Move to the final destination
                sys.stdout.write(
                    "* Moving new installation to final position.\n")
                shutil.move(self.working_dir, self.dest_dir)

                if in_place_move:
                    self.current_dir = os.path.realpath(tempdir)

            except Exception as e:
                if unicode(e) == "Windows sucks.":
                    # We expect this error for Windows (sometimes, see above).
                    sys.stdout.write(
                        "Copying contents from temp directory to original directory.\n"
                    )
                else:

                    # If the move above fails, then we are in trouble.
                    #   The only way to try and save our asses is to
                    #   move each file from the new installation to the dest location,
                    #   one by one.
                    sys.stdout.write(
                        "***** Warning: failed to move: %s to %s:\n" %
                        (self.dest_dir, tempdir))
                    sys.stdout.write("*****        '%s'\n" % e)
                    sys.stdout.write(
                        "***** Trying to copy contents into dest_dir\n")

                copy_success = 0  # count the # of files successfully moved over
                for root, dirs, files in os.walk(self.working_dir):
                    # Turn root into a relative path
                    assert root.startswith(
                        self.working_dir
                    ), "Root from os.walk should be an absolute path."
                    relpath = root[len(self.working_dir) + 1:]

                    # Loop over all directories to create destination directories
                    for d in dirs:
                        drelpath = os.path.join(relpath, d)
                        dabspath = os.path.join(self.dest_dir, drelpath)
                        if not os.path.exists(dabspath):
                            logging.debug("Created directory %s\n" %
                                          (dabspath))
                            os.mkdir(dabspath)

                    # Loop over all files, to move them over.
                    for f in files:
                        try:
                            frelpath = os.path.join(relpath, f)
                            shutil.copyfile(
                                os.path.join(self.working_dir, frelpath),
                                os.path.join(self.dest_dir, frelpath),
                            )
                            copy_success += 1
                        except:
                            sys.stderr.write(
                                "**** failed to copy %s\n" %
                                os.path.join(self.working_dir, frelpath))
                sys.stdout.write(
                    "* Successfully copied %d files into final directory\n" %
                    copy_success)
Example #6
0
File: setup.py Project: louhow/lex
    def handle(self, *args, **options):
        if not options["interactive"]:
            options["username"] = options[
                "username"] or settings.INSTALL_ADMIN_USERNAME or getpass.getuser(
                )
            options["hostname"] = options["hostname"] or get_host_name()

        sys.stdout.write("  _   __  ___    _     _ _        \n")
        sys.stdout.write(" | | / / / _ \  | |   (_) |       \n")
        sys.stdout.write(" | |/ / / /_\ \ | |    _| |_ ___  \n")
        sys.stdout.write(" |    \ |  _  | | |   | | __/ _ \ \n")
        sys.stdout.write(" | |\  \| | | | | |___| | ||  __/ \n")
        sys.stdout.write(" \_| \_/\_| |_/ \_____/_|\__\___| \n")
        sys.stdout.write("                                  \n")
        sys.stdout.write("http://kalite.learningequality.org\n")
        sys.stdout.write("                                  \n")
        sys.stdout.write("         version %s\n" % version.VERSION)
        sys.stdout.write("                                  \n")

        if sys.version_info >= (2, 8) or sys.version_info < (2, 6):
            raise CommandError(
                "You must have Python version 2.6.x or 2.7.x installed. Your version is: %s\n"
                % sys.version_info)

        if options["interactive"]:
            sys.stdout.write(
                "--------------------------------------------------------------------------------\n"
            )
            sys.stdout.write("\n")
            sys.stdout.write(
                "This script will configure the database and prepare it for use.\n"
            )
            sys.stdout.write("\n")
            sys.stdout.write(
                "--------------------------------------------------------------------------------\n"
            )
            sys.stdout.write("\n")
            raw_input("Press [enter] to continue...")
            sys.stdout.write("\n")

        # Tried not to be os-specific, but ... hey. :-/
        if not is_windows() and hasattr(os, "getuid") and os.getuid() == 502:
            sys.stdout.write(
                "-------------------------------------------------------------------\n"
            )
            sys.stdout.write(
                "WARNING: You are installing KA-Lite as root user!\n")
            sys.stdout.write(
                "\tInstalling as root may cause some permission problems while running\n"
            )
            sys.stdout.write("\tas a normal user in the future.\n")
            sys.stdout.write(
                "-------------------------------------------------------------------\n"
            )
            if options["interactive"]:
                if not raw_input_yn(
                        "Do you wish to continue and install it as root?"):
                    raise CommandError("Aborting script.\n")
                sys.stdout.write("\n")

        # Check to see if the current user is the owner of the install directory
        current_owner = find_owner(BASE_DIR)
        current_user = getpass.getuser()
        if current_owner != current_user:
            raise CommandError("""You are not the owner of this directory!
    Please copy all files to a directory that you own and then
    re-run this script.""")

        if not os.access(BASE_DIR, os.W_OK):
            raise CommandError(
                "You do not have permission to write to this directory!")

        database_file = settings.DATABASES["default"]["NAME"]
        install_clean = True
        if os.path.exists(database_file):
            # We found an existing database file.  By default,
            #   we will upgrade it; users really need to work hard
            #   to delete the file (but it's possible, which is nice).
            sys.stdout.write(
                "-------------------------------------------------------------------\n"
            )
            sys.stdout.write("WARNING: Database file already exists! \n")
            sys.stdout.write(
                "-------------------------------------------------------------------\n"
            )
            if not options["interactive"] \
               or raw_input_yn("Keep database file and upgrade to KA Lite version %s? " % version.VERSION) \
               or not raw_input_yn("Remove database file '%s' now? " % database_file) \
               or not raw_input_yn("WARNING: all data will be lost!  Are you sure? "):
                install_clean = False
                sys.stdout.write("Upgrading database to KA Lite version %s\n" %
                                 version.VERSION)

            if install_clean:
                # After all, don't delete--just move.
                sys.stdout.write("OK.  We will run a clean install; \n")
                sys.stdout.write(
                    "the database file will be moved to a deletable location.\n"
                )

        # Do all input at once, at the beginning
        if install_clean and options["interactive"]:
            if not options["username"] or not options["password"]:
                sys.stdout.write("\n")
                sys.stdout.write(
                    "Please choose a username and password for the admin account on this device.\n"
                )
                sys.stdout.write(
                    "\tYou must remember this login information, as you will need\n"
                )
                sys.stdout.write(
                    "\tto enter it to administer this installation of KA Lite.\n"
                )
                sys.stdout.write("\n")
            (username,
             password) = get_username_password(options["username"],
                                               options["password"])
            (hostname, description) = get_hostname_and_description(
                options["hostname"], options["description"])
        else:
            username = options["username"] or settings.INSTALL_ADMIN_USERNAME
            password = options["password"] or settings.INSTALL_ADMIN_PASSWORD
            hostname = options["hostname"]
            description = options["description"]

        if username and not validate_username(username):
            raise CommandError(
                "Username must contain only letters, digits, and underscores, and start with a letter.\n"
            )

        ########################
        # Now do stuff
        ########################

        # Move database file (if exists)
        if install_clean and os.path.exists(database_file):
            # This is an overwrite install; destroy the old db
            dest_file = tempfile.mkstemp()[1]
            sys.stdout.write(
                "(Re)moving database file to temp location, starting clean install.  Recovery location: %s\n"
                % dest_file)
            shutil.move(database_file, dest_file)

        # Got this far, it's OK to stop the server.
        import serverstop

        # Should clean_pyc for (clean) reinstall purposes
        call_command("clean_pyc",
                     interactive=False,
                     verbosity=options.get("verbosity"))

        # Migrate the database
        call_command("syncdb",
                     interactive=False,
                     verbosity=options.get("verbosity"))
        call_command("migrate", merge=True, verbosity=options.get("verbosity"))

        # Install data
        if install_clean:
            # Create device, load on any zone data
            call_command("generatekeys", verbosity=options.get("verbosity"))
            call_command("initdevice",
                         hostname,
                         description,
                         verbosity=options.get("verbosity"))

        else:
            # Device exists; load data if required.
            #
            # Hackish, as this duplicates code from initdevice.
            #
            if os.path.exists(InitCommand.data_json_file):
                # This is a pathway to install zone-based data on a software upgrade.
                sys.stdout.write("Loading zone data from '%s'\n" %
                                 InitCommand.data_json_file)
                load_data_for_offline_install(
                    in_file=InitCommand.data_json_file)

            confirm_or_generate_zone()
            initialize_facility()

        # Create the admin user
        if password:  # blank password (non-interactive) means don't create a superuser
            admin = get_object_or_None(User, username=username)
            if not admin:
                call_command("createsuperuser",
                             username=username,
                             email="*****@*****.**",
                             interactive=False,
                             verbosity=options.get("verbosity"))
                admin = User.objects.get(username=username)
            admin.set_password(password)
            admin.save()

        # Move scripts
        for script_name in ["start", "stop", "run_command"]:
            script_file = script_name + system_script_extension()
            dest_dir = os.path.join(settings.PROJECT_PATH, "..")
            src_dir = os.path.join(dest_dir, "scripts")
            shutil.copyfile(os.path.join(src_dir, script_file),
                            os.path.join(dest_dir, script_file))
            shutil.copystat(os.path.join(src_dir, script_file),
                            os.path.join(dest_dir, script_file))

        start_script_path = os.path.realpath(
            os.path.join(settings.PROJECT_PATH, "..",
                         "start%s" % system_script_extension()))

        # Run videoscan, on the distributed server.
        if not settings.CENTRAL_SERVER:
            sys.stdout.write(
                "Scanning for video files in the content directory (%s)\n" %
                settings.CONTENT_ROOT)
            call_command("videoscan")

        # done; notify the user.
        sys.stdout.write("\n")
        if install_clean:
            sys.stdout.write(
                "CONGRATULATIONS! You've finished setting up the KA Lite server software.\n"
            )
            sys.stdout.write("\tPlease run '%s' to start the server,\n" %
                             start_script_path)
            sys.stdout.write(
                "\tthen load 'http://127.0.0.1:%d/' in your browser to complete the device configuration.\n"
                % settings.user_facing_port())
        else:
            sys.stdout.write(
                "CONGRATULATIONS! You've finished updating the KA Lite server software.\n"
            )
            sys.stdout.write("\tPlease run '%s' to start the server.\n" %
                             start_script_path)
        sys.stdout.write("\n")