blob: 68b1ad1cca72db204c37fd1e4310fc3c876e26fd [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
ONOS GUI -- Topology Region Module.
Module that holds the current region in memory
*/
(function () {
'use strict';
// Injected Services
var Model;
// Internal
var instance;
// 'static' vars
var ROOT = '(root)';
angular.module('ovTopo2')
.factory('Topo2RegionService', [
'$log', 'Topo2Model', 'Topo2SubRegionService', 'Topo2DeviceService',
'Topo2HostService', 'Topo2LinkService', 'Topo2ZoomService', 'Topo2DetailsPanelService',
'Topo2BreadcrumbService', 'Topo2ViewController', 'Topo2SpriteLayerService', 'Topo2MapService',
'Topo2MapConfigService', 'Topo2PeerRegionService', 'Topo2NoDevicesConnectedService',
function ($log, _Model_, t2sr, t2ds, t2hs, t2ls, t2zs, t2dps, t2bcs, ViewController,
t2sls, t2ms, t2mcs, t2pr, t2ndcs) {
Model = _Model_;
var Region = ViewController.extend({
initialize: function () {
instance = this;
this.model = null;
this.bgRendered = false;
this.regionData = null;
this.peers = null;
var RegionModel = Model.extend({
findNodeById: this.findNodeById,
nodes: this.regionNodes.bind(this)
});
this.model = new RegionModel();
this.createEmptyModel();
},
createEmptyModel: function () {
this.model.set({ subregions: t2sr.createSubRegionCollection([], this) });
this.model.set({ devices: t2ds.createDeviceCollection([], this) });
this.model.set({ hosts: t2hs.createHostCollection([], this) });
this.model.set({ peerRegions: t2pr.createCollection([], this) });
this.model.set({ links: t2ls.createLinkCollection([], this) });
},
isLoadComplete: function() {
return this.bgRendered && this.regionData && this.peers;
},
loaded: function (key, value) {
this[key] = value;
if (this.isLoadComplete()) {
this.startRegion();
}
},
startRegion: function () {
this.model.set({
id: this.regionData.id,
layerOrder: this.regionData.layerOrder
});
this.sortMultiLinks();
this.assignPeerLocations();
this.model.set({ subregions: t2sr.createSubRegionCollection(this.regionData.subregions, this) });
this.model.set({ devices: t2ds.createDeviceCollection(this.regionData.devices, this) });
this.model.set({ hosts: t2hs.createHostCollection(this.regionData.hosts, this) });
this.model.set({ peerRegions: t2pr.createCollection(this.peers, this) });
this.model.set({ links: t2ls.createLinkCollection(this.regionData.links, this) });
// Hide Breadcrumbs if there are no subregions configured in the root region
if (this.isRootRegion() && !this.model.get('subregions').models.length) {
t2bcs.hide();
}
this.layout.createForceLayout();
this.displayNoDevs();
},
clear: function () {
this.regionData = null;
this.createEmptyModel();
},
removePort: function (key) {
var regex = new RegExp('^[^/]*');
return regex.exec(key)[0];
},
assignPeerLocations: function () {
var _this = this;
_.each(this.regionData.peerLocations, function (location, id) {
_.each(_this.peers, function (peer) {
if (peer.id === id) {
peer.location = location;
}
})
});
},
sortMultiLinks: function () {
var _this = this,
deviceConnections = {};
_.each(this.regionData.links, function (link) {
var epA = _this.removePort(link.epA),
epB = _this.removePort(link.epB),
key = epA + '~' + epB,
collection = deviceConnections[key] || [],
dup = _.find(collection, link);
// TODO: Investigate why region contains dup links?!?!
// FIXME: This shouldn't be needed - The backend is sending dups
// and this is preventing the client thinking its a multilink
if (!dup) {
collection.push(link);
}
deviceConnections[key] = collection;
});
_.forIn(deviceConnections, function (collection) {
if (collection.length > 1) {
_.each(collection, function (link, index) {
link.multiline = {
deviceLinks: collection.length,
index: index
}
});
}
})
},
isRootRegion: function () {
return this.model.get('id') === ROOT;
},
findNodeById: function (link, id) {
if (link.get('type') !== 'UiEdgeLink') {
id = this.removePort(id);
}
return this.model.get('devices').get(id) ||
this.model.get('hosts').get(id) ||
this.model.get('subregions').get(id);
},
regionNodes: function () {
if (this.model) {
return [].concat(
this.model.get('devices').models,
this.model.get('hosts').models,
this.model.get('subregions').models,
this.model.get('peerRegions').models
);
}
return [];
},
regionLinks: function () {
return this.model.get('links').models;
},
getLink: function (linkId) {
return this.model.get('links').get(linkId);
},
getDevice: function (deviceId) {
return this.model.get('devices').get(deviceId);
},
filterRegionNodes: function (predicate) {
var nodes = this.regionNodes();
return _.filter(nodes, predicate);
},
deselectAllNodes: function () {
var selected = this.filterRegionNodes(function (node) {
return node.get('selected', true);
});
if (selected.length) {
selected.forEach(function (node) {
node.deselect();
});
t2dps().el.hide();
return true;
}
return false;
},
deselectLink: function () {
var selected = _.filter(this.regionLinks(), function (link) {
return link.get('selected', true);
});
if (selected.length) {
selected.forEach(function (link) {
link.deselect();
});
t2dps().el.hide();
return true;
}
return false;
},
toggleHosts: function () {
var state = this.lookupPrefState('hosts');
this.updatePrefState('hosts', !state);
_.each(this.model.get('hosts').models, function (host) {
host.setVisibility();
});
_.each(this.model.get('links').models, function (link) {
link.setVisibility();
});
return !state;
},
toggleOfflineDevices: function () {
var state = this.lookupPrefState('offline_devices');
this.updatePrefState('offline_devices', !state);
_.each(this.regionNodes(), function (node) {
node.setOfflineVisibility();
});
return !state;
},
update: function (event) {
if (!this.isLoadComplete()){
this.layout.createForceLayout();
}
if (this[event.type]) {
this[event.type](event);
} else {
$log.error("Unhanded topology update", event);
}
this.layout.update()
this.displayNoDevs();
},
displayNoDevs: function () {
if (this.regionNodes().length > 0) {
t2ndcs.hide();
} else {
t2ndcs.show();
}
},
// Topology update event handlers
LINK_ADDED_OR_UPDATED: function (event) {
var regionLinks = this.model.get('links'),
device;
if (!regionLinks) {
this.model.set({ links: t2ls.createLinkCollection([], this) })
}
if (event.memo === 'added') {
var link = this.model.get('links').add(event.data);
link.createLink();
$log.debug('Added Link', link);
}
},
LINK_REMOVED: function (event) {
var link = this.getLink(event.subject);
link.remove();
this.model.get('links').remove(link);
},
DEVICE_ADDED_OR_UPDATED: function (event) {
var regionDevices = this.model.get('devices'),
device;
if (!regionDevices) {
this.model.set({ devices: t2ds.createDeviceCollection([], this) })
}
if (event.memo === 'added') {
device = this.model.get('devices').add(event.data);
$log.debug('Added device', device);
} else if (event.memo === 'updated') {
device = this.getDevice(event.subject);
device.set(event.data);
}
},
DEVICE_REMOVED: function (event) {
device.remove();
},
HOST_ADDED_OR_UPDATED: function (event) {
var regionHosts = this.model.get('hosts'),
host;
if (!regionHosts) {
this.model.set({ hosts: t2hs.createHostCollection([], this) })
}
if (event.memo === 'added') {
host = this.model.get('hosts').add(event.data);
$log.debug('Added host', host);
}
},
REGION_ADDED_OR_UPDATED: function (event) {
var regionSubRegions = this.model.get('subregions'),
region;
if (!regionSubRegions) {
this.model.set({ subregions: t2sr.createSubRegionCollection([], this) })
}
if (event.memo === 'added') {
region = this.model.get('subregions').add(event.data);
$log.debug('Added region', region);
}
}
});
function getInstance() {
return instance || new Region();
}
return getInstance();
}]);
})();