Ejemplo n.º 1
0
    def test_get_blueprint(self):
        import os

        expected_kwargs = {
            "static_folder": os.path.join(self.basefolder, "static"),
            "template_folder": os.path.join(self.basefolder, "templates"),
        }

        class MyPlugin(octoprint.plugin.BlueprintPlugin):
            @octoprint.plugin.BlueprintPlugin.route("/some/path",
                                                    methods=["GET"])
            def route_method(self):
                pass

            @octoprint.plugin.BlueprintPlugin.errorhandler(404)
            def errorhandler_method(self):
                pass

            @octoprint.plugin.BlueprintPlugin.route("/hidden/path",
                                                    methods=["GET"])
            def _hidden_method(self):
                pass

        plugin = MyPlugin()
        plugin._basefolder = self.basefolder
        plugin._identifier = "myplugin"

        with mock.patch("flask.Blueprint") as MockBlueprint:
            blueprint = mock.MagicMock()
            MockBlueprint.return_value = blueprint

            errorhandler = mock.MagicMock()
            blueprint.errorhandler.return_value = errorhandler

            result = plugin.get_blueprint()

        self.assertEqual(result, blueprint)

        MockBlueprint.assert_called_once_with("myplugin", "myplugin",
                                              **expected_kwargs)
        blueprint.add_url_rule.assert_called_once_with(
            "/some/path",
            "route_method",
            view_func=plugin.route_method,
            methods=["GET"])

        blueprint.errorhandler.assert_called_once_with(404)
        errorhandler.assert_called_once_with(plugin.errorhandler_method)
Ejemplo n.º 2
0
	def _register_blueprint_plugin(self, plugin):
		name = plugin._identifier
		blueprint = plugin.get_blueprint()
		if blueprint is None:
			return

		if plugin.is_blueprint_protected():
			from octoprint.server.util import apiKeyRequestHandler, corsResponseHandler
			blueprint.before_request(apiKeyRequestHandler)
			blueprint.after_request(corsResponseHandler)

		url_prefix = "/plugin/{name}".format(name=name)
		app.register_blueprint(blueprint, url_prefix=url_prefix)

		if self._logger:
			self._logger.debug("Registered API of plugin {name} under URL prefix {url_prefix}".format(name=name, url_prefix=url_prefix))
Ejemplo n.º 3
0
	def _register_blueprint_plugin(self, plugin):
		name = plugin._identifier
		blueprint = plugin.get_blueprint()
		if blueprint is None:
			return

		if plugin.is_blueprint_protected():
			from octoprint.server.util import apiKeyRequestHandler, corsResponseHandler
			blueprint.before_request(apiKeyRequestHandler)
			blueprint.after_request(corsResponseHandler)

		url_prefix = "/plugin/{name}".format(name=name)
		app.register_blueprint(blueprint, url_prefix=url_prefix)

		if self._logger:
			self._logger.debug("Registered API of plugin {name} under URL prefix {url_prefix}".format(name=name, url_prefix=url_prefix))
Ejemplo n.º 4
0
	def test_get_blueprint(self):
		import os
		expected_kwargs = dict(
			static_folder=os.path.join(self.basefolder, "static"),
			template_folder=os.path.join(self.basefolder, "templates")
		)

		class MyPlugin(octoprint.plugin.BlueprintPlugin):

			@octoprint.plugin.BlueprintPlugin.route("/some/path", methods=["GET"])
			def route_method(self):
				pass

			@octoprint.plugin.BlueprintPlugin.errorhandler(404)
			def errorhandler_method(self):
				pass

			@octoprint.plugin.BlueprintPlugin.route("/hidden/path", methods=["GET"])
			def _hidden_method(self):
				pass

		plugin = MyPlugin()
		plugin._basefolder = self.basefolder
		plugin._identifier = "myplugin"

		with mock.patch("flask.Blueprint") as MockBlueprint:
			blueprint = mock.MagicMock()
			MockBlueprint.return_value = blueprint

			errorhandler = mock.MagicMock()
			blueprint.errorhandler.return_value = errorhandler

			result = plugin.get_blueprint()

		self.assertEquals(result, blueprint)

		MockBlueprint.assert_called_once_with("plugin.myplugin", "myplugin", **expected_kwargs)
		blueprint.add_url_rule.assert_called_once_with("/some/path", "route_method", view_func=plugin.route_method, methods=["GET"])

		blueprint.errorhandler.assert_called_once_with(404)
		errorhandler.assert_called_once_with(plugin.errorhandler_method)
Ejemplo n.º 5
0
class Server():
    def __init__(self,
                 configfile=None,
                 basedir=None,
                 host="0.0.0.0",
                 port=5000,
                 debug=False,
                 allowRoot=False,
                 logConf=None):
        self._configfile = configfile
        self._basedir = basedir
        self._host = host
        self._port = port
        self._debug = debug
        self._allowRoot = allowRoot
        self._logConf = logConf
        self._server = None

    def run(self):
        if not self._allowRoot:
            self._checkForRoot()

        global printer
        global printerProfileManager
        global fileManager
        global slicingManager
        global analysisQueue
        global userManager
        global eventManager
        global loginManager
        global pluginManager
        global appSessionManager
        global debug

        from tornado.ioloop import IOLoop
        from tornado.web import Application

        import sys

        debug = self._debug
        import subprocess
        cmd1 = "/usr/bin/reset_user_info.sh"
        subprocess.Popen(cmd1, shell=True)
        cmd2 = "echo heartbeat > /sys/class/leds/usr1/trigger"
        subprocess.Popen(cmd2, shell=True)
        import time
        time.sleep(0.5)

        # first initialize the settings singleton and make sure it uses given configfile and basedir if available
        self._initSettings(self._configfile, self._basedir)

        # then initialize logging
        self._initLogging(self._debug, self._logConf)
        logger = logging.getLogger(__name__)

        def exception_logger(exc_type, exc_value, exc_tb):
            logger.error("Uncaught exception",
                         exc_info=(exc_type, exc_value, exc_tb))

        sys.excepthook = exception_logger
        logger.info("Starting OctoPrint %s" % DISPLAY_VERSION)

        # then initialize the plugin manager
        pluginManager = octoprint.plugin.plugin_manager(init=True)

        printerProfileManager = PrinterProfileManager()
        eventManager = events.eventManager()
        analysisQueue = octoprint.filemanager.analysis.AnalysisQueue()
        slicingManager = octoprint.slicing.SlicingManager(
            settings().getBaseFolder("slicingProfiles"), printerProfileManager)
        storage_managers = dict()
        storage_managers[
            octoprint.filemanager.FileDestinations.
            LOCAL] = octoprint.filemanager.storage.LocalFileStorage(
                settings().getBaseFolder("uploads"))
        fileManager = octoprint.filemanager.FileManager(
            analysisQueue,
            slicingManager,
            printerProfileManager,
            initial_storage_managers=storage_managers)
        printer = Printer(fileManager, analysisQueue, printerProfileManager)
        appSessionManager = util.flask.AppSessionManager()

        # configure additional template folders for jinja2
        template_plugins = pluginManager.get_implementations(
            octoprint.plugin.TemplatePlugin)
        additional_template_folders = []
        for plugin in template_plugins.values():
            folder = plugin.get_template_folder()
            if folder is not None:
                additional_template_folders.append(
                    plugin.get_template_folder())

        import jinja2
        jinja_loader = jinja2.ChoiceLoader([
            app.jinja_loader,
            jinja2.FileSystemLoader(additional_template_folders)
        ])
        app.jinja_loader = jinja_loader
        del jinja2

        # configure timelapse
        octoprint.timelapse.configureTimelapse()

        # setup command triggers
        events.CommandTrigger(printer)
        if self._debug:
            events.DebugEventListener()

        if settings().getBoolean(["accessControl", "enabled"]):
            userManagerName = settings().get(["accessControl", "userManager"])
            try:
                clazz = octoprint.util.getClass(userManagerName)
                userManager = clazz()
            except AttributeError, e:
                logger.exception(
                    "Could not instantiate user manager %s, will run with accessControl disabled!"
                    % userManagerName)

        app.wsgi_app = util.ReverseProxied(
            app.wsgi_app,
            settings().get(["server", "reverseProxy", "prefixHeader"]),
            settings().get(["server", "reverseProxy", "schemeHeader"]),
            settings().get(["server", "reverseProxy", "hostHeader"]),
            settings().get(["server", "reverseProxy", "prefixFallback"]),
            settings().get(["server", "reverseProxy", "schemeFallback"]),
            settings().get(["server", "reverseProxy", "hostFallback"]))

        secret_key = settings().get(["server", "secretKey"])
        if not secret_key:
            import string
            from random import choice
            chars = string.ascii_lowercase + string.ascii_uppercase + string.digits
            secret_key = "".join(choice(chars) for _ in xrange(32))
            settings().set(["server", "secretKey"], secret_key)
            settings().save()
        app.secret_key = secret_key
        loginManager = LoginManager()
        loginManager.session_protection = "strong"
        loginManager.user_callback = load_user
        if userManager is None:
            loginManager.anonymous_user = users.DummyUser
            principals.identity_loaders.appendleft(users.dummy_identity_loader)
        loginManager.init_app(app)

        if self._host is None:
            self._host = settings().get(["server", "host"])
        if self._port is None:
            self._port = settings().getInt(["server", "port"])

        app.debug = self._debug

        from octoprint.server.api import api
        from octoprint.server.apps import apps

        # register API blueprint
        app.register_blueprint(api, url_prefix="/api")
        app.register_blueprint(apps, url_prefix="/apps")

        # also register any blueprints defined in BlueprintPlugins
        blueprint_plugins = octoprint.plugin.plugin_manager(
        ).get_implementations(octoprint.plugin.BlueprintPlugin)
        for name, plugin in blueprint_plugins.items():
            blueprint = plugin.get_blueprint()
            if blueprint is None:
                continue

            if plugin.is_blueprint_protected():
                from octoprint.server.util import apiKeyRequestHandler, corsResponseHandler
                blueprint.before_request(apiKeyRequestHandler)
                blueprint.after_request(corsResponseHandler)

            url_prefix = "/plugin/{name}".format(name=name)
            app.register_blueprint(blueprint, url_prefix=url_prefix)
            logger.debug(
                "Registered API of plugin {name} under URL prefix {url_prefix}"
                .format(name=name, url_prefix=url_prefix))

        self._router = SockJSRouter(self._createSocketConnection, "/sockjs")

        upload_suffixes = dict(
            name=settings().get(["server", "uploads", "nameSuffix"]),
            path=settings().get(["server", "uploads", "pathSuffix"]))
        self._tornado_app = Application(
            self._router.urls +
            [(r"/downloadSD", util.tornado.LkjDownFileHandler),
             (r"/downloads/timelapse/([^/]*\.mpg)",
              util.tornado.LargeResponseHandler,
              dict(path=settings().getBaseFolder("timelapse"),
                   as_attachment=True)),
             (r"/downloads/files/local/([^/]*\.(gco|gcode|g))",
              util.tornado.LargeResponseHandler,
              dict(path=settings().getBaseFolder("uploads"),
                   as_attachment=True)),
             (r"/downloads/logs/([^/]*)", util.tornado.LargeResponseHandler,
              dict(path=settings().getBaseFolder("logs"),
                   as_attachment=True,
                   access_validation=util.tornado.access_validation_factory(
                       app, loginManager, util.flask.admin_validator))),
             (r"/downloads/camera/current", util.tornado.UrlForwardHandler,
              dict(url=settings().get(["webcam", "snapshot"]),
                   as_attachment=True,
                   access_validation=util.tornado.access_validation_factory(
                       app, loginManager, util.flask.user_validator))),
             (r".*", util.tornado.UploadStorageFallbackHandler,
              dict(fallback=util.tornado.WsgiInputContainer(app.wsgi_app),
                   file_prefix="octoprint-file-upload-",
                   file_suffix=".tmp",
                   suffixes=upload_suffixes))])
        max_body_sizes = [("POST", r"/api/files/([^/]*)",
                           settings().getInt(["server", "uploads",
                                              "maxSize"]))]
        self._server = util.tornado.CustomHTTPServer(
            self._tornado_app,
            max_body_sizes=max_body_sizes,
            default_max_body_size=settings().getInt(["server", "maxSize"]))
        self._server.listen(self._port, address=self._host)

        eventManager.fire(events.Events.STARTUP)
        if settings().getBoolean(["serial", "autoconnect"]):
            (port, baudrate) = settings().get(
                ["serial", "port"]), settings().getInt(["serial", "baudrate"])
            printer_profile = printerProfileManager.get_default()
            connectionOptions = getConnectionOptions()
            if port in connectionOptions["ports"]:
                printer.connect(transport_option_overrides=dict(
                    port=port, baudrate=baudrate),
                                profile=printer_profile["id"]
                                if "id" in printer_profile else "_default")

        # start up watchdogs
        observer = Observer()
        observer.schedule(
            util.watchdog.GcodeWatchdogHandler(fileManager, printer),
            settings().getBaseFolder("watched"))
        observer.start()

        ioloop = IOLoop.instance()

        # run our startup plugins
        octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin,
                                     "on_startup",
                                     args=(self._host, self._port))

        # prepare our after startup function
        def on_after_startup():
            logger.info("Listening on http://%s:%d" % (self._host, self._port))

            # now this is somewhat ugly, but the issue is the following: startup plugins might want to do things for
            # which they need the server to be already alive (e.g. for being able to resolve urls, such as favicons
            # or service xmls or the like). While they are working though the ioloop would block. Therefore we'll
            # create a single use thread in which to perform our after-startup-tasks, start that and hand back
            # control to the ioloop
            def work():
                octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin,
                                             "on_after_startup")

            import threading
            threading.Thread(target=work).start()

        ioloop.add_callback(on_after_startup)

        # prepare our shutdown function
        def on_shutdown():
            logger.info("Goodbye!")
            observer.stop()
            observer.join()
            octoprint.plugin.call_plugin(octoprint.plugin.ShutdownPlugin,
                                         "on_shutdown")

        atexit.register(on_shutdown)

        try:
            ioloop.start()
        except KeyboardInterrupt:
            pass
        except:
            logger.fatal(
                "Now that is embarrassing... Something really really went wrong here. Please report this including the stacktrace below in OctoPrint's bugtracker. Thanks!"
            )
            logger.exception("Stacktrace follows:")