yunohost/bin/yunohost
2020-05-07 22:15:51 +02:00

174 lines
5.6 KiB
Python
Executable file

#! /usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import argparse
import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize, get_locale
# Directory and file to be used by logging
LOG_DIR = '/var/log/yunohost'
LOG_FILE = 'yunohost-cli.log'
# Initialization & helpers functions -----------------------------------
def _die(message, title='Error:'):
"""Print error message and exit"""
print('%s %s' % (colorize(title, 'red'), message))
sys.exit(1)
def _parse_cli_args():
"""Parse additional arguments for the cli"""
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--output-as',
choices=['json', 'plain', 'none'], default=None,
help="Output result in another format",
)
parser.add_argument('--debug',
action='store_true', default=False,
help="Log and print debug messages",
)
parser.add_argument('--quiet',
action='store_true', default=False,
help="Don't produce any output",
)
parser.add_argument('--timeout',
type=int, default=None,
help="Number of seconds before this command will timeout because it can't acquire the lock (meaning that another command is currently running), by default there is no timeout and the command will wait until it can get the lock",
)
parser.add_argument('--admin-password',
default=None, dest='password', metavar='PASSWORD',
help="The admin password to use to authenticate",
)
# deprecated arguments
parser.add_argument('--plain',
action='store_true', default=False, help=argparse.SUPPRESS
)
parser.add_argument('--json',
action='store_true', default=False, help=argparse.SUPPRESS
)
opts, args = parser.parse_known_args()
# output compatibility
if opts.plain:
opts.output_as = 'plain'
elif opts.json:
opts.output_as = 'json'
return (parser, opts, args)
def _init_moulinette(debug=False, quiet=False):
"""Configure logging and initialize the moulinette"""
# Create log directory
if not os.path.isdir(LOG_DIR):
try:
os.makedirs(LOG_DIR, 0750)
except os.error as e:
_die(str(e))
# Custom logging configuration
logging = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'tty-debug': {
'format': '%(relativeCreated)-4d %(fmessage)s'
},
'precise': {
'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
},
'filters': {
'action': {
'()': 'moulinette.utils.log.ActionFilter',
},
},
'handlers': {
'tty': {
'level': 'DEBUG' if debug else 'INFO',
'class': 'moulinette.interfaces.cli.TTYHandler',
'formatter': 'tty-debug' if debug else '',
},
'file': {
'class': 'logging.FileHandler',
'formatter': 'precise',
'filename': '%s/%s' % (LOG_DIR, LOG_FILE),
'filters': ['action'],
},
},
'loggers': {
'yunohost': {
'level': 'DEBUG',
'handlers': ['file', 'tty'] if not quiet else ['file'],
'propagate': False,
},
'moulinette': {
'level': 'DEBUG',
'handlers': [],
'propagate': True,
},
'moulinette.interface': {
'level': 'DEBUG',
'handlers': ['file', 'tty'] if not quiet else ['file'],
'propagate': False,
},
},
'root': {
'level': 'DEBUG',
'handlers': ['file', 'tty'] if debug else ['file'],
},
}
# Initialize moulinette
moulinette.init(logging_config=logging)
def _retrieve_namespaces():
"""Return the list of namespaces to load"""
extensions = [n for n in ActionsMap.get_namespaces() if n.startswith('ynh_')]
return ['yunohost'] + extensions
# Stupid PATH management because sometimes (e.g. some cron job) PATH is only /usr/bin:/bin ...
default_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
if os.environ["PATH"] != default_path:
os.environ["PATH"] = default_path + ":" + os.environ["PATH"]
# Main action ----------------------------------------------------------
if __name__ == '__main__':
if os.geteuid() != 0:
# since moulinette isn't initialized, we can't use m18n here
sys.stderr.write("\033[1;31mError:\033[0m yunohost command must be " \
"run as root or with sudo.\n")
sys.exit(1)
parser, opts, args = _parse_cli_args()
_init_moulinette(opts.debug, opts.quiet)
# Check that YunoHost is installed
allowed_if_not_installed = ['tools postinstall', 'backup restore', 'log display']
if not os.path.isfile('/etc/yunohost/installed') and \
(len(args) < 2 or (args[0] +' '+ args[1] not in allowed_if_not_installed)):
from moulinette import m18n
# Init i18n
m18n.load_namespace('yunohost')
m18n.set_locale(get_locale())
# Print error and exit
_die(m18n.n('yunohost_not_installed'), m18n.g('error'))
# Execute the action
ret = moulinette.cli(
_retrieve_namespaces(),
args,
output_as=opts.output_as,
password=opts.password,
timeout=opts.timeout,
parser_kwargs={'top_parser': parser},
)
sys.exit(ret)