Commit f82a46e2 authored by Daniele Venzano's avatar Daniele Venzano

Allow resource limits to be set by the users on the web interface

parent 79c6919e
......@@ -50,7 +50,7 @@ def web_init(api_endpoint) -> List[tornado.web.URLSpec]:
tornado.web.url(r'/websocket', zoe_api.web.websockets.WebSocketEndpointWeb, route_args, name='websocket'),
tornado.web.url(r'/zapp-shop', zoe_api.web.zapp_shop.ZAppShopHomeWeb, route_args, name='zappshop'),
tornado.web.url(r'/zapp-shop/logo/([a-z\-.]+)', zoe_api.web.zapp_shop.ZAppLogoWeb, route_args, name='zappshop_logo'),
tornado.web.url(r'/zapp-shop/logo/([0-9a-z\-.]+)', zoe_api.web.zapp_shop.ZAppLogoWeb, route_args, name='zappshop_logo'),
tornado.web.url(r'/zapp-shop/start/([0-9a-z\-.]+)', zoe_api.web.zapp_shop.ZAppStartWeb, route_args, name='zappshop_start'),
tornado.web.url(r'/status', zoe_api.web.status.StatusEndpointWeb, route_args, name='status')
......
......@@ -186,6 +186,21 @@ form#zapp_start_form label.label-inline input {
display: inline;
}
.resource-form label {
float: left;
padding-bottom: 1.5em;
padding-right: 2em;
}
form#zapp_start_form h5,
form#zapp_start_form hr {
clear: both;
border: 0;
padding-bottom: 0;
margin-bottom: 10px;
}
/* footer */
div.status_line {
......
......@@ -3,9 +3,9 @@ function format_bytes(bytes, decimals) {
document.write('0 Byte');
return;
}
var k = 1000;
var k = 1024;
var dm = decimals + 1 || 3;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
var sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
document.write((bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i]);
}
......@@ -14,9 +14,9 @@ function format_bytes_ret(bytes, decimals) {
if(bytes === 0) {
return '0 Byte';
}
var k = 1000;
var k = 1024;
var dm = decimals + 1 || 3;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
var sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i];
}
......@@ -12,7 +12,7 @@
<div class="zapp-list">
{% for zapp in zapps[zapp_category] %}
<div class="zapp" id="{{ zapp.id }}-{{ zapp.manifest_index }}">
<img src="{{ reverse_url("zappshop_logo", zapp.id) }}" alt="logo">
<img src="{{ reverse_url("zappshop_logo", zapp.id + "-" + zapp.manifest_index|string) }}" alt="logo">
<p>{{ zapp.readable_name }}</p>
<div class="readable_description" id="rd-{{ zapp.id }}">{{ zapp.readable_description|safe }}</div>
</div>
......
......@@ -21,6 +21,7 @@ from zoe_api import zapp_shop
from zoe_api.api_endpoint import APIEndpoint # pylint: disable=unused-import
from zoe_api.web.utils import get_auth, catch_exceptions
from zoe_api.web.custom_request_handler import ZoeRequestHandler
from zoe_lib.config import get_conf
log = logging.getLogger(__name__)
......@@ -64,7 +65,12 @@ class ZAppLogoWeb(ZoeRequestHandler):
return self.redirect(self.get_argument('next', u'/login'))
self.set_header("Content-type", "image/png")
self.write(zapp_shop.get_logo(zapp_id))
manifest_index = int(zapp_id.split('-')[-1])
zapp_id = "-".join(zapp_id.split('-')[:-1])
zapps = zapp_shop.zshop_read_manifest(zapp_id)
zapp = zapps[manifest_index]
self.write(zapp_shop.get_logo(zapp))
class ZAppStartWeb(ZoeRequestHandler):
......@@ -89,7 +95,10 @@ class ZAppStartWeb(ZoeRequestHandler):
template_vars = {
"uid": uid,
"role": role,
'zapp': zapp
'zapp': zapp,
'max_core_limit': get_conf().max_core_limit,
'max_memory_limit': get_conf().max_memory_limit,
'resources_are_customizable': role == "admin" or (role != "guest" and (role == "user" and not get_conf().no_user_edit_limits_web))
}
self.render('zapp_start.html', **template_vars)
......@@ -107,7 +116,7 @@ class ZAppStartWeb(ZoeRequestHandler):
exec_name = self.get_argument('exec_name')
app_descr = self._set_parameters(zapp.zoe_description, zapp.parameters)
app_descr = self._set_parameters(zapp.zoe_description, zapp.parameters, role)
download_json = self.get_argument('download_json')
if download_json:
......@@ -121,17 +130,36 @@ class ZAppStartWeb(ZoeRequestHandler):
self.redirect(self.reverse_url('execution_inspect', new_id))
def _set_parameters(self, app_descr, params):
def _set_parameters(self, app_descr, params, role):
for param in params:
argument_name = param.name + '-' + param.kind
if param.kind == 'environment':
for service in app_descr['services']:
for env in service['environment']:
if env[0] == param.name:
env[1] = self.get_argument(param.name)
env[1] = self.get_argument(argument_name)
elif param.kind == 'command':
for service in app_descr['services']:
if service['name'] == param.name:
service['command'] = self.get_argument(param.name)
service['command'] = self.get_argument(argument_name)
break
elif param.kind == 'resource_memory_min' and role == "admin" or (role != "guest" and (role == "user" and not get_conf().no_user_edit_limits_web)):
for service in app_descr['services']:
if service['name'] == param.name:
if self.get_argument(argument_name) >= get_conf().max_memory_limit * (1024 ** 3):
val = get_conf().max_memory_limit * (1024 ** 3)
else:
val = self.get_argument(argument_name)
service["resources"]["memory"]["min"] = val
break
elif param.kind == 'resource_cores_min' and role == "admin" or (role != "guest" and (role == "user" and not get_conf().no_user_edit_limits_web)):
for service in app_descr['services']:
if service['name'] == param.name:
if self.get_argument(argument_name) >= get_conf().max_core_limit:
val = get_conf().max_core_limit
else:
val = self.get_argument(argument_name)
service["resources"]["cores"]["min"] = val
break
else:
log.warning('Unknown parameter kind: {}, ignoring...'.format(param.kind))
......
......@@ -62,6 +62,10 @@ class ZApp:
self.guest_access = zapp['guest_access']
else:
self.guest_access = False
if 'logo' in zapp:
self.logo = zapp['logo']
else:
self.logo = 'logo.png'
def parse_parameters(self, zapp_manifest):
"""Translates the parameters from the manifest into objects."""
......@@ -110,7 +114,7 @@ def zshop_read_manifest(zapp_id):
return zapps
def get_logo(zapp_id):
def get_logo(zapp: ZApp):
"""Return the ZApp PNG logo image file contents."""
logo_path = os.path.join(get_conf().zapp_shop_path, zapp_id, 'logo.png')
logo_path = os.path.join(get_conf().zapp_shop_path, zapp.id, zapp.logo)
return open(logo_path, "rb").read()
......@@ -116,6 +116,9 @@ def load_configuration(test_conf=None):
# other options
argparser.add_argument('--zapp-shop-path', help='Path where ZApp folders are stored', default='/var/lib/zoe-apps')
argparser.add_argument('--log-file', help='output logs to a file', default='stderr')
argparser.add_argument('--max-core-limit', help='Maximum amount of cores users are able to reserve', type=int, default=16)
argparser.add_argument('--max-memory-limit', help='Maximum amount of memory services can use (in GiB)', type=int, default=64)
argparser.add_argument('--no-user-edit-limits-web', action='store_true', help='Disable editing ZApp resource limits from the web interface (only admins will able to)')
argparser.add_argument('--aml-ttl', help='TimeToLive in hours for AML executions', type=int, default=4)
......
......@@ -35,11 +35,15 @@ class ServiceInstance:
self.memory_limit = None
else:
self.memory_limit = service.resource_reservation.memory
if self.memory_limit.max > get_conf().max_memory_limit:
self.memory_limit.max = get_conf().max_memory_limit
if service.resource_reservation.cores.min is None:
self.core_limit = None
else:
self.core_limit = service.resource_reservation.cores
if self.core_limit.max > get_conf().max_core_limit:
self.core_limit = get_conf().max_core_limit
self.labels = {
'zoe.execution.name': execution.name,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment