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 var baseUrl = window.location.protocol + '//' + window.location.hostname;
21 RestangularConfig.setBaseUrl(baseUrl + ':8080/controller/nb/v2/neutron');
25 ovsdb.register.factory('CacheFactory', function ($q) {
28 /*BUG : Using the persistant cache make the physical
29 * graph links to stop reacting with the force layout
30 * algorithm. The current behavior is to use the cache
31 * only the pile up the datas.
33 svc.obtainDataFromCache = function (key, fn, ctx) {
34 var cacheDefer = $q.defer();
36 if (angular.isUndefined(ovsCache[key])) {
37 fn.call(ctx, function (data) {
40 timestamp: Date.now() + 2000 //300000 // 5 mintues
42 cacheDefer.resolve(data);
45 var cacheObj = ovsCache[key];
46 if (cacheObj.timestamp < Date.now() || _.isEmpty(cacheObj.obj)) {
47 fn.call(ctx, function (data) {
50 timestamp: Date.now() + 2000 //300000 // 5 mintues
52 cacheDefer.resolve(data);
55 cacheDefer.resolve(cacheObj.obj);
59 return cacheDefer.promise;
62 svc.getCacheObj = function (key) {
63 if (angular.isUndefined(ovsCache[key])) {
72 var TopologySvc = function (OvsdbRestangular, nodeIdentifier, ovsNodeKeys, bridgeNodeKeys, tpKeys, flowInfoKeys, linkIdentifier, OVSConstant, $q, $http, CacheFactory) {
74 base: function (type) {
75 return OvsdbRestangular.one('restconf').one(type);
79 function parseOvsdbNode(node) {
84 connectionInfo = null;
86 connectionInfo = node[ovsNodeKeys.CONNECTION_INFO];
87 otherInfo = node[ovsNodeKeys.OTHER_CONFIG];
89 if (_.isObject(connectionInfo)) {
90 inetMgr = connectionInfo[ovsNodeKeys.LOCAL_IP] + ':' + connectionInfo[ovsNodeKeys.LOCAL_PORT];
91 inetNode = connectionInfo[ovsNodeKeys.REMOTE_IP] + ':' + connectionInfo[ovsNodeKeys.REMOTE_PORT];
94 if (_.isArray(otherInfo)) {
95 _.each(otherInfo, function (value) {
96 if (value[ovsNodeKeys.OTHER_CONFIG_KEY] === 'local_ip') {
97 otherLocalIp = value[ovsNodeKeys.OTHER_CONFIG_VALUE];
102 return new OvsCore.OvsNode(node[ovsNodeKeys.NODE_ID], inetMgr, inetNode, otherLocalIp, node[ovsNodeKeys.OVS_VERSION]);
105 function parseBridgeNode(node) {
106 var bridgeNode = null,
107 controllerTarget = '',
108 controllerConnected = false,
109 tp = node[bridgeNodeKeys.TP],
110 controllerEntries = node[bridgeNodeKeys.CONTROLLER_ENTRY];
112 _.each(controllerEntries, function (value) {
113 controllerTarget = value[bridgeNodeKeys.TARGET];
114 controllerEntries = value[bridgeNodeKeys.IS_CONNECTED];
115 return false; // break the anonymus function
118 bridgeNode = new OvsCore.BridgeNode(node[bridgeNodeKeys.NODE_ID], node[bridgeNodeKeys.DATA_PATH], node[bridgeNodeKeys.BRIDGE_NAME], controllerTarget, controllerConnected);
120 _.each(tp, function (value) {
121 var tp = parseBridgeTP(value);
123 if (tp.ofPort == '65534' && (tp.name === 'br-ex' || tp.name === 'br-int')) {
126 bridgeNode.addTerminationPoint(tp);
134 function parseBridgeTP(tp) {
137 extInfo = tp['ovsdb:port-external-ids'] || tp['ovsdb:interface-external-ids'],
138 type = tp[tpKeys.INTERFACE_TYPE];
140 _.each(extInfo, function (ext) {
141 if (ext[tpKeys.EXTERNAL_KEY_ID] === tpKeys.ATTACHED_MAC) {
142 mac = ext[tpKeys.EXTERNAL_KEY_VALUE];
144 if (ext[tpKeys.EXTERNAL_KEY_ID] === tpKeys.IFACE_ID) {
145 ifaceId = ext[tpKeys.EXTERNAL_KEY_VALUE] || '';
148 if (type === OVSConstant.TP_TYPE.VXLAN) {
151 _.each(tp['ovsdb:options'], function (option) {
152 switch (option.option) {
154 localIp = option.value;
157 remoteIp = option.value;
161 return new OvsCore.Tunnel(tp[tpKeys.NAME], tp[tpKeys.OF_PORT], type, mac, ifaceId, localIp, remoteIp);
163 return new OvsCore.TerminationPoint(tp[tpKeys.NAME], tp[tpKeys.OF_PORT], type, mac, ifaceId);
167 function fetchTopology(cb) {
168 var invNodeDefer = this.base('operational').one('opendaylight-inventory:nodes').getList();
169 var netTopoDefer = this.base('operational').one('network-topology:network-topology').getList();
171 // be sure all data are loaded
172 $q.all([invNodeDefer, netTopoDefer]).then(function (values) {
173 var invNode = values[0],
178 // check if the data look fine in network topology
179 if (!netTopo || !netTopo['network-topology'] || !netTopo['network-topology'].topology) {
180 throw new Error('Invalid json format while parsing network-topology');
183 // check if the data look fine in inventory node
184 if (!invNode || !invNode.nodes || !invNode.nodes.node) {
185 throw new Error('Invalid JSON format while parsing inventory-node');
188 // get all topologies and start looping
189 var topologies = netTopo['network-topology'].topology,
190 nodes = invNode.nodes.node,
191 topo = new OvsCore.Topology();
193 _.each(topologies, function (topology, topo_index) {
194 if (!topology.hasOwnProperty('topology-id')) {
195 throw new Error('Invalide JSON format, no topology-id for the topology [' + topo_index + ']');
198 // if there no node it will be an empty array so noop
199 (topology.node || []).forEach(function (node) {
200 if (!node[nodeIdentifier.ID]) {
201 throw new Error('Unexpected node : undefined ' + nodeIdentifier.ID + ' key');
203 index_hash[node[nodeIdentifier.ID]] = i++;
205 if (node['ovsdb:bridge-name']) {
207 topo.registerBridgeNode(parseBridgeNode(node));
208 } else if (node['ovsdb:connection-info']) {
210 topo.registerOvsdbNode(parseOvsdbNode(node));
214 // if there no link it will be an empty array so noop
215 (topology.link || []).forEach(function (link) {
217 var source = link[linkIdentifier.SRC]['source-node'],
218 dest = link[linkIdentifier.DEST]['dest-node'];
220 topo.registerLink(new OvsCore.Link(link[linkIdentifier.ID], source, dest));
225 _.each(nodes, function (node, index) {
230 var bridgeId = node.id;
232 var bridgeNode = _.filter(topo.bridgeNodes, function (bridgeNode) {
233 return bridgeNode.getFLowName() === bridgeId;
236 // match info for bridge node
238 bridgeNode.flowInfo.features = node[flowInfoKeys.FEATURE];
239 bridgeNode.flowInfo.software = node[flowInfoKeys.SOFTWARE];
240 bridgeNode.flowInfo.hardware = node[flowInfoKeys.HARDWARE];
241 bridgeNode.flowInfo.manufacturer = node[flowInfoKeys.MANUFACTURER];
242 bridgeNode.flowInfo.ip = node[flowInfoKeys.IP];
244 _.each(node[flowInfoKeys.TABLE], function (entry) {
245 if (!_.isUndefined(entry.id)) {
246 _.each(entry.flow, function (flow) {
247 bridgeNode.addFlowTableInfo({
257 // show relation between ovsNode and switch with a link
258 _.each(topo.ovsdbNodes, function (node, index) {
259 var bridges = _.filter(topo.bridgeNodes, function (bnode) {
260 return bnode.nodeId.indexOf(node.nodeId) > -1;
262 _.each(bridges, function (bridge) {
263 var size = _.size(topo.links),
264 link = new OvsCore.BridgeOvsLink(++size, node.nodeId, bridge.nodeId);
265 topo.registerLink(link);
269 function findVxlan(bridgeNode) {
272 _.each(bridgeNode, function (node) {
273 var ovsdbNode = _.find(topo.ovsdbNodes, function (oNode) {
274 return node.nodeId.indexOf(oNode.nodeId) > -1;
279 _.each(node.tPs, function (tp, index) {
280 if (tp instanceof OvsCore.Tunnel) {
292 // extract all tunnel paired with their bridge
293 var tunnels = findVxlan(topo.bridgeNodes);
294 // loop over all pairs
295 for (var index = 0; index < tunnels.length; ++index) {
296 var tunnel = tunnels[index],
297 currIp = tunnel.port.localIp,
298 destIp = tunnel.port.remoteIp,
300 linkedBridge = _.find(tunnels, function (t, i) {
302 return t.port.remoteIp === currIp && t.port.localIp == destIp;
306 tunnels.splice(pairIndex, 1);
307 topo.registerLink(new OvsCore.TunnelLink(tunnel.port.name + linkedBridge.port.name, tunnel.bridge.nodeId, linkedBridge.bridge.nodeId));
320 svc.getTopologies = function () {
321 return CacheFactory.obtainDataFromCache('topologies', fetchTopology, this);
326 TopologySvc.$inject = ['OvsdbRestangular', 'nodeIdentifier', 'ovsNodeKeys', 'bridgeNodeKeys', 'tpKeys', 'flowInfoKeys', 'linkIdentifier', 'OVSConstant', '$q', '$http', 'CacheFactory'];
328 var NeutronSvc = function (NeutronRestangular, CacheFactory, $q, $http) {
330 base: function (type) {
331 return NeutronRestangular.one(type);
336 function fetchSubNetworks(cb) {
337 var subnetskDefer = svc.base('subnets').getList();
338 subnetskDefer.then(function (data) {
342 if (!subnets || !subnets.subnets) {
343 throw new Error('Invalid format from neutron subnets');
346 _.each(subnets.subnets, function (subnet) {
347 if (!subnetHash[subnet.network_id]) {
348 subnetHash[subnet.network_id] = [];
350 tenant_hash[subnet.tenant_id] = {};
351 subnetHash[subnet.network_id].push(new OvsCore.Neutron.SubNet(
365 function fetchNetworks(cb) {
366 var networkDefer = svc.base('networks').getList();
367 var subnetskDefer = svc.getSubNets();
369 $q.all([subnetskDefer, networkDefer]).then(function (datas) {
370 var subnetsHash = datas[0],
374 if (!networks || !networks.networks) {
375 throw new Error('Invalid format from neutron networks');
378 _.each(networks.networks, function (network) {
379 var net = new OvsCore.Neutron.Network(
384 network['router:external'],
387 tenant_hash[net.tenantId] = {};
388 net.addSubNets(subnetsHash[net.id]);
389 networkArray.push(net);
395 function fetchRouters(cb) {
396 var routerDefer = svc.base('routers').getList();
397 routerDefer.then(function (data) {
398 var routers = data.routers,
402 throw new Error('Invalid format from neutron routers');
404 _.each(routers, function (router) {
407 status = router.status,
408 tenantId = router.tenant_id,
409 extGateWayInfo = router.external_gateway_info;
410 tenant_hash[tenantId] = {};
411 routerArray.push(new OvsCore.Neutron.Router(
412 id, name, status, tenantId, extGateWayInfo
419 function fetchPorts(cb) {
420 var portDefer = svc.base('ports').getList();
421 portDefer.then(function (data) {
422 var ports = data.ports,
426 throw new Error('Invalid format from neutron ports');
428 _.each(ports, function (port) {
429 tenant_hash[port.tenant_id] = {};
430 portArray.push(new OvsCore.Neutron.Port(
445 function fetchFloatingIps(cb) {
446 var floatingIpDefer = svc.base('floatingips').getList();
447 floatingIpDefer.then(function (data) {
448 var floatingIps = data.floatingips,
449 floatingIpArray = [];
452 throw new Error('Invalid format from neutron floatingIps');
455 _.each(floatingIps, function (fIp) {
456 tenant_hash[fIp.tenant_id] = {};
457 floatingIpArray.push(new OvsCore.Neutron.FloatingIp(
459 fIp.floating_network_id,
461 fIp.fixed_ip_address,
462 fIp.floating_ip_address,
472 svc.getNetworks = function () {
473 return CacheFactory.obtainDataFromCache('networks', fetchNetworks, this);
476 svc.getSubNets = function () {
477 return CacheFactory.obtainDataFromCache('subnet', fetchSubNetworks, this);
480 svc.getPorts = function () {
481 return CacheFactory.obtainDataFromCache('ports', fetchPorts, this);
484 svc.getRouters = function () {
485 return CacheFactory.obtainDataFromCache('routers', fetchRouters, this);
488 svc.getFloatingIps = function () {
489 return CacheFactory.obtainDataFromCache('floatingips', fetchFloatingIps, this);
492 svc.getAllTenants = function () {
493 return Object.keys(tenant_hash);
498 NeutronSvc.$inject = ['NeutronRestangular', 'CacheFactory', '$q', '$http'];
500 var OvsUtil = function (NeutronSvc, TopologySvc, CacheFactory, $q) {
503 function findOvsdbNodeForBridge(ovsdbNodes, bridge) {
504 return _.find(ovsdbNodes, function (node) {
505 return bridge.nodeId.indexOf(node.nodeId) > -1;
509 function pileUpTopologyData(cb) {
510 var networksDefer = NeutronSvc.getNetworks(),
511 routersDefer = NeutronSvc.getRouters(),
512 portsDefer = NeutronSvc.getPorts(),
513 floatingDefer = NeutronSvc.getFloatingIps(),
514 netTopoDefer = TopologySvc.getTopologies();
516 $q.all([networksDefer, routersDefer, portsDefer, floatingDefer, netTopoDefer]).then(function (datas) {
517 var networks = datas[0],
520 floatingIps = datas[3],
523 // match ports with elements
524 _.each(ports, function (port) {
526 // corelate port.topoInfo data with network topology termination point
527 _.each(topo.bridgeNodes, function (bridge) {
528 _.each(bridge.tPs, function (tp) {
529 if (tp.ifaceId === port.id) {
535 ovsNode: findOvsdbNodeForBridge(topo.ovsdbNodes, bridge)
541 switch (port.deviceOwner) {
542 case 'network:router_gateway':
543 case 'network:router_interface':
544 var router = _.find(routers, function (r) {
545 return r.id === port.deviceId;
548 router.interfaces.push({
550 networkId: port.networkId,
551 ip: port.fixed_ips[0],
553 type: port.deviceOwner.replace('network:', ''),
554 tenantId: port.tenantId,
555 topoInfo: port.topoInfo
562 var network = _.find(networks, function (n) {
563 return n.id === port.networkId;
568 inst = new OvsCore.Neutron.Instance(port.id, port.networkId,
569 port.name, port.fixed_ips[0].ip_address, port.mac,
570 port.deviceOwner, port.tenantId, port.topoInfo);
572 inst.extractFloatingIps(floatingIps);
573 network.instances.push(inst);
580 // find all routers for a specific network
581 _.each(networks, function (network) {
582 network.routers = _.filter(routers, function (router) {
583 return network.id === router.externalGateway.network_id;
586 // order instance by ip
587 network.instances.sort(function (a, b) {
588 var ipA = a.ip.slice(a.ip.lastIndexOf('.') + 1),
589 ipB = b.ip.slice(b.ip.lastIndexOf('.') + 1);
598 svc.getLogicalTopology = function () {
599 return CacheFactory.obtainDataFromCache('logicalTopology', pileUpTopologyData, this);
602 svc.extractLogicalByTenant = function (tenantId, subSet) {
603 var lTopoDefer = svc.getLogicalTopology(),
604 resultDefer = $q.defer();
605 lTopoDefer.then(function () {
606 var ports = CacheFactory.getCacheObj('ports').obj,
607 filteredPorts = _.filter(ports, function (p) {
608 return p.tenantId === tenantId;
611 if (!_.isEmpty(filteredPorts)) {
613 _.each(filteredPorts, function (p) {
614 if (!_.isEmpty(p.topoInfo) && !bridgeHash[p.topoInfo[0].bridge.nodeId]) {
615 bridgeHash[p.topoInfo[0].bridge.nodeId] = {};
619 _.each(filteredPorts, function (p) {
620 if (!_.isEmpty(p.topoInfo) && !ovsdbHash[p.topoInfo[0].ovsNode.nodeId]) {
621 ovsdbHash[p.topoInfo[0].ovsNode.nodeId] = {};
625 resultDefer.resolve([Object.keys(bridgeHash), Object.keys(ovsdbHash)]);
627 resultDefer.resolve([], []);
630 return resultDefer.promise;
633 svc.extractLogicalBySubnet = function (subnets, subSet) {
634 var lTopoDefer = svc.getLogicalTopology(),
635 resultDefer = $q.defer();
636 lTopoDefer.then(function () {
637 var ports = CacheFactory.getCacheObj('ports').obj,
638 networks = CacheFactory.getCacheObj('networks').obj;
640 var filteredPorts = _.filter(ports, function (p) {
641 var net = _.find(networks, function (d) {
642 return d.id === p.networkId;
645 return net.asSubnet(subnets);
647 if (!_.isEmpty(filteredPorts)) {
649 _.each(filteredPorts, function (p) {
650 if (!_.isEmpty(p.topoInfo) && !bridgeHash[p.topoInfo[0].bridge.nodeId]) {
651 bridgeHash[p.topoInfo[0].bridge.nodeId] = {};
655 _.each(filteredPorts, function (p) {
656 if (!_.isEmpty(p.topoInfo) && !ovsdbHash[p.topoInfo[0].ovsNode.nodeId]) {
657 ovsdbHash[p.topoInfo[0].ovsNode.nodeId] = {};
660 resultDefer.resolve([Object.keys(bridgeHash), Object.keys(ovsdbHash)]);
662 resultDefer.resolve([], []);
665 return resultDefer.promise;
671 OvsUtil.$inject = ['NeutronSvc', 'TopologySvc', 'CacheFactory', '$q'];
673 ovsdb.register.factory('TopologySvc', TopologySvc);
674 ovsdb.register.factory('NeutronSvc', NeutronSvc);
675 ovsdb.register.factory('OvsUtil', OvsUtil);