mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Merge branch 'dev' of github.com:YunoHost/moulinette into dev
This commit is contained in:
commit
797dd30549
5 changed files with 199 additions and 28 deletions
|
@ -339,6 +339,27 @@ app:
|
||||||
help: Delete the key
|
help: Delete the key
|
||||||
action: store_true
|
action: store_true
|
||||||
|
|
||||||
|
### app_service()
|
||||||
|
service:
|
||||||
|
action_help: Add or remove a YunoHost monitored service
|
||||||
|
api: POST /app/service/{service}
|
||||||
|
arguments:
|
||||||
|
service:
|
||||||
|
help: Service to add/remove
|
||||||
|
-s:
|
||||||
|
full: --status
|
||||||
|
help: Custom status command
|
||||||
|
-l:
|
||||||
|
full: --log
|
||||||
|
help: Absolute path to log file to display
|
||||||
|
-r:
|
||||||
|
full: --runlevel
|
||||||
|
help: Runlevel priority of the service
|
||||||
|
-R:
|
||||||
|
full: --remove
|
||||||
|
help: Remove service
|
||||||
|
action: store_true
|
||||||
|
|
||||||
### app_checkport()
|
### app_checkport()
|
||||||
checkport:
|
checkport:
|
||||||
action_help: Check availability of a local port
|
action_help: Check availability of a local port
|
||||||
|
@ -566,6 +587,18 @@ service:
|
||||||
nargs: "*"
|
nargs: "*"
|
||||||
metavar: NAME
|
metavar: NAME
|
||||||
|
|
||||||
|
### service_log()
|
||||||
|
log:
|
||||||
|
action_help: Log every log files of a service
|
||||||
|
arguments:
|
||||||
|
name:
|
||||||
|
help: Service name to log
|
||||||
|
-n:
|
||||||
|
full: --number
|
||||||
|
help: Number of lines to display
|
||||||
|
pattern: '^[0-9]+$'
|
||||||
|
default: 50
|
||||||
|
|
||||||
|
|
||||||
#############################
|
#############################
|
||||||
# Firewall #
|
# Firewall #
|
||||||
|
|
10
services.yml
10
services.yml
|
@ -1,22 +1,32 @@
|
||||||
nginx:
|
nginx:
|
||||||
status: service
|
status: service
|
||||||
|
log: /var/log/nginx
|
||||||
bind9:
|
bind9:
|
||||||
status: service
|
status: service
|
||||||
|
log: /var/log/daemon.log
|
||||||
dovecot:
|
dovecot:
|
||||||
status: service
|
status: service
|
||||||
|
log: [/var/log/mail.log,/var/log/mail.err]
|
||||||
postfix:
|
postfix:
|
||||||
status: service
|
status: service
|
||||||
|
log: [/var/log/mail.log,/var/log/mail.err]
|
||||||
mysql:
|
mysql:
|
||||||
status: service
|
status: service
|
||||||
|
log: [/var/log/mysql.log,/var/log/mysql.err]
|
||||||
glances:
|
glances:
|
||||||
status: service
|
status: service
|
||||||
tahoe-lafs:
|
tahoe-lafs:
|
||||||
status: ps aux | grep tahoe |grep -v grep
|
status: ps aux | grep tahoe |grep -v grep
|
||||||
|
log: /home/yunohost.backup/tahoe/logs/twistd.log
|
||||||
ssh:
|
ssh:
|
||||||
status: service
|
status: service
|
||||||
|
log: /var/log/auth.log
|
||||||
metronome:
|
metronome:
|
||||||
status: metronomectl status
|
status: metronomectl status
|
||||||
|
log: [/var/log/metronome/metronome.log,/var/log/metronome/metronome.err]
|
||||||
samba:
|
samba:
|
||||||
status: service
|
status: service
|
||||||
|
log: [/var/log/samba/log.smbd,/var/log/samba/log.nmbd]
|
||||||
slapd:
|
slapd:
|
||||||
status: service
|
status: service
|
||||||
|
log: /var/log/syslog
|
||||||
|
|
|
@ -416,6 +416,7 @@ def app_install(app, label=None, args=None):
|
||||||
# Move scripts and manifest to the right place
|
# Move scripts and manifest to the right place
|
||||||
os.system('cp '+ app_tmp_folder +'/manifest.json ' + app_setting_path)
|
os.system('cp '+ app_tmp_folder +'/manifest.json ' + app_setting_path)
|
||||||
os.system('cp -R ' + app_tmp_folder +'/scripts '+ app_setting_path)
|
os.system('cp -R ' + app_tmp_folder +'/scripts '+ app_setting_path)
|
||||||
|
try:
|
||||||
if hook_exec(app_tmp_folder + '/scripts/install', args_dict) == 0:
|
if hook_exec(app_tmp_folder + '/scripts/install', args_dict) == 0:
|
||||||
shutil.rmtree(app_tmp_folder)
|
shutil.rmtree(app_tmp_folder)
|
||||||
os.system('chmod -R 400 '+ app_setting_path)
|
os.system('chmod -R 400 '+ app_setting_path)
|
||||||
|
@ -428,6 +429,10 @@ def app_install(app, label=None, args=None):
|
||||||
shutil.rmtree(app_setting_path)
|
shutil.rmtree(app_setting_path)
|
||||||
shutil.rmtree(app_tmp_folder)
|
shutil.rmtree(app_tmp_folder)
|
||||||
raise YunoHostError(1, _("Installation failed"))
|
raise YunoHostError(1, _("Installation failed"))
|
||||||
|
except KeyboardInterrupt, EOFError:
|
||||||
|
shutil.rmtree(app_setting_path)
|
||||||
|
shutil.rmtree(app_tmp_folder)
|
||||||
|
raise YunoHostError(125, _("Interrupted"))
|
||||||
|
|
||||||
|
|
||||||
def app_remove(app):
|
def app_remove(app):
|
||||||
|
@ -520,7 +525,6 @@ def app_removeaccess(apps, users):
|
||||||
#TODO: Remove access
|
#TODO: Remove access
|
||||||
if not isinstance(users, list): users = [users]
|
if not isinstance(users, list): users = [users]
|
||||||
if not isinstance(apps, list): apps = [apps]
|
if not isinstance(apps, list): apps = [apps]
|
||||||
|
|
||||||
for app in apps:
|
for app in apps:
|
||||||
new_users = ''
|
new_users = ''
|
||||||
|
|
||||||
|
@ -530,7 +534,7 @@ def app_removeaccess(apps, users):
|
||||||
with open(apps_setting_path + app +'/settings.yml') as f:
|
with open(apps_setting_path + app +'/settings.yml') as f:
|
||||||
app_settings = yaml.load(f)
|
app_settings = yaml.load(f)
|
||||||
|
|
||||||
if 'mode' in app_settings and app_settings['mode'] == 'private':
|
if 'skipped_uris' not in app_settings or app_settings['skipped_uris'] != '/':
|
||||||
if 'allowed_users' in app_settings:
|
if 'allowed_users' in app_settings:
|
||||||
for allowed_user in app_settings['allowed_users'].split(','):
|
for allowed_user in app_settings['allowed_users'].split(','):
|
||||||
if allowed_user not in users:
|
if allowed_user not in users:
|
||||||
|
@ -538,6 +542,13 @@ def app_removeaccess(apps, users):
|
||||||
new_users = allowed_user
|
new_users = allowed_user
|
||||||
else:
|
else:
|
||||||
new_users = new_users +','+ allowed_user
|
new_users = new_users +','+ allowed_user
|
||||||
|
else:
|
||||||
|
new_users=''
|
||||||
|
for user in user_list()['Users']:
|
||||||
|
if user['Username'] not in users:
|
||||||
|
if new_users == '':
|
||||||
|
new_users = user['Username']
|
||||||
|
new_users=new_users+','+user['Username']
|
||||||
|
|
||||||
app_setting(app, 'allowed_users', new_users.strip())
|
app_setting(app, 'allowed_users', new_users.strip())
|
||||||
|
|
||||||
|
@ -581,6 +592,45 @@ def app_setting(app, key, value=None, delete=False):
|
||||||
yaml.safe_dump(app_settings, f, default_flow_style=False)
|
yaml.safe_dump(app_settings, f, default_flow_style=False)
|
||||||
|
|
||||||
|
|
||||||
|
def app_service(service, status=None, log=None, runlevel=None, remove=False):
|
||||||
|
"""
|
||||||
|
Add or remove a YunoHost monitored service
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
service -- Service to add/remove
|
||||||
|
status -- Custom status command
|
||||||
|
log -- Absolute path to log file to display
|
||||||
|
runlevel -- Runlevel priority of the service
|
||||||
|
remove -- Remove service
|
||||||
|
|
||||||
|
"""
|
||||||
|
service_file = '/etc/yunohost/services.yml'
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(service_file) as f:
|
||||||
|
services = yaml.load(f)
|
||||||
|
except IOError:
|
||||||
|
# Do not fail if service file is not there
|
||||||
|
services = {}
|
||||||
|
|
||||||
|
if remove and service in services:
|
||||||
|
del services[service]
|
||||||
|
else:
|
||||||
|
if status is None:
|
||||||
|
services[service] = { 'status': 'service' }
|
||||||
|
else:
|
||||||
|
services[service] = { 'status': status }
|
||||||
|
|
||||||
|
if log is not None:
|
||||||
|
services[service]['log'] = log
|
||||||
|
|
||||||
|
if runlevel is not None:
|
||||||
|
services[service]['runlevel'] = runlevel
|
||||||
|
|
||||||
|
with open(service_file, 'w') as f:
|
||||||
|
yaml.safe_dump(services, f, default_flow_style=False)
|
||||||
|
|
||||||
|
|
||||||
def app_checkport(port):
|
def app_checkport(port):
|
||||||
"""
|
"""
|
||||||
Check availability of a local port
|
Check availability of a local port
|
||||||
|
@ -690,14 +740,17 @@ def app_ssowatconf():
|
||||||
for user in user_list()['Users']:
|
for user in user_list()['Users']:
|
||||||
users[user['Username']] = app_map(user=user['Username'])
|
users[user['Username']] = app_map(user=user['Username'])
|
||||||
|
|
||||||
skipped_uri=[]
|
skipped_uri = []
|
||||||
apps={}
|
apps = {}
|
||||||
for app in app_list()['Apps']:
|
for app in app_list()['Apps']:
|
||||||
if _is_installed(app['ID']):
|
if _is_installed(app['ID']):
|
||||||
with open(apps_setting_path + app['ID'] +'/settings.yml') as f:
|
with open(apps_setting_path + app['ID'] +'/settings.yml') as f:
|
||||||
app_settings = yaml.load(f)
|
app_settings = yaml.load(f)
|
||||||
if 'skipped_uris' in app_settings:
|
if 'skipped_uris' in app_settings:
|
||||||
skipped_uri=[app_settings['domain'] + app_settings['path'][:-1] + item for item in app_settings['skipped_uris'].split(',')]
|
for item in app_settings['skipped_uris'].split(','):
|
||||||
|
if item[-1:] == '/':
|
||||||
|
item = item[:-1]
|
||||||
|
skipped_uri.append(app_settings['domain'] + app_settings['path'][:-1] + item)
|
||||||
|
|
||||||
for domain in domains:
|
for domain in domains:
|
||||||
skipped_uri.extend([domain +'/ynhadmin', domain +'/ynhapi'])
|
skipped_uri.extend([domain +'/ynhadmin', domain +'/ynhapi'])
|
||||||
|
@ -739,14 +792,16 @@ def _extract_app_from_file(path, remove=False):
|
||||||
"""
|
"""
|
||||||
global app_tmp_folder
|
global app_tmp_folder
|
||||||
|
|
||||||
|
print(_('Extracting...'))
|
||||||
|
|
||||||
if os.path.exists(app_tmp_folder): shutil.rmtree(app_tmp_folder)
|
if os.path.exists(app_tmp_folder): shutil.rmtree(app_tmp_folder)
|
||||||
os.makedirs(app_tmp_folder)
|
os.makedirs(app_tmp_folder)
|
||||||
|
|
||||||
if ".zip" in path:
|
if ".zip" in path:
|
||||||
extract_result = os.system('cd '+ os.getcwd() +' && unzip '+ path +' -d '+ app_tmp_folder)
|
extract_result = os.system('cd '+ os.getcwd() +' && unzip '+ path +' -d '+ app_tmp_folder +' > /dev/null 2>&1')
|
||||||
if remove: os.remove(path)
|
if remove: os.remove(path)
|
||||||
elif ".tar" in path:
|
elif ".tar" in path:
|
||||||
extract_result = os.system('cd '+ os.getcwd() +' && tar -xf '+ path +' -C '+ app_tmp_folder)
|
extract_result = os.system('cd '+ os.getcwd() +' && tar -xf '+ path +' -C '+ app_tmp_folder +' > /dev/null 2>&1')
|
||||||
if remove: os.remove(path)
|
if remove: os.remove(path)
|
||||||
elif (path[:1] == '/' and os.path.exists(path)) or (os.system('cd '+ os.getcwd() +'/'+ path) == 0):
|
elif (path[:1] == '/' and os.path.exists(path)) or (os.system('cd '+ os.getcwd() +'/'+ path) == 0):
|
||||||
shutil.rmtree(app_tmp_folder)
|
shutil.rmtree(app_tmp_folder)
|
||||||
|
@ -769,7 +824,7 @@ def _extract_app_from_file(path, remove=False):
|
||||||
except IOError:
|
except IOError:
|
||||||
raise YunoHostError(1, _("Invalid App file"))
|
raise YunoHostError(1, _("Invalid App file"))
|
||||||
|
|
||||||
win_msg(_("Sources extracted"))
|
print(_('OK'))
|
||||||
|
|
||||||
return manifest
|
return manifest
|
||||||
|
|
||||||
|
@ -787,13 +842,15 @@ def _fetch_app_from_git(app):
|
||||||
"""
|
"""
|
||||||
global app_tmp_folder
|
global app_tmp_folder
|
||||||
|
|
||||||
|
print(_('Downloading...'))
|
||||||
|
|
||||||
if ('@' in app) or ('http://' in app) or ('https://' in app):
|
if ('@' in app) or ('http://' in app) or ('https://' in app):
|
||||||
if "github.com" in app:
|
if "github.com" in app:
|
||||||
url = app.replace("git@github.com:", "https://github.com/")
|
url = app.replace("git@github.com:", "https://github.com/")
|
||||||
if ".git" in url[-4:]: url = url[:-4]
|
if ".git" in url[-4:]: url = url[:-4]
|
||||||
if "/" in url [-1:]: url = url[:-1]
|
if "/" in url [-1:]: url = url[:-1]
|
||||||
url = url + "/archive/master.zip"
|
url = url + "/archive/master.zip"
|
||||||
if os.system('wget "'+ url +'" -O "'+ app_tmp_folder +'.zip"') == 0:
|
if os.system('wget "'+ url +'" -O "'+ app_tmp_folder +'.zip" > /dev/null 2>&1') == 0:
|
||||||
return _extract_app_from_file(app_tmp_folder +'.zip', remove=True)
|
return _extract_app_from_file(app_tmp_folder +'.zip', remove=True)
|
||||||
|
|
||||||
git_result = os.system('git clone '+ app +' '+ app_tmp_folder)
|
git_result = os.system('git clone '+ app +' '+ app_tmp_folder)
|
||||||
|
@ -820,7 +877,7 @@ def _fetch_app_from_git(app):
|
||||||
if ".git" in url[-4:]: url = url[:-4]
|
if ".git" in url[-4:]: url = url[:-4]
|
||||||
if "/" in url [-1:]: url = url[:-1]
|
if "/" in url [-1:]: url = url[:-1]
|
||||||
url = url + "/archive/"+ str(app_info['git']['revision']) + ".zip"
|
url = url + "/archive/"+ str(app_info['git']['revision']) + ".zip"
|
||||||
if os.system('wget "'+ url +'" -O "'+ app_tmp_folder +'.zip"') == 0:
|
if os.system('wget "'+ url +'" -O "'+ app_tmp_folder +'.zip" > /dev/null 2>&1') == 0:
|
||||||
return _extract_app_from_file(app_tmp_folder +'.zip', remove=True)
|
return _extract_app_from_file(app_tmp_folder +'.zip', remove=True)
|
||||||
|
|
||||||
app_tmp_folder = install_tmp +'/'+ app
|
app_tmp_folder = install_tmp +'/'+ app
|
||||||
|
@ -832,7 +889,7 @@ def _fetch_app_from_git(app):
|
||||||
if not git_result == git_result_2 == 0:
|
if not git_result == git_result_2 == 0:
|
||||||
raise YunoHostError(22, _("Sources fetching failed"))
|
raise YunoHostError(22, _("Sources fetching failed"))
|
||||||
|
|
||||||
win_msg(_("Repository fetched"))
|
print(_('OK'))
|
||||||
|
|
||||||
return manifest
|
return manifest
|
||||||
|
|
||||||
|
|
|
@ -100,14 +100,25 @@ def hook_exec(file, args=None):
|
||||||
arg_list = []
|
arg_list = []
|
||||||
for arg in required_args:
|
for arg in required_args:
|
||||||
if arg['name'] in args:
|
if arg['name'] in args:
|
||||||
if 'choices' in arg and args[arg['name']] not in arg['choices'].split('|'):
|
if 'choices' in arg and args[arg['name']] not in arg['choices']:
|
||||||
raise YunoHostError(22, _("Invalid choice") + ': ' + args[arg['name']])
|
raise YunoHostError(22, _("Invalid choice") + ': ' + args[arg['name']])
|
||||||
arg_list.append(args[arg['name']])
|
arg_list.append(args[arg['name']])
|
||||||
else:
|
else:
|
||||||
|
if os.isatty(1) and 'ask' in arg:
|
||||||
|
ask_string = arg['ask']['en'] #TODO: I18n
|
||||||
|
if 'choices' in arg:
|
||||||
|
ask_string = ask_string +' ('+ '|'.join(arg['choices']) +')'
|
||||||
if 'default' in arg:
|
if 'default' in arg:
|
||||||
|
ask_string = ask_string +' (default: '+ arg['default'] +')'
|
||||||
|
|
||||||
|
input_string = raw_input(colorize(ask_string + ': ', 'cyan'))
|
||||||
|
|
||||||
|
if input_string == '' and 'default' in arg:
|
||||||
|
input_string = arg['default']
|
||||||
|
|
||||||
|
arg_list.append(input_string)
|
||||||
|
elif 'default' in arg:
|
||||||
arg_list.append(arg['default'])
|
arg_list.append(arg['default'])
|
||||||
elif os.isatty(1) and 'ask' in arg:
|
|
||||||
arg_list.append(raw_input(colorize(arg['ask']['en'] + ': ', 'cyan'))) #TODO: I18n
|
|
||||||
else:
|
else:
|
||||||
raise YunoHostError(22, _("Missing arguments") + ': ' + arg['name'])
|
raise YunoHostError(22, _("Missing arguments") + ': ' + arg['name'])
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,38 @@ def service_status(names=None):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def service_log(name, number=50):
|
||||||
|
"""
|
||||||
|
Log every log files of a service
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
name -- Services name to log
|
||||||
|
number -- Number of lines to display
|
||||||
|
|
||||||
|
"""
|
||||||
|
services = _get_services()
|
||||||
|
|
||||||
|
if name not in services.keys():
|
||||||
|
raise YunoHostError(1, _("Unknown service '%s'") % service)
|
||||||
|
|
||||||
|
if 'log' in services[name]:
|
||||||
|
log_list = services[name]['log']
|
||||||
|
result = {}
|
||||||
|
if not isinstance(log_list, list):
|
||||||
|
log_list = [log_list]
|
||||||
|
|
||||||
|
for log_path in log_list:
|
||||||
|
if os.path.isdir(log_path):
|
||||||
|
for log in [ f for f in os.listdir(log_path) if os.path.isfile(os.path.join(log_path, f)) and f[-4:] == '.log' ]:
|
||||||
|
result[os.path.join(log_path, log)] = _tail(os.path.join(log_path, log), int(number))
|
||||||
|
else:
|
||||||
|
result[log_path] = _tail(log_path, int(number))
|
||||||
|
else:
|
||||||
|
raise YunoHostError(1, _("Nothing to log for service '%s'") % name)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _run_service_command(action, service):
|
def _run_service_command(action, service):
|
||||||
"""
|
"""
|
||||||
Run services management command (start, stop, enable, disable)
|
Run services management command (start, stop, enable, disable)
|
||||||
|
@ -184,3 +216,31 @@ def _get_services():
|
||||||
with open('/etc/yunohost/services.yml', 'r') as f:
|
with open('/etc/yunohost/services.yml', 'r') as f:
|
||||||
services = yaml.load(f)
|
services = yaml.load(f)
|
||||||
return services
|
return services
|
||||||
|
|
||||||
|
|
||||||
|
def _tail(file, n, offset=None):
|
||||||
|
"""
|
||||||
|
Reads a n lines from f with an offset of offset lines. The return
|
||||||
|
value is a tuple in the form ``(lines, has_more)`` where `has_more` is
|
||||||
|
an indicator that is `True` if there are more lines in the file.
|
||||||
|
|
||||||
|
"""
|
||||||
|
avg_line_length = 74
|
||||||
|
to_read = n + (offset or 0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file, 'r') as f:
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
f.seek(-(avg_line_length * to_read), 2)
|
||||||
|
except IOError:
|
||||||
|
# woops. apparently file is smaller than what we want
|
||||||
|
# to step back, go to the beginning instead
|
||||||
|
f.seek(0)
|
||||||
|
pos = f.tell()
|
||||||
|
lines = f.read().splitlines()
|
||||||
|
if len(lines) >= to_read or pos == 0:
|
||||||
|
return lines[-to_read:offset and -offset or None]
|
||||||
|
avg_line_length *= 1.3
|
||||||
|
|
||||||
|
except IOError: return []
|
||||||
|
|
Loading…
Reference in a new issue