From 1d73f2824844ae20c6b2e0a8652007707493bd67 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Thu, 13 Sep 2018 09:27:02 -0700 Subject: [PATCH] refactoring to rest app to help breakup api code into smaller files --- webapp/app.py | 820 ++----------------------------------- webapp/core_utils.py | 81 ++++ webapp/emane_routes.py | 105 +++++ webapp/hook_routes.py | 36 ++ webapp/link_routes.py | 119 ++++++ webapp/mobility_routes.py | 8 +- webapp/node_routes.py | 149 +++++++ webapp/service_routes.py | 85 ++++ webapp/session_routes.py | 163 ++++++++ webapp/websocket_routes.py | 42 ++ webapp/wlan_routes.py | 27 ++ webapp/xml_routes.py | 47 +++ 12 files changed, 886 insertions(+), 796 deletions(-) create mode 100644 webapp/emane_routes.py create mode 100644 webapp/hook_routes.py create mode 100644 webapp/link_routes.py create mode 100644 webapp/node_routes.py create mode 100644 webapp/service_routes.py create mode 100644 webapp/session_routes.py create mode 100644 webapp/websocket_routes.py create mode 100644 webapp/wlan_routes.py create mode 100644 webapp/xml_routes.py diff --git a/webapp/app.py b/webapp/app.py index 9955ce51..3305ecc0 100644 --- a/webapp/app.py +++ b/webapp/app.py @@ -1,156 +1,48 @@ -import os -import tempfile -from functools import wraps -from threading import Lock - from bottle import HTTPError from flask import Flask from flask import jsonify -from flask import render_template from flask import request -from flask import send_file -from flask_socketio import SocketIO -from flask_socketio import emit -import core_utils +import emane_routes +import hook_routes +import link_routes import mobility_routes -from core import logger +import node_routes +import service_routes +import session_routes +import websocket_routes +import wlan_routes +import xml_routes from core.emulator.coreemu import CoreEmu -from core.emulator.emudata import InterfaceData -from core.emulator.emudata import LinkOptions -from core.emulator.emudata import NodeOptions -from core.enumerations import EventTypes -from core.enumerations import LinkTypes -from core.enumerations import NodeTypes -from core.misc import nodeutils from core.misc.ipaddress import Ipv4Prefix, Ipv6Prefix -from core.mobility import BasicRangeModel -from core.service import ServiceManager -CORE_LOCK = Lock() coreemu = CoreEmu() app = Flask(__name__) -socketio = SocketIO(app) app.config["SECRET_KEY"] = "core" - -mobility_routes.coreemu = coreemu -app.register_blueprint(mobility_routes.mobility_api, url_prefix="/sessions/") +websocket_routes.register(app) -def synchronized(function): - global CORE_LOCK +def register_blueprint(blueprint): + """ + Register api module and set coreemu object. - @wraps(function) - def wrapper(*args, **kwargs): - with CORE_LOCK: - return function(*args, **kwargs) - - return wrapper + :param module blueprint: module that defines api routes + :return: nothing + """ + blueprint.coreemu = coreemu + app.register_blueprint(blueprint.api) -def link_data_str(link, key): - value = link.get(key) - if value: - link[key] = str(value) - - -def convert_value(value): - if value is None: - return value - else: - return str(value) - - -def convert_link(session, link_data): - interface_one = None - interface_two = None - - if link_data.interface1_id is not None: - node = session.get_object(link_data.node1_id) - interface = node.netif(link_data.interface1_id) - interface_one = { - "id": link_data.interface1_id, - "name": interface.name, - "mac": convert_value(link_data.interface1_mac), - "ip4": convert_value(link_data.interface1_ip4), - "ip4mask": link_data.interface1_ip4_mask, - "ip6": convert_value(link_data.interface1_ip6), - "ip6mask": link_data.interface1_ip6_mask, - } - - if link_data.interface2_id is not None: - node = session.get_object(link_data.node2_id) - interface = node.netif(link_data.interface2_id) - interface_two = { - "id": link_data.interface2_id, - "name": interface.name, - "mac": convert_value(link_data.interface2_mac), - "ip4": convert_value(link_data.interface2_ip4), - "ip4mask": link_data.interface2_ip4_mask, - "ip6": convert_value(link_data.interface2_ip6), - "ip6mask": link_data.interface2_ip6_mask, - } - - return { - "node_one": link_data.node1_id, - "node_two": link_data.node2_id, - "type": link_data.link_type, - "interface_one": interface_one, - "interface_two": interface_two, - "options": { - "opaque": link_data.opaque, - "jitter": link_data.jitter, - "key": link_data.key, - "mburst": link_data.mburst, - "mer": link_data.mer, - "per": link_data.per, - "bandwidth": link_data.bandwidth, - "burst": link_data.burst, - "delay": link_data.delay, - "dup": link_data.dup, - "unidirectional": link_data.unidirectional - } - } - - -def broadcast_event(event): - socketio.emit("event", { - "node": event.node, - "event_type": event.event_type, - "name": event.name, - "data": event.data, - "time": event.time, - "session": event.session - }) - - -def broadcast_node(node): - socketio.emit("node", { - "id": node.id, - "name": node.name, - "model": node.model, - "position": { - "x": node.x_position, - "y": node.y_position, - }, - "services": node.services.split("|"), - }) - - -@socketio.on("connect") -def websocket_connect(): - emit("info", {"message": "You are connected!"}) - - -@socketio.on("disconnect") -def websocket_disconnect(): - logger.info("websocket client disconnected") - - -@app.route("/") -def home(): - return render_template("index.html") +register_blueprint(emane_routes) +register_blueprint(hook_routes) +register_blueprint(link_routes) +register_blueprint(mobility_routes) +register_blueprint(node_routes) +register_blueprint(service_routes) +register_blueprint(session_routes) +register_blueprint(wlan_routes) +register_blueprint(xml_routes) @app.route("/ips", methods=["POST"]) @@ -173,666 +65,10 @@ def get_ips(): ) -@app.route("/sessions//xml") -def save_xml(session_id): - session = coreemu.sessions.get(session_id) - if not session: - return jsonify(error="session does not exist"), 404 - - _, temp_path = tempfile.mkstemp() - session.save_xml(temp_path, "1.0") - return send_file( - temp_path, - as_attachment=True, - attachment_filename="session-%s.xml" % session_id - ) - - -@app.route("/sessions/xml", methods=["POST"]) -def open_xml(): - session = coreemu.create_session() - session.set_state(EventTypes.CONFIGURATION_STATE) - - logger.info("open xml: %s", request.files) - _, temp_path = tempfile.mkstemp() - session_file = request.files['session'] - session_file.save(temp_path) - - try: - session.open_xml(temp_path, start=True) - return jsonify(id=session.session_id) - except: - logger.exception("error opening session file") - coreemu.delete_session(session.session_id) - return jsonify(error="error opening session file"), 404 - - -@app.route("/services") -def get_services(): - groups = {} - for service in ServiceManager.services.itervalues(): - service_group = groups.setdefault(service.group, []) - service_group.append(service.name) - - return jsonify(groups=groups) - - -@app.route("/sessions") -def get_sessions(): - sessions = [] - for session in coreemu.sessions.itervalues(): - sessions.append({ - "id": session.session_id, - "state": session.state, - "nodes": session.get_node_count() - }) - return jsonify(sessions=sessions) - - -@app.route("/sessions", methods=["POST"]) -@synchronized -def create_session(): - session = coreemu.create_session() - session.set_state(EventTypes.DEFINITION_STATE) - - # set session location - session.location.setrefgeo(47.57917, -122.13232, 2.0) - session.location.refscale = 150.0 - - # add handlers - session.event_handlers.append(broadcast_event) - session.node_handlers.append(broadcast_node) - - response_data = jsonify( - id=session.session_id, - state=session.state, - url="/sessions/%s" % session.session_id - ) - return response_data, 201 - - -@app.route("/sessions/", methods=["DELETE"]) -@synchronized -def delete_session(session_id): - result = coreemu.delete_session(session_id) - if result: - return jsonify() - else: - return jsonify(error="session does not exist"), 404 - - -@app.route("/sessions//options") -def get_session_options(session_id): - session = core_utils.get_session(coreemu, session_id) - config = session.options.get_configs() - - config_options = [] - for configuration in session.options.configurations(): - value = config[configuration.id] - config_options.append({ - "label": configuration.label, - "name": configuration.id, - "value": value, - "type": configuration.type.value, - "select": configuration.options - }) - - response = [] - for config_group in session.emane.emane_config.config_groups(): - start = config_group.start - 1 - stop = config_group.stop - response.append({ - "name": config_group.name, - "options": config_options[start: stop] - }) - - return jsonify(groups=response) - - -@app.route("/sessions//options", methods=["PUT"]) -@synchronized -def set_session_options(session_id): - session = core_utils.get_session(coreemu, session_id) - data = request.get_json() or {} - values = data["values"] - config = {x["name"]: x["value"] for x in values} - session.options.set_configs(config) - return jsonify() - - -@app.route("/sessions/") -def get_session(session_id): - session = core_utils.get_session(coreemu, session_id) - - nodes = [] - links = [] - for node in session.objects.itervalues(): - emane_model = None - if nodeutils.is_node(node, NodeTypes.EMANE): - emane_model = node.model.name - - services = [x.name for x in getattr(node, "services", [])] - nodes.append({ - "id": node.objid, - "name": node.name, - "type": nodeutils.get_node_type(node.__class__).value, - "model": getattr(node, "type", None), - "position": { - "x": node.position.x, - "y": node.position.y, - "z": node.position.z - }, - "services": services, - "emane": emane_model, - "url": "/sessions/%s/nodes/%s" % (session_id, node.objid) - }) - - links_data = node.all_link_data(0) - for link_data in links_data: - link = convert_link(session, link_data) - links.append(link) - - return jsonify( - state=session.state, - nodes=nodes, - links=links - ) - - -@app.route("/sessions//hooks", methods=["POST"]) -def add_hook(session_id): - session = core_utils.get_session(coreemu, session_id) - data = request.get_json() or {} - state = data["state"] - file_name = data["file"] - file_data = data["data"] - session.add_hook(state, file_name, None, file_data) - return jsonify() - - -@app.route("/sessions//hooks") -def get_hooks(session_id): - session = core_utils.get_session(coreemu, session_id) - - hooks = [] - for state, state_hooks in session._hooks.iteritems(): - for file_name, file_data in state_hooks: - hooks.append({ - "state": state, - "file": file_name, - "data": file_data - }) - - return jsonify(hooks=hooks) - - -@app.route("/sessions//nodes//wlan") -def get_wlan_config(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - config = session.mobility.get_model_config(node_id, BasicRangeModel.name) - return jsonify(config) - - -@app.route("/sessions//nodes//wlan", methods=["PUT"]) -def set_wlan_config(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - config = request.get_json() or {} - session.mobility.set_model_config(node_id, BasicRangeModel.name, config) - return jsonify() - - -@app.route("/sessions//emane/config", methods=["PUT"]) -@synchronized -def set_emane_config(session_id): - session = core_utils.get_session(coreemu, session_id) - data = request.get_json() or {} - values = data["values"] - config = {x["name"]: x["value"] for x in values} - session.emane.set_configs(config) - return jsonify() - - -@app.route("/sessions//emane/model/config", methods=["PUT"]) -@synchronized -def set_emane_model_config(session_id): - session = core_utils.get_session(coreemu, session_id) - data = request.get_json() or {} - model_name = data["name"] - node_id = data.get("node") - values = data["values"] - config = {x["name"]: x["value"] for x in values} - session.emane.set_model_config(node_id, model_name, config) - return jsonify() - - -@app.route("/sessions//emane/config") -def get_emane_config(session_id): - session = core_utils.get_session(coreemu, session_id) - - config = session.emane.get_configs() - - config_options = [] - for configuration in session.emane.emane_config.configurations(): - value = config[configuration.id] - config_options.append({ - "label": configuration.label, - "name": configuration.id, - "value": value, - "type": configuration.type.value, - "select": configuration.options - }) - - response = [] - for config_group in session.emane.emane_config.config_groups(): - start = config_group.start - 1 - stop = config_group.stop - response.append({ - "name": config_group.name, - "options": config_options[start: stop] - }) - - return jsonify(groups=response) - - -@app.route("/sessions//emane/model/config") -def get_emane_model_config(session_id): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(request.args.get("node")) - - model_name = request.args["name"] - model = session.emane.models[model_name] - config = session.emane.get_model_config(node_id, model_name) - - config_options = [] - for configuration in model.configurations(): - value = config[configuration.id] - config_options.append({ - "label": configuration.label, - "name": configuration.id, - "value": value, - "type": configuration.type.value, - "select": configuration.options - }) - - response = [] - for config_group in model.config_groups(): - start = config_group.start - 1 - stop = config_group.stop - response.append({ - "name": config_group.name, - "options": config_options[start: stop] - }) - - return jsonify(groups=response) - - -@app.route("/sessions//emane/models") -def get_emane_models(session_id): - session = core_utils.get_session(coreemu, session_id) - - models = [] - for model in session.emane.models.keys(): - if len(model.split("_")) != 2: - continue - models.append(model) - - return jsonify(models=models) - - -@app.route("/sessions//nodes", methods=["POST"]) -@synchronized -def create_node(session_id): - session = core_utils.get_session(coreemu, session_id) - - data = request.get_json() or {} - node_id = data.get("id") - node_type = data.get("type", NodeTypes.DEFAULT.value) - node_type = NodeTypes(node_type) - logger.info("creating node: %s - %s", node_type.name, data) - - node_options = NodeOptions( - name=data.get("name"), - model=data.get("model") - ) - node_options.icon = data.get("icon") - node_options.opaque = data.get("opaque") - node_options.services = data.get("services", []) - position = data.get("position") - node_options.set_position(position.get("x"), position.get("y")) - node_options.set_location(data.get("lat"), data.get("lon"), data.get("alt")) - node = session.add_node(_type=node_type, _id=node_id, node_options=node_options) - - # configure emane if provided - emane_model = data.get("emane") - if emane_model: - session.emane.set_model_config(node_id, emane_model) - - return jsonify( - id=node.objid, - url="/sessions/%s/nodes/%s" % (session_id, node.objid) - ), 201 - - -@app.route("/sessions//nodes/", methods=["PUT"]) -@synchronized -def edit_node(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - - data = request.get_json() or {} - - node_options = NodeOptions() - x = data.get("x") - y = data.get("y") - node_options.set_position(x, y) - lat = data.get("lat") - lon = data.get("lon") - alt = data.get("alt") - node_options.set_location(lat, lon, alt) - - result = session.update_node(node_id, node_options) - if result: - return jsonify() - else: - return jsonify(error="error during node edit"), 404 - - -@app.route("/sessions//nodes/") -def get_node(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node = core_utils.get_node(session, node_id) - - interfaces = [] - for interface_id, interface in node._netif.iteritems(): - net_id = None - if interface.net: - net_id = interface.net.objid - - interfaces.append({ - "id": interface_id, - "netid": net_id, - "name": interface.name, - "mac": str(interface.hwaddr), - "mtu": interface.mtu, - "flowid": interface.flow_id - }) - - services = [x.name for x in getattr(node, "services", [])] - - emane_model = None - if nodeutils.is_node(node, NodeTypes.EMANE): - emane_model = node.model.name - - return jsonify( - name=node.name, - type=nodeutils.get_node_type(node.__class__).value, - services=services, - emane=emane_model, - model=node.type, - interfaces=interfaces, - linksurl="/sessions/%s/nodes/%s/links" % (session_id, node.objid) - ) - - -@app.route("/sessions//nodes//terminal") -def node_terminal(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node = core_utils.get_node(session, node_id) - terminal_command = node.termcmdstring("/bin/bash") - return jsonify(terminal_command) - - -@app.route("/sessions//nodes/", methods=["DELETE"]) -@synchronized -def delete_node(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - result = session.delete_node(node_id) - if result: - return jsonify() - else: - return jsonify(error="failure to delete node"), 404 - - -# TODO: this should just be a general service query -@app.route("/sessions//nodes//services") -def get_node_services(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - - services = {} - for service in ServiceManager.services.itervalues(): - service_group = services.setdefault(service.group, []) - service_group.append(service.name) - - return jsonify(services) - - -@app.route("/sessions//nodes//services/") -def get_node_service(session_id, node_id, service_name): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - - service = session.services.get_service(node_id, service_name, default_service=True) - service_config = { - "executables": service.executables, - "dependencies": service.dependencies, - "dirs": service.dirs, - "configs": service.configs, - "startup": service.startup, - "validate": service.validate, - "validation_mode": service.validation_mode.name, - "validation_timer": service.validation_timer, - "shutdown": service.shutdown, - "meta": service.meta, - } - return jsonify(service_config) - - -@app.route("/sessions//nodes//services/", methods=["PUT"]) -def set_node_service(session_id, node_id, service_name): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - - data = request.get_json() or {} - - logger.info("setting custom service node(%s) service(%s)", node_id, service_name) - # guarantee custom service exists - session.services.set_service(node_id, service_name) - service = session.services.get_service(node_id, service_name) - service.startup = tuple(data["startup"]) - logger.info("custom startup: %s", service.startup) - service.validate = tuple(data["validate"]) - logger.info("custom validate: %s", service.validate) - service.shutdown = tuple(data["shutdown"]) - logger.info("custom shutdown: %s", service.shutdown) - return jsonify() - - -@app.route("/sessions//nodes//services//file") -def get_node_service_file(session_id, node_id, service_name): - session = core_utils.get_session(coreemu, session_id) - node = core_utils.get_node(session, node_id) - - # get custom service file or default - service_file = request.args["file"] - file_data = session.services.get_service_file(node, service_name, service_file) - return jsonify(file_data.data) - - -@app.route("/sessions//nodes//services//file", methods=["PUT"]) -def set_node_service_file(session_id, node_id, service): - session = core_utils.get_session(coreemu, session_id) - node_id = core_utils.get_node_id(node_id) - - data = request.get_json() or {} - file_name = data["name"] - data = data["data"] - session.services.set_service_file(node_id, service, file_name, data) - return jsonify() - - -@app.route("/sessions//state", methods=["PUT"]) -@synchronized -def set_session_state(session_id): - session = core_utils.get_session(coreemu, session_id) - - data = request.get_json() - try: - state = EventTypes(data["state"]) - session.set_state(state) - - if state == EventTypes.INSTANTIATION_STATE: - # create session directory if it does not exist - if not os.path.exists(session.session_dir): - os.mkdir(session.session_dir) - session.instantiate() - elif state == EventTypes.SHUTDOWN_STATE: - session.shutdown() - elif state == EventTypes.DATACOLLECT_STATE: - session.data_collect() - elif state == EventTypes.DEFINITION_STATE: - session.clear() - - return jsonify() - except KeyError: - return jsonify(error="invalid state"), 404 - - -@app.route("/sessions//links", methods=["POST"]) -@synchronized -def add_link(session_id): - session = core_utils.get_session(coreemu, session_id) - - data = request.get_json() - logger.info("adding link: %s", data) - - node_one = data.get("node_one") - node_two = data.get("node_two") - - interface_one = None - interface_one_data = data.get("interface_one") - if interface_one_data: - interface_one = InterfaceData( - _id=interface_one_data.get("id"), - name=interface_one_data.get("name"), - mac=interface_one_data.get("mac"), - ip4=interface_one_data.get("ip4"), - ip4_mask=interface_one_data.get("ip4mask"), - ip6=interface_one_data.get("ip6"), - ip6_mask=interface_one_data.get("ip6mask"), - ) - - interface_two = None - interface_two_data = data.get("interface_two") - if interface_two_data: - interface_two = InterfaceData( - _id=interface_two_data.get("id"), - name=interface_two_data.get("name"), - mac=interface_two_data.get("mac"), - ip4=interface_two_data.get("ip4"), - ip4_mask=interface_two_data.get("ip4mask"), - ip6=interface_two_data.get("ip6"), - ip6_mask=interface_two_data.get("ip6mask"), - ) - - link_type = None - link_type_value = data.get("type") - if link_type_value is not None: - link_type = LinkTypes(link_type_value) - - options_data = data.get("options") - link_options = LinkOptions(_type=link_type) - if options_data: - link_options.delay = options_data.get("delay") - link_options.bandwidth = options_data.get("bandwidth") - link_options.session = options_data.get("session") - link_options.per = options_data.get("per") - link_options.dup = options_data.get("dup") - link_options.jitter = options_data.get("jitter") - link_options.mer = options_data.get("mer") - link_options.burst = options_data.get("burst") - link_options.mburst = options_data.get("mburst") - link_options.unidirectional = options_data.get("unidirectional") - link_options.key = options_data.get("key") - link_options.opaque = options_data.get("opaque") - - session.add_link(node_one, node_two, interface_one, interface_two, link_options=link_options) - return jsonify(), 201 - - -@app.route("/sessions//links", methods=["PUT"]) -@synchronized -def edit_link(session_id): - session = core_utils.get_session(coreemu, session_id) - - data = request.get_json() - - node_one = data.get("node_one") - node_two = data.get("node_two") - interface_one = data.get("interface_one") - interface_two = data.get("interface_two") - - options_data = data.get("options") - link_options = LinkOptions() - if options_data: - link_options.delay = options_data.get("delay") - link_options.bandwidth = options_data.get("bandwidth") - link_options.session = options_data.get("session") - link_options.per = options_data.get("per") - link_options.dup = options_data.get("dup") - link_options.jitter = options_data.get("jitter") - link_options.mer = options_data.get("mer") - link_options.burst = options_data.get("burst") - link_options.mburst = options_data.get("mburst") - link_options.unidirectional = options_data.get("unidirectional") - link_options.key = options_data.get("key") - link_options.opaque = options_data.get("opaque") - - session.update_link(node_one, node_two, link_options, interface_one, interface_two) - return jsonify(), 201 - - -@app.route("/sessions//links", methods=["DELETE"]) -@synchronized -def delete_link(session_id): - session = core_utils.get_session(coreemu, session_id) - data = request.get_json() - node_one = data.get("node_one") - node_two = data.get("node_two") - interface_one = data.get("interface_one") - interface_two = data.get("interface_two") - session.delete_link(node_one, node_two, interface_one, interface_two) - return jsonify() - - -@app.route("/sessions//nodes//links") -def get_node_links(session_id, node_id): - session = core_utils.get_session(coreemu, session_id) - node = core_utils.get_node(session, node_id) - - links_data = node.all_link_data(0) - links = [] - for link_data in links_data: - link = link_data._asdict() - del link["message_type"] - link_data_str(link, "interface1_ip4") - link_data_str(link, "interface1_ip6") - link_data_str(link, "interface1_mac") - link_data_str(link, "interface2_ip4") - link_data_str(link, "interface2_ip6") - link_data_str(link, "interface2_mac") - links.append(link) - - return jsonify(links=links) - - @app.errorhandler(HTTPError) def handle_error(e): return jsonify(message=e.body, status=e.status_code), e.status_code if __name__ == "__main__": - socketio.run(app, host="0.0.0.0", debug=True) + websocket_routes.socketio.run(app, host="0.0.0.0", debug=True) diff --git a/webapp/core_utils.py b/webapp/core_utils.py index c0b47b66..4cb091ed 100644 --- a/webapp/core_utils.py +++ b/webapp/core_utils.py @@ -1,5 +1,86 @@ +from functools import wraps +from threading import Lock + from bottle import abort +CORE_LOCK = Lock() + + +def synchronized(function): + global CORE_LOCK + + @wraps(function) + def wrapper(*args, **kwargs): + with CORE_LOCK: + return function(*args, **kwargs) + + return wrapper + + +def convert_value(value): + if value is None: + return value + else: + return str(value) + + +def convert_link(session, link_data): + interface_one = None + interface_two = None + + if link_data.interface1_id is not None: + node = session.get_object(link_data.node1_id) + interface = node.netif(link_data.interface1_id) + interface_one = { + "id": link_data.interface1_id, + "name": interface.name, + "mac": convert_value(link_data.interface1_mac), + "ip4": convert_value(link_data.interface1_ip4), + "ip4mask": link_data.interface1_ip4_mask, + "ip6": convert_value(link_data.interface1_ip6), + "ip6mask": link_data.interface1_ip6_mask, + } + + if link_data.interface2_id is not None: + node = session.get_object(link_data.node2_id) + interface = node.netif(link_data.interface2_id) + interface_two = { + "id": link_data.interface2_id, + "name": interface.name, + "mac": convert_value(link_data.interface2_mac), + "ip4": convert_value(link_data.interface2_ip4), + "ip4mask": link_data.interface2_ip4_mask, + "ip6": convert_value(link_data.interface2_ip6), + "ip6mask": link_data.interface2_ip6_mask, + } + + return { + "node_one": link_data.node1_id, + "node_two": link_data.node2_id, + "type": link_data.link_type, + "interface_one": interface_one, + "interface_two": interface_two, + "options": { + "opaque": link_data.opaque, + "jitter": link_data.jitter, + "key": link_data.key, + "mburst": link_data.mburst, + "mer": link_data.mer, + "per": link_data.per, + "bandwidth": link_data.bandwidth, + "burst": link_data.burst, + "delay": link_data.delay, + "dup": link_data.dup, + "unidirectional": link_data.unidirectional + } + } + + +def link_data_str(link, key): + value = link.get(key) + if value is not None: + link[key] = str(value) + def get_session(coreemu, session_id): session = coreemu.sessions.get(session_id) diff --git a/webapp/emane_routes.py b/webapp/emane_routes.py new file mode 100644 index 00000000..1f650f03 --- /dev/null +++ b/webapp/emane_routes.py @@ -0,0 +1,105 @@ +from flask import jsonify +from flask import request +from flask.blueprints import Blueprint + +import core_utils + +coreemu = None + +api = Blueprint("emane_api", __name__) + + +@api.route("/sessions//emane/config", methods=["PUT"]) +def set_emane_config(session_id): + session = core_utils.get_session(coreemu, session_id) + data = request.get_json() or {} + values = data["values"] + config = {x["name"]: x["value"] for x in values} + session.emane.set_configs(config) + return jsonify() + + +@api.route("/sessions//emane/model/config", methods=["PUT"]) +def set_emane_model_config(session_id): + session = core_utils.get_session(coreemu, session_id) + data = request.get_json() or {} + model_name = data["name"] + node_id = data.get("node") + values = data["values"] + config = {x["name"]: x["value"] for x in values} + session.emane.set_model_config(node_id, model_name, config) + return jsonify() + + +@api.route("/sessions//emane/config") +def get_emane_config(session_id): + session = core_utils.get_session(coreemu, session_id) + + config = session.emane.get_configs() + + config_options = [] + for configuration in session.emane.emane_config.configurations(): + value = config[configuration.id] + config_options.append({ + "label": configuration.label, + "name": configuration.id, + "value": value, + "type": configuration.type.value, + "select": configuration.options + }) + + response = [] + for config_group in session.emane.emane_config.config_groups(): + start = config_group.start - 1 + stop = config_group.stop + response.append({ + "name": config_group.name, + "options": config_options[start: stop] + }) + + return jsonify(groups=response) + + +@api.route("/sessions//emane/model/config") +def get_emane_model_config(session_id): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(request.args.get("node")) + + model_name = request.args["name"] + model = session.emane.models[model_name] + config = session.emane.get_model_config(node_id, model_name) + + config_options = [] + for configuration in model.configurations(): + value = config[configuration.id] + config_options.append({ + "label": configuration.label, + "name": configuration.id, + "value": value, + "type": configuration.type.value, + "select": configuration.options + }) + + response = [] + for config_group in model.config_groups(): + start = config_group.start - 1 + stop = config_group.stop + response.append({ + "name": config_group.name, + "options": config_options[start: stop] + }) + + return jsonify(groups=response) + + +@api.route("/sessions//emane/models") +def get_emane_models(session_id): + session = core_utils.get_session(coreemu, session_id) + + models = [] + for model in session.emane.models.keys(): + if len(model.split("_")) != 2: + continue + models.append(model) + + return jsonify(models=models) diff --git a/webapp/hook_routes.py b/webapp/hook_routes.py new file mode 100644 index 00000000..10d6a94f --- /dev/null +++ b/webapp/hook_routes.py @@ -0,0 +1,36 @@ +from flask import jsonify +from flask import request +from flask.blueprints import Blueprint + +import core_utils + +coreemu = None + +api = Blueprint("hooks_api", __name__) + + +@api.route("/sessions//hooks", methods=["POST"]) +def add_hook(session_id): + session = core_utils.get_session(coreemu, session_id) + data = request.get_json() or {} + state = data["state"] + file_name = data["file"] + file_data = data["data"] + session.add_hook(state, file_name, None, file_data) + return jsonify() + + +@api.route("/sessions//hooks") +def get_hooks(session_id): + session = core_utils.get_session(coreemu, session_id) + + hooks = [] + for state, state_hooks in session._hooks.iteritems(): + for file_name, file_data in state_hooks: + hooks.append({ + "state": state, + "file": file_name, + "data": file_data + }) + + return jsonify(hooks=hooks) diff --git a/webapp/link_routes.py b/webapp/link_routes.py new file mode 100644 index 00000000..f4d30376 --- /dev/null +++ b/webapp/link_routes.py @@ -0,0 +1,119 @@ +from flask import jsonify +from flask import request +from flask.blueprints import Blueprint + +import core_utils +from core import logger +from core.emulator.emudata import LinkOptions, InterfaceData +from core.enumerations import LinkTypes + +coreemu = None + +api = Blueprint("links_api", __name__) + + +@api.route("/sessions//links", methods=["POST"]) +@core_utils.synchronized +def add_link(session_id): + session = core_utils.get_session(coreemu, session_id) + + data = request.get_json() + logger.info("adding link: %s", data) + + node_one = data.get("node_one") + node_two = data.get("node_two") + + interface_one = None + interface_one_data = data.get("interface_one") + if interface_one_data: + interface_one = InterfaceData( + _id=interface_one_data.get("id"), + name=interface_one_data.get("name"), + mac=interface_one_data.get("mac"), + ip4=interface_one_data.get("ip4"), + ip4_mask=interface_one_data.get("ip4mask"), + ip6=interface_one_data.get("ip6"), + ip6_mask=interface_one_data.get("ip6mask"), + ) + + interface_two = None + interface_two_data = data.get("interface_two") + if interface_two_data: + interface_two = InterfaceData( + _id=interface_two_data.get("id"), + name=interface_two_data.get("name"), + mac=interface_two_data.get("mac"), + ip4=interface_two_data.get("ip4"), + ip4_mask=interface_two_data.get("ip4mask"), + ip6=interface_two_data.get("ip6"), + ip6_mask=interface_two_data.get("ip6mask"), + ) + + link_type = None + link_type_value = data.get("type") + if link_type_value is not None: + link_type = LinkTypes(link_type_value) + + options_data = data.get("options") + link_options = LinkOptions(_type=link_type) + if options_data: + link_options.delay = options_data.get("delay") + link_options.bandwidth = options_data.get("bandwidth") + link_options.session = options_data.get("session") + link_options.per = options_data.get("per") + link_options.dup = options_data.get("dup") + link_options.jitter = options_data.get("jitter") + link_options.mer = options_data.get("mer") + link_options.burst = options_data.get("burst") + link_options.mburst = options_data.get("mburst") + link_options.unidirectional = options_data.get("unidirectional") + link_options.key = options_data.get("key") + link_options.opaque = options_data.get("opaque") + + session.add_link(node_one, node_two, interface_one, interface_two, link_options=link_options) + return jsonify(), 201 + + +@api.route("/sessions//links", methods=["PUT"]) +@core_utils.synchronized +def edit_link(session_id): + session = core_utils.get_session(coreemu, session_id) + + data = request.get_json() + + node_one = data.get("node_one") + node_two = data.get("node_two") + interface_one = data.get("interface_one") + interface_two = data.get("interface_two") + + options_data = data.get("options") + link_options = LinkOptions() + if options_data: + link_options.delay = options_data.get("delay") + link_options.bandwidth = options_data.get("bandwidth") + link_options.session = options_data.get("session") + link_options.per = options_data.get("per") + link_options.dup = options_data.get("dup") + link_options.jitter = options_data.get("jitter") + link_options.mer = options_data.get("mer") + link_options.burst = options_data.get("burst") + link_options.mburst = options_data.get("mburst") + link_options.unidirectional = options_data.get("unidirectional") + link_options.key = options_data.get("key") + link_options.opaque = options_data.get("opaque") + + session.update_link(node_one, node_two, link_options, interface_one, interface_two) + return jsonify(), 201 + + +@api.route("/sessions//links", methods=["DELETE"]) +@core_utils.synchronized +def delete_link(session_id): + session = core_utils.get_session(coreemu, session_id) + data = request.get_json() + node_one = data.get("node_one") + node_two = data.get("node_two") + interface_one = data.get("interface_one") + interface_two = data.get("interface_two") + session.delete_link(node_one, node_two, interface_one, interface_two) + return jsonify() diff --git a/webapp/mobility_routes.py b/webapp/mobility_routes.py index f3fcdb0d..c21db4c7 100644 --- a/webapp/mobility_routes.py +++ b/webapp/mobility_routes.py @@ -4,12 +4,12 @@ from flask import request from core.mobility import Ns2ScriptedMobility -mobility_api = Blueprint("mobility_api", __name__) +api = Blueprint("mobility_api", __name__) coreemu = None -@mobility_api.route("/nodes//mobility", methods=["POST"]) +@api.route("/sessions//nodes//mobility", methods=["POST"]) def set_mobility_config(session_id, node_id): session = coreemu.sessions.get(session_id) if not session: @@ -25,7 +25,7 @@ def set_mobility_config(session_id, node_id): return jsonify() -@mobility_api.route("/nodes//mobility") +@api.route("/sessions//nodes//mobility") def get_mobility_config(session_id, node_id): session = coreemu.sessions.get(session_id) if not session: @@ -39,7 +39,7 @@ def get_mobility_config(session_id, node_id): return jsonify(config) -@mobility_api.route("/nodes//mobility/", methods=["PUT"]) +@api.route("/sessions//nodes//mobility/", methods=["PUT"]) def mobility_action(session_id, node_id, action): session = coreemu.sessions.get(session_id) if not session: diff --git a/webapp/node_routes.py b/webapp/node_routes.py new file mode 100644 index 00000000..cdc08411 --- /dev/null +++ b/webapp/node_routes.py @@ -0,0 +1,149 @@ +from flask import jsonify +from flask import request +from flask.blueprints import Blueprint + +import core_utils +from core import logger +from core.emulator.emudata import NodeOptions +from core.enumerations import NodeTypes +from core.misc import nodeutils + +coreemu = None + +api = Blueprint("nodes_api", __name__) + + +@api.route("/sessions//nodes", methods=["POST"]) +@core_utils.synchronized +def create_node(session_id): + session = core_utils.get_session(coreemu, session_id) + + data = request.get_json() or {} + node_id = data.get("id") + node_type = data.get("type", NodeTypes.DEFAULT.value) + node_type = NodeTypes(node_type) + logger.info("creating node: %s - %s", node_type.name, data) + + node_options = NodeOptions( + name=data.get("name"), + model=data.get("model") + ) + node_options.icon = data.get("icon") + node_options.opaque = data.get("opaque") + node_options.services = data.get("services", []) + position = data.get("position") + node_options.set_position(position.get("x"), position.get("y")) + node_options.set_location(data.get("lat"), data.get("lon"), data.get("alt")) + node = session.add_node(_type=node_type, _id=node_id, node_options=node_options) + + # configure emane if provided + emane_model = data.get("emane") + if emane_model: + session.emane.set_model_config(node_id, emane_model) + + return jsonify( + id=node.objid, + url="/sessions/%s/nodes/%s" % (session_id, node.objid) + ), 201 + + +@api.route("/sessions//nodes/", methods=["PUT"]) +@core_utils.synchronized +def edit_node(session_id, node_id): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(node_id) + + data = request.get_json() or {} + + node_options = NodeOptions() + x = data.get("x") + y = data.get("y") + node_options.set_position(x, y) + lat = data.get("lat") + lon = data.get("lon") + alt = data.get("alt") + node_options.set_location(lat, lon, alt) + + result = session.update_node(node_id, node_options) + if result: + return jsonify() + else: + return jsonify(error="error during node edit"), 404 + + +@api.route("/sessions//nodes/") +def get_node(session_id, node_id): + session = core_utils.get_session(coreemu, session_id) + node = core_utils.get_node(session, node_id) + + interfaces = [] + for interface_id, interface in node._netif.iteritems(): + net_id = None + if interface.net: + net_id = interface.net.objid + + interfaces.append({ + "id": interface_id, + "netid": net_id, + "name": interface.name, + "mac": str(interface.hwaddr), + "mtu": interface.mtu, + "flowid": interface.flow_id + }) + + services = [x.name for x in getattr(node, "services", [])] + + emane_model = None + if nodeutils.is_node(node, NodeTypes.EMANE): + emane_model = node.model.name + + return jsonify( + name=node.name, + type=nodeutils.get_node_type(node.__class__).value, + services=services, + emane=emane_model, + model=node.type, + interfaces=interfaces, + linksurl="/sessions/%s/nodes/%s/links" % (session_id, node.objid) + ) + + +@api.route("/sessions//nodes//terminal") +def node_terminal(session_id, node_id): + session = core_utils.get_session(coreemu, session_id) + node = core_utils.get_node(session, node_id) + terminal_command = node.termcmdstring("/bin/bash") + return jsonify(terminal_command) + + +@api.route("/sessions//nodes/", methods=["DELETE"]) +@core_utils.synchronized +def delete_node(session_id, node_id): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(node_id) + result = session.delete_node(node_id) + if result: + return jsonify() + else: + return jsonify(error="failure to delete node"), 404 + + +@api.route("/sessions//nodes//links") +def get_node_links(session_id, node_id): + session = core_utils.get_session(coreemu, session_id) + node = core_utils.get_node(session, node_id) + + links_data = node.all_link_data(0) + links = [] + for link_data in links_data: + link = link_data._asdict() + del link["message_type"] + core_utils.link_data_str(link, "interface1_ip4") + core_utils.link_data_str(link, "interface1_ip6") + core_utils.link_data_str(link, "interface1_mac") + core_utils.link_data_str(link, "interface2_ip4") + core_utils.link_data_str(link, "interface2_ip6") + core_utils.link_data_str(link, "interface2_mac") + links.append(link) + + return jsonify(links=links) diff --git a/webapp/service_routes.py b/webapp/service_routes.py new file mode 100644 index 00000000..eed93037 --- /dev/null +++ b/webapp/service_routes.py @@ -0,0 +1,85 @@ +from flask import jsonify +from flask import request +from flask.blueprints import Blueprint + +import core_utils +from core import logger +from core.service import ServiceManager + +coreemu = None + +api = Blueprint("service_api", __name__) + + +@api.route("/services") +def get_services(): + groups = {} + for service in ServiceManager.services.itervalues(): + service_group = groups.setdefault(service.group, []) + service_group.append(service.name) + + return jsonify(groups=groups) + + +@api.route("/sessions//nodes//services/") +def get_node_service(session_id, node_id, service_name): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(node_id) + + service = session.services.get_service(node_id, service_name, default_service=True) + service_config = { + "executables": service.executables, + "dependencies": service.dependencies, + "dirs": service.dirs, + "configs": service.configs, + "startup": service.startup, + "validate": service.validate, + "validation_mode": service.validation_mode.name, + "validation_timer": service.validation_timer, + "shutdown": service.shutdown, + "meta": service.meta, + } + return jsonify(service_config) + + +@api.route("/sessions//nodes//services/", methods=["PUT"]) +def set_node_service(session_id, node_id, service_name): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(node_id) + + data = request.get_json() or {} + + logger.info("setting custom service node(%s) service(%s)", node_id, service_name) + # guarantee custom service exists + session.services.set_service(node_id, service_name) + service = session.services.get_service(node_id, service_name) + service.startup = tuple(data["startup"]) + logger.info("custom startup: %s", service.startup) + service.validate = tuple(data["validate"]) + logger.info("custom validate: %s", service.validate) + service.shutdown = tuple(data["shutdown"]) + logger.info("custom shutdown: %s", service.shutdown) + return jsonify() + + +@api.route("/sessions//nodes//services//file") +def get_node_service_file(session_id, node_id, service_name): + session = core_utils.get_session(coreemu, session_id) + node = core_utils.get_node(session, node_id) + + # get custom service file or default + service_file = request.args["file"] + file_data = session.services.get_service_file(node, service_name, service_file) + return jsonify(file_data.data) + + +@api.route("/sessions//nodes//services//file", methods=["PUT"]) +def set_node_service_file(session_id, node_id, service): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(node_id) + + data = request.get_json() or {} + file_name = data["name"] + data = data["data"] + session.services.set_service_file(node_id, service, file_name, data) + return jsonify() diff --git a/webapp/session_routes.py b/webapp/session_routes.py new file mode 100644 index 00000000..17aed47d --- /dev/null +++ b/webapp/session_routes.py @@ -0,0 +1,163 @@ +import os + +from flask import jsonify +from flask import request +from flask.blueprints import Blueprint + +import core_utils +import websocket_routes +from core.enumerations import EventTypes, NodeTypes +from core.misc import nodeutils + +coreemu = None + +api = Blueprint("session_api", __name__) + + +@api.route("/sessions") +def get_sessions(): + sessions = [] + for session in coreemu.sessions.itervalues(): + sessions.append({ + "id": session.session_id, + "state": session.state, + "nodes": session.get_node_count() + }) + return jsonify(sessions=sessions) + + +@api.route("/sessions", methods=["POST"]) +@core_utils.synchronized +def create_session(): + session = coreemu.create_session() + session.set_state(EventTypes.DEFINITION_STATE) + + # set session location + session.location.setrefgeo(47.57917, -122.13232, 2.0) + session.location.refscale = 150.0 + + # add handlers + session.event_handlers.append(websocket_routes.broadcast_event) + session.node_handlers.append(websocket_routes.broadcast_node) + + response_data = jsonify( + id=session.session_id, + state=session.state, + url="/sessions/%s" % session.session_id + ) + return response_data, 201 + + +@api.route("/sessions/", methods=["DELETE"]) +@core_utils.synchronized +def delete_session(session_id): + result = coreemu.delete_session(session_id) + if result: + return jsonify() + else: + return jsonify(error="session does not exist"), 404 + + +@api.route("/sessions//options") +def get_session_options(session_id): + session = core_utils.get_session(coreemu, session_id) + config = session.options.get_configs() + + config_options = [] + for configuration in session.options.configurations(): + value = config[configuration.id] + config_options.append({ + "label": configuration.label, + "name": configuration.id, + "value": value, + "type": configuration.type.value, + "select": configuration.options + }) + + response = [] + for config_group in session.emane.emane_config.config_groups(): + start = config_group.start - 1 + stop = config_group.stop + response.append({ + "name": config_group.name, + "options": config_options[start: stop] + }) + + return jsonify(groups=response) + + +@api.route("/sessions//options", methods=["PUT"]) +@core_utils.synchronized +def set_session_options(session_id): + session = core_utils.get_session(coreemu, session_id) + data = request.get_json() or {} + values = data["values"] + config = {x["name"]: x["value"] for x in values} + session.options.set_configs(config) + return jsonify() + + +@api.route("/sessions/") +def get_session(session_id): + session = core_utils.get_session(coreemu, session_id) + + nodes = [] + links = [] + for node in session.objects.itervalues(): + emane_model = None + if nodeutils.is_node(node, NodeTypes.EMANE): + emane_model = node.model.name + + services = [x.name for x in getattr(node, "services", [])] + nodes.append({ + "id": node.objid, + "name": node.name, + "type": nodeutils.get_node_type(node.__class__).value, + "model": getattr(node, "type", None), + "position": { + "x": node.position.x, + "y": node.position.y, + "z": node.position.z + }, + "services": services, + "emane": emane_model, + "url": "/sessions/%s/nodes/%s" % (session_id, node.objid) + }) + + links_data = node.all_link_data(0) + for link_data in links_data: + link = core_utils.convert_link(session, link_data) + links.append(link) + + return jsonify( + state=session.state, + nodes=nodes, + links=links + ) + + +@api.route("/sessions//state", methods=["PUT"]) +@core_utils.synchronized +def set_session_state(session_id): + session = core_utils.get_session(coreemu, session_id) + + data = request.get_json() + try: + state = EventTypes(data["state"]) + session.set_state(state) + + if state == EventTypes.INSTANTIATION_STATE: + # create session directory if it does not exist + if not os.path.exists(session.session_dir): + os.mkdir(session.session_dir) + session.instantiate() + elif state == EventTypes.SHUTDOWN_STATE: + session.shutdown() + elif state == EventTypes.DATACOLLECT_STATE: + session.data_collect() + elif state == EventTypes.DEFINITION_STATE: + session.clear() + + return jsonify() + except KeyError: + return jsonify(error="invalid state"), 404 diff --git a/webapp/websocket_routes.py b/webapp/websocket_routes.py new file mode 100644 index 00000000..4d5bebc4 --- /dev/null +++ b/webapp/websocket_routes.py @@ -0,0 +1,42 @@ +from flask.ext.socketio import SocketIO, emit + +from core import logger + +socketio = None + + +def register(app): + global socketio + socketio = SocketIO(app) + + @socketio.on("connect") + def websocket_connect(): + logger.info("websocket client connected") + + @socketio.on("disconnect") + def websocket_disconnect(): + logger.info("websocket client disconnected") + + +def broadcast_event(event): + socketio.emit("event", { + "node": event.node, + "event_type": event.event_type, + "name": event.name, + "data": event.data, + "time": event.time, + "session": event.session + }) + + +def broadcast_node(node): + socketio.emit("node", { + "id": node.id, + "name": node.name, + "model": node.model, + "position": { + "x": node.x_position, + "y": node.y_position, + }, + "services": node.services.split("|"), + }) diff --git a/webapp/wlan_routes.py b/webapp/wlan_routes.py new file mode 100644 index 00000000..46d02f64 --- /dev/null +++ b/webapp/wlan_routes.py @@ -0,0 +1,27 @@ +from flask import jsonify +from flask import request +from flask.blueprints import Blueprint + +import core_utils +from core.mobility import BasicRangeModel + +coreemu = None + +api = Blueprint("wlan_api", __name__) + + +@api.route("/sessions//nodes//wlan") +def get_wlan_config(session_id, node_id): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(node_id) + config = session.mobility.get_model_config(node_id, BasicRangeModel.name) + return jsonify(config) + + +@api.route("/sessions//nodes//wlan", methods=["PUT"]) +def set_wlan_config(session_id, node_id): + session = core_utils.get_session(coreemu, session_id) + node_id = core_utils.get_node_id(node_id) + config = request.get_json() or {} + session.mobility.set_model_config(node_id, BasicRangeModel.name, config) + return jsonify() diff --git a/webapp/xml_routes.py b/webapp/xml_routes.py new file mode 100644 index 00000000..58f92187 --- /dev/null +++ b/webapp/xml_routes.py @@ -0,0 +1,47 @@ +import tempfile + +from flask import jsonify +from flask import request +from flask import send_file +from flask.blueprints import Blueprint + +from core import logger +from core.enumerations import EventTypes + +coreemu = None + +api = Blueprint("xml_api", __name__) + + +@api.route("/sessions//xml") +def save_xml(session_id): + session = coreemu.sessions.get(session_id) + if not session: + return jsonify(error="session does not exist"), 404 + + _, temp_path = tempfile.mkstemp() + session.save_xml(temp_path, "1.0") + return send_file( + temp_path, + as_attachment=True, + attachment_filename="session-%s.xml" % session_id + ) + + +@api.route("/sessions/xml", methods=["POST"]) +def open_xml(): + session = coreemu.create_session() + session.set_state(EventTypes.CONFIGURATION_STATE) + + logger.info("open xml: %s", request.files) + _, temp_path = tempfile.mkstemp() + session_file = request.files['session'] + session_file.save(temp_path) + + try: + session.open_xml(temp_path, start=True) + return jsonify(id=session.session_id) + except: + logger.exception("error opening session file") + coreemu.delete_session(session.session_id) + return jsonify(error="error opening session file"), 404