Пример #1
0
	def get_state(self):
		backups = self._get_backups()
		unknown_plugins = self._get_unknown_plugins()
		return flask.jsonify(backups=backups,
		                     backup_in_progress=len(self._in_progress) > 0,
		                     unknown_plugins=unknown_plugins,
		                     restore_supported=is_os_compatible(["!windows"]))
Пример #2
0
 def get_state(self):
     backups = self._get_backups()
     unknown_plugins = self._get_unknown_plugins()
     return flask.jsonify(backups=backups,
                          backup_in_progress=len(self._in_progress) > 0,
                          unknown_plugins=unknown_plugins,
                          restore_supported=is_os_compatible(["!windows"]))
Пример #3
0
			def on_install_plugins(plugins):
				if not plugins:
					return

				force_user = settings.global_get_boolean(["plugins", "pluginmanager", "pip_force_user"])
				pip_args = settings.global_get(["plugins", "pluginmanager", "pip_args"])

				def log(line):
					click.echo(u"\t{}".format(line))

				for plugin in plugins:
					octoprint_compatible = is_octoprint_compatible(*plugin["compatibility"]["octoprint"])
					os_compatible = is_os_compatible(plugin["compatibility"]["os"])
					compatible = octoprint_compatible and os_compatible
					if not compatible:
						if not octoprint_compatible and not os_compatible:
							click.echo(u"Cannot install plugin {}, it is incompatible to this version of "
							           u"OctoPrint and the underlying operating system".format(plugin["id"]))
						elif not octoprint_compatible:
							click.echo(u"Cannot install plugin {}, it is incompatible to this version of "
							           u"OctoPrint".format(plugin["id"]))
						elif not os_compatible:
							click.echo(u"Cannot install plugin {}, it is incompatible to the underlying "
							           u"operating system".format(plugin["id"]))
						continue

					click.echo(u"Installing plugin {}".format(plugin["id"]))
					self.__class__._install_plugin(plugin,
					                               force_user=force_user,
					                               pip_args=pip_args,
					                               on_log=log)
Пример #4
0
			def on_install_plugins(plugins):
				if not plugins:
					return

				force_user = settings.global_get_boolean(["plugins", "pluginmanager", "pip_force_user"])
				pip_args = settings.global_get(["plugins", "pluginmanager", "pip_args"])

				def log(line):
					click.echo("\t{}".format(line))

				for plugin in plugins:
					octoprint_compatible = is_octoprint_compatible(*plugin["compatibility"]["octoprint"])
					os_compatible = is_os_compatible(plugin["compatibility"]["os"])
					compatible = octoprint_compatible and os_compatible
					if not compatible:
						if not octoprint_compatible and not os_compatible:
							click.echo("Cannot install plugin {}, it is incompatible to this version of "
							           "OctoPrint and the underlying operating system".format(plugin["id"]))
						elif not octoprint_compatible:
							click.echo("Cannot install plugin {}, it is incompatible to this version of "
							           "OctoPrint".format(plugin["id"]))
						elif not os_compatible:
							click.echo("Cannot install plugin {}, it is incompatible to the underlying "
							           "operating system".format(plugin["id"]))
						continue

					click.echo("Installing plugin {}".format(plugin["id"]))
					self.__class__._install_plugin(plugin,
					                               force_user=force_user,
					                               pip_args=pip_args,
					                               on_log=log)
Пример #5
0
		def on_install_plugins(plugins):
			force_user = self._settings.global_get_boolean(["plugins", "pluginmanager", "pip_force_user"])
			pip_args = self._settings.global_get(["plugins", "pluginmanager", "pip_args"])

			def on_log(line):
				self._logger.info(line)
				self._send_client_message("logline", dict(line=line, type="stdout"))

			for plugin in plugins:
				octoprint_compatible = is_octoprint_compatible(*plugin["compatibility"]["octoprint"])
				os_compatible = is_os_compatible(plugin["compatibility"]["os"])
				compatible = octoprint_compatible and os_compatible
				if not compatible:
					if not octoprint_compatible and not os_compatible:
						self._logger.warn(u"Cannot install plugin {}, it is incompatible to this version "
						                  u"of OctoPrint and the underlying operating system".format(plugin["id"]))
					elif not octoprint_compatible:
						self._logger.warn(u"Cannot install plugin {}, it is incompatible to this version "
						                  u"of OctoPrint".format(plugin["id"]))
					elif not os_compatible:
						self._logger.warn(u"Cannot install plugin {}, it is incompatible to the underlying "
						                  u"operating system".format(plugin["id"]))
					self._send_client_message("plugin_incompatible", dict(plugin=plugin["id"],
					                                                      octoprint_compatible=octoprint_compatible,
					                                                      os_compatible=os_compatible))
					continue

				self._logger.info(u"Installing plugin {}".format(plugin["id"]))
				self._send_client_message("installing_plugin", dict(plugin=plugin["id"]))
				self.__class__._install_plugin(plugin,
				                               force_user=force_user,
				                               pip_args=pip_args,
				                               on_log=on_log)
Пример #6
0
		def on_install_plugins(plugins):
			force_user = self._settings.global_get_boolean(["plugins", "pluginmanager", "pip_force_user"])
			pip_args = self._settings.global_get(["plugins", "pluginmanager", "pip_args"])

			def on_log(line):
				self._logger.info(line)
				self._send_client_message("logline", dict(line=line, type="stdout"))

			for plugin in plugins:
				octoprint_compatible = is_octoprint_compatible(*plugin["compatibility"]["octoprint"])
				os_compatible = is_os_compatible(plugin["compatibility"]["os"])
				compatible = octoprint_compatible and os_compatible
				if not compatible:
					if not octoprint_compatible and not os_compatible:
						self._logger.warn("Cannot install plugin {}, it is incompatible to this version "
						                  "of OctoPrint and the underlying operating system".format(plugin["id"]))
					elif not octoprint_compatible:
						self._logger.warn("Cannot install plugin {}, it is incompatible to this version "
						                  "of OctoPrint".format(plugin["id"]))
					elif not os_compatible:
						self._logger.warn("Cannot install plugin {}, it is incompatible to the underlying "
						                  "operating system".format(plugin["id"]))
					self._send_client_message("plugin_incompatible", dict(plugin=plugin["id"],
					                                                      octoprint_compatible=octoprint_compatible,
					                                                      os_compatible=os_compatible))
					continue

				self._logger.info("Installing plugin {}".format(plugin["id"]))
				self._send_client_message("installing_plugin", dict(plugin=plugin["id"]))
				self.__class__._install_plugin(plugin,
				                               force_user=force_user,
				                               pip_args=pip_args,
				                               on_log=on_log)
Пример #7
0
    def test_is_os_compatible(self, current_os, sys_platform, entries,
                              expected):
        with mock.patch("sys.platform", sys_platform):
            from octoprint.util.platform import is_os_compatible

            actual = is_os_compatible(entries, current_os=current_os)
            self.assertEqual(actual, expected)
Пример #8
0
def map_repository_entry(entry):
    if not isinstance(entry, dict):
        return None

    result = copy.deepcopy(entry)

    if not "follow_dependency_links" in result:
        result["follow_dependency_links"] = False

    result["is_compatible"] = dict(octoprint=True, os=True)

    if "compatibility" in entry:
        if "octoprint" in entry["compatibility"] and entry["compatibility"][
                "octoprint"] is not None and isinstance(
                    entry["compatibility"]["octoprint"],
                    (list, tuple)) and len(
                        entry["compatibility"]["octoprint"]):
            result["is_compatible"]["octoprint"] = is_octoprint_compatible(
                *entry["compatibility"]["octoprint"])

        if "os" in entry["compatibility"] and entry[
                "compatibility"]["os"] is not None and isinstance(
                    entry["compatibility"]["os"],
                    (list, tuple)) and len(entry["compatibility"]["os"]):
            result["is_compatible"]["os"] = is_os_compatible(
                entry["compatibility"]["os"])

    return result
Пример #9
0
def map_repository_entry(entry):
	result = copy.deepcopy(entry)

	if not "follow_dependency_links" in result:
		result["follow_dependency_links"] = False

	result["is_compatible"] = dict(
		octoprint=True,
		os=True
	)

	if "compatibility" in entry:
		if "octoprint" in entry["compatibility"] and entry["compatibility"]["octoprint"] is not None and isinstance(
			entry["compatibility"]["octoprint"], (list, tuple)) and len(entry["compatibility"]["octoprint"]):
			result["is_compatible"]["octoprint"] = is_octoprint_compatible(*entry["compatibility"]["octoprint"])

		if "os" in entry["compatibility"] and entry["compatibility"]["os"] is not None and isinstance(
			entry["compatibility"]["os"], (list, tuple)) and len(entry["compatibility"]["os"]):
			result["is_compatible"]["os"] = is_os_compatible(entry["compatibility"]["os"])

	return result
Пример #10
0
	def test_is_os_compatible(self, current_os, sys_platform, entries, expected):
		with mock.patch("sys.platform", sys_platform):
			from octoprint.util.platform import is_os_compatible
			actual = is_os_compatible(entries, current_os=current_os)
			self.assertEqual(actual, expected)
Пример #11
0
    def _restore_backup(cls,
                        path,
                        settings=None,
                        plugin_manager=None,
                        datafolder=None,
                        on_install_plugins=None,
                        on_report_unknown_plugins=None,
                        on_invalid_backup=None,
                        on_log_progress=None,
                        on_log_error=None,
                        on_restore_start=None,
                        on_restore_done=None,
                        on_restore_failed=None):
        if not is_os_compatible(["!windows"]):
            if callable(on_log_error):
                on_log_error(
                    u"Restore is not supported on this operating system")
            if callable(on_restore_failed):
                on_restore_failed(path)
            return False

        restart_command = settings.global_get(
            ["server", "commands", "serverRestartCommand"])

        basedir = settings._basedir
        cls._clean_dir_backup(basedir, on_log_progress=on_log_progress)

        plugin_repo = dict()
        repo_url = settings.global_get(
            ["plugins", "pluginmanager", "repository"])
        if repo_url:
            plugin_repo = cls._get_plugin_repository_data(repo_url)

        if callable(on_restore_start):
            on_restore_start(path)

        try:

            with zipfile.ZipFile(path, "r") as zip:
                # read metadata
                try:
                    metadata_zipinfo = zip.getinfo("metadata.json")
                except KeyError:
                    if callable(on_invalid_backup):
                        on_invalid_backup(
                            u"Not an OctoPrint backup, lacks metadata.json")
                    if callable(on_restore_failed):
                        on_restore_failed(path)
                    return False

                metadata_bytes = zip.read(metadata_zipinfo)
                metadata = json.loads(metadata_bytes)

                backup_version = get_comparable_version(metadata["version"],
                                                        base=True)
                if backup_version > get_octoprint_version(base=True):
                    if callable(on_invalid_backup):
                        on_invalid_backup(
                            u"Backup is from a newer version of OctoPrint and cannot be applied"
                        )
                    if callable(on_restore_failed):
                        on_restore_failed(path)
                    return False

                # unzip to temporary folder
                temp = tempfile.mkdtemp()
                try:
                    if callable(on_log_progress):
                        on_log_progress(
                            u"Unpacking backup to {}...".format(temp))
                    abstemp = os.path.abspath(temp)
                    for member in zip.infolist():
                        abspath = os.path.abspath(
                            os.path.join(temp, member.filename))
                        if abspath.startswith(abstemp):
                            zip.extract(member, temp)

                    # sanity check
                    configfile = os.path.join(temp, "basedir", "config.yaml")
                    if not os.path.exists(configfile):
                        if callable(on_invalid_backup):
                            on_invalid_backup(u"Backup lacks config.yaml")
                        if callable(on_restore_failed):
                            on_restore_failed(path)
                        return False

                    import yaml

                    with codecs.open(configfile) as f:
                        configdata = yaml.safe_load(f)

                    if configdata.get("accessControl",
                                      dict()).get("enabled", True):
                        userfile = os.path.join(temp, "basedir", "users.yaml")
                        if not os.path.exists(userfile):
                            if callable(on_invalid_backup):
                                on_invalid_backup(u"Backup lacks users.yaml")
                            if callable(on_restore_failed):
                                on_restore_failed(path)
                            return False

                    if callable(on_log_progress):
                        on_log_progress(u"Unpacked")

                    # install available plugins
                    plugins = []
                    plugin_list_file = os.path.join(temp, "plugin_list.json")
                    if os.path.exists(plugin_list_file):
                        with codecs.open(plugin_list_file, "r") as f:
                            plugins = json.load(f)

                    known_plugins = []
                    unknown_plugins = []
                    if plugins:
                        if plugin_repo:
                            for plugin in plugins:
                                if plugin["key"] in plugin_manager.plugins:
                                    # already installed
                                    continue

                                if plugin["key"] in plugin_repo:
                                    # not installed, can be installed from repository url
                                    known_plugins.append(
                                        plugin_repo[plugin["key"]])
                                else:
                                    # not installed, not installable
                                    unknown_plugins.append(plugin)

                        else:
                            # no repo, all plugins are not installable
                            unknown_plugins = plugins

                        if callable(on_log_progress):
                            if known_plugins:
                                on_log_progress(
                                    u"Known and installable plugins: {}".
                                    format(u", ".join(
                                        map(lambda x: x["id"],
                                            known_plugins))))
                            if unknown_plugins:
                                on_log_progress(u"Unknown plugins: {}".format(
                                    u", ".join(
                                        map(lambda x: x["key"],
                                            unknown_plugins))))

                        if callable(on_install_plugins):
                            on_install_plugins(known_plugins)

                        if callable(on_report_unknown_plugins):
                            on_report_unknown_plugins(unknown_plugins)

                    # move config data
                    basedir_backup = basedir + ".bck"
                    basedir_extracted = os.path.join(temp, "basedir")

                    if callable(on_log_progress):
                        on_log_progress(u"Renaming {} to {}...".format(
                            basedir, basedir_backup))
                    shutil.move(basedir, basedir_backup)

                    try:
                        if callable(on_log_progress):
                            on_log_progress(u"Moving {} to {}...".format(
                                basedir_extracted, basedir))
                        shutil.move(basedir_extracted, basedir)
                    except:
                        if callable(on_log_error):
                            on_log_error(u"Error while restoring config data",
                                         exc_info=sys.exc_info())
                            on_log_error(u"Rolling back old config data")

                        shutil.move(basedir_backup, basedir)

                        if callable(on_restore_failed):
                            on_restore_failed(path)
                        return False

                    if unknown_plugins:
                        if callable(on_log_progress):
                            on_log_progress(
                                u"Writing info file about unknown plugins")

                        if not os.path.isdir(datafolder):
                            os.makedirs(datafolder)

                        unknown_plugins_path = os.path.join(
                            datafolder, UNKNOWN_PLUGINS_FILE)
                        try:
                            with codecs.open(unknown_plugins_path,
                                             mode="w",
                                             encoding="utf-8") as f:
                                json.dump(unknown_plugins, f)
                        except:
                            if callable(on_log_error):
                                on_log_error(
                                    u"Could not persist list of unknown plugins to {}"
                                    .format(unknown_plugins_path),
                                    exc_info=sys.exc_info())

                finally:
                    if callable(on_log_progress):
                        on_log_progress(u"Removing temporary unpacked folder")
                    shutil.rmtree(temp)

        except:
            exc_info = sys.exc_info()
            try:
                if callable(on_log_error):
                    on_log_error(u"Error while running restore",
                                 exc_info=exc_info)
                if callable(on_restore_failed):
                    on_restore_failed(path)
            finally:
                del exc_info
            return False

        finally:
            # remove zip
            if callable(on_log_progress):
                on_log_progress(u"Removing temporary zip")
            os.remove(path)

        # restart server
        if restart_command:
            import sarge

            if callable(on_log_progress):
                on_log_progress(u"Restarting...")
            if callable(on_restore_done):
                on_restore_done(path)

            try:
                sarge.run(restart_command, async_=True)
            except:
                if callable(on_log_error):
                    on_log_error(
                        u"Error while restarting via command {}".format(
                            restart_command),
                        exc_info=sys.exc_info())
                    on_log_error(u"Please restart OctoPrint manually")
                return False

        else:
            if callable(on_restore_done):
                on_restore_done(path)

        return True
Пример #12
0
    def perform_restore(self):
        if not is_os_compatible(["!windows"]):
            return flask.make_response(
                u"Invalid request, the restores are not supported on the underlying operating system",
                400)

        input_name = "file"
        input_upload_path = input_name + "." + self._settings.global_get(
            ["server", "uploads", "pathSuffix"])

        if input_upload_path in flask.request.values:
            # file to restore was uploaded
            path = flask.request.values[input_upload_path]

        elif flask.request.json and "path" in flask.request.json:
            # existing backup is supposed to be restored
            backup_folder = self.get_plugin_data_folder()
            path = os.path.realpath(
                os.path.join(backup_folder, flask.request.json["path"]))
            if not path.startswith(backup_folder) \
             or not os.path.exists(path) \
             or is_hidden_path(path):
                return flask.abort(404)

        else:
            return flask.make_response(
                u"Invalid request, neither a file nor a path of a file to restore provided",
                400)

        def on_install_plugins(plugins):
            force_user = self._settings.global_get_boolean(
                ["plugins", "pluginmanager", "pip_force_user"])
            pip_args = self._settings.global_get(
                ["plugins", "pluginmanager", "pip_args"])

            def on_log(line):
                self._logger.info(line)
                self._send_client_message("logline",
                                          dict(line=line, type="stdout"))

            for plugin in plugins:
                octoprint_compatible = is_octoprint_compatible(
                    *plugin["compatibility"]["octoprint"])
                os_compatible = is_os_compatible(plugin["compatibility"]["os"])
                compatible = octoprint_compatible and os_compatible
                if not compatible:
                    if not octoprint_compatible and not os_compatible:
                        self._logger.warn(
                            u"Cannot install plugin {}, it is incompatible to this version "
                            u"of OctoPrint and the underlying operating system"
                            .format(plugin["id"]))
                    elif not octoprint_compatible:
                        self._logger.warn(
                            u"Cannot install plugin {}, it is incompatible to this version "
                            u"of OctoPrint".format(plugin["id"]))
                    elif not os_compatible:
                        self._logger.warn(
                            u"Cannot install plugin {}, it is incompatible to the underlying "
                            u"operating system".format(plugin["id"]))
                    self._send_client_message(
                        "plugin_incompatible",
                        dict(plugin=plugin["id"],
                             octoprint_compatible=octoprint_compatible,
                             os_compatible=os_compatible))
                    continue

                self._logger.info(u"Installing plugin {}".format(plugin["id"]))
                self._send_client_message("installing_plugin",
                                          dict(plugin=plugin["id"]))
                self.__class__._install_plugin(plugin,
                                               force_user=force_user,
                                               pip_args=pip_args,
                                               on_log=on_log)

        def on_report_unknown_plugins(plugins):
            self._send_client_message("unknown_plugins",
                                      payload=dict(plugins=plugins))

        def on_log_progress(line):
            self._logger.info(line)
            self._send_client_message("logline",
                                      payload=dict(line=line, stream="stdout"))

        def on_log_error(line, exc_info=None):
            self._logger.error(line, exc_info=exc_info)
            self._send_client_message("logline",
                                      payload=dict(line=line, stream="stderr"))

            if exc_info is not None:
                exc_type, exc_value, exc_tb = exc_info
                output = traceback.format_exception(exc_type, exc_value,
                                                    exc_tb)
                for line in output:
                    self._send_client_message("logline",
                                              payload=dict(line=line.rstrip(),
                                                           stream="stderr"))

        def on_restore_start(path):
            self._send_client_message("restore_started")

        def on_restore_done(path):
            self._send_client_message("restore_done")

        def on_restore_failed(path):
            self._send_client_message("restore_failed")

        def on_invalid_backup(line):
            on_log_error(line)

        archive = tempfile.NamedTemporaryFile(delete=False)
        archive.close()
        shutil.copy(path, archive.name)
        path = archive.name

        # noinspection PyTypeChecker
        thread = threading.Thread(
            target=self._restore_backup,
            args=(path, ),
            kwargs=dict(settings=self._settings,
                        plugin_manager=self._plugin_manager,
                        datafolder=self.get_plugin_data_folder(),
                        on_install_plugins=on_install_plugins,
                        on_report_unknown_plugins=on_report_unknown_plugins,
                        on_invalid_backup=on_invalid_backup,
                        on_log_progress=on_log_progress,
                        on_log_error=on_log_error,
                        on_restore_start=on_restore_start,
                        on_restore_done=on_restore_done,
                        on_restore_failed=on_restore_failed))
        thread.daemon = True
        thread.start()

        return flask.jsonify(started=True)
Пример #13
0
	def _restore_backup(cls, path,
	                    settings=None,
	                    plugin_manager=None,
	                    datafolder=None,
	                    on_install_plugins=None,
	                    on_report_unknown_plugins=None,
	                    on_invalid_backup=None,
	                    on_log_progress=None,
	                    on_log_error=None,
	                    on_restore_start=None,
	                    on_restore_done=None,
	                    on_restore_failed=None):
		if not is_os_compatible(["!windows"]):
			if callable(on_log_error):
				on_log_error(u"Restore is not supported on this operating system")
			if callable(on_restore_failed):
				on_restore_failed(path)
			return False

		restart_command = settings.global_get(["server", "commands", "serverRestartCommand"])

		basedir = settings._basedir
		cls._clean_dir_backup(basedir,
		                       on_log_progress=on_log_progress)

		plugin_repo = dict()
		repo_url = settings.global_get(["plugins", "pluginmanager", "repository"])
		if repo_url:
			plugin_repo = cls._get_plugin_repository_data(repo_url)

		if callable(on_restore_start):
			on_restore_start(path)

		try:

			with zipfile.ZipFile(path, "r") as zip:
				# read metadata
				try:
					metadata_zipinfo = zip.getinfo("metadata.json")
				except KeyError:
					if callable(on_invalid_backup):
						on_invalid_backup(u"Not an OctoPrint backup, lacks metadata.json")
					if callable(on_restore_failed):
						on_restore_failed(path)
					return False

				metadata_bytes = zip.read(metadata_zipinfo)
				metadata = json.loads(metadata_bytes)

				backup_version = get_comparable_version(metadata["version"], base=True)
				if backup_version > get_octoprint_version(base=True):
					if callable(on_invalid_backup):
						on_invalid_backup(u"Backup is from a newer version of OctoPrint and cannot be applied")
					if callable(on_restore_failed):
						on_restore_failed(path)
					return False

				# unzip to temporary folder
				temp = tempfile.mkdtemp()
				try:
					if callable(on_log_progress):
						on_log_progress(u"Unpacking backup to {}...".format(temp))
					abstemp = os.path.abspath(temp)
					for member in zip.infolist():
						abspath = os.path.abspath(os.path.join(temp, member.filename))
						if abspath.startswith(abstemp):
							zip.extract(member, temp)

					# sanity check
					configfile = os.path.join(temp, "basedir", "config.yaml")
					if not os.path.exists(configfile):
						if callable(on_invalid_backup):
							on_invalid_backup(u"Backup lacks config.yaml")
						if callable(on_restore_failed):
							on_restore_failed(path)
						return False

					import yaml

					with codecs.open(configfile) as f:
						configdata = yaml.safe_load(f)

					if configdata.get("accessControl", dict()).get("enabled", True):
						userfile = os.path.join(temp, "basedir", "users.yaml")
						if not os.path.exists(userfile):
							if callable(on_invalid_backup):
								on_invalid_backup(u"Backup lacks users.yaml")
							if callable(on_restore_failed):
								on_restore_failed(path)
							return False

					if callable(on_log_progress):
						on_log_progress(u"Unpacked")

					# install available plugins
					with codecs.open(os.path.join(temp, "plugin_list.json"), "r") as f:
						plugins = json.load(f)

					known_plugins = []
					unknown_plugins = []
					if plugins:
						if plugin_repo:
							for plugin in plugins:
								if plugin["key"] in plugin_manager.plugins:
									# already installed
									continue

								if plugin["key"] in plugin_repo:
									# not installed, can be installed from repository url
									known_plugins.append(plugin_repo[plugin["key"]])
								else:
									# not installed, not installable
									unknown_plugins.append(plugin)

						else:
							# no repo, all plugins are not installable
							unknown_plugins = plugins

						if callable(on_log_progress):
							if known_plugins:
								on_log_progress(u"Known and installable plugins: {}".format(u", ".join(map(lambda x: x["id"], known_plugins))))
							if unknown_plugins:
								on_log_progress(u"Unknown plugins: {}".format(u", ".join(map(lambda x: x["key"], unknown_plugins))))

						if callable(on_install_plugins):
							on_install_plugins(known_plugins)

						if callable(on_report_unknown_plugins):
							on_report_unknown_plugins(unknown_plugins)

					# move config data
					basedir_backup = basedir + ".bck"
					basedir_extracted = os.path.join(temp, "basedir")

					if callable(on_log_progress):
						on_log_progress(u"Renaming {} to {}...".format(basedir, basedir_backup))
					os.rename(basedir, basedir_backup)

					try:
						if callable(on_log_progress):
							on_log_progress(u"Moving {} to {}...".format(basedir_extracted, basedir))
						os.rename(basedir_extracted, basedir)
					except:
						if callable(on_log_error):
							on_log_error(u"Error while restoring config data", exc_info=sys.exc_info())
							on_log_error(u"Rolling back old config data")

						os.rename(basedir_backup, basedir)

						if callable(on_restore_failed):
							on_restore_failed(path)
						return False

					if unknown_plugins:
						if callable(on_log_progress):
							on_log_progress(u"Writing info file about unknown plugins")

						if not os.path.isdir(datafolder):
							os.makedirs(datafolder)

						unknown_plugins_path = os.path.join(datafolder, UNKNOWN_PLUGINS_FILE)
						try:
							with codecs.open(unknown_plugins_path, mode="w", encoding="utf-8") as f:
								json.dump(unknown_plugins, f)
						except:
							if callable(on_log_error):
								on_log_error(u"Could not persist list of unknown plugins to {}".format(unknown_plugins_path),
								             exc_info = sys.exc_info())

				finally:
					if callable(on_log_progress):
						on_log_progress(u"Removing temporary unpacked folder")
					shutil.rmtree(temp)

		except:
			exc_info = sys.exc_info()
			try:
				if callable(on_log_error):
					on_log_error(u"Error while running restore", exc_info=exc_info)
				if callable(on_restore_failed):
					on_restore_failed(path)
			finally:
				del exc_info
			return False

		finally:
			# remove zip
			if callable(on_log_progress):
				on_log_progress(u"Removing temporary zip")
			os.remove(path)

		# restart server
		if restart_command:
			import sarge

			if callable(on_log_progress):
				on_log_progress(u"Restarting...")
			if callable(on_restore_done):
				on_restore_done(path)

			try:
				sarge.run(restart_command, async_=True)
			except:
				if callable(on_log_error):
					on_log_error(u"Error while restarting via command {}".format(restart_command),
					             exc_info=sys.exc_info())
					on_log_error(u"Please restart OctoPrint manually")
				return False

		else:
			if callable(on_restore_done):
				on_restore_done(path)

		return True
Пример #14
0
	def perform_restore(self):
		if not is_os_compatible(["!windows"]):
			return flask.make_response(u"Invalid request, the restores are not supported on the underlying operating system", 400)

		input_name = "file"
		input_upload_path = input_name + "." + self._settings.global_get(["server", "uploads", "pathSuffix"])

		if input_upload_path in flask.request.values:
			# file to restore was uploaded
			path = flask.request.values[input_upload_path]

		elif flask.request.json and "path" in flask.request.json:
			# existing backup is supposed to be restored
			backup_folder = self.get_plugin_data_folder()
			path = os.path.realpath(os.path.join(backup_folder, flask.request.json["path"]))
			if not path.startswith(backup_folder) \
				or not os.path.exists(path) \
				or is_hidden_path(path):
				return flask.abort(404)

		else:
			return flask.make_response(u"Invalid request, neither a file nor a path of a file to restore provided", 400)

		def on_install_plugins(plugins):
			force_user = self._settings.global_get_boolean(["plugins", "pluginmanager", "pip_force_user"])
			pip_args = self._settings.global_get(["plugins", "pluginmanager", "pip_args"])

			def on_log(line):
				self._logger.info(line)
				self._send_client_message("logline", dict(line=line, type="stdout"))

			for plugin in plugins:
				octoprint_compatible = is_octoprint_compatible(*plugin["compatibility"]["octoprint"])
				os_compatible = is_os_compatible(plugin["compatibility"]["os"])
				compatible = octoprint_compatible and os_compatible
				if not compatible:
					if not octoprint_compatible and not os_compatible:
						self._logger.warn(u"Cannot install plugin {}, it is incompatible to this version "
						                  u"of OctoPrint and the underlying operating system".format(plugin["id"]))
					elif not octoprint_compatible:
						self._logger.warn(u"Cannot install plugin {}, it is incompatible to this version "
						                  u"of OctoPrint".format(plugin["id"]))
					elif not os_compatible:
						self._logger.warn(u"Cannot install plugin {}, it is incompatible to the underlying "
						                  u"operating system".format(plugin["id"]))
					self._send_client_message("plugin_incompatible", dict(plugin=plugin["id"],
					                                                      octoprint_compatible=octoprint_compatible,
					                                                      os_compatible=os_compatible))
					continue

				self._logger.info(u"Installing plugin {}".format(plugin["id"]))
				self._send_client_message("installing_plugin", dict(plugin=plugin["id"]))
				self.__class__._install_plugin(plugin,
				                               force_user=force_user,
				                               pip_args=pip_args,
				                               on_log=on_log)

		def on_report_unknown_plugins(plugins):
			self._send_client_message("unknown_plugins", payload=dict(plugins=plugins))

		def on_log_progress(line):
			self._logger.info(line)
			self._send_client_message("logline", payload=dict(line=line, stream="stdout"))

		def on_log_error(line, exc_info=None):
			self._logger.error(line, exc_info=exc_info)
			self._send_client_message("logline", payload=dict(line=line, stream="stderr"))

			if exc_info is not None:
				exc_type, exc_value, exc_tb = exc_info
				output = traceback.format_exception(exc_type, exc_value, exc_tb)
				for line in output:
					self._send_client_message("logline", payload=dict(line=line.rstrip(), stream="stderr"))

		def on_restore_start(path):
			self._send_client_message("restore_started")

		def on_restore_done(path):
			self._send_client_message("restore_done")

		def on_restore_failed(path):
			self._send_client_message("restore_failed")

		def on_invalid_backup(line):
			on_log_error(line)

		archive = tempfile.NamedTemporaryFile(delete=False)
		archive.close()
		shutil.copy(path, archive.name)
		path = archive.name

		# noinspection PyTypeChecker
		thread = threading.Thread(target=self._restore_backup,
		                          args=(path,),
		                          kwargs=dict(settings=self._settings,
		                                      plugin_manager=self._plugin_manager,
		                                      datafolder=self.get_plugin_data_folder(),
		                                      on_install_plugins=on_install_plugins,
		                                      on_report_unknown_plugins=on_report_unknown_plugins,
		                                      on_invalid_backup=on_invalid_backup,
		                                      on_log_progress=on_log_progress,
		                                      on_log_error=on_log_error,
		                                      on_restore_start=on_restore_start,
		                                      on_restore_done=on_restore_done,
		                                      on_restore_failed=on_restore_failed))
		thread.daemon = True
		thread.start()

		return flask.jsonify(started=True)
Пример #15
0
 def is_wizard_required(self):
     return self._settings.global_get(["server", "firstRun"
                                       ]) and is_os_compatible(["!windows"])
Пример #16
0
 def _restore_supported(cls, settings):
     return (is_os_compatible(["!windows"])
             and not settings.get_boolean(["restore_unsupported"])
             and os.environ.get("OCTOPRINT_BACKUP_RESTORE_UNSUPPORTED",
                                False) not in valid_boolean_trues)