2015-10-27 17:08:27 +00:00
|
|
|
# coding=utf-8
|
2016-07-15 07:16:58 +00:00
|
|
|
from __future__ import absolute_import, division, print_function
|
2015-10-27 17:08:27 +00:00
|
|
|
|
|
|
|
|
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
|
|
|
|
__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import click
|
|
|
|
|
import logging
|
|
|
|
|
import sys
|
|
|
|
|
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
from octoprint.cli import bulk_options, standard_options, set_ctx_obj_option, get_ctx_obj_option
|
2015-10-27 17:08:27 +00:00
|
|
|
|
2016-11-18 12:01:14 +00:00
|
|
|
def run_server(basedir, configfile, host, port, debug, allow_root, logging_config, verbosity, safe_mode, octoprint_daemon=None):
|
2015-10-29 16:37:05 +00:00
|
|
|
"""Initializes the environment and starts up the server."""
|
|
|
|
|
|
2016-07-08 10:35:46 +00:00
|
|
|
from octoprint import init_platform, __display_version__, FatalStartupError
|
2015-10-27 17:08:27 +00:00
|
|
|
|
2016-11-18 12:01:14 +00:00
|
|
|
def log_startup(recorder=None, safe_mode=None, **kwargs):
|
2017-02-24 09:53:16 +00:00
|
|
|
from octoprint.logging import get_divider_line
|
|
|
|
|
|
2016-11-18 09:25:24 +00:00
|
|
|
logger = logging.getLogger("octoprint.server")
|
2017-02-24 09:53:16 +00:00
|
|
|
|
|
|
|
|
logger.info(get_divider_line("*"))
|
2016-11-18 09:25:24 +00:00
|
|
|
logger.info("Starting OctoPrint {}".format(__display_version__))
|
2016-11-18 12:01:14 +00:00
|
|
|
if safe_mode:
|
|
|
|
|
logger.info("Starting in SAFE MODE. Third party plugins will be disabled!")
|
2016-11-18 09:25:24 +00:00
|
|
|
|
|
|
|
|
if recorder and len(recorder):
|
2017-02-24 09:53:16 +00:00
|
|
|
logger.info(get_divider_line("-", "Logged during platform initialization:"))
|
2016-11-18 09:25:24 +00:00
|
|
|
|
|
|
|
|
from octoprint.logging.handlers import CombinedLogHandler
|
|
|
|
|
handler = CombinedLogHandler(*logging.getLogger().handlers)
|
|
|
|
|
recorder.setTarget(handler)
|
|
|
|
|
recorder.flush()
|
|
|
|
|
|
2017-02-24 09:53:16 +00:00
|
|
|
logger.info(get_divider_line("-"))
|
2015-10-27 17:08:27 +00:00
|
|
|
|
2015-10-29 14:26:58 +00:00
|
|
|
from octoprint import urllib3_ssl
|
|
|
|
|
if not urllib3_ssl:
|
|
|
|
|
logging.getLogger("octoprint.server")\
|
|
|
|
|
.warn("requests/urllib3 will run in an insecure SSL environment. "
|
|
|
|
|
"You might see corresponding warnings logged later "
|
|
|
|
|
"(\"InsecurePlatformWarning\"). It is recommended to either "
|
|
|
|
|
"update to a Python version >= 2.7.9 or alternatively "
|
|
|
|
|
"install PyOpenSSL plus its dependencies. For details see "
|
|
|
|
|
"https://urllib3.readthedocs.org/en/latest/security.html#openssl-pyopenssl")
|
2017-02-24 09:53:16 +00:00
|
|
|
logger.info(get_divider_line("*"))
|
|
|
|
|
|
|
|
|
|
def log_register_rollover(safe_mode=None, plugin_manager=None, **kwargs):
|
|
|
|
|
from octoprint.logging import get_handler, log_to_handler, get_divider_line
|
|
|
|
|
from octoprint.logging.handlers import OctoPrintLogHandler
|
|
|
|
|
|
|
|
|
|
def rollover_callback():
|
|
|
|
|
handler = get_handler("file")
|
|
|
|
|
if handler is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("octoprint.server")
|
|
|
|
|
|
|
|
|
|
def _log(message, level=logging.INFO):
|
|
|
|
|
log_to_handler(logger, handler, level, message)
|
|
|
|
|
|
|
|
|
|
_log(get_divider_line("-", "Log roll over detected"))
|
|
|
|
|
_log("OctoPrint {}".format(__display_version__))
|
|
|
|
|
if safe_mode:
|
|
|
|
|
_log("SAFE MODE is active. Third party plugins are disabled!")
|
|
|
|
|
plugin_manager.log_all_plugins(only_to_handler=handler)
|
|
|
|
|
_log(get_divider_line("-"))
|
|
|
|
|
|
|
|
|
|
OctoPrintLogHandler.registerRolloverCallback(rollover_callback)
|
2015-10-29 14:26:58 +00:00
|
|
|
|
2016-07-08 10:35:46 +00:00
|
|
|
try:
|
2016-11-18 12:01:14 +00:00
|
|
|
settings, _, safe_mode, plugin_manager = init_platform(basedir,
|
|
|
|
|
configfile,
|
|
|
|
|
logging_file=logging_config,
|
|
|
|
|
debug=debug,
|
|
|
|
|
verbosity=verbosity,
|
|
|
|
|
uncaught_logger=__name__,
|
|
|
|
|
safe_mode=safe_mode,
|
2017-02-24 09:53:16 +00:00
|
|
|
after_safe_mode=log_startup,
|
|
|
|
|
after_plugin_manager=log_register_rollover)
|
2016-07-08 10:35:46 +00:00
|
|
|
except FatalStartupError as e:
|
|
|
|
|
click.echo(e.message, err=True)
|
|
|
|
|
click.echo("There was a fatal error starting up OctoPrint.", err=True)
|
|
|
|
|
else:
|
|
|
|
|
from octoprint.server import Server
|
|
|
|
|
octoprint_server = Server(settings=settings,
|
|
|
|
|
plugin_manager=plugin_manager,
|
|
|
|
|
host=host,
|
|
|
|
|
port=port,
|
|
|
|
|
debug=debug,
|
2016-11-18 12:01:14 +00:00
|
|
|
safe_mode=safe_mode,
|
2016-07-08 10:35:46 +00:00
|
|
|
allow_root=allow_root,
|
|
|
|
|
octoprint_daemon=octoprint_daemon)
|
|
|
|
|
octoprint_server.run()
|
2015-10-27 17:08:27 +00:00
|
|
|
|
2015-10-29 15:26:50 +00:00
|
|
|
#~~ server options
|
|
|
|
|
|
|
|
|
|
server_options = bulk_options([
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
click.option("--host", type=click.STRING, callback=set_ctx_obj_option,
|
2015-10-29 15:26:50 +00:00
|
|
|
help="Specify the host on which to bind the server."),
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
click.option("--port", type=click.INT, callback=set_ctx_obj_option,
|
2015-10-29 15:26:50 +00:00
|
|
|
help="Specify the port on which to bind the server."),
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
click.option("--logging", type=click.Path(), callback=set_ctx_obj_option,
|
2015-10-29 15:26:50 +00:00
|
|
|
help="Specify the config file to use for configuring logging."),
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
click.option("--iknowwhatimdoing", "allow_root", is_flag=True, callback=set_ctx_obj_option,
|
2015-10-29 15:26:50 +00:00
|
|
|
help="Allow OctoPrint to run as user root."),
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
click.option("--debug", is_flag=True, callback=set_ctx_obj_option,
|
|
|
|
|
help="Enable debug mode.")
|
2015-10-29 15:26:50 +00:00
|
|
|
])
|
2015-10-29 16:37:05 +00:00
|
|
|
"""Decorator to add the options shared among the server commands: ``--host``, ``--port``,
|
|
|
|
|
``--logging``, ``--iknowwhatimdoing`` and ``--debug``."""
|
2015-10-29 15:26:50 +00:00
|
|
|
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
daemon_options = bulk_options([
|
|
|
|
|
click.option("--pid", type=click.Path(), default="/tmp/octoprint.pid", callback=set_ctx_obj_option,
|
|
|
|
|
help="Pidfile to use for daemonizing.")
|
|
|
|
|
])
|
|
|
|
|
"""Decorator to add the options for the daemon subcommand: ``--pid``."""
|
|
|
|
|
|
2015-10-29 15:26:50 +00:00
|
|
|
#~~ "octoprint serve" and "octoprint daemon" commands
|
2015-10-27 17:08:27 +00:00
|
|
|
|
|
|
|
|
@click.group()
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
def server_commands():
|
2015-10-27 17:08:27 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@server_commands.command(name="serve")
|
2015-10-29 15:26:50 +00:00
|
|
|
@server_options
|
|
|
|
|
@standard_options(hidden=True)
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
@click.pass_context
|
|
|
|
|
def serve_command(ctx, **kwargs):
|
2015-10-27 17:08:27 +00:00
|
|
|
"""Starts the OctoPrint server."""
|
Don't care about common params on CLI
--basedir, --config, --verbose, --safe may now come before or after
subcommands and should still be evaluated.
For the server commands (legacy, "server" and "daemon"), the same
should now hold true for the related parameters --host, --port, --debug,
--logging, --iknowwhatimdoing and also --pid (for daemon command).
While having the parameters belong to the individual commands and only
there (which is click's basic approach) is way more cleaner, too many people
were running into issues with that strict approach after all.
I just hope the somewhat hackish approach with context injection needed to
get the less strict version to work won't backfire badly in the long run.
See also #1633 and #1657
2016-12-16 11:18:05 +00:00
|
|
|
|
|
|
|
|
def get_value(key):
|
|
|
|
|
return get_ctx_obj_option(ctx, key, kwargs.get(key))
|
|
|
|
|
|
|
|
|
|
host = get_value("host")
|
|
|
|
|
port = get_value("port")
|
|
|
|
|
logging = get_value("logging")
|
|
|
|
|
allow_root = get_value("allow_root")
|
|
|
|
|
debug = get_value("debug")
|
|
|
|
|
|
|
|
|
|
basedir = get_value("basedir")
|
|
|
|
|
configfile = get_value("configfile")
|
|
|
|
|
verbosity = get_value("verbosity")
|
|
|
|
|
safe_mode = get_value("safe_mode")
|
|
|
|
|
|
|
|
|
|
run_server(basedir, configfile, host, port, debug,
|
|
|
|
|
allow_root, logging, verbosity, safe_mode)
|
2015-10-27 17:08:27 +00:00
|
|
|
|
|
|
|
|
|
2017-04-06 08:02:48 +00:00
|
|
|
if sys.platform != "win32" and sys.platform != "darwin":
|
|
|
|
|
# we do not support daemon mode under windows or macosx
|
2017-01-20 16:10:59 +00:00
|
|
|
|
|
|
|
|
@server_commands.command(name="daemon")
|
|
|
|
|
@server_options
|
|
|
|
|
@daemon_options
|
|
|
|
|
@standard_options(hidden=True)
|
|
|
|
|
@click.argument("command", type=click.Choice(["start", "stop", "restart", "status"]),
|
|
|
|
|
metavar="start|stop|restart|status")
|
|
|
|
|
@click.pass_context
|
|
|
|
|
def daemon_command(ctx, command, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Starts, stops or restarts in daemon mode.
|
|
|
|
|
|
|
|
|
|
Please note that daemon mode is only supported under Linux right now.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get_value(key):
|
|
|
|
|
return get_ctx_obj_option(ctx, key, kwargs.get(key))
|
|
|
|
|
|
|
|
|
|
host = get_value("host")
|
|
|
|
|
port = get_value("port")
|
|
|
|
|
logging = get_value("logging")
|
|
|
|
|
allow_root = get_value("allow_root")
|
|
|
|
|
debug = get_value("debug")
|
|
|
|
|
pid = get_value("pid")
|
|
|
|
|
|
|
|
|
|
basedir = get_value("basedir")
|
|
|
|
|
configfile = get_value("configfile")
|
|
|
|
|
verbosity = get_value("verbosity")
|
|
|
|
|
safe_mode = get_value("safe_mode")
|
|
|
|
|
|
|
|
|
|
if pid is None:
|
|
|
|
|
click.echo("No path to a pidfile set",
|
|
|
|
|
file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
from octoprint.daemon import Daemon
|
|
|
|
|
class OctoPrintDaemon(Daemon):
|
|
|
|
|
def __init__(self, pidfile, basedir, configfile, host, port, debug, allow_root, logging_config, verbosity, safe_mode):
|
|
|
|
|
Daemon.__init__(self, pidfile)
|
|
|
|
|
|
|
|
|
|
self._basedir = basedir
|
|
|
|
|
self._configfile = configfile
|
|
|
|
|
self._host = host
|
|
|
|
|
self._port = port
|
|
|
|
|
self._debug = debug
|
|
|
|
|
self._allow_root = allow_root
|
|
|
|
|
self._logging_config = logging_config
|
|
|
|
|
self._verbosity = verbosity
|
|
|
|
|
self._safe_mode = safe_mode
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
run_server(self._basedir, self._configfile, self._host, self._port, self._debug,
|
|
|
|
|
self._allow_root, self._logging_config, self._verbosity, self._safe_mode,
|
|
|
|
|
octoprint_daemon=self)
|
|
|
|
|
|
|
|
|
|
octoprint_daemon = OctoPrintDaemon(pid, basedir, configfile, host, port, debug, allow_root, logging, verbosity,
|
|
|
|
|
safe_mode)
|
|
|
|
|
|
|
|
|
|
if command == "start":
|
|
|
|
|
octoprint_daemon.start()
|
|
|
|
|
elif command == "stop":
|
|
|
|
|
octoprint_daemon.stop()
|
|
|
|
|
elif command == "restart":
|
|
|
|
|
octoprint_daemon.restart()
|
|
|
|
|
elif command == "status":
|
|
|
|
|
octoprint_daemon.status()
|
2015-10-27 17:08:27 +00:00
|
|
|
|
|
|
|
|
|