Don't restart apps on update-common, use session messages
This commit is contained in:
parent
8f7cb14305
commit
340323a0b5
@ -13,7 +13,6 @@ class ActionItem:
|
|||||||
class ActionQueue:
|
class ActionQueue:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.actions = {}
|
self.actions = {}
|
||||||
# Priority 0 = restart/shutdown, 1 = config update, 2 = apps actions
|
|
||||||
self.queue = deque()
|
self.queue = deque()
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
|
@ -14,6 +14,7 @@ from cryptography.hazmat.primitives.asymmetric import ec
|
|||||||
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
||||||
|
|
||||||
from . import tools
|
from . import tools
|
||||||
|
from . import validator
|
||||||
|
|
||||||
PUB_FILE = '/etc/vmmgr/packages.pub'
|
PUB_FILE = '/etc/vmmgr/packages.pub'
|
||||||
LXC_ROOT = '/var/lib/lxc'
|
LXC_ROOT = '/var/lib/lxc'
|
||||||
@ -234,20 +235,18 @@ class AppMgr:
|
|||||||
|
|
||||||
def update_common_settings(self, email, gmaps_api_key):
|
def update_common_settings(self, email, gmaps_api_key):
|
||||||
# Update common configuration values
|
# Update common configuration values
|
||||||
if email != None:
|
if not validator.is_valid_email(email):
|
||||||
# Update email
|
raise validator.InvalidValueException('email', email)
|
||||||
if not validator.is_valid_email(email):
|
self.conf['common']['email'] = email
|
||||||
raise validator.InvalidValueException('email', email)
|
self.conf['common']['gmaps-api-key'] = gmaps_api_key
|
||||||
self.conf['common']['email'] = email
|
self.conf.save()
|
||||||
if gmaps_api_key != None:
|
|
||||||
# Update Google Maps API key
|
def update_repo_settings(self, url, user, pwd):
|
||||||
self.conf['common']['gmaps-api-key'] = gmaps_api_key
|
# Update lxc repository configuration
|
||||||
# Save config to file
|
self.conf['repo']['url'] = url
|
||||||
|
self.conf['repo']['user'] = user
|
||||||
|
self.conf['repo']['pwd'] = pwd
|
||||||
self.conf.save()
|
self.conf.save()
|
||||||
for app in self.conf['apps'].copy():
|
|
||||||
# Restart currently running apps in order to update their config
|
|
||||||
if tools.is_service_started(app):
|
|
||||||
tools.restart_service(app)
|
|
||||||
|
|
||||||
def hash_file(file_path):
|
def hash_file(file_path):
|
||||||
sha512 = hashlib.sha512()
|
sha512 = hashlib.sha512()
|
||||||
|
@ -31,6 +31,37 @@ class WSGIApp(object):
|
|||||||
self.appmgr.clean_pending_packages()
|
self.appmgr.clean_pending_packages()
|
||||||
self.jinja_env = Environment(loader=FileSystemLoader('/usr/share/vmmgr/templates'), autoescape=True, lstrip_blocks=True, trim_blocks=True)
|
self.jinja_env = Environment(loader=FileSystemLoader('/usr/share/vmmgr/templates'), autoescape=True, lstrip_blocks=True, trim_blocks=True)
|
||||||
self.jinja_env.globals.update(is_app_visible=self.is_app_visible)
|
self.jinja_env.globals.update(is_app_visible=self.is_app_visible)
|
||||||
|
self.url_map = Map((
|
||||||
|
Rule('/', endpoint='portal_view'),
|
||||||
|
Rule('/login', methods=['GET'], endpoint='login_view'),
|
||||||
|
Rule('/login', methods=['POST'], endpoint='login_action'),
|
||||||
|
Rule('/setup-host', redirect_to='/login?redir=setup-host'),
|
||||||
|
Rule('/setup-apps', redirect_to='/login?redir=setup-apps')
|
||||||
|
))
|
||||||
|
self.admin_url_map = Map((
|
||||||
|
Rule('/', endpoint='portal_view'),
|
||||||
|
Rule('/logout', endpoint='logout_action'),
|
||||||
|
Rule('/setup-host', endpoint='setup_host_view'),
|
||||||
|
Rule('/setup-apps', endpoint='setup_apps_view'),
|
||||||
|
Rule('/update-host', endpoint='update_host_action'),
|
||||||
|
Rule('/verify-dns', endpoint='verify_dns_action'),
|
||||||
|
Rule('/verify-https', endpoint='verify_http_action', defaults={'proto': 'https'}),
|
||||||
|
Rule('/verify-http', endpoint='verify_http_action', defaults={'proto': 'http'}),
|
||||||
|
Rule('/update-cert', endpoint='update_cert_action'),
|
||||||
|
Rule('/update-common', endpoint='update_common_action'),
|
||||||
|
Rule('/update-repo', endpoint='update_repo_action'),
|
||||||
|
Rule('/update-app-visibility', endpoint='update_app_visibility_action'),
|
||||||
|
Rule('/update-app-autostart', endpoint='update_app_autostart_action'),
|
||||||
|
Rule('/start-app', endpoint='start_app_action'),
|
||||||
|
Rule('/stop-app', endpoint='stop_app_action'),
|
||||||
|
Rule('/install-app', endpoint='install_app_action'),
|
||||||
|
Rule('/get-app-status', endpoint='get_app_status_action'),
|
||||||
|
Rule('/clear-app-status', endpoint='clear_app_status_action'),
|
||||||
|
Rule('/uninstall-app', endpoint='uninstall_app_action'),
|
||||||
|
Rule('/update-password', endpoint='update_password_action'),
|
||||||
|
Rule('/shutdown-vm', endpoint='shutdown_vm_action'),
|
||||||
|
Rule('/reboot-vm', endpoint='reboot_vm_action')
|
||||||
|
))
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
return self.wsgi_app(environ, start_response)
|
return self.wsgi_app(environ, start_response)
|
||||||
@ -42,15 +73,17 @@ class WSGIApp(object):
|
|||||||
request.session.lang = WSGILang()
|
request.session.lang = WSGILang()
|
||||||
# Dispatch request
|
# Dispatch request
|
||||||
response = self.dispatch_request(request)
|
response = self.dispatch_request(request)
|
||||||
# Save session if changed
|
|
||||||
request.session.save(response)
|
|
||||||
return response(environ, start_response)
|
return response(environ, start_response)
|
||||||
|
|
||||||
def dispatch_request(self, request):
|
def dispatch_request(self, request):
|
||||||
adapter = self.get_url_map(request.session).bind_to_environ(request.environ)
|
map = self.admin_url_map if request.session['admin'] else self.url_map
|
||||||
|
adapter = map.bind_to_environ(request.environ)
|
||||||
try:
|
try:
|
||||||
endpoint, values = adapter.match()
|
endpoint, values = adapter.match()
|
||||||
return getattr(self, endpoint)(request, **values)
|
response = getattr(self, endpoint)(request, **values)
|
||||||
|
# Save session if changed
|
||||||
|
request.session.save(response)
|
||||||
|
return response
|
||||||
except NotFound as e:
|
except NotFound as e:
|
||||||
# Return custom 404 page
|
# Return custom 404 page
|
||||||
response = self.render_html('404.html', request)
|
response = self.render_html('404.html', request)
|
||||||
@ -59,43 +92,6 @@ class WSGIApp(object):
|
|||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def get_url_map(self, session):
|
|
||||||
rules = [
|
|
||||||
Rule('/', endpoint='portal_view'),
|
|
||||||
Rule('/login', methods=['GET'], endpoint='login_view', defaults={'redirect': '/'}),
|
|
||||||
Rule('/login', methods=['POST'], endpoint='login_action'),
|
|
||||||
Rule('/logout', endpoint='logout_action')
|
|
||||||
]
|
|
||||||
if session['admin']:
|
|
||||||
rules += [
|
|
||||||
Rule('/setup-host', endpoint='setup_host_view'),
|
|
||||||
Rule('/setup-apps', endpoint='setup_apps_view'),
|
|
||||||
Rule('/update-host', endpoint='update_host_action'),
|
|
||||||
Rule('/verify-dns', endpoint='verify_dns_action'),
|
|
||||||
Rule('/verify-https', endpoint='verify_http_action', defaults={'proto': 'https'}),
|
|
||||||
Rule('/verify-http', endpoint='verify_http_action', defaults={'proto': 'http'}),
|
|
||||||
Rule('/update-cert', endpoint='update_cert_action'),
|
|
||||||
Rule('/update-common', endpoint='update_common_action'),
|
|
||||||
Rule('/update-repo', endpoint='update_repo_action'),
|
|
||||||
Rule('/update-app-visibility', endpoint='update_app_visibility_action'),
|
|
||||||
Rule('/update-app-autostart', endpoint='update_app_autostart_action'),
|
|
||||||
Rule('/start-app', endpoint='start_app_action'),
|
|
||||||
Rule('/stop-app', endpoint='stop_app_action'),
|
|
||||||
Rule('/install-app', endpoint='install_app_action'),
|
|
||||||
Rule('/get-app-status', endpoint='get_app_status_action'),
|
|
||||||
Rule('/clear-app-status', endpoint='clear_app_status_action'),
|
|
||||||
Rule('/uninstall-app', endpoint='uninstall_app_action'),
|
|
||||||
Rule('/update-password', endpoint='update_password_action'),
|
|
||||||
Rule('/shutdown-vm', endpoint='shutdown_vm_action'),
|
|
||||||
Rule('/reboot-vm', endpoint='reboot_vm_action'),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
rules += [
|
|
||||||
Rule('/setup-host', endpoint='login_view', defaults={'redirect': '/setup-host'}),
|
|
||||||
Rule('/setup-apps', endpoint='login_view', defaults={'redirect': '/setup-apps'}),
|
|
||||||
]
|
|
||||||
return Map(rules)
|
|
||||||
|
|
||||||
def render_template(self, template_name, request, **context):
|
def render_template(self, template_name, request, **context):
|
||||||
# Enhance context
|
# Enhance context
|
||||||
context['conf'] = self.conf
|
context['conf'] = self.conf
|
||||||
@ -112,17 +108,28 @@ class WSGIApp(object):
|
|||||||
def render_json(self, data):
|
def render_json(self, data):
|
||||||
return Response(json.dumps(data), mimetype='application/json')
|
return Response(json.dumps(data), mimetype='application/json')
|
||||||
|
|
||||||
|
def get_session_message(self, request):
|
||||||
|
# Consume and retrieve message stored in session
|
||||||
|
if 'msg' not in request.session:
|
||||||
|
return None
|
||||||
|
message = request.session['msg']
|
||||||
|
del request.session['msg']
|
||||||
|
# Message is in format location:type:text
|
||||||
|
return message.split(':', 3)
|
||||||
|
|
||||||
def login_view(self, request, **kwargs):
|
def login_view(self, request, **kwargs):
|
||||||
return self.render_html('login.html', request, redirect=kwargs['redirect'])
|
redir = request.args.get('redir')
|
||||||
|
message = self.get_session_message(request)
|
||||||
|
return self.render_html('login.html', request, redir=redir, message=message)
|
||||||
|
|
||||||
def login_action(self, request):
|
def login_action(self, request):
|
||||||
password = request.form['password']
|
password = request.form['password']
|
||||||
redir_url = request.form['redirect']
|
redir = request.form['redir']
|
||||||
if tools.adminpwd_verify(password, self.conf['host']['adminpwd']):
|
if tools.adminpwd_verify(password, self.conf['host']['adminpwd']):
|
||||||
request.session['admin'] = True
|
request.session['admin'] = True
|
||||||
return redirect(redir_url)
|
return redirect('/{}'.format(redir))
|
||||||
else:
|
request.session['msg'] = 'login:error:{}'.format(request.session.lang.bad_password())
|
||||||
return self.render_html('login.html', request, message=request.session.lang.bad_password())
|
return redirect('/login?redir={}'.format(redir)) if redir else redirect('/login')
|
||||||
|
|
||||||
def logout_action(self, request):
|
def logout_action(self, request):
|
||||||
request.session.reset()
|
request.session.reset()
|
||||||
@ -152,7 +159,8 @@ class WSGIApp(object):
|
|||||||
pass
|
pass
|
||||||
repo_reachable = bool(self.appmgr.online_packages)
|
repo_reachable = bool(self.appmgr.online_packages)
|
||||||
table = self.render_setup_apps_table(request)
|
table = self.render_setup_apps_table(request)
|
||||||
return self.render_html('setup-apps.html', request, repo_reachable=repo_reachable, table=table)
|
message = self.get_session_message(request)
|
||||||
|
return self.render_html('setup-apps.html', request, repo_reachable=repo_reachable, table=table, message=message)
|
||||||
|
|
||||||
def render_setup_apps_table(self, request):
|
def render_setup_apps_table(self, request):
|
||||||
lang = request.session.lang
|
lang = request.session.lang
|
||||||
@ -298,20 +306,26 @@ class WSGIApp(object):
|
|||||||
def update_common_action(self, request):
|
def update_common_action(self, request):
|
||||||
# Update common settings shared between apps - admin e-mail address, Google Maps API key
|
# Update common settings shared between apps - admin e-mail address, Google Maps API key
|
||||||
try:
|
try:
|
||||||
self.appmgr.update_common_settings(request.form['email'], request.form['gmaps-api-key'])
|
email = request.form['email']
|
||||||
|
gmaps_api_key = request.form['gmaps-api-key']
|
||||||
|
self.appmgr.update_common_settings(email, gmaps_api_key)
|
||||||
|
request.session['msg'] = 'common:info:{}'.format(request.session.lang.common_updated())
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
return self.render_json({'error': request.session.lang.malformed_request()})
|
return self.render_json({'error': request.session.lang.malformed_request()})
|
||||||
return self.render_json({'ok': request.session.lang.common_updated()})
|
except InvalidValueException:
|
||||||
|
request.session['msg'] = 'common:error:{}'.format(request.session.lang.invalid_email(email))
|
||||||
|
return redirect('/setup-apps')
|
||||||
|
|
||||||
def update_repo_action(self, request):
|
def update_repo_action(self, request):
|
||||||
# Update repository URL and credentials
|
# Update repository URL and credentials
|
||||||
try:
|
try:
|
||||||
self.conf['repo']['url'] = request.form['repourl']
|
url = request.form['repourl']
|
||||||
self.conf['repo']['user'] = request.form['repousername']
|
user = request.form['repousername']
|
||||||
self.conf['repo']['pwd'] = request.form['repopassword']
|
pwd = request.form['repopassword']
|
||||||
self.conf.save()
|
self.appmgr.update_repo_settings(url, user, pwd)
|
||||||
except:
|
request.session['msg'] = 'repo:info:{}'.format(request.session.lang.repo_updated())
|
||||||
pass
|
except BadRequest:
|
||||||
|
return self.render_json({'error': request.session.lang.malformed_request()})
|
||||||
return redirect('/setup-apps')
|
return redirect('/setup-apps')
|
||||||
|
|
||||||
def update_app_visibility_action(self, request):
|
def update_app_visibility_action(self, request):
|
||||||
|
@ -17,7 +17,9 @@ class WSGILang:
|
|||||||
'key_file_missing': 'Nebyl vybrán soubor se soukromým klíčem.',
|
'key_file_missing': 'Nebyl vybrán soubor se soukromým klíčem.',
|
||||||
'cert_request_error': 'Došlo k chybě při žádosti o certifikát. Zkontrolujte, zda je virtuální stroj dostupný z internetu na portu 80.',
|
'cert_request_error': 'Došlo k chybě při žádosti o certifikát. Zkontrolujte, zda je virtuální stroj dostupný z internetu na portu 80.',
|
||||||
'cert_installed': 'Certifikát byl úspěšně nainstalován. Přejděte na URL <a href="{}">{}</a> nebo restartujte webový prohlížeč pro jeho načtení.',
|
'cert_installed': 'Certifikát byl úspěšně nainstalován. Přejděte na URL <a href="{}">{}</a> nebo restartujte webový prohlížeč pro jeho načtení.',
|
||||||
'common_updated': 'Nastavení aplikací bylo úspěšně změněno.',
|
'invalid_email': 'Zadaný e-mail "{}" není platný.',
|
||||||
|
'common_updated': 'Nastavení aplikací bylo úspěšně změněno. Pokud je některá z aplikací spuštěna, změny se projeví po jejím restartu.',
|
||||||
|
'repo_updated': 'Nastavení distribučního serveru bylo úspěšně změněno.',
|
||||||
'stop_start_error': 'Došlo k chybě při spouštění/zastavování. Zkuste akci opakovat nebo restartuje virtuální stroj.',
|
'stop_start_error': 'Došlo k chybě při spouštění/zastavování. Zkuste akci opakovat nebo restartuje virtuální stroj.',
|
||||||
'installation_in_progress': 'Probíhá instalace jiného balíku. Vyčkejte na její dokončení.',
|
'installation_in_progress': 'Probíhá instalace jiného balíku. Vyčkejte na její dokončení.',
|
||||||
'package_manager_error': 'Došlo k chybě při instalaci aplikace. Zkuste akci opakovat nebo restartuje virtuální stroj.',
|
'package_manager_error': 'Došlo k chybě při instalaci aplikace. Zkuste akci opakovat nebo restartuje virtuální stroj.',
|
||||||
|
@ -8,7 +8,6 @@ $(function() {
|
|||||||
$('#verify-http').on('click', verify_http);
|
$('#verify-http').on('click', verify_http);
|
||||||
$('#cert-method').on('change', toggle_cert_method);
|
$('#cert-method').on('change', toggle_cert_method);
|
||||||
$('#update-cert').on('submit', update_cert);
|
$('#update-cert').on('submit', update_cert);
|
||||||
$('#update-common').on('submit', update_common);
|
|
||||||
$('#app-manager')
|
$('#app-manager')
|
||||||
.on('click', '.app-visible', update_app_visibility)
|
.on('click', '.app-visible', update_app_visibility)
|
||||||
.on('click', '.app-autostart', update_app_autostart)
|
.on('click', '.app-autostart', update_app_autostart)
|
||||||
@ -107,23 +106,6 @@ function update_cert() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_common() {
|
|
||||||
$('#common-submit').hide();
|
|
||||||
$('#common-message').hide();
|
|
||||||
$('#common-wait').show();
|
|
||||||
$.post('/update-common', {'email': $('#email').val(), 'gmaps-api-key': $('#gmaps-api-key').val()}, function(data) {
|
|
||||||
$('#common-wait').hide();
|
|
||||||
if (data.error) {
|
|
||||||
$('#common-message').attr('class','error').html(data.error).show();
|
|
||||||
$('#common-submit').show();
|
|
||||||
} else {
|
|
||||||
$('#common-message').attr('class','info').html(data.ok).show();
|
|
||||||
$('#common-submit').show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _update_app(item, ev) {
|
function _update_app(item, ev) {
|
||||||
var el = $(ev.target);
|
var el = $(ev.target);
|
||||||
var app = el.closest('tr').data('app');
|
var app = el.closest('tr').data('app');
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="setup-box">
|
<div class="setup-box">
|
||||||
<h2>Přihlášení</h2>
|
<h2>Přihlášení</h2>
|
||||||
<form action="/login" method="post">
|
<form method="post">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Jméno:</td>
|
<td>Jméno:</td>
|
||||||
@ -14,12 +14,12 @@
|
|||||||
<td><input type="password" name="password"></td>
|
<td><input type="password" name="password"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><input type="hidden" name="redirect" value="{{ redirect }}"></td>
|
<td><input type="hidden" name="redir" value="{{ redir }}"></td>
|
||||||
<td><input type="submit" value="Přihlásit"></td>
|
<td><input type="submit" value="Přihlásit"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
{% if message is defined %}
|
{% if message %}
|
||||||
<p class="error">{{ message }}</p>
|
<p class="{{ message[1] }}">{{ message[2] }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,6 +40,9 @@
|
|||||||
<td> </td>
|
<td> </td>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<input type="submit" id="repo-submit" value="Nastavit hodnoty">
|
<input type="submit" id="repo-submit" value="Nastavit hodnoty">
|
||||||
|
{% if message and message[0] == 'repo' %}
|
||||||
|
<div class="{{ message[1] }}">{{ message[2] }}</div>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -53,23 +56,21 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>E-mail</td>
|
<td>E-mail</td>
|
||||||
<td><input type="text" name="email" id="email" value="{{ conf['common']['email'] }}"></td>
|
<td><input type="text" name="email" value="{{ conf['common']['email'] }}"></td>
|
||||||
<td class="remark">Administrativní e-mail na který budou doručovány zprávy a upozornění z aplikací. Stejná e-mailová adresa bude také využita některými aplikacemi pro odesílání zpráv uživatelům.</td>
|
<td class="remark">Administrativní e-mail na který budou doručovány zprávy a upozornění z aplikací. Stejná e-mailová adresa bude také využita některými aplikacemi pro odesílání zpráv uživatelům.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Google Maps API klíč</td>
|
<td>Google Maps API klíč</td>
|
||||||
<td><input type="text" name="gmaps-api-key" id="gmaps-api-key" value="{{ conf['common']['gmaps-api-key'] }}"></td>
|
<td><input type="text" name="gmaps-api-key" value="{{ conf['common']['gmaps-api-key'] }}"></td>
|
||||||
<td class="remark">API klíč pro službu Google Maps, která je využita některými aplikacemi.</td>
|
<td class="remark">API klíč pro službu Google Maps, která je využita některými aplikacemi.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<input type="submit" id="common-submit" value="Nastavit hodnoty">
|
<input type="submit" id="common-submit" value="Nastavit hodnoty">
|
||||||
<div id="common-message"></div>
|
{% if message and message[0] == 'common' %}
|
||||||
<div id="common-wait" class="loader-wrap">
|
<div class="{{ message[1] }}">{{ message[2] }}</div>
|
||||||
<div class="loader"></div>
|
{% endif %}
|
||||||
<span>Provádí se změna nastavení, prosím čekejte...</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
Loading…
Reference in New Issue
Block a user