Commit 04ac52e9 authored by Daniele Venzano's avatar Daniele Venzano

Modify existing pages for the new internal organization (templates are not yet revised)

parent 6501c61e
# Copyright (c) 2015, Daniele Venzano
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from flask import Flask
from zoe_web.web import web_bp
app = Flask(__name__, static_url_path='/does-not-exist')
app.register_blueprint(web_bp, url_prefix='')
......@@ -15,6 +15,8 @@
from zoe_lib.configargparse import ArgumentParser, Namespace
api_endpoint = None # singleton
config_paths = [
'zoe-web.conf',
'/etc/zoe/zoe-web.conf'
......@@ -33,8 +35,23 @@ def load_configuration():
argparser.add_argument('--debug', action='store_true', help='Enable debug output')
argparser.add_argument('--listen-address', type=str, help='Address to listen to for incoming connections', default="0.0.0.0")
argparser.add_argument('--listen-port', type=int, help='Port to listen to for incoming connections', default=5001)
argparser.add_argument('--master-url', help='URL of the Zoe master process', default='http://127.0.0.1:4850')
argparser.add_argument('--cookie-secret', help='key used to encrypt cookies', default="hr4h3H'kmn F8fz/;CJN5a!")
argparser.add_argument('--master-url', help='URL of the Zoe master process', default='tcp://127.0.0.1:4850')
argparser.add_argument('--deployment-name', help='name of this Zoe deployment', default='prod')
# argparser.add_argument('--cookie-secret', help='key used to encrypt cookies', default="hr4h3H'kmn F8fz/;CJN5a!")
argparser.add_argument('--ldap-server-uri', help='LDAP server to use for authentication', default='ldap://localhost')
argparser.add_argument('--ldap-base-dn', help='LDAP base DN for users', default='ou=something,dc=any,dc=local')
argparser.add_argument('--ldap-bind-user', help='LDAP user to bind as for user lookup', default='cn=guest,dc=bigfoot,dc=eurecom,dc=fr')
argparser.add_argument('--ldap-bind-password', help='LDAP user password', default='notsosecret')
argparser.add_argument('--ldap-admin-gid', type=int, help='LDAP group ID for admins', default=5000)
argparser.add_argument('--ldap-user-gid', type=int, help='LDAP group ID for users', default=5001)
argparser.add_argument('--ldap-guest-gid', type=int, help='LDAP group ID for guests', default=5002)
argparser.add_argument('--dbname', help='DB name', default='zoe')
argparser.add_argument('--dbuser', help='DB user', default='zoe')
argparser.add_argument('--dbpass', help='DB password', default='zoe')
argparser.add_argument('--dbhost', help='DB hostname', default='localhost')
argparser.add_argument('--dbport', type=int, help='DB port', default=5432)
opts = argparser.parse_args()
if opts.debug:
......
# Copyright (c) 2015, Daniele Venzano
# Copyright (c) 2016, Daniele Venzano
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
......@@ -15,12 +15,16 @@
import logging
from flask import Flask
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from zoe_web import app
from zoe_web.config import load_configuration, get_conf
import zoe_web.config as config
import zoe_web.db_init
import zoe_web.api_endpoint
import zoe_web.rest_api
import zoe_web.web
log = logging.getLogger("zoe_web")
LOG_FORMAT = '%(asctime)-15s %(levelname)s %(name)s (%(threadName)s): %(message)s'
......@@ -31,8 +35,8 @@ def zoe_web_main() -> int:
This is the entry point for the Zoe Web script.
:return: int
"""
load_configuration()
args = get_conf()
config.load_configuration()
args = config.get_conf()
if args.debug:
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)
else:
......@@ -41,8 +45,15 @@ def zoe_web_main() -> int:
logging.getLogger("tornado").setLevel(logging.DEBUG)
log.info("Starting HTTP server...")
app = Flask(__name__, static_url_path='/does-not-exist')
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
app.secret_key = args.cookie_secret
app.register_blueprint(zoe_web.rest_api.api_init())
app.register_blueprint(zoe_web.web.web_init())
zoe_web.db_init.init()
config.api_endpoint = zoe_web.api_endpoint.APIEndpoint()
http_server = HTTPServer(WSGIContainer(app))
http_server.listen(args.listen_port, args.listen_address)
......
# Copyright (c) 2016, Daniele Venzano
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class ZoeException(Exception):
"""
A generic exception.
"""
def __init__(self, message='Something happened'):
self.message = message
def __str__(self):
return self.message
class ZoeAuthException(ZoeException):
pass
class ZoeNotFoundException(ZoeException):
pass
class ZoeRestAPIException(ZoeException):
"""
An exception generated by the REST API.
"""
def __init__(self, message, status_code=400, headers=None):
super().__init__(message)
self.status_code = status_code
self.headers = headers
def __str__(self):
return '[{}] {}'.format(self.status_code, self.message)
......@@ -14,16 +14,29 @@
# limitations under the License.
from flask import Blueprint
web_bp = Blueprint('web', __name__, template_folder='templates', static_folder='static')
import zoe_web.web.start
import zoe_web.web.executions
from zoe_lib.version import ZOE_API_VERSION, ZOE_VERSION
@web_bp.context_processor
def web_init() -> Blueprint:
web_bp = Blueprint('web', __name__, template_folder='templates', static_folder='static')
web_bp.context_processor(inject_version)
web_bp.add_url_rule('/', 'index', zoe_web.web.start.index)
web_bp.add_url_rule('/user', 'home_user', zoe_web.web.start.home_user)
web_bp.add_url_rule('/executions/new', 'execution_define', zoe_web.web.executions.execution_define)
web_bp.add_url_rule('/executions/start', 'execution_start', zoe_web.web.executions.execution_start, methods=['POST'])
web_bp.add_url_rule('/executions/restart/<int:execution_id>', 'execution_restart', zoe_web.web.executions.execution_restart)
web_bp.add_url_rule('/executions/terminate/<int:execution_id>', 'execution_terminate', zoe_web.web.executions.execution_terminate)
web_bp.add_url_rule('/executions/inspect/<int:execution_id>', 'execution_inspect', zoe_web.web.executions.execution_inspect)
return web_bp
def inject_version():
return {
'zoe_version': ZOE_VERSION,
......
......@@ -14,106 +14,65 @@
# limitations under the License.
import json
import re
from flask import render_template, request, redirect, url_for
from zoe_lib.services import ZoeServiceAPI
from zoe_lib.executions import ZoeExecutionsAPI
import zoe_lib.exceptions
import zoe_lib.applications
from zoe_web.web.utils import get_auth, catch_exceptions
import zoe_web.config as config
import zoe_web.api_endpoint
import zoe_web.exceptions
from zoe_web.config import get_conf
from zoe_web.web import web_bp
from zoe_web.web.auth import missing_auth
def error_page(error_message, status):
return render_template('error.html', error=error_message), status
@web_bp.route('/executions/new')
@catch_exceptions
def execution_define():
auth = request.authorization
if not auth:
return missing_auth()
get_auth(request)
return render_template('execution_new.html')
@web_bp.route('/executions/start', methods=['POST'])
@catch_exceptions
def execution_start():
auth = request.authorization
if not auth:
return missing_auth()
guest_identifier = auth.username
guest_password = auth.password
app_descr_json = request.files['file'].read()
app_descr = json.loads(app_descr_json.decode('utf-8'))
try:
zoe_lib.applications.app_validate(app_descr)
except zoe_lib.exceptions.InvalidApplicationDescription as e:
return error_page(e.message, 400)
uid, role = get_auth(request)
assert isinstance(config.api_endpoint, zoe_web.api_endpoint.APIEndpoint)
app_descr_json = request.files['file'].read().decode('utf-8')
app_descr = json.loads(app_descr_json)
exec_name = request.form['exec_name']
if 3 > len(exec_name) > 128:
return error_page("Execution name must be between 4 and 128 characters long", 400)
if not re.match(r'^[a-zA-Z0-9\-]+$', exec_name):
return error_page("Execution name can contain only letters, numbers and dashes. '{}' is not valid.".format(exec_name))
exec_api = ZoeExecutionsAPI(get_conf().master_url, guest_identifier, guest_password)
new_id = exec_api.execution_start(exec_name, app_descr)
new_id = config.api_endpoint.execution_start(uid, role, exec_name, app_descr)
return redirect(url_for('web.execution_inspect', execution_id=new_id))
@web_bp.route('/executions/restart/<int:execution_id>')
@catch_exceptions
def execution_restart(execution_id):
auth = request.authorization
if not auth:
return missing_auth()
uid, role = get_auth(request)
assert isinstance(config.api_endpoint, zoe_web.api_endpoint.APIEndpoint)
guest_identifier = auth.username
guest_password = auth.password
exec_api = ZoeExecutionsAPI(get_conf().master_url, guest_identifier, guest_password)
e = exec_api.execution_get(execution_id)
new_id = exec_api.execution_start(e['name'], e['application'])
e = config.api_endpoint.execution_by_id(uid, role, execution_id)
new_id = config.api_endpoint.execution_start(uid, role, e.name, e.description)
return redirect(url_for('web.execution_inspect', execution_id=new_id))
@web_bp.route('/executions/terminate/<int:execution_id>')
@catch_exceptions
def execution_terminate(execution_id):
auth = request.authorization
if not auth:
return missing_auth()
guest_identifier = auth.username
guest_password = auth.password
uid, role = get_auth(request)
assert isinstance(config.api_endpoint, zoe_web.api_endpoint.APIEndpoint)
exec_api = ZoeExecutionsAPI(get_conf().master_url, guest_identifier, guest_password)
e = exec_api.execution_get(execution_id)
exec_api.terminate(execution_id)
success, message = config.api_endpoint.execution_terminate(uid, role, execution_id)
if not success:
raise zoe_web.exceptions.ZoeException(message)
return redirect(url_for('web.home_user'))
@web_bp.route('/executions/inspect/<int:execution_id>')
@catch_exceptions
def execution_inspect(execution_id):
auth = request.authorization
if not auth:
return missing_auth()
guest_identifier = auth.username
guest_password = auth.password
exec_api = ZoeExecutionsAPI(get_conf().master_url, guest_identifier, guest_password)
cont_api = ZoeServiceAPI(get_conf().master_url, guest_identifier, guest_password)
uid, role = get_auth(request)
assert isinstance(config.api_endpoint, zoe_web.api_endpoint.APIEndpoint)
e = exec_api.execution_get(execution_id)
e = config.api_endpoint.execution_by_id(uid, role, execution_id)
services = []
for sid in e['services']:
......
......@@ -19,61 +19,43 @@ import json
from flask import render_template, request
from zoe_lib.services import ZoeServiceAPI
from zoe_lib.executions import ZoeExecutionsAPI
from zoe_lib.query import ZoeQueryAPI
from zoe_lib.users import ZoeUserAPI
from zoe_lib.exceptions import ZoeAPIException
from zoe_web.config import get_conf
from zoe_web.web import web_bp
from zoe_web.web.auth import missing_auth
import zoe_web.config as config
import zoe_web.api_endpoint
from zoe_web.web.utils import get_auth, catch_exceptions
guest_id_pattern = re.compile('^\w+$')
@web_bp.route('/', methods=['GET', 'POST'])
@catch_exceptions
def index():
return render_template('index.html')
@web_bp.route('/guest')
def home_guest():
auth = request.authorization
if not auth:
return missing_auth()
guest_identifier = auth.username
guest_password = auth.password
match = guest_id_pattern.match(guest_identifier)
if match is None:
return missing_auth()
query_api = ZoeQueryAPI(get_conf().master_url, guest_identifier, guest_password)
user_api = ZoeUserAPI(get_conf().master_url, guest_identifier, guest_password)
@catch_exceptions
def home_user():
uid, role = get_auth(request)
assert isinstance(config.api_endpoint, zoe_web.api_endpoint.APIEndpoint)
template_vars = {
'refresh': randint(2, 8),
'user_gateway': 'Please wait...',
'execution_status': 'Please wait...',
'execution_urls': [],
'guest_identifier': guest_identifier
}
if role == 'user' or role == 'admin':
executions = config.api_endpoint.execution_list(uid, role)
try:
user = user_api.get(guest_identifier)
except ZoeAPIException:
return missing_auth()
if user is None:
return missing_auth()
template_vars = {
'executions': executions,
'is_admin': role == 'admin',
}
return render_template('home_user.html', **template_vars)
else:
template_vars['user_gateway'] = user['gateway_urls'][0]
template_vars['gateway_ip'] = user['gateway_urls'][0].split('/')[2].split(':')[0]
exec_api = ZoeExecutionsAPI(get_conf().master_url, guest_identifier, guest_password)
template_vars = {
'refresh': randint(2, 8),
'execution_status': 'Please wait...',
'execution_urls': [],
}
app_descr = json.load(open('contrib/zoeapps/eurecom_aml_lab.json', 'r'))
execution = query_api.query('execution', name='aml-lab')
execution = config.api_endpoint.execution_list(uid, role, name='aml-lab')
if len(execution) == 0 or execution[0]['status'] == 'terminated' or execution[0]['status'] == 'finished':
exec_api.execution_start('aml-lab', app_descr)
config.api_endpoint.execution_start(uid, role, 'aml-lab', app_descr)
template_vars['execution_status'] = 'submitted'
return render_template('home_guest.html', **template_vars)
else:
......@@ -91,35 +73,3 @@ def home_guest():
for p in c['ports']:
template_vars['execution_urls'].append(('{}'.format(p['name']), '{}://{}:{}{}'.format(p['protocol'], ip, p['port_number'], p['path'])))
return render_template('home_guest.html', **template_vars)
@web_bp.route('/user')
def home_user():
auth = request.authorization
if not auth:
return missing_auth()
guest_identifier = auth.username
guest_password = auth.password
query_api = ZoeQueryAPI(get_conf().master_url, guest_identifier, guest_password)
try:
user = query_api.query('user', name=guest_identifier)
except ZoeAPIException:
return missing_auth()
if len(user) == 0:
return missing_auth()
user = user[0]
if user['role'] == 'guest':
return missing_auth()
executions = query_api.query('execution')
template_vars = {
'executions': executions,
'is_admin': user['role'] == 'admin',
'user_gateway': user['gateway_urls'][0]
}
return render_template('home_user.html', **template_vars)
# Copyright (c) 2016, Daniele Venzano
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from flask import Response, render_template
from zoe_web.auth.ldap import LDAPAuthenticator
import zoe_web.exceptions
log = logging.getLogger(__name__)
def catch_exceptions(func):
"""
Decorator function used to work around the static exception system available in Flask-RESTful
:param func:
:return:
"""
def func_wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except zoe_web.exceptions.ZoeAuthException:
return missing_auth()
except zoe_web.exceptions.ZoeNotFoundException as e:
return error_page(e.message, 404)
except zoe_web.exceptions.ZoeException as e:
return error_page(e, 400)
except Exception as e:
log.exception(str(e))
return {'message': str(e)}, 500
return func_wrapper
def missing_auth():
"""Sends a 401 response that enables basic auth"""
return Response('Could not verify your access level for that URL.\nYou have to login with proper credentials',
401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def get_auth(request):
auth = request.authorization
if not auth:
raise zoe_web.exceptions.ZoeAuthException
authenticator = LDAPAuthenticator()
uid, role = authenticator.auth(auth.username, auth.password)
if uid is None:
raise zoe_web.exceptions.ZoeAuthException
return uid, role
def error_page(error_message, status):
return render_template('error.html', error=error_message), status
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