2 * Copyright (c) 2015 Inocybe Technologies and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 define(['app/ovsdb/ovsdb.module', 'app/ovsdb/OvsCore', 'underscore', 'app/ovsdb/ovsdb.constant'], function(ovsdb, OvsCore, _) {
11 ovsdb.register.factory('OvsdbRestangular', ['Restangular', 'ENV', function(Restangular, ENV) {
12 return Restangular.withConfig(function(RestangularConfig) {
13 RestangularConfig.setBaseUrl(ENV.getBaseURL("MD_SAL"));
17 // nbv2 support depricated in dlux
18 ovsdb.register.factory('NeutronRestangular', ['Restangular', function(Restangular) {
19 return Restangular.withConfig(function(RestangularConfig) {
20 RestangularConfig.setBaseUrl('http://localhost:8080/controller/nb/v2/neutron');
24 ovsdb.register.factory('CacheFactory', function($q) {
27 /*BUG : Using the persistant cache make the physical
28 * graph links to stop reacting with the force layout
29 * algorithm. The current behavior is to use the cache
30 * only the pile up the datas.
32 svc.obtainDataFromCache = function(key, fn, ctx) {
33 var cacheDefer = $q.defer();
35 if (angular.isUndefined(ovsCache[key])) {
36 fn.call(ctx, function(data) {
39 timestamp : Date.now() + 2000//300000 // 5 mintues
41 cacheDefer.resolve(data);
44 var cacheObj = ovsCache[key];
45 if (cacheObj.timestamp < Date.now() || _.isEmpty(cacheObj.obj)) {
46 fn.call(ctx, function(data) {
49 timestamp : Date.now() + 2000//300000 // 5 mintues
51 cacheDefer.resolve(data);
54 cacheDefer.resolve(cacheObj.obj);
58 return cacheDefer.promise;
61 svc.getCacheObj = function(key) {
62 if (angular.isUndefined(ovsCache[key])) {
71 var TopologySvc = function(OvsdbRestangular, nodeIdentifier, ovsNodeKeys, bridgeNodeKeys, tpKeys, flowInfoKeys, linkIdentifier, $q, $http, CacheFactory) {
73 base: function(type) {
74 return OvsdbRestangular.one('restconf').one(type);
78 function parseOvsdbNode(node) {
83 connectionInfo = null;
85 connectionInfo = node[ovsNodeKeys.CONNECTION_INFO];
86 otherInfo = node[ovsNodeKeys.OTHER_CONFIG];
88 if (_.isObject(connectionInfo)) {
89 inetMgr = connectionInfo[ovsNodeKeys.LOCAL_IP] + ':' + connectionInfo[ovsNodeKeys.LOCAL_PORT];
90 inetNode = connectionInfo[ovsNodeKeys.REMOTE_IP] + ':' + connectionInfo[ovsNodeKeys.REMOTE_PORT];
93 if (_.isArray(otherInfo)) {
94 _.each(otherInfo, function(value) {
95 if (value[ovsNodeKeys.OTHER_CONFIG_KEY] === 'local_ip') {
96 otherLocalIp = value[ovsNodeKeys.OTHER_CONFIG_VALUE];
101 return new OvsCore.OvsNode(node[ovsNodeKeys.NODE_ID], inetMgr, inetNode, otherLocalIp, node[ovsNodeKeys.OVS_VERSION]);
104 function parseBridgeNode(node) {
105 var bridgeNode = null,
106 controllerTarget = '',
107 controllerConnected = false,
108 tp = node[bridgeNodeKeys.TP],
109 controllerEntries = node[bridgeNodeKeys.CONTROLLER_ENTRY];
111 _.each(controllerEntries, function(value) {
112 controllerTarget = value[bridgeNodeKeys.TARGET];
113 controllerEntries = value[bridgeNodeKeys.IS_CONNECTED];
114 return false; // break the anonymus function
117 bridgeNode = new OvsCore.BridgeNode(node[bridgeNodeKeys.NODE_ID], node[bridgeNodeKeys.DATA_PATH], node[bridgeNodeKeys.BRIDGE_NAME], controllerTarget, controllerConnected);
119 _.each(tp, function(value) {
120 var tp = parseBridgeTP(value);
122 if (tp.ofPort == '65534' && (tp.name === 'br-ex' || tp.name === 'br-int')) {
125 bridgeNode.addTerminationPoint(tp);
133 function parseBridgeTP(tp) {
136 extInfo = tp['ovsdb:port-external-ids'] || tp['ovsdb:interface-external-ids'];
138 _.each(extInfo, function(ext) {
139 if (ext[tpKeys.EXTERNAL_KEY_ID] === tpKeys.ATTACHED_MAC) {
140 mac = ext[tpKeys.EXTERNAL_KEY_VALUE];
142 if (ext[tpKeys.EXTERNAL_KEY_ID] === tpKeys.IFACE_ID) {
143 ifaceId = ext[tpKeys.EXTERNAL_KEY_VALUE] || '';
147 return new OvsCore.TerminationPoint(tp[tpKeys.NAME], tp[tpKeys.OF_PORT], tp[tpKeys.INTERFACE_TYPE], mac, ifaceId);
150 function fetchTopology(cb) {
151 var invNodeDefer = this.base('operational').one('opendaylight-inventory:nodes').getList();
152 var netTopoDefer = this.base('operational').one('network-topology:network-topology').getList();
154 // be sure all data are loaded
155 $q.all([invNodeDefer, netTopoDefer]).then(function(values) {
156 var invNode = values[0],
161 // check if the data look fine in network topology
162 if (!netTopo || !netTopo['network-topology'] || !netTopo['network-topology'].topology) {
163 throw new Error('Invalid json format while parsing network-topology');
166 // check if the data look fine in inventory node
167 if (!invNode || !invNode.nodes || !invNode.nodes.node) {
168 throw new Error('Invalid JSON format while parsing inventory-node');
171 // get all topologies and start looping
172 var topologies = netTopo['network-topology'].topology,
173 nodes = invNode.nodes.node,
174 topo = new OvsCore.Topology();
176 _.each(topologies, function(topology, topo_index) {
177 if (!topology.hasOwnProperty('topology-id')) {
178 throw new Error('Invalide JSON format, no topology-id for the topology [' + topo_index + ']');
181 // if there no node it will be an empty array so noop
182 (topology.node || []).forEach(function(node) {
183 if (!node[nodeIdentifier.ID]) {
184 throw new Error('Unexpected node : undefined ' + nodeIdentifier.ID + ' key');
186 index_hash[node[nodeIdentifier.ID]] = i++;
188 if (node['ovsdb:bridge-name']) {
190 topo.registerBridgeNode(parseBridgeNode(node));
191 } else if (node['ovsdb:connection-info']) {
193 topo.registerOvsdbNode(parseOvsdbNode(node));
197 // if there no link it will be an empty array so noop
198 (topology.link || []).forEach(function(link) {
200 var source = link[linkIdentifier.SRC]['source-node'],
201 dest = link[linkIdentifier.DEST]['dest-node'];
203 topo.registerLink(new OvsCore.Link(link[linkIdentifier.ID], source, dest));
208 _.each(nodes, function(node, index) {
213 var bridgeId = node.id;
215 var bridgeNode = _.filter(topo.bridgeNodes, function(bridgeNode) {
216 return bridgeNode.getFLowName() === bridgeId;
219 // match info for bridge node
221 bridgeNode.flowInfo.features = node[flowInfoKeys.FEATURE];
222 bridgeNode.flowInfo.software = node[flowInfoKeys.SOFTWARE];
223 bridgeNode.flowInfo.hardware = node[flowInfoKeys.HARDWARE];
224 bridgeNode.flowInfo.manufacturer = node[flowInfoKeys.MANUFACTURER];
225 bridgeNode.flowInfo.ip = node[flowInfoKeys.IP];
227 _.each(node[flowInfoKeys.TABLE], function(entry) {
228 if (!_.isUndefined(entry.id)) {
229 _.each(entry.flow, function(flow) {
230 bridgeNode.addFlowTableInfo({ key: flow.table_id, value: flow.id});
237 // show relation between ovsNode and switch with a link
238 _.each(topo.ovsdbNodes, function(node, index) {
239 var bridges = _.filter(topo.bridgeNodes, function(bnode) {
240 return bnode.nodeId.indexOf(node.nodeId) > -1;
242 _.each(bridges, function(bridge) {
243 var size = _.size(topo.links),
244 link = new OvsCore.BridgeOvsLink(++size, node.nodeId, bridge.nodeId);
245 topo.registerLink(link);
249 function findVxlan(bridgeNode) {
252 _.each(bridgeNode, function(node) {
253 var ovsdbNode = _.find(topo.ovsdbNodes, function(oNode) {
254 return node.nodeId.indexOf(oNode.nodeId) > -1;
259 _.each(node.tPs, function(tp, index) {
260 if (tp.name.indexOf('vxlan-') > -1) {
264 ovsIp : ovsdbNode.otherLocalIp || ovsdbNode.inetMgr
273 // extract all tunnel paired with their bridge
274 var tunnels = findVxlan(topo.bridgeNodes);
275 // loop over all pairs
276 _.each(tunnels, function(tunnel, index) {
277 var currIp = tunnel.ovsIp,
278 destIp = tunnel.port.name.replace('vxlan-', ''),
279 linkedBridge = _.find(tunnels.slice(index), function(t) {
280 var vxlanIp = t.port.name.replace('vxlan-', '');
281 return vxlanIp === currIp;
285 topo.registerLink(new OvsCore.TunnelLink(tunnel.port.name + linkedBridge.port.name, tunnel.bridge.nodeId, linkedBridge.bridge.nodeId));
298 svc.getTopologies = function() {
299 return CacheFactory.obtainDataFromCache('topologies', fetchTopology, this);
304 TopologySvc.$inject = ['OvsdbRestangular', 'nodeIdentifier', 'ovsNodeKeys', 'bridgeNodeKeys', 'tpKeys', 'flowInfoKeys', 'linkIdentifier', '$q', '$http', 'CacheFactory'];
306 var NeutronSvc = function(NeutronRestangular, CacheFactory, $q, $http) {
308 base: function(type) {
309 return NeutronRestangular.one(type);
314 function fetchSubNetworks(cb) {
315 var subnetskDefer = svc.base('subnets').getList();
316 subnetskDefer.then(function(data) {
320 if (!subnets || !subnets.subnets) {
321 throw new Error('Invalid format from neutron subnets');
324 _.each(subnets.subnets, function(subnet) {
325 if (!subnetHash[subnet.network_id]) {
326 subnetHash[subnet.network_id] = [];
328 tenant_hash[subnet.tenant_id] = {};
329 subnetHash[subnet.network_id].push(new OvsCore.Neutron.SubNet(
343 function fetchNetworks(cb) {
344 var networkDefer = svc.base('networks').getList();
345 var subnetskDefer = svc.getSubNets();
347 $q.all([subnetskDefer, networkDefer]).then(function(datas) {
348 var subnetsHash = datas[0],
352 if (!networks || !networks.networks) {
353 throw new Error('Invalid format from neutron networks');
356 _.each(networks.networks, function(network) {
357 var net = new OvsCore.Neutron.Network(
362 network['router:external'],
365 tenant_hash[net.tenantId] = {};
366 net.addSubNets(subnetsHash[net.id]);
367 networkArray.push(net);
373 function fetchRouters(cb) {
374 var routerDefer = svc.base('routers').getList();
375 routerDefer.then(function(data) {
376 var routers = data.routers,
380 throw new Error('Invalid format from neutron routers');
382 _.each(routers, function(router) {
385 status = router.status,
386 tenantId = router.tenant_id,
387 extGateWayInfo = router.external_gateway_info;
388 tenant_hash[tenantId] = {};
389 routerArray.push(new OvsCore.Neutron.Router(
390 id, name, status, tenantId, extGateWayInfo
397 function fetchPorts(cb) {
398 var portDefer = svc.base('ports').getList();
399 portDefer.then(function(data){
400 var ports = data.ports,
404 throw new Error('Invalid format from neutron ports');
406 _.each(ports, function(port) {
407 tenant_hash[port.tenant_id] = {};
408 portArray.push(new OvsCore.Neutron.Port(
423 function fetchFloatingIps(cb) {
424 var floatingIpDefer = svc.base('floatingips').getList();
425 floatingIpDefer.then(function(data) {
426 var floatingIps = data.floatingips,
427 floatingIpArray = [];
430 throw new Error('Invalid format from neutron floatingIps');
433 _.each(floatingIps, function(fIp) {
434 tenant_hash[fIp.tenant_id] = {};
435 floatingIpArray.push(new OvsCore.Neutron.FloatingIp(
437 fIp.floating_network_id,
439 fIp.fixed_ip_address,
440 fIp.floating_ip_address,
450 svc.getNetworks = function() {
451 return CacheFactory.obtainDataFromCache('networks', fetchNetworks, this);
454 svc.getSubNets = function() {
455 return CacheFactory.obtainDataFromCache('subnet', fetchSubNetworks, this);
458 svc.getPorts = function() {
459 return CacheFactory.obtainDataFromCache('ports', fetchPorts, this);
462 svc.getRouters = function() {
463 return CacheFactory.obtainDataFromCache('routers', fetchRouters, this);
466 svc.getFloatingIps = function() {
467 return CacheFactory.obtainDataFromCache('floatingips', fetchFloatingIps, this);
470 svc.getAllTenants = function() {
471 return Object.keys(tenant_hash);
476 NeutronSvc.$inject = ['NeutronRestangular', 'CacheFactory', '$q', '$http'];
478 var OvsUtil = function(NeutronSvc, TopologySvc, CacheFactory, $q) {
481 function findOvsdbNodeForBridge(ovsdbNodes, bridge) {
482 return _.find(ovsdbNodes, function(node) {
483 return bridge.nodeId.indexOf(node.nodeId) > -1;
487 function pileUpTopologyData(cb) {
488 var networksDefer = NeutronSvc.getNetworks(),
489 routersDefer = NeutronSvc.getRouters(),
490 portsDefer = NeutronSvc.getPorts(),
491 floatingDefer = NeutronSvc.getFloatingIps(),
492 netTopoDefer = TopologySvc.getTopologies();
494 $q.all([networksDefer, routersDefer, portsDefer, floatingDefer, netTopoDefer]).then(function (datas) {
495 var networks = datas[0],
498 floatingIps = datas[3],
501 // match ports with elements
502 _.each(ports, function(port) {
504 // corelate port.topoInfo data with network topology termination point
505 _.each(topo.bridgeNodes, function(bridge) {
506 _.each(bridge.tPs, function(tp) {
507 if (tp.ifaceId === port.id) {
513 ovsNode : findOvsdbNodeForBridge(topo.ovsdbNodes, bridge)
519 switch(port.deviceOwner) {
520 case 'network:router_gateway':
521 case 'network:router_interface':
522 var router = _.find(routers, function(r) { return r.id === port.deviceId; });
524 router.interfaces.push({
526 networkId : port.networkId,
527 ip : port.fixed_ips[0],
529 type: port.deviceOwner.replace('network:', ''),
530 tenantId : port.tenantId,
531 topoInfo: port.topoInfo
538 var network = _.find(networks, function(n) { return n.id === port.networkId;}),
542 inst = new OvsCore.Neutron.Instance(port.id, port.networkId,
543 port.name, port.fixed_ips[0].ip_address, port.mac,
544 port.deviceOwner, port.tenantId, port.topoInfo );
546 inst.extractFloatingIps(floatingIps);
547 network.instances.push(inst);
554 // find all routers for a specific network
555 _.each(networks, function(network) {
556 network.routers = _.filter(routers, function(router) {
557 return network.id === router.externalGateway.network_id;
560 // order instance by ip
561 network.instances.sort(function(a, b) {
562 var ipA = a.ip.slice(a.ip.lastIndexOf('.') + 1),
563 ipB = b.ip.slice(b.ip.lastIndexOf('.') + 1);
572 svc.getLogicalTopology = function() {
573 return CacheFactory.obtainDataFromCache('logicalTopology', pileUpTopologyData, this);
576 svc.extractLogicalByTenant = function(tenantId, subSet) {
577 var lTopoDefer = svc.getLogicalTopology(),
578 resultDefer = $q.defer();
579 lTopoDefer.then(function() {
580 var ports = CacheFactory.getCacheObj('ports').obj,
581 filteredPorts = _.filter(ports, function(p) {
582 return p.tenantId === tenantId;
585 if (!_.isEmpty(filteredPorts)) {
587 _.each(filteredPorts, function(p) {
588 if (!_.isEmpty(p.topoInfo) && !bridgeHash[p.topoInfo[0].bridge.nodeId]) {
589 bridgeHash[p.topoInfo[0].bridge.nodeId] = {};
593 _.each(filteredPorts, function(p) {
594 if (!_.isEmpty(p.topoInfo) && !ovsdbHash[p.topoInfo[0].ovsNode.nodeId]) {
595 ovsdbHash[p.topoInfo[0].ovsNode.nodeId] = {};
599 resultDefer.resolve([Object.keys(bridgeHash), Object.keys(ovsdbHash)]);
601 resultDefer.resolve([], []);
604 return resultDefer.promise;
607 svc.extractLogicalBySubnet = function(subnets, subSet) {
608 var lTopoDefer = svc.getLogicalTopology(),
609 resultDefer = $q.defer();
610 lTopoDefer.then(function() {
611 var ports = CacheFactory.getCacheObj('ports').obj,
612 networks = CacheFactory.getCacheObj('networks').obj;
614 var filteredPorts = _.filter(ports, function(p) {
615 var net = _.find(networks, function(d) {
616 return d.id === p.networkId;
619 return net.asSubnet(subnets);
621 if (!_.isEmpty(filteredPorts)) {
623 _.each(filteredPorts, function(p) {
624 if (!_.isEmpty(p.topoInfo) && !bridgeHash[p.topoInfo[0].bridge.nodeId]) {
625 bridgeHash[p.topoInfo[0].bridge.nodeId] = {};
629 _.each(filteredPorts, function(p) {
630 if (!_.isEmpty(p.topoInfo) && !ovsdbHash[p.topoInfo[0].ovsNode.nodeId]) {
631 ovsdbHash[p.topoInfo[0].ovsNode.nodeId] = {};
634 resultDefer.resolve([Object.keys(bridgeHash), Object.keys(ovsdbHash)]);
636 resultDefer.resolve([], []);
639 return resultDefer.promise;
645 OvsUtil.$inject = ['NeutronSvc', 'TopologySvc', 'CacheFactory', '$q'];
647 ovsdb.register.factory('TopologySvc', TopologySvc);
648 ovsdb.register.factory('NeutronSvc', NeutronSvc);
649 ovsdb.register.factory('OvsUtil', OvsUtil);