Merge branch 'dev' of github.com:YunoHost/moulinette into dev

This commit is contained in:
Kload 2013-12-07 17:54:02 +01:00
commit 797dd30549
5 changed files with 199 additions and 28 deletions

View file

@ -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 #

View file

@ -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

View file

@ -416,18 +416,23 @@ 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)
if hook_exec(app_tmp_folder + '/scripts/install', args_dict) == 0: try:
shutil.rmtree(app_tmp_folder) if hook_exec(app_tmp_folder + '/scripts/install', args_dict) == 0:
os.system('chmod -R 400 '+ app_setting_path) shutil.rmtree(app_tmp_folder)
os.system('chown -R root: '+ app_setting_path) os.system('chmod -R 400 '+ app_setting_path)
os.system('chown -R admin: '+ app_setting_path +'/scripts') os.system('chown -R root: '+ app_setting_path)
app_ssowatconf() os.system('chown -R admin: '+ app_setting_path +'/scripts')
win_msg(_("Installation complete")) app_ssowatconf()
else: win_msg(_("Installation complete"))
#TODO: display script fail messages else:
#TODO: display script fail messages
shutil.rmtree(app_setting_path)
shutil.rmtree(app_tmp_folder)
raise YunoHostError(1, _("Installation failed"))
except KeyboardInterrupt, EOFError:
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(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,8 +542,15 @@ 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())
app_ssowatconf() app_ssowatconf()
@ -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

View file

@ -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 'default' in arg: 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:
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'])

View file

@ -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 []