refactoring to rest app to help breakup api code into smaller files

This commit is contained in:
Blake J. Harnden 2018-09-13 09:27:02 -07:00
parent 2593d97cab
commit 1d73f28248
12 changed files with 886 additions and 796 deletions

View file

@ -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/<int:session_id>")
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/<int:session_id>/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/<int:session_id>", 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/<int:session_id>/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/<int:session_id>/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/<int:session_id>")
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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/nodes/<node_id>/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/<int:session_id>/nodes/<node_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/nodes/<node_id>", 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/<int:session_id>/nodes/<node_id>")
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/<int:session_id>/nodes/<node_id>/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/<int:session_id>/nodes/<node_id>", 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/<int:session_id>/nodes/<node_id>/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/<int:session_id>/nodes/<node_id>/services/<service_name>")
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/<int:session_id>/nodes/<node_id>/services/<service_name>", 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/<int:session_id>/nodes/<node_id>/services/<service_name>/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/<int:session_id>/nodes/<node_id>/services/<service>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/nodes/<node_id>/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)

View file

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

105
webapp/emane_routes.py Normal file
View file

@ -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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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)

36
webapp/hook_routes.py Normal file
View file

@ -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/<int:session_id>/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/<int:session_id>/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)

119
webapp/link_routes.py Normal file
View file

@ -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/<int:session_id>/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/<int:session_id>/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/<int:session_id>/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()

View file

@ -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/<node_id>/mobility", methods=["POST"])
@api.route("/sessions/<int:session_id>/nodes/<node_id>/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/<node_id>/mobility")
@api.route("/sessions/<int:session_id>/nodes/<node_id>/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/<node_id>/mobility/<action>", methods=["PUT"])
@api.route("/sessions/<int:session_id>/nodes/<node_id>/mobility/<action>", methods=["PUT"])
def mobility_action(session_id, node_id, action):
session = coreemu.sessions.get(session_id)
if not session:

149
webapp/node_routes.py Normal file
View file

@ -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/<int:session_id>/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/<int:session_id>/nodes/<node_id>", 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/<int:session_id>/nodes/<node_id>")
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/<int:session_id>/nodes/<node_id>/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/<int:session_id>/nodes/<node_id>", 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/<int:session_id>/nodes/<node_id>/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)

85
webapp/service_routes.py Normal file
View file

@ -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/<int:session_id>/nodes/<node_id>/services/<service_name>")
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/<int:session_id>/nodes/<node_id>/services/<service_name>", 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/<int:session_id>/nodes/<node_id>/services/<service_name>/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/<int:session_id>/nodes/<node_id>/services/<service>/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()

163
webapp/session_routes.py Normal file
View file

@ -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/<int:session_id>", 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/<int:session_id>/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/<int:session_id>/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/<int:session_id>")
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/<int:session_id>/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

View file

@ -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("|"),
})

27
webapp/wlan_routes.py Normal file
View file

@ -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/<int:session_id>/nodes/<node_id>/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/<int:session_id>/nodes/<node_id>/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()

47
webapp/xml_routes.py Normal file
View file

@ -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/<int:session_id>/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