horizon/openstack_dashboard/static/js/horizon.networktopology.js
Kenji Ishii 295175826b Move js files into openstack_dashboard folder.
hozoin doc says that the horizon directory holds the generic
libraries and components that can be used in any Django project.

So, this patch move js files related with Openstack components into
openstack_dashboard folder.

Change-Id: Ib4ed05e1136ee3f70a201139e456d4c3b93519f3
Closes-bug: #1635519
2016-12-18 15:27:42 +09:00

1091 lines
34 KiB
JavaScript

/**
* 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.
*/
/* global Hogan */
/* Namespace for core functionality related to Network Topology. */
function Network(data) {
for (var key in data) {
if ({}.hasOwnProperty.call(data, key)) {
this[key] = data[key];
}
}
this.iconType = 'text';
this.icon = '\uf0c2'; // Cloud
this.collapsed = false;
this.type = 'network';
this.instances = 0;
}
function ExternalNetwork(data) {
for (var key in data) {
if ({}.hasOwnProperty.call(data, key)) {
this[key] = data[key];
}
}
this.collapsed = false;
this.iconType = 'text';
this.icon = '\uf0ac'; // Globe
this.instances = 0;
}
function Router(data) {
for (var key in data) {
if ({}.hasOwnProperty.call(data, key)) {
this[key] = data[key];
}
}
this.iconType = 'path';
this.svg = 'router';
this.networks = [];
this.ports = [];
this.type = 'router';
}
function Server(data) {
for (var key in data) {
if ({}.hasOwnProperty.call(data, key)) {
this[key] = data[key];
}
}
this.iconType = 'text';
this.icon = '\uf108'; // Server
this.networks = [];
this.type = 'instance';
this.ip_addresses = [];
}
function listContains(obj, list) {
// Function to help checking if an object is present on a list
for (var i = 0; i < list.length; i++) {
if (angular.equals(list[i], obj)) {
return true;
}
}
return false;
}
horizon.network_topology = {
fa_globe_glyph: '\uf0ac',
fa_globe_glyph_width: 15,
svg:'#topology_canvas',
nodes: [],
links: [],
data: [],
zoom: d3.behavior.zoom(),
data_loaded: false,
svg_container:'#topologyCanvasContainer',
balloonTmpl : null,
balloon_deviceTmpl : null,
balloon_portTmpl : null,
balloon_netTmpl : null,
balloon_instanceTmpl : null,
network_index: {},
balloonID:null,
network_height : 0,
init:function() {
var self = this;
angular.element(self.svg_container).spin(horizon.conf.spinner_options.modal);
if (angular.element('#networktopology').length === 0) {
return;
}
self.data = {};
self.data.networks = {};
self.data.routers = {};
self.data.servers = {};
self.data.ports = {};
// Setup balloon popups
self.balloonTmpl = Hogan.compile(angular.element('#balloon_container').html());
self.balloon_deviceTmpl = Hogan.compile(angular.element('#balloon_device').html());
self.balloon_portTmpl = Hogan.compile(angular.element('#balloon_port').html());
self.balloon_netTmpl = Hogan.compile(angular.element('#balloon_net').html());
self.balloon_instanceTmpl = Hogan.compile(angular.element('#balloon_instance').html());
angular.element(document)
.on('click', 'a.closeTopologyBalloon', function(e) {
e.preventDefault();
self.delete_balloon();
})
.on('click', '.topologyBalloon', function(e) {
e.stopPropagation();
})
.on('click', 'a.vnc_window', function(e) {
e.preventDefault();
var vncWindow = window.open(angular.element(this).attr('href'), vncWindow,
'width=760,height=560');
self.delete_balloon();
});
angular.element('#toggle_labels').click(function() {
if (angular.element('.nodeLabel').css('display') == 'none') {
angular.element('.nodeLabel').show();
horizon.cookies.put('show_labels', true);
} else {
angular.element('.nodeLabel').hide();
horizon.cookies.put('show_labels', false);
}
});
angular.element('#toggle_networks').click(function() {
for (var n in self.nodes) {
if ({}.hasOwnProperty.call(self.nodes, n)) {
if (self.nodes[n].data instanceof Network || self.nodes[n].data instanceof ExternalNetwork) {
self.collapse_network(self.nodes[n]);
}
if (horizon.cookies.get('show_labels')) {
angular.element('.nodeLabel').show();
}
}
}
var current = horizon.cookies.get('are_networks_collapsed');
horizon.cookies.put('are_networks_collapsed', !current);
});
angular.element('#topologyCanvasContainer').spin(horizon.conf.spinner_options.modal);
self.create_vis();
self.loading();
self.force_direction(0.05,70,-700);
if(horizon.networktopologyloader.model !== null) {
self.retrieve_network_info(true);
}
d3.select(window).on('resize', function() {
var width = angular.element('#topologyCanvasContainer').width();
var height = angular.element('#topologyCanvasContainer').height();
self.force.size([width, height]).resume();
});
angular.element('#networktopology').on('change', function() {
self.retrieve_network_info(true);
});
// register for message notifications
horizon.networktopologymessager.addMessageHandler(this.handleMessage, this);
},
handleMessage:function(message) {
var self = this;
var deleteData = horizon.networktopologymessager.delete_data;
if (message.type == 'success') {
self.remove_node_on_delete(deleteData);
}
},
// Get the json data about the current deployment
retrieve_network_info: function(force_start) {
var self = this;
self.data_loaded = true;
self.load_topology(horizon.networktopologyloader.model);
if (force_start) {
var i = 0;
self.force.start();
while (i <= 100) {
self.force.tick();
i++;
}
}
},
// Load config from cookie
load_config: function() {
var labels = horizon.cookies.get('show_labels');
var networks = horizon.cookies.get('are_networks_collapsed');
if (labels) {
angular.element('.nodeLabel').show();
angular.element('#toggle_labels').addClass('active');
}
if (networks) {
for (var n in this.nodes) {
if ({}.hasOwnProperty.call(this.nodes, n)) {
this.collapse_network(this.nodes[n], true);
}
}
angular.element('#toggle_networks').addClass('active');
}
},
getScreenCoords: function(x, y) {
var self = this;
if (self.translate) {
var xn = self.translate[0] + x * self.zoom.scale();
var yn = self.translate[1] + y * self.zoom.scale();
return { x: xn, y: yn };
} else {
return { x: x, y: y };
}
},
// Setup the main visualisation
create_vis: function() {
var self = this;
angular.element('#topologyCanvasContainer').html('');
// Main svg
self.outer_group = d3.select('#topologyCanvasContainer').append('svg')
.attr('width', '100%')
.attr('height', angular.element(document).height() - 270 + "px")
.attr('pointer-events', 'all')
.append('g')
.call(self.zoom
.scaleExtent([0.1,1.5])
.on('zoom', function() {
self.delete_balloon();
self.vis.attr('transform', 'translate(' + d3.event.translate + ')scale(' +
self.zoom.scale() + ')');
self.translate = d3.event.translate;
})
)
.on('dblclick.zoom', null);
// Background for capturing mouse events
self.outer_group.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('fill', 'white')
.on('click', function() {
self.delete_balloon();
});
// svg wrapper for nodes to sit on
self.vis = self.outer_group.append('g');
},
loading: function() {
var self = this;
var load_text = self.vis.append('text')
.style('fill', 'black')
.style('font-size', '40')
.attr('x', '50%')
.attr('y', '50%')
.text('');
var counter = 0;
var timer = setInterval(function() {
var i;
var str = '';
for (i = 0; i <= counter; i++) {
str += '.';
}
load_text.text(str);
if (counter >= 9) {
counter = 0;
} else {
counter++;
}
if (self.data_loaded) {
clearInterval(timer);
load_text.remove();
}
}, 100);
},
// Calculate the hulls that surround networks
convex_hulls: function(nodes) {
var net, _i, _len, _ref, _h, i;
var hulls = {};
var networkids = {};
var k = 0;
var offset = 40;
while (k < nodes.length) {
var n = nodes[k];
if (n.data !== undefined) {
if (n.data instanceof Server) {
_ref = n.data.networks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
net = _ref[_i];
if (net instanceof Network) {
_h = hulls[net.id] || (hulls[net.id] = []);
_h.push([n.x - offset, n.y - offset]);
_h.push([n.x - offset, n.y + offset]);
_h.push([n.x + offset, n.y - offset]);
_h.push([n.x + offset, n.y + offset]);
}
}
} else if (n.data instanceof Network) {
net = n.data;
networkids[net.id] = n;
_h = hulls[net.id] || (hulls[net.id] = []);
_h.push([n.x - offset, n.y - offset]);
_h.push([n.x - offset, n.y + offset]);
_h.push([n.x + offset, n.y - offset]);
_h.push([n.x + offset, n.y + offset]);
}
}
++k;
}
var hullset = [];
for (i in hulls) {
if ({}.hasOwnProperty.call(hulls, i)) {
hullset.push({group: i, network: networkids[i], path: d3.geom.hull(hulls[i])});
}
}
return hullset;
},
// Setup the force direction
force_direction: function(grav, dist, ch) {
var self = this;
angular.element('[data-toggle="tooltip"]').tooltip({container: 'body'});
self.curve = d3.svg.line()
.interpolate('cardinal-closed')
.tension(0.85);
self.fill = d3.scale.category10();
self.force = d3.layout.force()
.gravity(grav)
.linkDistance(function(d) {
if (d.source.data instanceof Server || d.target.data instanceof Server) {
if (d.source.data.networks) {
return (dist * d.source.data.networks.length) + (5 * d.target.data.instances) + 20;
} else if (d.target.data.networks) {
return (dist * d.target.data.networks.length) + (5 * d.source.data.instances) + 20;
}
} else if (d.source.data instanceof Router || d.target.data instanceof Router) {
if (d.source.data.networks) {
if (d.source.data.networks.length === 0) {
return dist + 20;
} else if (d.target.data.instances) {
return dist * d.source.data.networks.length + (10 * d.target.data.instances) + 20;
}
return dist * d.source.data.networks.length + 20;
} else if (d.target.data.networks) {
if (d.target.data.networks.length === 0) {
return dist + 20;
} else if (d.source.data.instances) {
return dist * d.target.data.networks.length + (10 * d.source.data.instances) + 20;
}
return dist * d.source.data.networks.length + 20;
}
} else {
return dist;
}
})
.charge(ch)
.size([angular.element('#topologyCanvasContainer').width(),
angular.element('#topologyCanvasContainer').height()])
.nodes(self.nodes)
.links(self.links)
.on('tick', function() {
self.vis.selectAll('g.node')
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
self.vis.selectAll('line.link')
.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y; });
self.vis.selectAll('path.hulls')
.data(self.convex_hulls(self.vis.selectAll('g.node').data()))
.attr('d', function(d) {
return self.curve(d.path);
})
.enter().insert('path', 'g')
.attr('class', 'hulls')
.style('fill', function(d) {
return self.fill(d.group);
})
.style('stroke', function(d) {
return self.fill(d.group);
})
.style('stroke-linejoin', 'round')
.style('stroke-width', 10)
.style('opacity', 0.2);
});
},
// Create a new node
new_node: function(data, x, y) {
var self = this;
data = {data: data};
if (x && y) {
data.x = x;
data.y = y;
}
self.nodes.push(data);
var node = self.vis.selectAll('g.node').data(self.nodes);
var nodeEnter = node.enter().append('g')
.attr('class', 'node')
.style('fill', 'white')
.call(self.force.drag);
nodeEnter.append('circle')
.attr('class', 'frame')
.attr('r', function(d) {
switch (Object.getPrototypeOf(d.data)) {
case ExternalNetwork.prototype:
return 35;
case Network.prototype:
return 30;
case Router.prototype:
return 25;
case Server.prototype:
return 20;
}
})
.style('fill', 'white')
.style('stroke', 'black')
.style('stroke-width', 3);
switch (data.data.iconType) {
case 'text':
nodeEnter.append('text')
.style('fill', 'black')
.style('font', '20px FontAwesome')
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.text(function(d) { return d.data.icon; })
.attr('transform', function(d) {
switch (Object.getPrototypeOf(d.data)) {
case ExternalNetwork.prototype:
return 'scale(2.5)';
case Network.prototype:
return 'scale(1.5)';
case Server.prototype:
return 'scale(1)';
}
});
break;
case 'path':
nodeEnter.append('path')
.attr('class', 'svgpath')
.style('fill', 'black')
.attr('d', function(d) { return self.svgs(d.data.svg); })
.attr('transform', function() {
return 'scale(1.2)translate(-16,-15)';
});
break;
}
nodeEnter.append('text')
.attr('class', 'nodeLabel')
.style('display',function() {
var labels = horizon.cookies.get('topology_labels');
if (labels) {
return 'inline';
} else {
return 'none';
}
})
.style('fill','black')
.text(function(d) {
return d.data.name;
})
.attr('transform', function(d) {
switch (Object.getPrototypeOf(d.data)) {
case ExternalNetwork.prototype:
return 'translate(40,3)';
case Network.prototype:
return 'translate(35,3)';
case Router.prototype:
return 'translate(30,3)';
case Server.prototype:
return 'translate(25,3)';
}
});
if (data.data instanceof Network || data.data instanceof ExternalNetwork) {
nodeEnter.append('svg:text')
.attr('class','vmCount')
.style('fill', 'black')
.style('font-size','20')
.text('')
.attr('transform', 'translate(26,38)');
}
nodeEnter.on('click', function(d) {
self.show_balloon(d.data, d, angular.element(this));
});
// Highlight the links for currently selected node
nodeEnter.on('mouseover', function(d) {
self.vis.selectAll('line.link').filter(function(z) {
if (z.source === d || z.target === d) {
return true;
} else {
return false;
}
}).style('stroke-width', '3px');
});
// Remove the highlight on the links
nodeEnter.on('mouseout', function() {
self.vis.selectAll('line.link').style('stroke-width','1px');
});
},
collapse_network: function(d, only_collapse) {
var self = this;
var server, vm;
var filterNode = function(obj) {
return function(d) {
return obj == d.data;
};
};
if (!d.data.collapsed) {
var vmCount = 0;
for (vm in self.data.servers) {
if (self.data.servers[vm] !== undefined) {
if (self.data.servers[vm].networks.length == 1) {
if (self.data.servers[vm].networks[0].id == d.data.id) {
vmCount += 1;
self.removeNode(self.data.servers[vm]);
}
}
}
}
d.data.collapsed = true;
if (vmCount > 0) {
self.vis.selectAll('.vmCount').filter(filterNode(d.data))[0][0].textContent = vmCount;
}
} else if (!only_collapse) {
for (server in self.data.servers) {
if ({}.hasOwnProperty.call(self.data.servers, server)) {
var _vm = self.data.servers[server];
if (_vm !== undefined) {
if (_vm.networks.length === 1) {
if (_vm.networks[0].id == d.data.id) {
self.new_node(_vm, d.x, d.y);
self.new_link(self.find_by_id(_vm.id), self.find_by_id(d.data.id));
self.force.start();
}
}
}
}
}
d.data.collapsed = false;
self.vis.selectAll('.vmCount').filter(filterNode(d.data))[0][0].textContent = '';
var i = 0;
while (i <= 100) {
self.force.tick();
i++;
}
}
},
new_link: function(source, target) {
var self = this;
self.links.push({source: source, target: target});
var line = self.vis.selectAll('line.link').data(self.links);
line.enter().insert('line', 'g.node')
.attr('class', 'link')
.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y; })
.style('stroke', 'black')
.style('stroke-width', 2);
},
find_by_id: function(id) {
var self = this;
var obj, _i, _len, _ref;
_ref = self.vis.selectAll('g.node').data();
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
obj = _ref[_i];
if (obj.data.id == id) {
return obj;
}
}
return undefined;
},
already_in_graph: function(data, node) {
// Check for gateway that may not have unique id
if (data == this.data.ports) {
for (var p in data) {
if (JSON.stringify(data[p]) == JSON.stringify(node)) {
return true;
}
}
return false;
}
// All other node types have UUIDs
for (var n in data) {
if (n == node.id) {
return true;
}
}
return false;
},
load_topology: function(data) {
var self = this;
var net, _i, _netlen, _netref, rou, _j, _roulen, _rouref, port, _l, _portlen, _portref,
ser, _k, _serlen, _serref, obj, vmCount;
var change = false;
var filterNode = function(obj) {
return function(d) {
return obj == d.data;
};
};
// Networks
_netref = data.networks;
for (_i = 0, _netlen = _netref.length; _i < _netlen; _i++) {
net = _netref[_i];
var network = null;
if (net['router:external'] === true) {
network = new ExternalNetwork(net);
} else {
network = new Network(net);
}
if (!self.already_in_graph(self.data.networks, network)) {
self.new_node(network);
change = true;
} else {
obj = self.find_by_id(network.id);
if (obj) {
network.collapsed = obj.data.collapsed;
network.instances = obj.data.instances;
obj.data = network;
}
}
self.data.networks[network.id] = network;
}
// Routers
_rouref = data.routers;
for (_j = 0, _roulen = _rouref.length; _j < _roulen; _j++) {
rou = _rouref[_j];
var router = new Router(rou);
if (!self.already_in_graph(self.data.routers, router)) {
self.new_node(router);
change = true;
} else {
obj = self.find_by_id(router.id);
if (obj) {
// Keep networks list
router.networks = obj.data.networks;
// Keep ports list
router.ports = obj.data.ports;
obj.data = router;
}
}
self.data.routers[router.id] = router;
}
// Servers
_serref = data.servers;
for (_k = 0, _serlen = _serref.length; _k < _serlen; _k++) {
ser = _serref[_k];
var server = new Server(ser);
if (!self.already_in_graph(self.data.servers, server)) {
self.new_node(server);
change = true;
} else {
obj = self.find_by_id(server.id);
if (obj) {
// Keep networks list
server.networks = obj.data.networks;
// Keep ip address list
server.ip_addresses = obj.data.ip_addresses;
obj.data = server;
} else if (self.data.servers[server.id] !== undefined) {
// This is used when servers are hidden because the network is
// collapsed
server.networks = self.data.servers[server.id].networks;
server.ip_addresses = self.data.servers[server.id].ip_addresses;
}
}
self.data.servers[server.id] = server;
}
// Ports
_portref = data.ports;
for (_l = 0, _portlen = _portref.length; _l < _portlen; _l++) {
port = _portref[_l];
if (!self.already_in_graph(self.data.ports, port)) {
var device = self.find_by_id(port.device_id);
var _network = self.find_by_id(port.network_id);
if (angular.isDefined(device) && angular.isDefined(_network)) {
if (port.device_owner == 'compute:nova' || port.device_owner == 'compute:None') {
_network.data.instances++;
device.data.networks.push(_network.data);
if (port.fixed_ips) {
for(var ip in port.fixed_ips) {
if (!listContains(port.fixed_ips[ip], device.data.ip_addresses)) {
device.data.ip_addresses.push(port.fixed_ips[ip]);
}
}
}
// Remove the recently added node if connected to a network which is
// currently collapsed
if (_network.data.collapsed) {
if (device.data.networks.length == 1) {
self.data.servers[device.data.id].networks = device.data.networks;
self.data.servers[device.data.id].ip_addresses = device.data.ip_addresses;
self.removeNode(self.data.servers[port.device_id]);
vmCount = Number(self.vis.selectAll('.vmCount').filter(filterNode(_network.data))[0][0].textContent);
self.vis.selectAll('.vmCount').filter(filterNode(_network.data))[0][0].textContent = vmCount + 1;
continue;
}
}
} else if (port.device_owner == 'network:router_interface') {
device.data.networks.push(_network.data);
device.data.ports.push(port);
} else if (device.data.ports) {
device.data.ports.push(port);
}
self.new_link(self.find_by_id(port.device_id), self.find_by_id(port.network_id));
change = true;
} else if (angular.isDefined(_network) && port.device_owner == 'compute:nova') {
// Need to add a previously hidden node to the graph because it is
// connected to more than 1 network
if (_network.data.collapsed) {
server = self.data.servers[port.device_id];
server.networks.push(_network.data);
if (port.fixed_ips) {
for(var ip in port.fixed_ips) {
server.ip_addresses.push(port.fixed_ips[ip]);
}
}
self.new_node(server);
// decrease collapsed vm count on network
vmCount = Number(self.vis.selectAll('.vmCount').filter(filterNode(server.networks[0]))[0][0].textContent);
if (vmCount == 1) {
self.vis.selectAll('.vmCount').filter(filterNode(server.networks[0]))[0][0].textContent = '';
} else {
self.vis.selectAll('.vmCount').filter(filterNode(server.networks[0]))[0][0].textContent = vmCount - 1;
}
// Add back in first network link
self.new_link(self.find_by_id(port.device_id), self.find_by_id(server.networks[0].id));
// Add new link
self.new_link(self.find_by_id(port.device_id), self.find_by_id(port.network_id));
change = true;
}
}
}
self.data.ports[port.id+port.device_id+port.network_id] = port;
}
if (change) {
self.force.start();
}
self.load_config();
},
removeNode: function(obj) {
var filterNetwork, filterNode, n, node, _i, _len, _ref;
_ref = this.nodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
n = _ref[_i];
if (n.data === obj) {
node = n;
break;
}
}
if (node) {
this.nodes.splice(this.nodes.indexOf(node), 1);
filterNode = function(obj) {
return function(d) {
return obj === d.data;
};
};
filterNetwork = function(obj) {
return function(d) {
return obj === d.network.data;
};
};
if (obj instanceof Network) {
this.vis.selectAll('.hulls').filter(filterNetwork(obj)).remove();
}
this.vis.selectAll('g.node').filter(filterNode(obj)).remove();
return this.removeNodesLinks(obj);
}
},
removeNodesLinks: function(node) {
var l, linksToRemove, _i, _j, _len, _len1, _ref;
linksToRemove = [];
_ref = this.links;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
l = _ref[_i];
if (l.source.data === node) {
linksToRemove.push(l);
} else if (l.target.data === node) {
linksToRemove.push(l);
}
}
for (_j = 0, _len1 = linksToRemove.length; _j < _len1; _j++) {
l = linksToRemove[_j];
this.removeLink(l);
}
return this.force.resume();
},
removeLink: function(link) {
var i, index, l, _i, _len, _ref;
index = -1;
_ref = this.links;
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
l = _ref[i];
if (l === link) {
index = i;
break;
}
}
if (index !== -1) {
this.links.splice(index, 1);
}
return this.vis.selectAll('line.link').data(this.links).exit().remove();
},
delete_device: function(type, deviceId) {
var message = {id:deviceId};
horizon.networktopologymessager.post_message(deviceId,type,message,type,'delete',data={});
},
remove_node_on_delete: function(deleteData) {
var self = this;
var deviceId = deleteData.device_id;
switch (deleteData.device_type) {
case 'router':
self.removeNode(self.data.routers[deviceId]);
break;
case 'instance':
self.removeNode(self.data.servers[deviceId]);
this.data.servers[deviceId] = undefined;
break;
case 'network':
self.removeNode(self.data.networks[deviceId]);
break;
case 'port':
self.removePort(deviceId, deleteData.device_data);
break;
}
self.delete_balloon();
},
removePort: function(portId, deviceData) {
var self = this;
var routerId = deviceData.router_id;
var networkId = deviceData.network_id;
if (routerId) {
for (var l in self.links) {
var data = null;
if(self.links[l].source.data.id == routerId && self.links[l].target.data.id == networkId) {
data = self.links[l].source.data;
} else if (self.links[l].target.data.id == routerId && self.links[l].source.data.id == networkId) {
data = self.links[l].target.data;
}
if (data) {
for (var p in data.ports) {
if ((data.ports[p].id == portId) && (data.ports[p].network_id == networkId)) {
self.removeLink(self.links[l]);
// Update Router to remove deleted port
var router = self.find_by_id(routerId);
router.data.ports.splice(router.data.ports.indexOf(data.ports[p]), 1);
self.force.start();
return;
}
}
}
}
}
},
delete_port: function(routerId, portId, networkId) {
var message = {id:portId};
var data = {network_id:networkId,routerId:routerId};
if (routerId) {
horizon.networktopologymessager.post_message(portId, 'router/' + routerId + '/', message, 'port', 'delete', data);
} else {
horizon.networktopologymessager.post_message(portId, 'network/' + networkId + '/', message, 'port', 'delete', data);
}
},
show_balloon: function(d,d2,element) {
var self = this;
var balloonTmpl = self.balloonTmpl;
var deviceTmpl = self.balloon_deviceTmpl;
var portTmpl = self.balloon_portTmpl;
var netTmpl = self.balloon_netTmpl;
var instanceTmpl = self.balloon_instanceTmpl;
var balloonID = 'bl_' + d.id;
var ports = [];
var subnets = [];
if (self.balloonID) {
if (self.balloonID == balloonID) {
self.delete_balloon();
return;
}
self.delete_balloon();
}
self.force.stop();
if (d.hasOwnProperty('ports')) {
angular.element.each(d.ports, function(i, port) {
var object = {};
object.id = port.id;
object.router_id = port.device_id;
object.url = port.url;
object.port_status = port.status;
object.port_status_css = (port.original_status === 'ACTIVE') ? 'active' : 'down';
var ipAddress = '';
try {
for (var ip in port.fixed_ips) {
ipAddress += port.fixed_ips[ip].ip_address + ' ';
}
}catch(e) {
ipAddress = gettext('None');
}
var deviceOwner = '';
try {
deviceOwner = port.device_owner.replace('network:','');
}catch(e) {
deviceOwner = gettext('None');
}
var networkId = '';
try {
networkId = port.network_id;
}catch(e) {
networkId = gettext('None');
}
object.ip_address = ipAddress;
object.device_owner = deviceOwner;
object.network_id = networkId;
object.is_interface = (deviceOwner === 'router_interface' || deviceOwner === 'router_gateway');
ports.push(object);
});
} else if (d.hasOwnProperty('subnets')) {
angular.element.each(d.subnets, function(i, snet) {
var object = {};
object.id = snet.id;
object.cidr = snet.cidr;
object.url = snet.url;
subnets.push(object);
});
}
var htmlData = {
balloon_id:balloonID,
id:d.id,
url:d.url,
name:d.name,
type:d.type,
delete_label: gettext('Delete'),
status:d.status,
status_class: (d.original_status === 'ACTIVE') ? 'active' : 'down',
status_label: gettext('STATUS'),
id_label: gettext('ID'),
interfaces_label: gettext('Interfaces'),
subnets_label: gettext('Subnets'),
delete_interface_label: gettext('Delete Interface'),
delete_subnet_label: gettext('Delete Subnet'),
open_console_label: gettext('Open Console'),
view_details_label: gettext('View Details'),
ips_label: gettext('IP Addresses')
};
var html;
if (d instanceof Router) {
htmlData.delete_label = gettext('Delete Router');
htmlData.view_details_label = gettext('View Router Details');
htmlData.port = ports;
htmlData.add_interface_url = 'router/' + d.id + '/addinterface';
htmlData.add_interface_label = gettext('Add Interface');
html = balloonTmpl.render(htmlData,{
table1:deviceTmpl,
table2:portTmpl
});
} else if (d instanceof Server) {
htmlData.delete_label = gettext('Delete Instance');
htmlData.view_details_label = gettext('View Instance Details');
htmlData.console_id = d.id;
htmlData.ips = d.ip_addresses;
htmlData.console = d.console;
html = balloonTmpl.render(htmlData,{
table1:deviceTmpl,
table2:instanceTmpl
});
} else if (d instanceof Network || d instanceof ExternalNetwork) {
for (var s in subnets) {
subnets[s].network_id = d.id;
}
htmlData.subnet = subnets;
if (d instanceof Network) {
htmlData.delete_label = gettext('Delete Network');
}
htmlData.add_subnet_url = 'network/' + d.id + '/subnet/create';
htmlData.add_subnet_label = gettext('Create Subnet');
html = balloonTmpl.render(htmlData,{
table1:deviceTmpl,
table2:netTmpl
});
} else {
return;
}
angular.element(self.svg_container).append(html);
var devicePosition = self.getScreenCoords(d2.x, d2.y);
var x = devicePosition.x;
var y = devicePosition.y;
var xoffset = 100;
var yoffset = 95;
angular.element('#' + balloonID).css({
'left': x + xoffset + 'px',
'top': y + yoffset + 'px'
}).show();
var _balloon = angular.element('#' + balloonID);
if (element.x + _balloon.outerWidth() > angular.element(window).outerWidth()) {
_balloon
.css({
'left': 0 + 'px'
})
.css({
'left': (x - _balloon.outerWidth() + 'px')
})
.addClass('leftPosition');
}
_balloon.find('.delete-device').click(function() {
var _this = angular.element(this);
_this.prop('disabled', true);
d3.select('#id_' + _this.data('device-id')).classed('loading',true);
self.delete_device(_this.data('type'),_this.data('device-id'));
});
_balloon.find('.delete-port').click(function() {
var _this = angular.element(this);
self.delete_port(_this.data('router-id'),_this.data('port-id'),_this.data('network-id'));
self.delete_balloon();
});
self.balloonID = balloonID;
},
delete_balloon:function() {
var self = this;
if (self.balloonID) {
angular.element('#' + self.balloonID).remove();
self.balloonID = null;
self.force.start();
}
},
svgs: function(name) {
switch (name) {
case 'router':
return 'm 26.628571,16.08 -8.548572,0 0,8.548571 2.08,-2.079998 6.308572,6.30857 4.38857,-4.388572 -6.308571,-6.30857 z m -21.2571429,-4.159999 8.5485709,0 0,-8.5485723 -2.08,2.08 L 5.5314281,-0.85714307 1.1428571,3.5314287 7.4514281,9.84 z m -3.108571,7.268571 0,8.548571 8.5485709,0 L 8.7314281,25.657144 15.039999,19.325715 10.674285,14.96 4.3428571,21.268573 z M 29.737142,8.8114288 l 0,-8.54857147 -8.548572,0 2.08,2.07999987 -6.308571,6.3085716 4.388572,4.3885722 6.308571,-6.3085723 z';
default:
return '';
}
}
};