2016-12-02 17:06:04 +07:00
/ * *
* 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 .
* /
2015-05-06 14:33:53 -07:00
/* global Hogan */
2012-09-24 20:34:33 +00:00
/* Namespace for core functionality related to Network Topology. */
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 = [ ] ;
}
2016-02-20 13:07:47 +01:00
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 ;
}
2012-09-24 20:34:33 +00:00
horizon . network _topology = {
2014-12-11 17:09:09 +03:00
fa _globe _glyph : '\uf0ac' ,
fa _globe _glyph _width : 15 ,
2013-08-12 17:20:38 -07:00
svg : '#topology_canvas' ,
2014-12-03 15:00:47 +00:00
nodes : [ ] ,
links : [ ] ,
data : [ ] ,
zoom : d3 . behavior . zoom ( ) ,
data _loaded : false ,
2013-08-12 17:20:38 -07:00
svg _container : '#topologyCanvasContainer' ,
2014-12-03 15:00:47 +00:00
balloonTmpl : null ,
balloon _deviceTmpl : null ,
balloon _portTmpl : null ,
balloon _netTmpl : null ,
balloon _instanceTmpl : null ,
2012-09-24 20:34:33 +00:00
network _index : { } ,
2014-12-03 15:00:47 +00:00
balloonID : null ,
2013-08-12 17:20:38 -07:00
network _height : 0 ,
2014-12-03 15:00:47 +00:00
2013-08-12 17:20:38 -07:00
init : function ( ) {
2012-09-24 20:34:33 +00:00
var self = this ;
2014-12-03 15:00:47 +00:00
angular . element ( self . svg _container ) . spin ( horizon . conf . spinner _options . modal ) ;
if ( angular . element ( '#networktopology' ) . length === 0 ) {
2013-08-12 17:20:38 -07:00
return ;
}
2014-12-03 15:00:47 +00:00
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 )
2013-08-12 17:20:38 -07:00
. 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 ( ) ;
2014-12-03 15:00:47 +00:00
var vncWindow = window . open ( angular . element ( this ) . attr ( 'href' ) , vncWindow ,
'width=760,height=560' ) ;
2013-08-12 17:20:38 -07:00
self . delete _balloon ( ) ;
} ) ;
2014-12-03 15:00:47 +00:00
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 ) ;
2013-08-12 17:20:38 -07:00
} ) ;
2014-12-03 15:00:47 +00:00
angular . element ( '#topologyCanvasContainer' ) . spin ( horizon . conf . spinner _options . modal ) ;
self . create _vis ( ) ;
self . loading ( ) ;
self . force _direction ( 0.05 , 70 , - 700 ) ;
2016-02-19 14:15:20 -07:00
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 ) ;
}
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
// Get the json data about the current deployment
retrieve _network _info : function ( force _start ) {
2012-09-24 20:34:33 +00:00
var self = this ;
2016-02-19 14:15:20 -07:00
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 ++ ;
2012-09-24 20:34:33 +00:00
}
2016-02-19 14:15:20 -07:00
}
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
// 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 ) {
2013-08-12 17:20:38 -07:00
var self = this ;
2014-12-03 15:00:47 +00:00
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 } ;
2013-02-20 14:01:52 -08:00
} else {
2014-12-03 15:00:47 +00:00
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%' )
2016-02-19 14:15:20 -07:00
. attr ( 'height' , angular . element ( document ) . height ( ) - 270 + "px" )
2014-12-03 15:00:47 +00:00
. 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' )
2015-11-12 15:03:06 -08:00
. on ( 'click' , function ( ) {
2014-12-03 15:00:47 +00:00
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 ;
2013-08-12 17:20:38 -07:00
} else {
2014-12-03 15:00:47 +00:00
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 ;
2016-07-22 15:22:22 -06:00
while ( k < nodes . length ) {
2014-12-03 15:00:47 +00:00
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 ] ) ;
}
2013-08-12 17:20:38 -07:00
}
2014-12-03 15:00:47 +00:00
++ k ;
2012-09-24 20:34:33 +00:00
}
2014-12-03 15:00:47 +00:00
var hullset = [ ] ;
for ( i in hulls ) {
if ( { } . hasOwnProperty . call ( hulls , i ) ) {
hullset . push ( { group : i , network : networkids [ i ] , path : d3 . geom . hull ( hulls [ i ] ) } ) ;
2012-09-24 20:34:33 +00:00
}
2014-12-03 15:00:47 +00:00
}
return hullset ;
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
// Setup the force direction
force _direction : function ( grav , dist , ch ) {
2012-09-24 20:34:33 +00:00
var self = this ;
2014-12-03 15:00:47 +00:00
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 ) {
2015-11-12 14:39:59 -08:00
return dist * d . source . data . networks . length + ( 10 * d . target . data . instances ) + 20 ;
2014-12-03 15:00:47 +00:00
}
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 ) {
2015-11-12 14:39:59 -08:00
return dist * d . target . data . networks . length + ( 10 * d . source . data . instances ) + 20 ;
2014-12-03 15:00:47 +00:00
}
return dist * d . source . data . networks . length + 20 ;
2013-08-12 17:20:38 -07:00
}
2014-12-03 15:00:47 +00:00
} 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 ) ;
2013-08-12 17:20:38 -07:00
} ) ;
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
// Create a new node
new _node : function ( data , x , y ) {
2012-09-24 20:34:33 +00:00
var self = this ;
2014-12-03 15:00:47 +00:00
data = { data : data } ;
if ( x && y ) {
data . x = x ;
data . y = y ;
2013-08-12 17:20:38 -07:00
}
2014-12-03 15:00:47 +00:00
self . nodes . push ( data ) ;
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ) ;
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ;
2013-08-12 17:20:38 -07:00
}
2013-11-26 15:36:35 +01:00
} )
2014-12-03 15:00:47 +00:00
. style ( 'fill' , 'white' )
. style ( 'stroke' , 'black' )
. style ( 'stroke-width' , 3 ) ;
2016-07-22 15:22:22 -06:00
switch ( data . data . iconType ) {
2014-12-03 15:00:47 +00:00
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 ;
2014-12-11 17:09:09 +03:00
}
2014-12-03 15:00:47 +00:00
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' )
2014-12-11 17:09:09 +03:00
. text ( function ( d ) {
2014-12-03 15:00:47 +00:00
return d . data . name ;
2014-12-11 17:09:09 +03:00
} )
2014-12-03 15:00:47 +00:00
. 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)' ;
}
2014-12-11 17:09:09 +03:00
} ) ;
2014-12-03 15:00:47 +00:00
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)' ) ;
}
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
nodeEnter . on ( 'click' , function ( d ) {
self . show _balloon ( d . data , d , angular . element ( this ) ) ;
} ) ;
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
// 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' ) ;
} ) ;
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
// Remove the highlight on the links
nodeEnter . on ( 'mouseout' , function ( ) {
self . vis . selectAll ( 'line.link' ) . style ( 'stroke-width' , '1px' ) ;
} ) ;
} ,
2013-12-27 14:48:59 +01:00
2014-12-03 15:00:47 +00:00
collapse _network : function ( d , only _collapse ) {
var self = this ;
var server , vm ;
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ] ) ;
}
}
2013-08-12 17:20:38 -07:00
}
2014-12-03 15:00:47 +00:00
}
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 ( ) ;
}
}
}
2013-08-12 17:20:38 -07:00
}
}
2014-12-03 15:00:47 +00:00
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 ++ ;
2013-08-12 17:20:38 -07:00
}
2014-12-03 15:00:47 +00:00
}
} ,
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ) ;
} ,
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ;
} ,
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ;
} ,
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ;
} ;
} ;
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
// 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 ) ;
}
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
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 ;
}
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
// 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 ;
2013-12-27 14:48:59 +01:00
}
2014-12-03 15:00:47 +00:00
}
self . data . routers [ router . id ] = router ;
}
2013-08-12 17:20:38 -07:00
2014-12-03 15:00:47 +00:00
// 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 ;
}
2013-12-27 14:48:59 +01:00
}
2014-12-03 15:00:47 +00:00
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 ) {
2016-02-20 13:07:47 +01:00
if ( ! listContains ( port . fixed _ips [ ip ] , device . data . ip _addresses ) ) {
device . data . ip _addresses . push ( port . fixed _ips [ ip ] ) ;
}
2014-12-03 15:00:47 +00:00
}
}
// 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 ] ) ;
2015-09-17 13:54:54 -07:00
vmCount = Number ( self . vis . selectAll ( '.vmCount' ) . filter ( filterNode ( _network . data ) ) [ 0 ] [ 0 ] . textContent ) ;
2014-12-03 15:00:47 +00:00
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
2015-09-17 13:54:54 -07:00
vmCount = Number ( self . vis . selectAll ( '.vmCount' ) . filter ( filterNode ( server . networks [ 0 ] ) ) [ 0 ] [ 0 ] . textContent ) ;
2014-12-03 15:00:47 +00:00
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
2015-09-17 13:54:54 -07:00
self . new _link ( self . find _by _id ( port . device _id ) , self . find _by _id ( port . network _id ) ) ;
2014-12-03 15:00:47 +00:00
change = true ;
}
}
}
self . data . ports [ port . id + port . device _id + port . network _id ] = port ;
}
if ( change ) {
self . force . start ( ) ;
}
self . load _config ( ) ;
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
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 ;
2012-09-24 20:34:33 +00:00
}
2014-12-03 15:00:47 +00:00
}
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 ) ;
}
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
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 ( ) ;
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
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 ;
2013-08-12 17:20:38 -07:00
break ;
}
}
2014-12-03 15:00:47 +00:00
if ( index !== - 1 ) {
this . links . splice ( index , 1 ) ;
}
return this . vis . selectAll ( 'line.link' ) . data ( this . links ) . exit ( ) . remove ( ) ;
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
2016-09-07 13:14:42 +08:00
delete _device : function ( device _type , deviceId ) {
2014-12-03 15:00:47 +00:00
var message = { id : deviceId } ;
2016-09-07 13:14:42 +08:00
var target = device _type === 'instance' ? 'instance?id=' + deviceId : device _type ;
horizon . networktopologymessager . post _message ( deviceId , target , message , device _type , 'delete' , data = { } ) ;
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
2016-02-19 14:15:20 -07:00
remove _node _on _delete : function ( deleteData ) {
2012-09-24 20:34:33 +00:00
var self = this ;
2016-02-19 14:15:20 -07:00
var deviceId = deleteData . device _id ;
switch ( deleteData . device _type ) {
2014-12-03 15:00:47 +00:00
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 ;
2016-02-19 14:15:20 -07:00
case 'port' :
self . removePort ( deviceId , deleteData . device _data ) ;
break ;
2014-12-03 15:00:47 +00:00
}
self . delete _balloon ( ) ;
2013-08-12 17:20:38 -07:00
} ,
2014-12-03 15:00:47 +00:00
2016-02-19 14:15:20 -07:00
removePort : function ( portId , deviceData ) {
2013-08-12 17:20:38 -07:00
var self = this ;
2016-02-19 14:15:20 -07:00
var routerId = deviceData . router _id ;
var networkId = deviceData . network _id ;
2014-12-03 15:00:47 +00:00
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 ;
}
}
}
}
2016-02-19 14:15:20 -07:00
}
} ,
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 ) ;
2014-12-03 15:00:47 +00:00
} else {
2016-02-19 14:15:20 -07:00
horizon . networktopologymessager . post _message ( portId , 'network/' + networkId + '/' , message , 'port' , 'delete' , data ) ;
2013-08-12 17:20:38 -07:00
}
2014-12-03 15:00:47 +00:00
} ,
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 ;
2013-08-12 17:20:38 -07:00
var ports = [ ] ;
2014-12-03 15:00:47 +00:00
var subnets = [ ] ;
if ( self . balloonID ) {
if ( self . balloonID == balloonID ) {
self . delete _balloon ( ) ;
return ;
2013-08-12 17:20:38 -07:00
}
2014-12-03 15:00:47 +00:00
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 ;
2016-01-15 12:29:52 +01:00
object . port _status _css = ( port . original _status === 'ACTIVE' ) ? 'active' : 'down' ;
2014-12-03 15:00:47 +00:00
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 ;
2016-02-22 10:52:20 +01:00
object . is _interface = ( deviceOwner === 'router_interface' || deviceOwner === 'router_gateway' ) ;
2014-12-03 15:00:47 +00:00
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 ,
2013-08-12 17:20:38 -07:00
id : d . id ,
url : d . url ,
name : d . name ,
type : d . type ,
2014-12-03 15:00:47 +00:00
delete _label : gettext ( 'Delete' ) ,
2013-08-12 17:20:38 -07:00
status : d . status ,
2016-01-15 12:29:52 +01:00
status _class : ( d . original _status === 'ACTIVE' ) ? 'active' : 'down' ,
2014-12-03 15:00:47 +00:00
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' )
2013-08-12 17:20:38 -07:00
} ;
2014-12-03 15:00:47 +00:00
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 ) {
2015-10-06 20:24:22 +09:00
htmlData . delete _label = gettext ( 'Delete Instance' ) ;
2014-12-03 15:00:47 +00:00
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
2013-08-12 17:20:38 -07:00
} ) ;
2014-12-03 15:00:47 +00:00
} 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
2012-09-24 20:34:33 +00:00
} ) ;
2013-08-12 17:20:38 -07:00
} else {
return ;
2013-03-11 11:14:30 -07:00
}
2014-12-03 15:00:47 +00:00
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
2013-08-12 17:20:38 -07:00
. css ( {
'left' : 0 + 'px'
} )
. css ( {
2014-12-03 15:00:47 +00:00
'left' : ( x - _balloon . outerWidth ( ) + 'px' )
2013-08-12 17:20:38 -07:00
} )
. addClass ( 'leftPosition' ) ;
}
2014-12-03 15:00:47 +00:00
_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' ) ) ;
2013-08-12 17:20:38 -07:00
} ) ;
2014-12-03 15:00:47 +00:00
_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 ( ) ;
2013-08-12 17:20:38 -07:00
} ) ;
2014-12-03 15:00:47 +00:00
self . balloonID = balloonID ;
2012-09-24 20:34:33 +00:00
} ,
2014-12-03 15:00:47 +00:00
2013-08-12 17:20:38 -07:00
delete _balloon : function ( ) {
2012-09-24 20:34:33 +00:00
var self = this ;
2014-12-03 15:00:47 +00:00
if ( self . balloonID ) {
angular . element ( '#' + self . balloonID ) . remove ( ) ;
self . balloonID = null ;
self . force . start ( ) ;
2012-09-24 20:34:33 +00:00
}
} ,
2014-12-03 15:00:47 +00:00
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 '' ;
}
2012-09-24 20:34:33 +00:00
}
2013-08-12 17:20:38 -07:00
} ;