21 from flask
import Flask, flash, redirect, render_template, request, session, abort, url_for, Response, jsonify
22 from flask_login
import LoginManager, UserMixin, login_required, login_user, logout_user, current_user
25 from werkzeug
import exceptions
as http_errors
27 from app
import application
28 from .
import neo4j_tools
30 logger = logging.getLogger(__name__)
32 config = ConfigParser.RawConfigParser()
33 config.read(
'topology_engine_rest.ini')
35 neo4j_connect = neo4j_tools.connect(config)
37 DATETIME_FORMAT =
'%Y-%m-%dT%H:%M:%S.%fZ' 38 UNIX_EPOCH = datetime.datetime(1970, 1, 1, 0, 0, 0, 0, pytz.utc)
41 @application.route(
'/api/v1/topology/network')
45 2017.03.08 (carmine) - this is now identical to api_v1_topology. 46 :return: the switches and links 49 query =
'MATCH (n:switch) return n' 51 for record
in neo4j_connect.run(query).
data():
55 for rel
in neo4j_connect.match(nodes=[record], r_type=
'isl')]
58 'name': record[
'name'],
59 'outgoing_relationships': relations
62 topology.sort(key=
lambda x:x[
'name'])
63 topology = {
'nodes': topology}
65 return json.dumps(topology, default=
lambda o: o.__dict__, sort_keys=
True)
70 return json.dumps(self, default=
lambda o: o.__dict__, sort_keys=
False, indent=4)
74 return json.dumps(self, default=
lambda o: o.__dict__, sort_keys=
False, indent=4)
78 return json.dumps(self, default=
lambda o: o.__dict__, sort_keys=
False, indent=4)
81 @application.route(
'/api/v1/topology/nodes')
85 for record
in neo4j_connect.run(
'MATCH (n:switch) return n').
data():
88 for rel
in neo4j_connect.match(nodes=[source], r_type=
'isl'):
92 s.label = source[
'name']
93 s.id = py2neo.remote(source)._id
96 t.label = dest[
'name']
97 t.id = py2neo.remote(dest)._id
100 edge.value =
"{} to {}".
format(s.label, t.label)
106 edges.sort(key=
lambda x: (x.source[
'id'], x.target[
'id']))
110 return nodes.toJSON()
113 @application.route(
'/api/v1/topology/clear')
117 Clear the entire topology 118 :returns the result of api_v1_network() after the delete 120 query =
'MATCH (n) detach delete n' 121 neo4j_connect.run(query)
125 @application.route(
'/topology/network', methods=[
'GET'])
128 return render_template(
'topologynetwork.html')
131 @application.route(
'/api/v1/topology/flows')
135 query =
"MATCH (a:switch)-[r:flow]->(b:switch) RETURN r" 136 result = neo4j_connect.run(query).
data()
138 return json.dumps(flows)
139 except Exception
as e:
140 return "error: {}".
format(str(e))
143 @application.route(
'/api/v1/topology/flows/<flow_id>')
147 "MATCH (a:switch)-[r:flow]->(b:switch)\n" 148 "WHERE r.flowid = {flow_id}\n" 150 result = neo4j_connect.run(query, flow_id=flow_id).
data()
152 return http_errors.NotFound(
153 'There is no flow with flow_id={}'.
format(flow_id))
155 return http_errors.NotFound(
'Flow data corrupted (too few results)')
156 elif 2 < len(result):
157 return http_errors.NotFound(
'Flow data corrupted (too many results)')
159 flow_pair = [
format_flow(record[
'r']) for record in result] 161 flow_data = dict(zip([
'reverse',
'forward'], flow_pair))
163 return jsonify(flow_data)
168 :param link: A valid Link returned from the db 169 :return: A dictionary in the form of org.openkilda.messaging.info.event.IslInfoData 172 'clazz':
'org.openkilda.messaging.info.event.IslInfoData',
173 'latency_ns': int(link[
'latency']),
174 'path': [{
'switch_id': link[
'src_switch'],
175 'port_no': int(link[
'src_port']),
177 'segment_latency': int(link[
'latency'])},
178 {
'switch_id': link[
'dst_switch'],
179 'port_no': int(link[
'dst_port']),
181 'segment_latency': 0}],
182 'speed': link[
'speed'],
184 'available_bandwidth': link[
'available_bandwidth'],
190 already_used = list(isl.keys())
191 for k,v
in link.iteritems():
192 if k
not in already_used:
199 if link[
'status'] ==
'active':
201 elif link[
'status'] ==
'moved':
209 :param switch: A valid Switch returned from the db 210 :return: A dictionary in the form of org.openkilda.messaging.info.event.SwitchInfoData 213 'clazz':
'org.openkilda.messaging.info.event.SwitchInfoData',
214 'switch_id': switch[
'name'],
215 'address': switch[
'address'],
216 'hostname': switch[
'hostname'],
217 'state':
'ACTIVATED' if switch[
'state'] ==
'active' else 'DEACTIVATED',
218 'description': switch[
'description']
223 flow = raw_flow.copy()
225 path = json.loads(raw_flow[
'flowpath'])
226 path[
'clazz'] =
'org.openkilda.messaging.info.event.PathInfoData' 228 flow[
'flowpath'] = path
233 @application.route(
'/api/v1/topology/links')
237 :return: all isl relationships in the database 240 query =
"MATCH (a:switch)-[r:isl]->(b:switch) RETURN r" 241 result = neo4j_connect.run(query).
data()
245 neo4j_connect.pull(link[
'r']) 248 application.logger.info('links found %d', len(result))
250 return jsonify(links)
251 except Exception
as e:
252 return "error: {}".
format(str(e))
255 @application.route(
'/api/v1/topology/switches')
259 :return: all switches in the database 262 query =
"MATCH (n:switch) RETURN n" 263 result = neo4j_connect.run(query).
data()
267 neo4j_connect.pull(sw[
'n'])
270 application.logger.info(
'switches found %d', len(result))
272 return jsonify(switches)
273 except Exception
as e:
274 return "error: {}".
format(str(e))
277 @application.route(
'/api/v1/topology/links/bandwidth/<src_switch>/<int:src_port>')
281 "MATCH (a:switch)-[r:isl]->(b:switch) " 282 "WHERE r.src_switch = '{}' AND r.src_port = {} " 283 "RETURN r.available_bandwidth").
format(src_switch, int(src_port))
285 return neo4j_connect.run(query).
data()[0][
'r.available_bandwidth']
288 @application.route(
'/api/v1/topology/routes/src/<src_switch>/dst/<dst_switch>')
291 depth = request.args.get(
'depth')
or '10' 293 "MATCH p=(src:switch{{name:'{src_switch}'}})-[:isl*..{depth}]->" 294 "(dst:switch{{name:'{dst_switch}'}}) " 295 "WHERE ALL(x IN NODES(p) WHERE SINGLE(y IN NODES(p) WHERE y = x)) " 296 "WITH RELATIONSHIPS(p) as links " 297 "WHERE ALL(l IN links WHERE l.status = 'active') " 299 ).
format(src_switch=src_switch, depth=depth, dst_switch=dst_switch)
301 result = neo4j_connect.run(query).
data()
306 for isl
in links[
'links']:
308 current_path.extend(path_node)
312 return jsonify(paths)
317 'clazz':
'org.openkilda.messaging.info.event.PathInfoData',
325 'clazz':
'org.openkilda.messaging.info.event.PathNode',
326 'switch_id': link[
'src_switch'],
327 'port_no': int(link[
'src_port']),
330 nodes.append(src_node)
333 'clazz':
'org.openkilda.messaging.info.event.PathNode',
334 'switch_id': link[
'dst_switch'],
335 'port_no': int(link[
'dst_port']),
338 nodes.append(dst_node)
345 return int(cookie) & 0x4000000000000000
352 value = datetime.datetime.strptime(value, DATETIME_FORMAT)
353 value = value.replace(tzinfo=pytz.utc)
355 from_epoch = value - UNIX_EPOCH
356 seconds = from_epoch.total_seconds()
357 return seconds * 1000 + from_epoch.microseconds // 1000
def api_v1_topology_get_flow(flow_id)
def api_v1_topology_links()
def build_path_nodes(link, seq_id)
def api_v1_topology_nodes()
def format_flow(raw_flow)
def api_v1_routes_between_nodes(src_switch, dst_switch)
def is_forward_cookie(cookie)
def format_db_datetime(value)
def format_switch(switch)
def api_v1_topology_switches()
def api_v1_topology_link_bandwidth(src_switch, src_port)
def api_v1_topology_flows()
def build_path_info(path)