Add seed code for sdnr app based on ONF Centennial
[ccsdk/apps.git] / sdnr / wireless-transport / code-Carbon-SR1 / ux / mwtnConnect / mwtnConnect-module / src / main / resources / mwtnConnect / mwtnConnect.controller.js
1 /*
2  * Copyright (c) 2016 highstreet technologies GmbH 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
9 define(['app/mwtnConnect/mwtnConnect.module',
10         'app/mwtnConnect/mwtnConnect.services',
11         'app/mwtnCommons/mwtnCommons.module'], 
12         function(mwtnConnectApp) {
13
14   mwtnConnectApp.register.controller('mwtnConnectCtrl', ['$scope', '$rootScope', '$timeout', '$window', '$q', 'uiGridConstants', '$uibModal', '$mwtnConnect', '$mwtnLog', 'NetConfServer',  
15                                                          function($scope, $rootScope, $timeout, $window, $q, uiGridConstants, $uibModal, $mwtnConnect, $mwtnLog, NetConfServer) {
16
17     var COMPONENT = 'mwtnConnectCtrl';
18     $mwtnLog.info({component: COMPONENT, message: 'mwtnConnectCtrl started!'});
19
20     $rootScope.section_logo = 'src/app/mwtnConnect/images/sdncConnect.png'; // Add your topbar logo location here such as 'assets/images/logo_topology.gif'
21
22     $scope.highlightFilteredHeader = $mwtnConnect.highlightFilteredHeader;
23     
24     $scope.oneAtATime = true;
25     $scope.status = {requiredNes: true};
26     $scope.spinner = {};
27     $scope.spinner.TEST = true;
28     
29     var requiredNesConnectionStatusCellTemplate = [
30       '<div class="ui-grid-cell-contents" ng-class="{ \'green\': grid.getCellValue(row, col) === \'connected\'}">',
31       '  <i ng-class="{\'fa fa-link\': grid.getCellValue(row, col) === \'connected\', \'fa fa-chain-broken\': grid.getCellValue(row, col) !== \'connected\'}" aria-hidden="true"></i>',
32       '  <span>{{grid.getCellValue(row, col)}}</span>',
33       '</div>'].join('');
34     
35     var nameCellTemplate = [
36       '<div class="ui-grid-cell-contents">',
37       '  <a href="{{row.entity.webUri}}" target="_blank" title="Access NE web application" ng-show="row.entity.webUri">',
38       '    <i class="fa fa-external-link" aria-hidden="true"></i>',
39       '    <span>{{grid.getCellValue(row, col)}}</span>',
40       '  </a>',
41       '  <span ng-show="!row.entity.webUri">{{grid.getCellValue(row, col)}}</span>',
42       '</div>'].join('');
43
44    //    '<button class="btn btn-primary" ng-click="grid.appScope.edit(row.entity)"><i class="fa fa-pencil"></i></button>',
45    //    '<button class="btn btn-default" ng-click="grid.appScope.delete(row.entity)"><i class="fa fa-times mwtnError"></i></button>',
46     var requiredNesActionCellTemplate = [
47       '<span>&nbsp;&nbsp;</span>',
48       '<div class="btn-group">',
49       '  <button class="btn btn-primary" ng-click="grid.appScope.connect(row.entity)" title="Mount"><i class="fa fa-link" aria-hidden="true"></i></button>',
50       '  <button class="btn btn-warning" ng-click="grid.appScope.disconnect(row.entity)" title="Unmount"><i class="fa fa-chain-broken" aria-hidden="true"></i></button>',
51       '  <button class="btn btn-success" ng-click="grid.appScope.showDetails(row.entity)" title="Information"><i class="fa fa-info-circle" aria-hidden="true"></i></button>',
52       '<div class="btn-group">',
53       '<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>',
54       '</div>',
55       '  <a class="btn btn-primary" ng-href="#/pnfFault/{{row.entity.name}}" title="Fault Management" target="fm">F</a>',
56       '  <a class="btn btn-primary" ng-href="#/pnfBrowser/{{row.entity.name}}" title="Configuration Management" target="cm">C</a>',
57       '  <a class="btn btn-default" title="Accounting Management" target="am">A</a>',
58       '  <a class="btn btn-primary" ng-href="#/pnfPerformanceHistory/{{row.entity.name}}" title="Performance Management" target="pm">P</a>',
59       '  <a class="btn btn-default" title="Security Management" target="sm">S</a>',      
60       '</div>',
61       '<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>',
62       '<div class="btn-group">',
63       '  <a class="btn btn-primary" ng-href="#/pnfInventory/{{row.entity.name}}" title="Inventory Management" target="im">I</a>',
64       '</div>',
65       '<span>&nbsp;</span>'].join('');
66
67
68     $scope.requiredNesGridOptions = JSON.parse(JSON.stringify($mwtnConnect.gridOptions));
69     $scope.requiredNesGridOptions.rowHeight  = 44;
70     $scope.requiredNesGridOptions.columnDefs = [
71        { field: 'connectionStatus', type: 'string', displayName: 'Connection status',  headerCellClass: $scope.highlightFilteredHeader, width : 160, cellTemplate: requiredNesConnectionStatusCellTemplate },
72       //  { field: 'id', type: 'number', displayName: 'Id',  headerCellClass: $scope.highlightFilteredHeader, width : 50, cellClass: 'number', pinnedLeft : true , sort: {
73       //    direction: uiGridConstants.ASC,
74       //    ignoreSort: false,
75       //    priority: 0
76       //   }},
77        { field: 'name', type: 'string', displayName: 'Name',  headerCellClass: $scope.highlightFilteredHeader, width : 230, cellTemplate: nameCellTemplate, pinnedLeft : true , sort: {
78          direction: uiGridConstants.ASC,
79          ignoreSort: false,
80          priority: 0
81         }},
82        { field: 'ipaddress',  type: 'number', displayName: 'IP address',  headerCellClass: $scope.highlightFilteredHeader, width : 140, cellClass: 'number' },
83        { field: 'port',  type: 'number', displayName: 'Port',  headerCellClass: $scope.highlightFilteredHeader, width : 80, cellClass: 'number' },
84        { field: 'client',  type: 'number', displayName: 'Client',  headerCellClass: $scope.highlightFilteredHeader, width : 140 },
85        { field: 'username', type: 'string', displayName: 'User name',  headerCellClass: $scope.highlightFilteredHeader, width : 100, visible: false },
86        { field: 'password', type: 'string', displayName: 'Password',  headerCellClass: $scope.highlightFilteredHeader, width : 100, visible: false },
87        { field: 'radioSignalIds', type: 'string', displayName: 'Radio signal ids',  headerCellClass: $scope.highlightFilteredHeader, width : 150, visible: false},
88        {
89          name : 'actions',
90          enableSorting : false,
91          enableFiltering: false,
92          cellTemplate: requiredNesActionCellTemplate,
93          width : 400,
94          pinnedRight : true
95        }
96      ];
97     
98     var unknownNesActionCellTemplate = [
99      '<a class="vCenter" ng-class="{attention: grid.appScope.hover, hidden: onfAirInterfaceRevision}" >',
100      '<button class="btn btn-default" ng-show="row.entity[\'node-id\'] !== \'controller-config\'" ng-click="grid.appScope.unmount(row.entity)">Unmount</button>',
101      '<button class="btn btn-primary" ng-show="row.entity[\'node-id\'] !== \'controller-config\' && row.entity.onfCoreModelRevision" ng-click="grid.appScope.addToRequiredNetworkElements(row.entity)">',
102      '<i class="pull-left fa fa-spinner fa-pulse" ng-show="row.entity.spinner"></i>',
103      '<span class="white">{{ "MWTN_MAKE_KNOWN" | translate }}<span>',
104      '</button>',
105      '<button class="btn btn-default" ng-click="grid.appScope.showDetails(row.entity, false)"><i class="fa fa-info-circle" aria-hidden="true"></i></button>',
106      '</a>' ].join('<span>&nbsp;</span>');
107     $scope.unknownNesGridOptions = JSON.parse(JSON.stringify($mwtnConnect.gridOptions));
108     $scope.unknownNesGridOptions.rowHeight  = 44;
109     $scope.unknownNesGridOptions.columnDefs = [
110       { field: 'node-id', type: 'string', displayName: 'Name',  headerCellClass: $scope.highlightFilteredHeader, width : 200, sort: {
111         direction: uiGridConstants.ASC,
112         priority: 0
113        }},
114       { field: 'netconf-node-topology:host',  type: 'number', displayName: 'IP address',  headerCellClass: $scope.highlightFilteredHeader, width : 150, cellClass: 'number' },
115       { field: 'netconf-node-topology:port',  type: 'number', displayName: 'NetConf port',  headerCellClass: $scope.highlightFilteredHeader, width : 120, cellClass: 'number' },
116       { field: 'onfCoreModelRevision', type: 'string', displayName: 'CoreModel revision',  headerCellClass: $scope.highlightFilteredHeader, width : 160,  cellClass: 'number' },
117       { field: 'onfAirInterfaceRevision', type: 'string', displayName: 'AirInterface revision',  headerCellClass: $scope.highlightFilteredHeader, width : 160,  cellClass: 'number' },
118       { field: 'netconf-node-topology:connection-status', type: 'string', displayName: 'Connection status',  headerCellClass: $scope.highlightFilteredHeader, width : 160 },
119       {
120         name : 'actions',
121         enableSorting : false,
122         enableFiltering: false,
123         cellTemplate: unknownNesActionCellTemplate,
124         width : 330,
125         pinnedRight : true
126       }
127     ];
128     
129     /**
130      * Request all defined (required) network elements from database and
131      * update the corresponding table in the ui.
132      */
133     var getRequiredNetworkElements = function() {
134       $mwtnConnect.getRequiredNetworkElements().then(function(networkElements) {
135         $scope.requiredNesGridOptions.data = networkElements;
136         getActualNetworkElements();
137       }, function(error){
138         $scope.requiredNesGridOptions.data = [];
139       });
140     };
141     getRequiredNetworkElements();
142     
143     /**
144      * A function, which returns a boolean, which indicates, whether a network
145      * element is required or not.
146      * @param {string} neId - A mount point identifier.
147      * @return {boolean} True, if network element is required in the network, otherwise false.
148      */
149     var isRequired = function(neId) {
150       if ($scope.requiredNesGridOptions.data) {
151         var result = false;
152         $scope.requiredNesGridOptions.data.map(function(rne) {
153           if (rne.name === neId) result = true;
154         });
155         return result;
156       } else {
157         return false;
158       }
159     };
160
161     var setConnectionStatus = function(neId, connectionStatus) {
162       if ($scope.requiredNesGridOptions.data) {
163         $scope.requiredNesGridOptions.data.map(function(rne) {
164           if (rne.name === neId) rne.connectionStatus = connectionStatus;
165         });
166       }
167     };
168
169     var setValues = function(ane) {
170       if ($scope.requiredNesGridOptions.data) {
171
172         var getExtension = function(extensions, name) {
173           if (extensions === undefined) {
174             return undefined;
175           }
176           var result = extensions.filter(function(ex){
177             return ex['value-name'] === name;
178           }).map(function(ex){
179             return ex.value;
180           });
181           if (result.length > 0) {
182             result = result[0];
183           } else {
184             result = undefined;
185           }
186           return result;
187         }; 
188
189         $scope.requiredNesGridOptions.data.map(function(rne) {
190           // TODO use filter! and avoid push?
191           if (rne.name === ane['node-id']) {
192             rne.connectionStatus = ane['netconf-node-topology:connection-status'];
193             rne.ipaddress = ane['netconf-node-topology:host'];
194             rne.port = ane['netconf-node-topology:port'];
195             rne.client = ane.client;
196             
197             rne.onfCapabilities = ane.onfCapabilities;
198
199             var extensions = ['webUri', 'cliAddress', 'appCommand'];
200             extensions.map(function(extension){
201               rne[extension] = undefined;
202             });
203             if (ane.onfCoreModelRevision) {
204               $mwtnConnect.getActualNetworkElement(ane['node-id'], ane.onfCoreModelRevision).then(function(success){
205                 success = $mwtnConnect.yangifyObject(success);
206                 extensions.map(function(extension){
207                   rne[extension] = getExtension(success['network-element'].extension, extension);
208                 });
209               }, function(error){
210                 // ignore
211               });
212             }
213           }
214         });
215       }
216     };
217
218     var getActualNetworkElements = function() {
219       // aneHash = [];
220       if ($scope.requiredNesGridOptions.data) {
221         $scope.requiredNesGridOptions.data.map(function(ne) {
222           // ne.connectionStatus = 'disconnected';
223         });
224       }
225       $mwtnConnect.getMountPoints().then(function(mountpoints) {
226         $scope.unknownNesGridOptions.data = [];
227         mountpoints.map(function(mountpoint) {
228             if (isRequired(mountpoint['node-id'])) {
229               // setConnectionStatus(ne['node-id'], ne['netconf-node-topology:connection-status']);
230               setValues(mountpoint);
231             } else {
232               // console.log('mountpoint', JSON.stringify(mountpoint));
233               $scope.unknownNesGridOptions.data.push(mountpoint);
234             }
235         });
236       }, function(error){
237         $mwtnLog.info({component: COMPONENT, message: JSON.stringify(error)});
238       });
239     }; 
240     
241     $scope.netconfServer = {};
242
243     /**
244      * A function which inquires data from a netconf server and stores it in a database.
245      * @param {{'node-id': string, ipAddress: string, port: number, username: string, password: string}} netconfServer - A netConf server object with all connectivity parameters.
246      */
247     $scope.addToRequiredNetworkElements = function(netconfServer) {
248
249       $scope.netconfServer = netconfServer;
250       var neId = netconfServer['node-id'];
251
252       var index = $scope.unknownNesGridOptions.data.map(function(item) { 
253         return item['node-id']; 
254       }).indexOf(netconfServer['node-id']);
255
256       if (index !== -1 ) {
257         $mwtnConnect.getSingleDocument('mwtn', 'required-networkelement', neId).then(function(doc){
258           // Network element alrady exists in database
259           doc.required = true;
260           $scope.unknownNesGridOptions.data[index].spinner = true;
261           // add to aai
262           // $onapAai.createPnf(neId, doc).then(function(success){
263           //   // do nothing
264           // }, function(error) {
265           //   // do nothing
266           // });
267           // add to es
268           $mwtnConnect.createSingleDocument('mwtn', 'required-networkelement', neId, doc).then(function(success){
269             $timeout(function() {
270               $scope.status.requiredNes =  true;
271               $scope.unknownNesGridOptions.data[index].spinner = false;
272             }, 1000); 
273           }, function(error) {
274             $timeout(function() {
275               $scope.status.requiredNes =  true;
276               $scope.unknownNesGridOptions.data[index].spinner = false;
277             }, 1000); 
278             console.log(JSON.stringify(error));
279           });
280         }, function(error) {
281           // Network element does not exist in database, data inqured from real network element
282           var modalInstance = $uibModal.open({
283             animation: true,
284             ariaLabelledBy: 'modal-title',
285             ariaDescribedBy: 'modal-body',
286             templateUrl: 'src/app/mwtnConnect/templates/addToRequired.tpl.html',
287             controller: 'AddToRequiredMessageCtrl',
288             size: 'lg',
289             resolve: {
290               netconfServer: function () {
291                 return $scope.netconfServer;
292               }
293             }
294           });
295
296           modalInstance.result.then(function (netconfServer) {
297             $scope.unknownNesGridOptions.data[index].spinner = true;
298             $mwtnConnect.addRequiredNetworkElement(netconfServer).then(function(success){
299               $mwtnLog.info({component: COMPONENT, message: 'Adding to database: ' + netconfServer['node-id']});
300
301               // $onapAai.createPnf(netconfServer['node-id'], success.config.data).then(function(success){
302               //   // do nothing
303               // }, function(error) {
304               //   // do nothing
305               // });
306
307               $timeout(function() {
308                 $scope.status.requiredNes =  true;
309                 $scope.unknownNesGridOptions.data[index].spinner = false;
310               }, 1000); 
311             }, function(error){
312               $scope.unknownNesGridOptions.data[index].spinner = false;
313               $mwtnLog.error({component: COMPONENT, message: JSON.stringify(error)});
314             });
315           }, function () {
316             $mwtnLog.info({component: COMPONENT, message: 'Creation of new planned NetworkElement dismissed!'});
317           });
318         });
319       }
320     };
321     
322     $scope.newMountingPoint = {
323         name: 'new-netconf-server',
324         ipaddress: '127.0.0.1',
325         port: 830,
326         username: 'admin',
327         password: 'admin'
328     };
329     
330     $scope.mount = function() {
331       $scope.mountError = undefined;
332       $scope.mountSuccess = undefined;
333       $scope.processing = true;
334       $mwtnConnect.mount($scope.newMountingPoint).then(function(success){
335         $scope.processing = false;
336         $scope.mountSuccess = ['NETCONF server', $scope.newMountingPoint.name, 'successfully mounted.'].join(' ');
337       }, function(error){
338         $scope.processing = false;
339         $scope.mountError = ['NETCONF server', $scope.newMountingPoint.name, 'could not be mounted.'].join(' ');
340       });
341     };
342     
343     $scope.connect = function(ne) {
344       ne.connectionStatus = 'connecting...';
345       $mwtnConnect.mount(ne).then(function(success){
346         // ne.connectionStatus = 'connected';
347       }, function(error){
348         // ne.connectionStatus = 'connected';
349       });
350     };
351     
352     $scope.disconnect = function(ne) {
353       ne.connectionStatus = 'connecting...';
354       $mwtnConnect.unmount(ne.name).then(function(success){
355         ne.connectionStatus = 'disconnected';
356       }, function(error){
357         ne.connectionStatus = 'unknown';
358       });
359     };
360         
361     $scope.showDetails = function(ne, required) {
362       $scope.currentNetworkElement = ne;
363       $scope.currentNetworkElement.required = required;
364       if (required !== false) {
365         $scope.currentNetworkElement.required = true;
366       }
367       var modalInstance = $uibModal.open({
368         animation: true,
369         ariaLabelledBy: 'modal-title',
370         ariaDescribedBy: 'modal-body',
371         templateUrl: 'src/app/mwtnConnect/templates/mountPointDetails.tpl.html',
372         controller: 'MountPointDetailsCtrl',
373         size: 'lg',
374         resolve: {
375           currentNetworkElement: function () {
376             return $scope.currentNetworkElement;
377           }
378         }
379       });
380
381       modalInstance.result.then(function(success) {
382         if (success.hide || success.delete) {
383           var nodeId = success.hide || success.delete;
384           $scope.requiredNesGridOptions.data.map(function(item, index, array){
385             if (item.name === nodeId) {
386               array.splice(index, 1);
387             }
388           });
389         }
390         $mwtnLog.info({component: COMPONENT, message: 'Mointpoint details closed'});
391       }, function (error) {
392         $mwtnLog.info({component: COMPONENT, message: 'Mointpoint details dismissed!'});
393       });
394     };
395                   
396     var removeFromNodeList = function(nodeId) {
397       var index = $scope.unknownNesGridOptions.data.length;
398       while (index--) {
399         if ($scope.unknownNesGridOptions.data[index]['node-id'] === nodeId) {
400           $scope.unknownNesGridOptions.data.splice(index, 1);
401         } 
402       }
403     };
404     
405     $scope.unmount = function(netConfServer) {
406       netConfServer['netconf-node-topology:connection-status'] = 'disconnecting...';
407       $mwtnConnect.unmount(netConfServer['node-id']).then(function(response) {
408         $mwtnLog.info({component: COMPONENT, message: 'Mounting point deleted: ' + netConfServer['node-id'] });
409         removeFromNodeList(netConfServer['node-id']);
410       }, function(error){
411         $mwtnLog.info({component: COMPONENT, message: 'Unmounting of '+netConfServer['node-id']+' failed!'});
412         removeFromNodeList(netConfServer['node-id']);
413       });
414     };
415     
416     $scope.edit = function(row) {
417       console.info(JSON.stringify(row));
418     };
419     $scope.delete = function(row) {
420       console.info(JSON.stringify(row));
421     };
422     
423     // Connection status log
424     $scope.gridOptionsConnectionStatusLog = {
425         paginationPageSizes: [25, 100, 1000, 10000],
426         paginationPageSize: 25,
427         useExternalPagination: true,
428         useExternalSorting: true,
429         enablePaginationControls: false,
430         enableFiltering: true,
431         useExternalFiltering: true,
432         maxCount: 0,
433         columnDefs : [
434            // { field: 'id', type: 'number', displayName: 'No.',  headerCellClass: $scope.highlightFilteredHeader, width : 50, cellClass: 'number', pinnedLeft : true },
435            { field: 'timestamp',  type: 'string', displayName: 'Timestamp',  headerCellClass: $scope.highlightFilteredHeader, width : 200, sort: {
436            direction: uiGridConstants.DESC,
437            priority: 1
438            } },
439            { field: 'nodeName',  type: 'string', displayName: 'Node name',  headerCellClass: $scope.highlightFilteredHeader, width: 200 },
440            { field: 'connectionStatus',  type: 'string', displayName: 'Connection status',  headerCellClass: $scope.highlightFilteredHeader, width: 500, cellTemplate: requiredNesConnectionStatusCellTemplate }
441         ],
442         data: 'data',
443         onRegisterApi: function(gridApi) {
444           $scope.gridApi = gridApi;
445           $scope.gridApi.core.on.filterChanged( $scope, $scope.filter);
446           $scope.gridApi.core.on.sortChanged( $scope, $scope.sortChanged );
447           $scope.sortChanged($scope.gridApi.grid, [ $scope.gridOptionsConnectionStatusLog.columnDefs[1] ] );
448
449           $scope.gridApi.pagination.on.paginationChanged($scope, function (newPage, pageSize) {
450             $scope.paginationOptions.pageNumber = newPage;
451             $scope.paginationOptions.pageSize = pageSize;
452             getPage();
453           });
454         }
455     };
456
457     // data visible in connection status log
458     $scope.data = [];
459
460     //stateobject
461     $scope.state = {
462       filter: false,
463       sort: false,
464       lastfilter: null,
465       lastSort: null
466     };
467
468    //filter 
469    $scope.filter = function() {
470      var grid = this.grid;
471      var columns=[];
472
473      //get all columns where data was typed in
474      angular.forEach(grid.columns, function(value, key) {
475        if(value.filters[0].term) {
476
477          var col = findColumn(value.displayName);
478          var val = value.filters[0].term;
479          
480          switch (col) {
481          case 'event.timeStamp':
482            //convert timestamp to db format
483            val = $mwtnConnect.TimeStampToONFFormat(value.filters[0].term);
484            break;
485          case 'event.type':
486            if ('connected'.startsWith(value.filters[0].term.toLowerCase())) {
487              val = 'ObjectCreationNotificationXml'.toLowerCase();
488            }
489            if ('disconnected'.startsWith(value.filters[0].term.toLowerCase())) {
490              val = 'ObjectDeletionNotificationXml'.toLowerCase();
491            }
492            break;
493          }       
494          columns.push({ column: col, value: val }); //create column object
495        }
496     });
497
498     if (columns.length ===0 ) { //all filter data cleared away
499       $scope.state.filter = false;
500       $scope.state.lastfilter = null;
501
502       //get unfiltered data
503       $mwtnConnect.getData(($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize,
504                            $scope.paginationOptions.pageSize,
505                            $scope.state.lastSort,
506                            $scope.state.lastfilter).then(function(response) {
507         if (response.data.hits.hits) { 
508           processResponseRecreateList(response);
509         }
510       });
511     } else {
512       //base filter string
513       var filter = {"query": {"bool":{"must":[]}}};
514       //create filter objects
515       var prefixs = columns.map(function(obj){
516         var prefixObj={};
517         prefixObj[obj.column] = obj.value;//add  like: {column: "fault.counter", value: "1"} => {"fault.counter":1}
518         return {prefix:prefixObj}; // => {"prefix":{...}}
519       });
520       prefixs.push({"match":{"event.nodeName":"SDN-Controller"}});
521       
522       //add objects to must property
523       filter.query.bool.must = prefixs;
524       //save filter
525       $scope.state.lastfilter=filter;
526       $scope.state.filter=true;
527      
528       //send data to sdnevents/eventlog/_search
529       $mwtnConnect.getData(($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize,
530                             $scope.paginationOptions.pageSize,
531                             $scope.state.lastSort,
532                             $scope.state.lastfilter).then(function(response) {
533         if (response.data.hits.total>0) { //only, when hits exist
534           processResponseRecreateList(response);
535           $scope.gridOptionsConnectionStatusLog.totalItems = response.data.hits.total;
536         } else {
537           //clear data from list
538           $scope.data=[];
539           $scope.gridOptionsConnectionStatusLog.totalItems = 0;
540           $scope.gridOptionsConnectionStatusLog.maxCount = 0;
541           $scope.state.filter=false;
542           $scope.state.lastfilter=null;
543         }
544       });
545     }
546    };
547   
548     //sort
549     $scope.sortChanged=function(grid, sortColumns){    // sortColumns is an array containing just the column sorted in the grid
550       if (sortColumns.length > 0) {
551         if (sortColumns[0].sort) {
552           var name = sortColumns[0].displayName; // the name of the column sorted
553           var direction = sortColumns[0].sort.direction; // the direction of the column sorted: "desc" or "asc"
554           sort(direction, findColumn(name) );
555         }
556       } else {
557          $scope.state.sort = false;
558          $scope.state.lastSort=null;
559          //get unsorted data
560          $mwtnConnect.getData( ($scope.paginationOptions.pageNumber-1) * $scope.paginationOptions.pageSize,
561                                $scope.paginationOptions.pageSize,
562                                $scope.state.lastSort,
563                                $scope.state.lastfilter).then(function(response) {
564            processResponseRecreateList(response);
565          }, function(error) {
566            processResponseRecreateList([]);
567          });
568       }
569     };
570
571     var processResponseRecreateList = function(response) {
572       if (!response.data || !response.data.hits) {
573         $scope.data = [];
574         $scope.gridOptionsConnectionStatusLog.totalItems = 0; // needed by ui-grid to calculate page number, always update!
575         $scope.gridOptionsConnectionStatusLog.maxCount = 0;
576         return;
577       }
578       
579       if($scope.gridOptionsConnectionStatusLog.maxCount < response.data.hits.total){
580         $scope.gridOptionsConnectionStatusLog.maxCount = response.data.hits.total; //only if total is higher (can be lower due to eg filtering)
581       }
582       var list = response.data.hits.hits.map(function(entry) {
583         
584         var status = 'unknown';
585         if (entry._source.event.type === 'ObjectCreationNotificationXml' ) {
586           status = 'connected';
587         } else if (entry._source.event.type === 'ObjectDeletionNotificationXml' ) {
588           status = 'disconnected';
589         }
590         return {
591           id: entry._id,
592           timestamp: $mwtnConnect.formatTimeStamp(entry._source.event.timeStamp),
593           nodeName: entry._source.event.objectId,
594           connectionStatus: status
595         };
596       });
597       $scope.data=list;
598       $scope.gridOptionsConnectionStatusLog.totalItems = response.data.hits.total; // needed by ui-grid to calculate page number, always update!
599     };
600
601     $scope.paginationOptions = {
602       pageNumber: 1,
603       pageSize: 25,
604       sort: null
605     };
606
607     var getPage = function() {
608        //from, how many, sort, filter
609        $mwtnConnect.getData( ($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize, 
610                  $scope.paginationOptions.pageSize,
611                  $scope.state.lastSort,
612                  $scope.state.lastfilter).then(function (success) {
613          $scope.gridOptionsConnectionStatusLog.totalItems = success.data.hits.total;
614          processResponseRecreateList(success);
615        }, function(error) {
616          $scope.gridOptionsConnectionStatusLog.totalItems = success.data.hits.total;
617          processResponseRecreateList([]);
618          $mwtnLog.info({component: COMPONENT, message: JSON.stringify(error)});
619        });
620      };
621      
622      var processResponseAddToList = function(response) {
623        if (response.data.hits.hits) {
624          if($scope.gridOptionsConnectionStatusLog.maxCount < response.data.hits.total) {
625            $scope.gridOptionsConnectionStatusLog.maxCount = response.data.hits.total; //only if total is higher (can be lower due to eg filtering)
626          }
627          response.data.hits.hits.map(function(entry) {
628            var status = 'disconnected';
629            if (entry._source.event.type === 'ObjectCreationNotificationXml' ) {
630              status = 'connected';
631            }
632            var log = {
633              id: entry._id,
634              timestamp: $mwtnConnect.formatTimeStamp(entry._source.event.timeStamp),
635              nodeName: entry._source.event.objectId,
636              connectionStatus: status
637            };
638            $scope.data.push(log);
639          });
640        }
641        $scope.gridOptionsConnectionStatusLog.totalItems = response.data.hits.total; // needed by ui-grid to calculate page number, always update!
642     };
643
644     var sort = function (direction, columnName) {
645
646       var sortObj={};
647       var sort = [ sortObj ];
648       switch (direction) {
649         case uiGridConstants.ASC:
650            //create sort object
651            sortObj[columnName]={order : 'asc'};
652            sort = [ sortObj ];
653            //save last sort
654            $scope.state.lastSort=sort;
655
656            $mwtnConnect.getData(($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize,
657                                  $scope.paginationOptions.pageSize,
658                                  $scope.state.lastSort,
659                                  $scope.state.lastfilter).then(function(response) {
660               if (response.data.hits.hits) {
661                   processResponseRecreateList(response);
662                   $scope.state.sort=true;
663               }
664            });
665            break;
666
667         case uiGridConstants.DESC:
668           sortObj[columnName]={order : 'desc'};
669           sort = [ sortObj];
670           $scope.state.lastSort=sort;
671
672           $mwtnConnect.getData(($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize,$scope.paginationOptions.pageSize,$scope.state.lastSort,$scope.state.lastfilter).then(function(response) {
673             if (response.data.hits.hits) {
674               processResponseRecreateList(response);
675               $scope.state.sort=true;
676             }
677           });
678           break;
679
680         case undefined:
681             
682          $scope.state.sort=false;
683          $scope.state.lastSort=null;
684
685          $mwtnConnect.getData(($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize,
686                                $scope.paginationOptions.pageSize,
687                                $scope.state.lastSort,
688                                $scope.state.lastfilter).then(function(response) {
689               processResponseRecreateList(response);
690          });
691          break;
692       }
693     };
694
695     var findColumn= function(name) {
696       if(name === 'Timestamp'){ return 'event.timeStamp'; }
697       else if(name === 'Node name'){ return 'event.objectId'; }
698       else if(name === 'Connection status'){ return 'event.type'; }
699     };
700
701     $scope.paginationStatusMessage = function() {
702       
703       var startnum=($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize+1;
704       var pagenednum=($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize+1+$scope.data.length;
705       if(pagenednum>$scope.gridOptionsConnectionStatusLog.totalItems) pagenednum=pagenednum-1; //reduce by initial added 1
706       
707       if($scope.state.filter){
708         var filterTpl = 'Showing {0} to {1} of {2} items (filtered from {3} total items)';
709         return filterTpl.format(startnum, pagenednum, $scope.gridOptionsConnectionStatusLog.totalItems, $scope.gridOptionsConnectionStatusLog.maxCount);
710
711       } else {
712         var defaultTpl = 'Showing {0} to {1} of {2} total items';
713         return defaultTpl.format( startnum, pagenednum, $scope.gridOptionsConnectionStatusLog.maxCount);
714       }
715     };
716
717     $scope.refreshLog = function() {
718        
719       
720        var from =  ($scope.paginationOptions.pageNumber-1)*$scope.paginationOptions.pageSize;
721        var size = $scope.paginationOptions.pageSize;
722        $scope.processing = true;
723        $scope.spinner.connectionStatusLog = true;
724        $mwtnConnect.getData(from, size, $scope.state.lastSort, $scope.state.lastfilter).then(function(logEntries){
725          $scope.processing = false;
726          $scope.spinner.connectionStatusLog = false;
727          processResponseRecreateList(logEntries);
728        }, function(error){
729          $scope.processing = false;
730          $scope.spinner.connectionStatusLog = false;
731          console.error(JSON.stringify(error));
732        });      
733      };
734     
735     // browser events
736     $scope.status = {requiredNes : true};
737     $scope.separator = $mwtnConnect.separator; //'&nbsp;'
738     $scope.$watch('status', function(status, oldValue) {
739       Object.keys(status).map(function(key){
740         if (status[key] && status[key] !== oldValue[key]) {
741           switch (key) {
742           case 'requiredNes':
743             getRequiredNetworkElements();
744             break;
745           case 'unkownNes':
746             getActualNetworkElements();
747             break;
748           case 'mount':
749             $scope.mountSuccess = undefined;
750             $scope.mountError = undefined;
751             break;
752           case 'connectionStatusLog':
753             $window.dispatchEvent(new Event("resize"));
754             $scope.refreshLog();
755             break;
756           }
757         }
758       });
759     }, true);
760     
761     // odl events
762     // actualNetworkElements - NE added/deleted
763     var listenToActualNetworkElementsNotifications = function(socketLocation) {
764       try {
765         var notificatinSocket = new WebSocket(socketLocation);
766
767         notificatinSocket.onmessage = function(event) {
768           console.log('Event received.');
769           setTimeout(function() {
770             getActualNetworkElements();
771           }, 1000);
772         };
773         notificatinSocket.onerror = function(error) {
774           console.error("Socket error: " + error);
775         };
776         notificatinSocket.onopen = function(event) {
777           console.info("Socket connection opened.");
778         };
779         notificatinSocket.onclose = function(event) {
780           console.info("Socket connection closed.");
781         };
782       } catch (e) {
783         console.error("Error when creating WebSocket" + e);
784       }
785     };
786     
787     // var path = '/network-topology:network-topology/network-topology:topology[network-topology:topology-id = "topology-netconf"]'; 
788     // var path = "/opendaylight-inventory:nodes";  // [sko] works!
789     var path = '/network-topology:network-topology'; // for whatever reason it does not work, but work in Lithium.
790     $mwtnConnect.registerForOdlEvents(path, function(socketLocation) {
791       listenToActualNetworkElementsNotifications(socketLocation);
792     });
793     
794     var listenToEventManagerNotifications = function() {
795       $mwtnConnect.getMwtnWebSocketUrl().then(function(success){
796         try {
797           var notificationSocket = new WebSocket(success);
798
799           notificationSocket.onmessage = function(event) {
800             // we process our received event here
801             if (typeof event.data === 'string') {
802               // console.log("Client Received:\n" + event.data);
803               // console.log("---------------------------");
804               $mwtnConnect.formatData(event).then(function(formated) {
805                 switch (formated.notifType) {
806                 case 'ProblemNotification':
807                 case 'AttributeValueChangedNotification':
808                   // ignore
809                   break;
810                 case 'ObjectCreationNotification':
811                 case 'ObjectDeletionNotification':
812                   if (formated.nodeName === 'SDN-Controller') {
813                     $timeout(function() {getActualNetworkElements();}, 500); 
814                   }
815                   break;
816                 default:
817                   console.error('Missing implementation for', formated.notifType);
818                 }
819               }, function(error) {
820                 // do nothing
821               });
822             }
823           };
824
825           notificationSocket.onerror = function(error) {
826             console.log("Socket error: " + error);
827           };
828
829           notificationSocket.onopen = function(event) {
830             console.log("Socket connection opened.");
831
832             function subscribe() {
833               if (notificationSocket.readyState === notificationSocket.OPEN) {
834                 var data = {
835                   'data' : 'scopes',
836                   'scopes' : [ 'ObjectCreationNotification', 'ObjectDeletionNotification' ]
837                 };
838                 notificationSocket.send(JSON.stringify(data));
839               }
840             }
841             subscribe();
842           };
843
844           notificationSocket.onclose = function(event) {
845             console.log("Socket connection closed.");
846           };
847         } catch (e) {
848           console.error("Error when creating WebSocket.\n" + e);
849         }
850       }, function(error){
851         console.error("Error when creating WebSocket.\n" + error);
852       });
853     };
854     listenToEventManagerNotifications();
855     
856   }]);
857
858   mwtnConnectApp.register.controller('AddToRequiredMessageCtrl', ['$scope', '$uibModalInstance', '$mwtnConnect', 'netconfServer', 
859                                                          function ($scope, $uibModalInstance, $mwtnConnect, netconfServer) {
860
861     $scope.netconfServer = netconfServer;
862     
863     $mwtnConnect.getMountPoint(netconfServer['node-id']).then(
864       /**
865        * @param {{'node-id':string, 'netconf-node-topology:tcp-only': boolean, 'netconf-node-topology:host', string, 'netconf-node-topology:keepalive-delay':number, 'netconf-node-topology:port':number, 'netconf-node-topology:username':string, 'netconf-node-topology:password': string}} mountpoint - The mointpoint form topology-netconf
866        */
867       function(mountpoint) {
868         $scope.netconfServer.username = mountpoint['netconf-node-topology:username'];
869         $scope.netconfServer.password = mountpoint['netconf-node-topology:password'];
870       }, 
871       function(error){
872         console.log(JSON.stringify(error));
873       }
874     );
875
876     $scope.sites = [];
877     var sort = [ {
878       id : {
879         order : 'asc'
880       }
881     }];
882     $mwtnConnect.getAllData('mwtn', 'site', 0, 20, sort).then(
883       function(sites){
884         // console.log(JSON.stringify(sites));
885         if (sites.data.hits.hits) {
886           sites.data.hits.hits.map(function(site){
887             $scope.sites.push({id:site._source.id, name:site._source.name});
888           });
889           // console.log($scope.sites);
890         }
891       }, 
892       function(error){
893         $scope.sites = [];
894       }
895     );
896   
897     $scope.ok = function () {
898       $uibModalInstance.close($scope.netconfServer);
899     };
900   
901     $scope.cancel = function () {
902       $uibModalInstance.dismiss('cancel');
903     };
904
905     $scope.$watch('netconfServer.site', function(newValue, oldValue) {
906       if (newValue !== oldValue) {
907         var sort = [ {
908           id : {
909             order : 'asc'
910           }
911         }];
912         var query = {
913           prefix: {
914             name: newValue
915           }
916         };
917
918         $mwtnConnect.getFilteredSortedData('mwtn', 'site', 0, 20, sort, query).then(
919           function(sites){
920             // console.log(JSON.stringify(sites));
921             if (sites.data.hits.hits) {
922               $scope.sites = sites.data.hits.hits.map(function(site){
923                 return {id:site._source.id, name:site._source.name};
924               });
925               // console.log($scope.sites);
926             }
927           }, 
928           function(error){
929             $scope.sites = [];
930           }
931         );
932       }
933     });
934   }]);
935
936   mwtnConnectApp.register.controller('MountPointDetailsCtrl', ['$scope', '$uibModalInstance', '$uibModal', '$mwtnConnect', '$mwtnLog', 'currentNetworkElement', 
937                                                                   function ($scope, $uibModalInstance, $uibModal, $mwtnConnect, $mwtnLog, currentNetworkElement) {
938
939     var COMPONENT = 'MountPointDetailsCtrl';
940     // $mwtnLog.info({component: COMPONENT, message: 'MountPointDetailsCtrl started!'});
941
942     $scope.data = {
943         ne: currentNetworkElement,
944         web: {
945           supported: false,
946           protocol: 'http',
947           ipAddress: '127.0.0.1',
948           port: 80,
949           url: '/',
950           getLink : function() {
951             if (currentNetworkElement.webUri) {
952               return currentNetworkElement.webUri;
953             }
954             return [this.protocol,'://', this.ipAddress, this.url ].join('');
955           }
956         },
957         terminal: {
958           supported: false,
959           copied: false,
960           protocol: 'ssh',
961           user: 'cli',
962           ipAddress: '127.0.0.1',
963           getCommand : function() {
964             if (currentNetworkElement.cliAddress) { 
965               return currentNetworkElement.cliAddress;
966             }
967             return [this.protocol, ' ', this.user, '@', this.ipAddress].join('');
968           },
969           copyToClipboard : function () {
970             $scope.data.application.copied = false;
971             this.copied =  true;
972             var message = 'Copied to clipboard! ' + this.getCommand();
973             $mwtnLog.info({component: COMPONENT, message: message});
974           }
975         },
976         application: {
977           supported: false,
978           copied: false,
979           commandTemplate: 'VendorApp --ne {0}',
980           ipAddress: '127.0.0.1',
981           getCommand : function() {
982             if (currentNetworkElement.appCommand) {
983               return currentNetworkElement.appCommand;
984             }
985             return this.commandTemplate.format(this.ipAddress);
986           },
987           copyToClipboard : function () {
988             $scope.data.terminal.copied = false;
989             $scope.data.application.copied =  true;
990             var message = 'Copied to clipboard! ' + this.getCommand();
991             $mwtnLog.info({component: COMPONENT, message: message});
992           }
993         }
994     };
995
996     var nodeId = $scope.data.ne.name || $scope.data.ne['node-id'];
997     $mwtnConnect.getMountPoint(nodeId).then(function(success){
998       $scope.data.mountpoint = success;
999     }, function(error){
1000       $scope.data.mountpoint = undefined;
1001     });
1002     
1003     if (currentNetworkElement.webUri && currentNetworkElement.webUri !== '') {
1004       $scope.data.web.supported = true;
1005       $scope.data.web.protocol = currentNetworkElement.webUri.split(':')[0];
1006       var matches = currentNetworkElement.webUri.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
1007       $scope.data.web.ipAddress = matches && matches[1];  // ipAddress will be null if no match is found
1008     }
1009     if (currentNetworkElement.cliAddress && currentNetworkElement.cliAddress !== '') {
1010       $scope.data.terminal.supported = true;
1011       var matches = currentNetworkElement.cliAddress.match(/([^@]+)@([^@]+)/i);
1012       $scope.data.terminal.user = matches && matches[1];
1013       $scope.data.terminal.ipAddress = matches && matches[2];
1014     }
1015     if (currentNetworkElement.appCommand && currentNetworkElement.appCommand !== '') {
1016       $scope.data.application.supported = true;
1017       // [sko] TODO once a mediator supports it
1018     }
1019
1020     $scope.yangCapabilitiesGridOptions = JSON.parse(JSON.stringify($mwtnConnect.gridOptions));
1021     $scope.yangCapabilitiesGridOptions.columnDefs = [
1022        { field: 'module', type: 'string', displayName: 'module',  headerCellClass: $scope.highlightFilteredHeader, width : 600 },
1023        { field: 'revision', type: 'string', displayName: 'Revision',  headerCellClass: $scope.highlightFilteredHeader, width : 150}
1024      ];
1025     $scope.yangCapabilitiesGridOptions.data = currentNetworkElement.onfCapabilities;
1026     
1027     // clipboard
1028     $scope.supported = false;
1029     $scope.error = function (err) {
1030       $mwtnLog.error({component: COMPONENT, message: err});
1031     };    
1032     
1033     $scope.ok = function () {
1034       $uibModalInstance.close({ne: currentNetworkElement});
1035     };
1036   
1037     $scope.hide = function (ne) {
1038       $scope.hideNe = ne;
1039       var modalInstance = $uibModal.open({
1040         animation: true,
1041         ariaLabelledBy: 'modal-title',
1042         ariaDescribedBy: 'modal-body',
1043         templateUrl: 'src/app/mwtnConnect/templates/confirmHide.tpl.html',
1044         controller: 'MountPointHideCtrl',
1045         size: 'lg',
1046         resolve: {
1047           hideNe: function () {
1048             return $scope.hideNe;
1049           }
1050         }
1051       });
1052
1053       modalInstance.result.then(
1054         function(neId) {
1055           $mwtnConnect.getSingleDocument('mwtn', 'required-networkelement', neId).then(
1056             function(doc){
1057               doc.required = false;
1058               $mwtnConnect.createSingleDocument('mwtn', 'required-networkelement', neId, doc).then(
1059                 function(success){
1060                   $uibModalInstance.close({hide: neId});
1061                 }, 
1062                 function(error) {
1063                   $uibModalInstance.close({hide: neId});
1064                 }
1065               );
1066             },  
1067             function(error) {
1068               $uibModalInstance.close({hide: neId});
1069             }
1070           );
1071           $mwtnLog.info({component: COMPONENT, message: 'Confirm hide closed'});
1072         }, 
1073         function (error) {
1074           $mwtnLog.info({component: COMPONENT, message: 'Confirm hide dismissed!'});
1075         }
1076       );
1077     };
1078   
1079     $scope.delete = function (ne) {
1080       $scope.deleteNe = ne;
1081       var modalInstance = $uibModal.open({
1082         animation: true,
1083         ariaLabelledBy: 'modal-title',
1084         ariaDescribedBy: 'modal-body',
1085         templateUrl: 'src/app/mwtnConnect/templates/confirmDelete.tpl.html',
1086         controller: 'MountPointDeleteCtrl',
1087         size: 'lg',
1088         resolve: {
1089           deleteNe: function () {
1090             return $scope.deleteNe;
1091           }
1092         }
1093       });
1094
1095       modalInstance.result.then(function(success) {
1096         // delete from AAi
1097         // $onapAai.deletePnf(success).then(function(deleted){
1098         //   $mwtnLog.info({component: COMPONENT, message: success + ' deleted from AAI.'});
1099         // }, function(error){
1100         //   $mwtnLog.info({component: COMPONENT, message: 'Deletion from AAI failed: ' + success + '\n' + error});
1101         // });
1102         // delete from ES
1103         $mwtnConnect.deleteSingleDocument('mwtn', 'required-networkelement', success).then(function(deleted){
1104           $mwtnLog.info({component: COMPONENT, message: success + ' deleted from database.'});
1105           $uibModalInstance.close({hide: success});
1106         }, function(error){
1107           $mwtnLog.info({component: COMPONENT, message: 'Deletion from database failed: ' + success + '\n' + error});
1108         });
1109       }, function (error) {
1110         $mwtnLog.info({component: COMPONENT, message: 'Confirm delete dismissed!'});
1111       });
1112     };
1113   
1114     $scope.cancel = function () {
1115       $uibModalInstance.dismiss('cancel');
1116     };
1117   }]);
1118
1119   mwtnConnectApp.register.controller('MountPointHideCtrl', ['$scope', '$uibModalInstance', '$mwtnConnect', '$mwtnLog', 'hideNe', 
1120                                                    function ($scope, $uibModalInstance, $mwtnConnect, $mwtnLog, hideNe) {
1121     $scope.hideNe = hideNe;
1122     $scope.ok = function () {
1123       $uibModalInstance.close($scope.hideNe.name);
1124     };
1125     $scope.cancel = function () {
1126       $uibModalInstance.dismiss('cancel');
1127     };
1128
1129   }]);
1130
1131   mwtnConnectApp.register.controller('MountPointDeleteCtrl', ['$scope', '$uibModalInstance', '$mwtnConnect', '$mwtnLog', 'deleteNe', 
1132                                                    function ($scope, $uibModalInstance, $mwtnConnect, $mwtnLog, deleteNe) {
1133     $scope.deleteNe = deleteNe;
1134     $scope.ok = function () {
1135       $uibModalInstance.close($scope.deleteNe.name);
1136     };
1137     $scope.cancel = function () {
1138       $uibModalInstance.dismiss('cancel');
1139     };
1140
1141   }]);
1142
1143 });