db4c47d252
Now when user click the link "Open Console" on the instance balloon of network topology panel, two windows will be opened. This patch fixes that. Closes-Bug: #1702189 Change-Id: I7459e990cac4c6df79c4972e2d5b5b4dee9eb428
653 lines
22 KiB
JavaScript
653 lines
22 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. */
|
|
|
|
horizon.flat_network_topology = {
|
|
model: null,
|
|
fa_globe_glyph: '\uf0ac',
|
|
fa_globe_glyph_width: 15,
|
|
svg:'#topology_canvas',
|
|
svg_container:'#flatTopologyCanvasContainer',
|
|
network_tmpl:{
|
|
small:'#topology_template > .network_container_small',
|
|
normal:'#topology_template > .network_container_normal'
|
|
},
|
|
router_tmpl: {
|
|
small:'#topology_template > .router_small',
|
|
normal:'#topology_template > .router_normal'
|
|
},
|
|
instance_tmpl: {
|
|
small:'#topology_template > .instance_small',
|
|
normal:'#topology_template > .instance_normal'
|
|
},
|
|
balloon_tmpl : null,
|
|
balloon_device_tmpl : null,
|
|
balloon_port_tmpl : null,
|
|
network_index: {},
|
|
balloon_id:null,
|
|
reload_duration: 10000,
|
|
draw_mode:'normal',
|
|
network_height : 0,
|
|
previous_message : null,
|
|
element_properties:{
|
|
normal:{
|
|
network_width:270,
|
|
network_min_height:500,
|
|
top_margin:80,
|
|
default_height:50,
|
|
margin:20,
|
|
device_x:98.5,
|
|
device_width:90,
|
|
port_margin:16,
|
|
port_height:6,
|
|
port_width:82,
|
|
port_text_margin:{x:6,y:-4},
|
|
texts_bg_y:32,
|
|
type_y:46,
|
|
balloon_margin:{x:12,y:-12}
|
|
},
|
|
small :{
|
|
network_width:100,
|
|
network_min_height:400,
|
|
top_margin:50,
|
|
default_height:20,
|
|
margin:30,
|
|
device_x:47.5,
|
|
device_width:20,
|
|
port_margin:5,
|
|
port_height:3,
|
|
port_width:32.5,
|
|
port_text_margin:{x:0,y:0},
|
|
texts_bg_y:0,
|
|
type_y:0,
|
|
balloon_margin:{x:12,y:-30}
|
|
},
|
|
cidr_margin:5,
|
|
device_name_max_size:9,
|
|
device_name_suffix:'..'
|
|
},
|
|
init:function() {
|
|
var self = this;
|
|
self.$container = $(self.svg_container);
|
|
self.$loading_template = horizon.networktopologyloader.setup_loader($(self.$container));
|
|
|
|
if($('#networktopology').length === 0) {
|
|
return;
|
|
}
|
|
self.color = d3.scale.category10();
|
|
self.balloon_tmpl = Hogan.compile($('#balloon_container').html());
|
|
self.balloon_device_tmpl = Hogan.compile($('#balloon_device').html());
|
|
self.balloon_port_tmpl = Hogan.compile($('#balloon_port').html());
|
|
|
|
$(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();
|
|
e.stopImmediatePropagation();
|
|
var vnc_window = window.open($(this).attr('href'), vnc_window, 'width=760,height=560');
|
|
self.delete_balloon();
|
|
})
|
|
.click(function(){
|
|
self.delete_balloon();
|
|
});
|
|
|
|
$('.toggle-view > .btn').click(function(){
|
|
self.draw_mode = $(this).data('value');
|
|
$('g.network').remove();
|
|
horizon.cookies.put('ntp_draw_mode',self.draw_mode);
|
|
self.data_convert();
|
|
});
|
|
|
|
self.$loading_template.show();
|
|
$('#networktopology').on('change', function() {
|
|
self.load_network_info();
|
|
});
|
|
|
|
// register for message notifications
|
|
//horizon.networktopologymessager.addMessageHandler(self.handleMessage, this);
|
|
},
|
|
|
|
/*handleMessage:function(message) {
|
|
// noop
|
|
},*/
|
|
|
|
load_network_info:function(){
|
|
var self = this;
|
|
self.model = horizon.networktopologyloader.model;
|
|
self.data_convert();
|
|
},
|
|
select_draw_mode:function() {
|
|
var self = this;
|
|
var draw_mode = 'normal';
|
|
try {
|
|
draw_mode = horizon.cookies.get('ntp_draw_mode');
|
|
}
|
|
catch(e) {
|
|
// if the cookie does not exist, angular-cookie passes "undefined" to
|
|
// JSON.parse which throws an exception
|
|
}
|
|
|
|
if (draw_mode && (draw_mode === 'normal' || draw_mode === 'small')) {
|
|
self.draw_mode = draw_mode;
|
|
} else {
|
|
if (self.model.networks.length *
|
|
self.element_properties.normal.network_width > $('#topologyCanvas').width()) {
|
|
self.draw_mode = 'small';
|
|
} else {
|
|
self.draw_mode = 'normal';
|
|
}
|
|
horizon.cookies.put('ntp_draw_mode',self.draw_mode);
|
|
}
|
|
$('.toggle-view > .btn').each(function(){
|
|
var $this = $(this);
|
|
if($this.data('value') === self.draw_mode) {
|
|
$this.addClass('active');
|
|
} else {
|
|
$this.removeClass('active');
|
|
}
|
|
});
|
|
},
|
|
data_convert:function() {
|
|
var self = this;
|
|
var model = self.model;
|
|
$.each(model.networks, function(index, network) {
|
|
self.network_index[network.id] = index;
|
|
});
|
|
self.select_draw_mode();
|
|
var element_properties = self.element_properties[self.draw_mode];
|
|
self.network_height = element_properties.top_margin;
|
|
$.each([
|
|
{model:model.routers, type:'router'},
|
|
{model:model.servers, type:'instance'}
|
|
], function(index, devices) {
|
|
var type = devices.type;
|
|
var model = devices.model;
|
|
$.each(model, function(index, device) {
|
|
device.type = type;
|
|
device.ports = self.select_port(device.id);
|
|
var hasports = device.ports.length > 0;
|
|
device.parent_network = (hasports) ? self.select_main_port(device.ports).network_id : self.model.networks[0].id;
|
|
var height = element_properties.port_margin*(device.ports.length - 1);
|
|
device.height = (self.draw_mode === 'normal' && height > element_properties.default_height) ? height : element_properties.default_height;
|
|
device.pos_y = self.network_height;
|
|
device.port_height = (self.draw_mode === 'small' && height > device.height) ? 1 : element_properties.port_height;
|
|
device.port_margin = (self.draw_mode === 'small' && height > device.height) ? device.height/device.ports.length : element_properties.port_margin;
|
|
self.network_height += device.height + element_properties.margin;
|
|
});
|
|
});
|
|
$.each(model.networks, function(index, network) {
|
|
network.devices = [];
|
|
$.each([model.routers, model.servers],function(index, devices) {
|
|
$.each(devices,function(index, device) {
|
|
if(network.id === device.parent_network) {
|
|
network.devices.push(device);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
self.network_height += element_properties.top_margin;
|
|
self.network_height = (self.network_height > element_properties.network_min_height) ? self.network_height : element_properties.network_min_height;
|
|
self.draw_topology();
|
|
self.$loading_template.hide();
|
|
},
|
|
draw_topology:function() {
|
|
var self = this;
|
|
$(self.svg_container).removeClass('noinfo');
|
|
if (self.model.networks.length <= 0) {
|
|
$('g.network').remove();
|
|
$(self.svg_container).addClass('noinfo');
|
|
return;
|
|
}
|
|
var svg = d3.select(self.svg);
|
|
var element_properties = self.element_properties[self.draw_mode];
|
|
svg
|
|
.attr('width',self.model.networks.length*element_properties.network_width)
|
|
.attr('height',self.network_height);
|
|
|
|
var network = svg.selectAll('g.network')
|
|
.data(self.model.networks);
|
|
|
|
network.enter()
|
|
.append('g')
|
|
.attr('class','network')
|
|
.each(function(d){
|
|
this.appendChild(d3.select(self.network_tmpl[self.draw_mode]).node().cloneNode(true));
|
|
var $this = d3.select(this).select('.network-rect');
|
|
if (d.url) {
|
|
$this
|
|
.on('mouseover',function(){
|
|
$this.transition().style('fill', function() {
|
|
return d3.rgb(self.get_network_color(d.id)).brighter(0.5);
|
|
});
|
|
})
|
|
.on('mouseout',function(){
|
|
$this.transition().style('fill', function() {
|
|
return self.get_network_color(d.id);
|
|
});
|
|
})
|
|
.on('click',function(){
|
|
window.location.href = d.url;
|
|
});
|
|
} else {
|
|
$this.classed('nourl', true);
|
|
}
|
|
});
|
|
|
|
network
|
|
.attr('id',function(d) { return 'id_' + d.id; })
|
|
.attr('transform',function(d,i){
|
|
return 'translate(' + element_properties.network_width * i + ',' + 0 + ')';
|
|
})
|
|
.select('.network-rect')
|
|
.attr('height', function() { return self.network_height; })
|
|
.style('fill', function(d) { return self.get_network_color(d.id); });
|
|
network
|
|
.select('.network-name')
|
|
.attr('x', function() { return self.network_height/2; })
|
|
.text(function(d) { return d.name; });
|
|
network
|
|
.select('.network-cidr')
|
|
.attr('x', function(d) {
|
|
var padding = isExternalNetwork(d) ? self.fa_globe_glyph_width : 0;
|
|
return self.network_height - self.element_properties.cidr_margin -
|
|
padding;
|
|
})
|
|
.text(function(d) {
|
|
var cidr = $.map(d.subnets,function(n){
|
|
return n.cidr;
|
|
});
|
|
return cidr.join(', ');
|
|
});
|
|
function isExternalNetwork(d) {
|
|
return d['router:external'];
|
|
}
|
|
network
|
|
.select('.network-type')
|
|
.text(function(d) {
|
|
return isExternalNetwork(d) ? self.fa_globe_glyph : '';
|
|
})
|
|
.attr('x', function() {
|
|
return self.network_height - self.element_properties.cidr_margin;
|
|
});
|
|
|
|
$('[data-toggle="tooltip"]').tooltip({container: 'body'});
|
|
|
|
network.exit().remove();
|
|
|
|
var device = network.selectAll('g.device')
|
|
.data(function(d) { return d.devices; });
|
|
|
|
var device_enter = device.enter()
|
|
.append("g")
|
|
.attr('class','device')
|
|
.each(function(d){
|
|
var device_template = self[d.type + '_tmpl'][self.draw_mode];
|
|
this.appendChild(d3.select(device_template).node().cloneNode(true));
|
|
});
|
|
|
|
device_enter
|
|
.on('mouseenter',function(d){
|
|
var $this = $(this);
|
|
self.show_balloon(d,$this);
|
|
})
|
|
.on('click',function(){
|
|
d3.event.stopPropagation();
|
|
});
|
|
|
|
device
|
|
.attr('id',function(d) { return 'id_' + d.id; })
|
|
.attr('transform',function(d){
|
|
return 'translate(' + element_properties.device_x + ',' + d.pos_y + ')';
|
|
})
|
|
.select('.frame')
|
|
.attr('height',function(d) { return d.height; });
|
|
device
|
|
.select('.texts_bg')
|
|
.attr('y',function(d) {
|
|
return element_properties.texts_bg_y + d.height - element_properties.default_height;
|
|
});
|
|
device
|
|
.select('.type')
|
|
.attr('y',function(d) {
|
|
return element_properties.type_y + d.height - element_properties.default_height;
|
|
});
|
|
device
|
|
.select('.name')
|
|
.text(function(d) { return self.string_truncate(d.name); });
|
|
device.each(function(d) {
|
|
if (d.status === 'BUILD') {
|
|
d3.select(this).classed('loading',true);
|
|
} else if (d.task === 'deleting') {
|
|
d3.select(this).classed('loading',true);
|
|
if ('bl_' + d.id === self.balloon_id) {
|
|
self.delete_balloon();
|
|
}
|
|
} else {
|
|
d3.select(this).classed('loading',false);
|
|
if ('bl_' + d.id === self.balloon_id) {
|
|
var $this = $(this);
|
|
self.show_balloon(d,$this);
|
|
}
|
|
}
|
|
});
|
|
|
|
device.exit().each(function(d){
|
|
if ('bl_' + d.id === self.balloon_id) {
|
|
self.delete_balloon();
|
|
}
|
|
}).remove();
|
|
|
|
var port = device.select('g.ports')
|
|
.selectAll('g.port')
|
|
.data(function(d) { return d.ports; });
|
|
|
|
var port_enter = port.enter()
|
|
.append('g')
|
|
.attr('class','port')
|
|
.attr('id',function(d) { return 'id_' + d.id; });
|
|
|
|
port_enter
|
|
.append('line')
|
|
.attr('class','port_line');
|
|
|
|
port_enter
|
|
.append('text')
|
|
.attr('class','port_text');
|
|
|
|
device.select('g.ports').each(function(d){
|
|
this._portdata = {};
|
|
this._portdata.ports_length = d.ports.length;
|
|
this._portdata.parent_network = d.parent_network;
|
|
this._portdata.device_height = d.height;
|
|
this._portdata.port_height = d.port_height;
|
|
this._portdata.port_margin = d.port_margin;
|
|
this._portdata.left = 0;
|
|
this._portdata.right = 0;
|
|
$(this).mouseenter(function(e){
|
|
e.stopPropagation();
|
|
});
|
|
});
|
|
|
|
port.each(function(d){
|
|
var index_diff = self.get_network_index(this.parentNode._portdata.parent_network) -
|
|
self.get_network_index(d.network_id);
|
|
this._index_diff = index_diff = (index_diff >= 0)? ++index_diff : index_diff;
|
|
this._direction = (this._index_diff < 0)? 'right' : 'left';
|
|
this._index = this.parentNode._portdata[this._direction] ++;
|
|
|
|
});
|
|
|
|
port.attr('transform',function(){
|
|
var x = (this._direction === 'left') ? 0 : element_properties.device_width;
|
|
var ports_length = this.parentNode._portdata[this._direction];
|
|
var distance = this.parentNode._portdata.port_margin;
|
|
var y = (this.parentNode._portdata.device_height -
|
|
(ports_length -1)*distance)/2 + this._index*distance;
|
|
return 'translate(' + x + ',' + y + ')';
|
|
});
|
|
|
|
port
|
|
.select('.port_line')
|
|
.attr('stroke-width',function() {
|
|
return this.parentNode.parentNode._portdata.port_height;
|
|
})
|
|
.attr('stroke', function(d) {
|
|
return self.get_network_color(d.network_id);
|
|
})
|
|
.attr('x1',0).attr('y1',0).attr('y2',0)
|
|
.attr('x2',function() {
|
|
var parent = this.parentNode;
|
|
var width = (Math.abs(parent._index_diff) - 1)*element_properties.network_width +
|
|
element_properties.port_width;
|
|
return (parent._direction === 'left') ? -1*width : width;
|
|
});
|
|
|
|
port
|
|
.select('.port_text')
|
|
.attr('x',function() {
|
|
var parent = this.parentNode;
|
|
if (parent._direction === 'left') {
|
|
d3.select(this).classed('left',true);
|
|
return element_properties.port_text_margin.x*-1;
|
|
} else {
|
|
d3.select(this).classed('left',false);
|
|
return element_properties.port_text_margin.x;
|
|
}
|
|
})
|
|
.attr('y',function() {
|
|
return element_properties.port_text_margin.y;
|
|
})
|
|
.text(function(d) {
|
|
var ip_label = [];
|
|
$.each(d.fixed_ips, function() {
|
|
ip_label.push(this.ip_address);
|
|
});
|
|
return ip_label.join(',');
|
|
});
|
|
|
|
port.exit().remove();
|
|
},
|
|
get_network_color: function(network_id) {
|
|
return this.color(this.get_network_index(network_id));
|
|
},
|
|
get_network_index: function(network_id) {
|
|
return this.network_index[network_id];
|
|
},
|
|
select_port: function(device_id){
|
|
return $.map(this.model.ports,function(port){
|
|
if (port.device_id === device_id) {
|
|
return port;
|
|
}
|
|
});
|
|
},
|
|
select_main_port: function(ports){
|
|
var _self = this;
|
|
var main_port_index = 0;
|
|
var MAX_INT = 4294967295;
|
|
var min_port_length = MAX_INT;
|
|
$.each(ports, function(index, port){
|
|
var port_length = _self.sum_port_length(port.network_id, ports);
|
|
if(port_length < min_port_length){
|
|
min_port_length = port_length;
|
|
main_port_index = index;
|
|
}
|
|
});
|
|
return ports[main_port_index];
|
|
},
|
|
sum_port_length: function(network_id, ports){
|
|
var self = this;
|
|
var sum_port_length = 0;
|
|
var base_index = self.get_network_index(network_id);
|
|
$.each(ports, function(index, port){
|
|
sum_port_length += base_index - self.get_network_index(port.network_id);
|
|
});
|
|
return sum_port_length;
|
|
},
|
|
string_truncate: function(string) {
|
|
var self = this;
|
|
var str = string;
|
|
var max_size = self.element_properties.device_name_max_size;
|
|
var suffix = self.element_properties.device_name_suffix;
|
|
var bytes = 0;
|
|
for (var i = 0; i < str.length; i++) {
|
|
bytes += str.charCodeAt(i) <= 255 ? 1 : 2;
|
|
if (bytes > max_size) {
|
|
str = str.substr(0, i) + suffix;
|
|
break;
|
|
}
|
|
}
|
|
return str;
|
|
},
|
|
delete_device: function(device_type, device_id) {
|
|
var message = {id:device_id};
|
|
var target = device_type === 'instance' ? 'instance?id=' + device_id : device_type;
|
|
horizon.networktopologymessager.post_message(device_id, target, message, device_type, 'delete', data={});
|
|
},
|
|
delete_port: function(router_id, port_id, network_id) {
|
|
var message = {id:port_id};
|
|
var data = {router_id: router_id, network_id: network_id};
|
|
horizon.networktopologymessager.post_message(port_id, 'router/' + router_id + '/', message, 'port', 'delete', data);
|
|
},
|
|
show_balloon:function(d,element) {
|
|
var self = this;
|
|
var element_properties = self.element_properties[self.draw_mode];
|
|
if (self.balloon_id) {
|
|
self.delete_balloon();
|
|
}
|
|
var balloon_tmpl = self.balloon_tmpl;
|
|
var device_tmpl = self.balloon_device_tmpl;
|
|
var port_tmpl = self.balloon_port_tmpl;
|
|
var balloon_id = 'bl_' + d.id;
|
|
var ports = [];
|
|
$.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_class = (port.original_status === "ACTIVE")? 'active' : 'down';
|
|
var ip_address = '';
|
|
try {
|
|
ip_address = port.fixed_ips[0].ip_address;
|
|
}catch(e){
|
|
ip_address = gettext('None');
|
|
}
|
|
var device_owner = '';
|
|
try {
|
|
device_owner = port.device_owner.replace('network:','');
|
|
}catch(e){
|
|
device_owner = gettext('None');
|
|
}
|
|
var network_id = '';
|
|
try {
|
|
network_id = port.network_id;
|
|
}catch(e) {
|
|
network_id = gettext('None');
|
|
}
|
|
object.network_id = network_id;
|
|
object.ip_address = ip_address;
|
|
object.device_owner = device_owner;
|
|
object.is_interface = (
|
|
device_owner === 'router_interface' ||
|
|
device_owner === 'router_gateway' ||
|
|
device_owner === 'ha_router_replicated_interface'
|
|
);
|
|
// (device_owner === 'router_interface' || device_owner === 'router_gateway');
|
|
ports.push(object);
|
|
});
|
|
var html;
|
|
var html_data = {
|
|
balloon_id:balloon_id,
|
|
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"),
|
|
delete_interface_label: gettext("Delete Interface"),
|
|
open_console_label: gettext("Open Console"),
|
|
view_details_label: gettext("View Details")
|
|
};
|
|
if (d.type === 'router') {
|
|
html_data.delete_label = gettext("Delete Router");
|
|
html_data.view_details_label = gettext("View Router Details");
|
|
html_data.port = ports;
|
|
html_data.add_interface_url = d.url + 'addinterface';
|
|
html_data.add_interface_label = gettext("Add Interface");
|
|
html = balloon_tmpl.render(html_data,{
|
|
table1:device_tmpl,
|
|
table2:(ports.length > 0) ? port_tmpl : null
|
|
});
|
|
} else if (d.type === 'instance') {
|
|
html_data.delete_label = gettext("Delete Instance");
|
|
html_data.view_details_label = gettext("View Instance Details");
|
|
html_data.console_id = d.id;
|
|
html_data.console = d.console;
|
|
html = balloon_tmpl.render(html_data,{
|
|
table1:device_tmpl
|
|
});
|
|
} else {
|
|
return;
|
|
}
|
|
$(self.svg_container).append(html);
|
|
var device_position = element.find('.frame');
|
|
var sidebar_width = $("#sidebar").width();
|
|
var navbar_height = $(".navbar").height();
|
|
var breadcrumb_height = $(".breadcrumb").outerHeight(true);
|
|
var pageheader_height = $(".page-header").outerHeight(true);
|
|
var launchbuttons_height = $(".launchButtons").height();
|
|
var height_offset = navbar_height + breadcrumb_height + pageheader_height + launchbuttons_height;
|
|
var device_offset = device_position.offset();
|
|
var x = Math.round(device_offset.left + element_properties.device_width + element_properties.balloon_margin.x - sidebar_width);
|
|
// 15 is magic pixel value that seems to make things line up
|
|
var y = Math.round(device_offset.top + element_properties.balloon_margin.y - height_offset + 15);
|
|
var $balloon = $('#' + balloon_id);
|
|
$balloon.css({
|
|
'left': '0px',
|
|
'top': y + 'px'
|
|
});
|
|
var balloon_width = $balloon.outerWidth();
|
|
var left_x = device_offset.left - balloon_width - element_properties.balloon_margin.x - sidebar_width;
|
|
var right_x = x + balloon_width + element_properties.balloon_margin.x + sidebar_width;
|
|
|
|
if (left_x > 0 && right_x > $(window).outerWidth()) {
|
|
x = left_x;
|
|
$balloon.addClass('leftPosition');
|
|
}
|
|
$balloon.css({
|
|
'left': x + 'px'
|
|
}).show();
|
|
|
|
$balloon.find('.delete-device').click(function(){
|
|
var $this = $(this);
|
|
var delete_modal = horizon.datatables.confirm($this);
|
|
delete_modal.find('.btn-primary').click(function () {
|
|
$this.prop('disabled', true);
|
|
d3.select('#id_' + $this.data('device-id')).classed('loading',true);
|
|
self.delete_device($this.data('type'),$this.data('device-id'));
|
|
horizon.modals.spinner.modal('hide');
|
|
});
|
|
});
|
|
$balloon.find('.delete-port').click(function(){
|
|
var $this = $(this);
|
|
var delete_modal = horizon.datatables.confirm($this);
|
|
delete_modal.find('.btn-primary').click(function () {
|
|
$this.prop('disabled', true);
|
|
self.delete_port($this.data('router-id'),$this.data('port-id'),$this.data('network-id'));
|
|
horizon.modals.spinner.modal('hide');
|
|
});
|
|
});
|
|
self.balloon_id = balloon_id;
|
|
},
|
|
delete_balloon:function() {
|
|
var self = this;
|
|
if(self.balloon_id) {
|
|
$('#' + self.balloon_id).remove();
|
|
self.balloon_id = null;
|
|
}
|
|
}
|
|
};
|