Merge "[Bug 5131]- Hard-coded base url in Ovsdb-ui"
[netvirt.git] / ovsdb-ui / module / src / main / resources / ovsdb / ovsdb.services.js
1 /*
2  * Copyright (c) 2015 Inocybe Technologies and others.  All rights reserved.
3  *
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
7  */
8 define(['app/ovsdb/ovsdb.module', 'app/ovsdb/OvsCore', 'underscore', 'app/ovsdb/ovsdb.constant'], function (ovsdb, OvsCore, _) {
9   'use strict';
10
11   ovsdb.register.factory('OvsdbRestangular', ['Restangular', 'ENV', function (Restangular, ENV) {
12     return Restangular.withConfig(function (RestangularConfig) {
13       RestangularConfig.setBaseUrl(ENV.getBaseURL("MD_SAL"));
14     });
15   }]);
16
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');
22     });
23   }]);
24
25   ovsdb.register.factory('CacheFactory', function ($q) {
26     var svc = {},
27       ovsCache = {};
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.
32      */
33     svc.obtainDataFromCache = function (key, fn, ctx) {
34       var cacheDefer = $q.defer();
35
36       if (angular.isUndefined(ovsCache[key])) {
37         fn.call(ctx, function (data) {
38           ovsCache[key] = {
39             obj: data,
40             timestamp: Date.now() + 2000 //300000 // 5 mintues
41           };
42           cacheDefer.resolve(data);
43         });
44       } else {
45         var cacheObj = ovsCache[key];
46         if (cacheObj.timestamp < Date.now() || _.isEmpty(cacheObj.obj)) {
47           fn.call(ctx, function (data) {
48             ovsCache[key] = {
49               obj: data,
50               timestamp: Date.now() + 2000 //300000 // 5 mintues
51             };
52             cacheDefer.resolve(data);
53           });
54         } else {
55           cacheDefer.resolve(cacheObj.obj);
56         }
57       }
58
59       return cacheDefer.promise;
60     };
61
62     svc.getCacheObj = function (key) {
63       if (angular.isUndefined(ovsCache[key])) {
64         ovsCache[key] = {};
65       }
66       return ovsCache[key];
67     };
68
69     return svc;
70   });
71
72   var TopologySvc = function (OvsdbRestangular, nodeIdentifier, ovsNodeKeys, bridgeNodeKeys, tpKeys, flowInfoKeys, linkIdentifier, OVSConstant, $q, $http, CacheFactory) {
73     var svc = {
74       base: function (type) {
75         return OvsdbRestangular.one('restconf').one(type);
76       }
77     };
78
79     function parseOvsdbNode(node) {
80       var inetMgr = '',
81         inetNode = '',
82         otherLocalIp = '',
83         otherInfo = null,
84         connectionInfo = null;
85
86       connectionInfo = node[ovsNodeKeys.CONNECTION_INFO];
87       otherInfo = node[ovsNodeKeys.OTHER_CONFIG];
88
89       if (_.isObject(connectionInfo)) {
90         inetMgr = connectionInfo[ovsNodeKeys.LOCAL_IP] + ':' + connectionInfo[ovsNodeKeys.LOCAL_PORT];
91         inetNode = connectionInfo[ovsNodeKeys.REMOTE_IP] + ':' + connectionInfo[ovsNodeKeys.REMOTE_PORT];
92       }
93
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];
98           }
99         });
100       }
101
102       return new OvsCore.OvsNode(node[ovsNodeKeys.NODE_ID], inetMgr, inetNode, otherLocalIp, node[ovsNodeKeys.OVS_VERSION]);
103     }
104
105     function parseBridgeNode(node) {
106       var bridgeNode = null,
107         controllerTarget = '',
108         controllerConnected = false,
109         tp = node[bridgeNodeKeys.TP],
110         controllerEntries = node[bridgeNodeKeys.CONTROLLER_ENTRY];
111
112       _.each(controllerEntries, function (value) {
113         controllerTarget = value[bridgeNodeKeys.TARGET];
114         controllerEntries = value[bridgeNodeKeys.IS_CONNECTED];
115         return false; // break the anonymus function
116       });
117
118       bridgeNode = new OvsCore.BridgeNode(node[bridgeNodeKeys.NODE_ID], node[bridgeNodeKeys.DATA_PATH], node[bridgeNodeKeys.BRIDGE_NAME], controllerTarget, controllerConnected);
119
120       _.each(tp, function (value) {
121         var tp = parseBridgeTP(value);
122
123         if (tp.ofPort == '65534' && (tp.name === 'br-ex' || tp.name === 'br-int')) {
124           return;
125         } else {
126           bridgeNode.addTerminationPoint(tp);
127         }
128
129       });
130
131       return bridgeNode;
132     }
133
134     function parseBridgeTP(tp) {
135       var mac = '',
136         ifaceId = '',
137         extInfo = tp['ovsdb:port-external-ids'] || tp['ovsdb:interface-external-ids'],
138         type = tp[tpKeys.INTERFACE_TYPE];
139
140       _.each(extInfo, function (ext) {
141         if (ext[tpKeys.EXTERNAL_KEY_ID] === tpKeys.ATTACHED_MAC) {
142           mac = ext[tpKeys.EXTERNAL_KEY_VALUE];
143         }
144         if (ext[tpKeys.EXTERNAL_KEY_ID] === tpKeys.IFACE_ID) {
145           ifaceId = ext[tpKeys.EXTERNAL_KEY_VALUE] || '';
146         }
147       });
148       if (type === OVSConstant.TP_TYPE.VXLAN) {
149         var localIp = null,
150           remoteIp = null;
151         _.each(tp['ovsdb:options'], function (option) {
152           switch (option.option) {
153             case 'local_ip':
154               localIp = option.value;
155               break;
156             case 'remote_ip':
157               remoteIp = option.value;
158               break;
159           }
160         });
161         return new OvsCore.Tunnel(tp[tpKeys.NAME], tp[tpKeys.OF_PORT], type, mac, ifaceId, localIp, remoteIp);
162       }
163       return new OvsCore.TerminationPoint(tp[tpKeys.NAME], tp[tpKeys.OF_PORT], type, mac, ifaceId);
164
165     }
166
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();
170
171       // be sure all data are loaded
172       $q.all([invNodeDefer, netTopoDefer]).then(function (values) {
173           var invNode = values[0],
174             netTopo = values[1],
175             index_hash = [],
176             i = 0;
177
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');
181           }
182
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');
186           }
187
188           // get all topologies and start looping
189           var topologies = netTopo['network-topology'].topology,
190             nodes = invNode.nodes.node,
191             topo = new OvsCore.Topology();
192
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 + ']');
196             }
197
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');
202               }
203               index_hash[node[nodeIdentifier.ID]] = i++;
204
205               if (node['ovsdb:bridge-name']) {
206                 //bridge Node
207                 topo.registerBridgeNode(parseBridgeNode(node));
208               } else if (node['ovsdb:connection-info']) {
209                 // obsvdb Node
210                 topo.registerOvsdbNode(parseOvsdbNode(node));
211               }
212             });
213
214             // if there no link it will be an empty array so noop
215             (topology.link || []).forEach(function (link) {
216
217               var source = link[linkIdentifier.SRC]['source-node'],
218                 dest = link[linkIdentifier.DEST]['dest-node'];
219
220               topo.registerLink(new OvsCore.Link(link[linkIdentifier.ID], source, dest));
221             });
222
223           });
224
225           _.each(nodes, function (node, index) {
226             if (!node.id) {
227               return;
228             }
229
230             var bridgeId = node.id;
231
232             var bridgeNode = _.filter(topo.bridgeNodes, function (bridgeNode) {
233               return bridgeNode.getFLowName() === bridgeId;
234             })[0];
235
236             // match info for bridge node
237             if (bridgeNode) {
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];
243
244               _.each(node[flowInfoKeys.TABLE], function (entry) {
245                 if (!_.isUndefined(entry.id)) {
246                   _.each(entry.flow, function (flow) {
247                     bridgeNode.addFlowTableInfo({
248                       key: flow.table_id,
249                       value: flow.id
250                     });
251                   });
252                 }
253               });
254             }
255           });
256
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;
261             });
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);
266             });
267           });
268
269           function findVxlan(bridgeNode) {
270             var tunnels = [];
271
272             _.each(bridgeNode, function (node) {
273               var ovsdbNode = _.find(topo.ovsdbNodes, function (oNode) {
274                 return node.nodeId.indexOf(oNode.nodeId) > -1;
275               });
276               if (!ovsdbNode) {
277                 return false;
278               }
279               _.each(node.tPs, function (tp, index) {
280                 if (tp instanceof OvsCore.Tunnel) {
281                   tunnels.push({
282                     port: tp,
283                     bridge: node
284                   });
285                 }
286               });
287             });
288
289             return tunnels;
290           }
291
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,
299               pairIndex = 0,
300               linkedBridge = _.find(tunnels, function (t, i) {
301                 pairIndex = i;
302                 return t.port.remoteIp === currIp && t.port.localIp == destIp;
303               });
304
305             if (linkedBridge) {
306               tunnels.splice(pairIndex, 1);
307               topo.registerLink(new OvsCore.TunnelLink(tunnel.port.name + linkedBridge.port.name, tunnel.bridge.nodeId, linkedBridge.bridge.nodeId));
308             }
309           }
310
311           topo.updateLink();
312           cb(topo);
313         },
314         function (err) {
315           throw err;
316         }
317       );
318     }
319
320     svc.getTopologies = function () {
321       return CacheFactory.obtainDataFromCache('topologies', fetchTopology, this);
322     };
323
324     return svc;
325   };
326   TopologySvc.$inject = ['OvsdbRestangular', 'nodeIdentifier', 'ovsNodeKeys', 'bridgeNodeKeys', 'tpKeys', 'flowInfoKeys', 'linkIdentifier', 'OVSConstant', '$q', '$http', 'CacheFactory'];
327
328   var NeutronSvc = function (NeutronRestangular, CacheFactory, $q, $http) {
329     var svc = {
330         base: function (type) {
331           return NeutronRestangular.one(type);
332         }
333       },
334       tenant_hash = {};
335
336     function fetchSubNetworks(cb) {
337       var subnetskDefer = svc.base('subnets').getList();
338       subnetskDefer.then(function (data) {
339         var subnets = data,
340           subnetHash = {};
341
342         if (!subnets || !subnets.subnets) {
343           throw new Error('Invalid format from neutron subnets');
344         }
345
346         _.each(subnets.subnets, function (subnet) {
347           if (!subnetHash[subnet.network_id]) {
348             subnetHash[subnet.network_id] = [];
349           }
350           tenant_hash[subnet.tenant_id] = {};
351           subnetHash[subnet.network_id].push(new OvsCore.Neutron.SubNet(
352             subnet.id,
353             subnet.network_id,
354             subnet.name,
355             subnet.ip_version,
356             subnet.cidr,
357             subnet.gateway_ip,
358             subnet.tenant_id
359           ));
360         });
361         cb(subnetHash);
362       });
363     }
364
365     function fetchNetworks(cb) {
366       var networkDefer = svc.base('networks').getList();
367       var subnetskDefer = svc.getSubNets();
368
369       $q.all([subnetskDefer, networkDefer]).then(function (datas) {
370         var subnetsHash = datas[0],
371           networks = datas[1],
372           networkArray = [];
373
374         if (!networks || !networks.networks) {
375           throw new Error('Invalid format from neutron networks');
376         }
377
378         _.each(networks.networks, function (network) {
379           var net = new OvsCore.Neutron.Network(
380             network.id,
381             network.name,
382             network.shared,
383             network.status,
384             network['router:external'],
385             network.tenant_id
386           );
387           tenant_hash[net.tenantId] = {};
388           net.addSubNets(subnetsHash[net.id]);
389           networkArray.push(net);
390         });
391         cb(networkArray);
392       });
393     }
394
395     function fetchRouters(cb) {
396       var routerDefer = svc.base('routers').getList();
397       routerDefer.then(function (data) {
398         var routers = data.routers,
399           routerArray = [];
400
401         if (!routers) {
402           throw new Error('Invalid format from neutron routers');
403         }
404         _.each(routers, function (router) {
405           var id = router.id,
406             name = router.name,
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
413           ));
414         });
415         cb(routerArray);
416       });
417     }
418
419     function fetchPorts(cb) {
420       var portDefer = svc.base('ports').getList();
421       portDefer.then(function (data) {
422         var ports = data.ports,
423           portArray = [];
424
425         if (!ports) {
426           throw new Error('Invalid format from neutron ports');
427         }
428         _.each(ports, function (port) {
429           tenant_hash[port.tenant_id] = {};
430           portArray.push(new OvsCore.Neutron.Port(
431             port.id,
432             port.network_id,
433             port.name,
434             port.tenant_id,
435             port.device_id,
436             port.device_owner,
437             port.fixed_ips,
438             port.mac_address
439           ));
440         });
441         cb(portArray);
442       });
443     }
444
445     function fetchFloatingIps(cb) {
446       var floatingIpDefer = svc.base('floatingips').getList();
447       floatingIpDefer.then(function (data) {
448         var floatingIps = data.floatingips,
449           floatingIpArray = [];
450
451         if (!floatingIps) {
452           throw new Error('Invalid format from neutron floatingIps');
453         }
454
455         _.each(floatingIps, function (fIp) {
456           tenant_hash[fIp.tenant_id] = {};
457           floatingIpArray.push(new OvsCore.Neutron.FloatingIp(
458             fIp.id,
459             fIp.floating_network_id,
460             fIp.port_id,
461             fIp.fixed_ip_address,
462             fIp.floating_ip_address,
463             fIp.tenant_id,
464             fIp.status
465           ));
466         });
467
468         cb(floatingIpArray);
469       });
470     }
471
472     svc.getNetworks = function () {
473       return CacheFactory.obtainDataFromCache('networks', fetchNetworks, this);
474     };
475
476     svc.getSubNets = function () {
477       return CacheFactory.obtainDataFromCache('subnet', fetchSubNetworks, this);
478     };
479
480     svc.getPorts = function () {
481       return CacheFactory.obtainDataFromCache('ports', fetchPorts, this);
482     };
483
484     svc.getRouters = function () {
485       return CacheFactory.obtainDataFromCache('routers', fetchRouters, this);
486     };
487
488     svc.getFloatingIps = function () {
489       return CacheFactory.obtainDataFromCache('floatingips', fetchFloatingIps, this);
490     };
491
492     svc.getAllTenants = function () {
493       return Object.keys(tenant_hash);
494     };
495
496     return svc;
497   };
498   NeutronSvc.$inject = ['NeutronRestangular', 'CacheFactory', '$q', '$http'];
499
500   var OvsUtil = function (NeutronSvc, TopologySvc, CacheFactory, $q) {
501     var svc = {};
502
503     function findOvsdbNodeForBridge(ovsdbNodes, bridge) {
504       return _.find(ovsdbNodes, function (node) {
505         return bridge.nodeId.indexOf(node.nodeId) > -1;
506       });
507     }
508
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();
515
516       $q.all([networksDefer, routersDefer, portsDefer, floatingDefer, netTopoDefer]).then(function (datas) {
517         var networks = datas[0],
518           routers = datas[1],
519           ports = datas[2],
520           floatingIps = datas[3],
521           topo = datas[4];
522
523         // match ports with elements
524         _.each(ports, function (port) {
525           port.topoInfo = [];
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) {
530                 port.topoInfo.push({
531                   name: tp.name,
532                   ofPort: tp.ofPort,
533                   mac: bridge.dpIp,
534                   bridge: bridge,
535                   ovsNode: findOvsdbNodeForBridge(topo.ovsdbNodes, bridge)
536                 });
537               }
538             });
539           });
540
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;
546               });
547               if (router) {
548                 router.interfaces.push({
549                   id: port.id,
550                   networkId: port.networkId,
551                   ip: port.fixed_ips[0],
552                   mac: port.mac,
553                   type: port.deviceOwner.replace('network:', ''),
554                   tenantId: port.tenantId,
555                   topoInfo: port.topoInfo
556                 });
557               }
558               break;
559             case 'compute:None':
560             case 'compute:nova':
561             case 'network:dhcp':
562               var network = _.find(networks, function (n) {
563                   return n.id === port.networkId;
564                 }),
565                 inst = null;
566
567               if (network) {
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);
571
572                 inst.extractFloatingIps(floatingIps);
573                 network.instances.push(inst);
574               }
575               break;
576           }
577
578         });
579
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;
584           });
585
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);
590             return ipA - ipB;
591           });
592         });
593
594         cb(networks);
595       });
596     }
597
598     svc.getLogicalTopology = function () {
599       return CacheFactory.obtainDataFromCache('logicalTopology', pileUpTopologyData, this);
600     };
601
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;
609           });
610
611         if (!_.isEmpty(filteredPorts)) {
612           var bridgeHash = {};
613           _.each(filteredPorts, function (p) {
614             if (!_.isEmpty(p.topoInfo) && !bridgeHash[p.topoInfo[0].bridge.nodeId]) {
615               bridgeHash[p.topoInfo[0].bridge.nodeId] = {};
616             }
617           });
618           var ovsdbHash = {};
619           _.each(filteredPorts, function (p) {
620             if (!_.isEmpty(p.topoInfo) && !ovsdbHash[p.topoInfo[0].ovsNode.nodeId]) {
621               ovsdbHash[p.topoInfo[0].ovsNode.nodeId] = {};
622             }
623           });
624
625           resultDefer.resolve([Object.keys(bridgeHash), Object.keys(ovsdbHash)]);
626         } else {
627           resultDefer.resolve([], []);
628         }
629       });
630       return resultDefer.promise;
631     };
632
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;
639
640         var filteredPorts = _.filter(ports, function (p) {
641           var net = _.find(networks, function (d) {
642             return d.id === p.networkId;
643           });
644
645           return net.asSubnet(subnets);
646         });
647         if (!_.isEmpty(filteredPorts)) {
648           var bridgeHash = {};
649           _.each(filteredPorts, function (p) {
650             if (!_.isEmpty(p.topoInfo) && !bridgeHash[p.topoInfo[0].bridge.nodeId]) {
651               bridgeHash[p.topoInfo[0].bridge.nodeId] = {};
652             }
653           });
654           var ovsdbHash = {};
655           _.each(filteredPorts, function (p) {
656             if (!_.isEmpty(p.topoInfo) && !ovsdbHash[p.topoInfo[0].ovsNode.nodeId]) {
657               ovsdbHash[p.topoInfo[0].ovsNode.nodeId] = {};
658             }
659           });
660           resultDefer.resolve([Object.keys(bridgeHash), Object.keys(ovsdbHash)]);
661         } else {
662           resultDefer.resolve([], []);
663         }
664       });
665       return resultDefer.promise;
666     };
667
668     return svc;
669   };
670
671   OvsUtil.$inject = ['NeutronSvc', 'TopologySvc', 'CacheFactory', '$q'];
672
673   ovsdb.register.factory('TopologySvc', TopologySvc);
674   ovsdb.register.factory('NeutronSvc', NeutronSvc);
675   ovsdb.register.factory('OvsUtil', OvsUtil);
676 });