~ubuntu-branches/ubuntu/trusty/horizon/trusty-updates

« back to all changes in this revision

Viewing changes to horizon/static/horizon/js/horizon.networktopology.js

  • Committer: Package Import Robot
  • Author(s): Adam Gandelman
  • Date: 2013-09-06 11:59:43 UTC
  • mfrom: (1.1.30)
  • Revision ID: package-import@ubuntu.com-20130906115943-h3td0l7tp16mb9oc
Tags: 1:2013.2~b3-0ubuntu1
* New upstream release.
* debian/control: Minimum python-openstack-auth version >= 1.1.1.
* debian/control: Add python-troveclient.
* debian/static: Refresh static assets for 2013.2~b3.
* debian/patches: ubuntu_local_settings.patch -> ubuntu_settings.patch, also
  patch location of secret key in openstack_dashboard/settings.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* Namespace for core functionality related to Network Topology. */
 
2
 
2
3
horizon.network_topology = {
3
4
  model: null,
4
 
  network_margin: 270,
5
 
  topologyCanvas_padding: 120,
6
 
  min_network_height:500,
7
 
  port_margin: 20,
8
 
  device_initial_position : 40,
9
 
  device_last_position : 0,
10
 
  device_left_position : 90,
11
 
  device_margin : 20,
12
 
  device_min_height : 45,
13
 
  port_initial_position: 1,
 
5
  svg:'#topology_canvas',
 
6
  svg_container:'#topologyCanvasContainer',
 
7
  post_messages:'#topologyMessages',
 
8
  network_tmpl:{
 
9
    small:'#topology_template > .network_container_small',
 
10
    normal:'#topology_template > .network_container_normal'
 
11
  },
 
12
  router_tmpl: {
 
13
    small:'#topology_template > .router_small',
 
14
    normal:'#topology_template > .router_normal'
 
15
  },
 
16
  instance_tmpl: {
 
17
    small:'#topology_template > .instance_small',
 
18
    normal:'#topology_template > .instance_normal'
 
19
  },
 
20
  balloon_tmpl : null,
 
21
  balloon_device_tmpl : null,
 
22
  balloon_port_tmpl : null,
14
23
  network_index: {},
15
 
  network_color_unit: 0,
16
 
  network_saturation: 1,
17
 
  network_lightness: 0.7,
 
24
  balloon_id:null,
18
25
  reload_duration: 10000,
19
 
  spinner:null,
20
 
  init:function(){
 
26
  draw_mode:'normal',
 
27
  network_height : 0,
 
28
  previous_message : null,
 
29
  element_properties:{
 
30
    normal:{
 
31
      network_width:270,
 
32
      network_min_height:500,
 
33
      top_margin:80,
 
34
      default_height:50,
 
35
      margin:20,
 
36
      device_x:98.5,
 
37
      device_width:90,
 
38
      port_margin:16,
 
39
      port_height:6,
 
40
      port_width:82,
 
41
      port_text_margin:{x:6,y:-4},
 
42
      texts_bg_y:32,
 
43
      type_y:46,
 
44
      balloon_margin:{x:12,y:-12}
 
45
    },
 
46
    small :{
 
47
      network_width:100,
 
48
      network_min_height:400,
 
49
      top_margin:50,
 
50
      default_height:20,
 
51
      margin:30,
 
52
      device_x:47.5,
 
53
      device_width:20,
 
54
      port_margin:5,
 
55
      port_height:3,
 
56
      port_width:32.5,
 
57
      port_text_margin:{x:0,y:0},
 
58
      texts_bg_y:0,
 
59
      type_y:0,
 
60
      balloon_margin:{x:12,y:-30}
 
61
    },
 
62
    cidr_margin:5,
 
63
    device_name_max_size:9,
 
64
    device_name_suffix:'..'
 
65
  },
 
66
  init:function() {
21
67
    var self = this;
22
 
    $("#topologyCanvas").spin(horizon.conf.spinner_options.modal);
23
 
    self.retrieve_network_info();
 
68
    $(self.svg_container).spin(horizon.conf.spinner_options.modal);
 
69
    if($('#networktopology').length === 0) {
 
70
      return;
 
71
    }
 
72
    self.color = d3.scale.category10();
 
73
    self.balloon_tmpl = Hogan.compile($('#balloon_container').html());
 
74
    self.balloon_device_tmpl = Hogan.compile($('#balloon_device').html());
 
75
    self.balloon_port_tmpl = Hogan.compile($('#balloon_port').html());
 
76
 
 
77
    $(document)
 
78
      .on('click', 'a.closeTopologyBalloon', function(e) {
 
79
        e.preventDefault();
 
80
        self.delete_balloon();
 
81
      })
 
82
      .on('click', '.topologyBalloon', function(e) {
 
83
        e.stopPropagation();
 
84
      })
 
85
      .on('click', 'a.vnc_window', function(e) {
 
86
        e.preventDefault();
 
87
        var vnc_window = window.open($(this).attr('href'), vnc_window, 'width=760,height=560');
 
88
        self.delete_balloon();
 
89
      })
 
90
      .click(function(){
 
91
        self.delete_balloon();
 
92
      });
 
93
 
 
94
    $('.toggleView > .btn').click(function(){
 
95
      self.draw_mode = $(this).data('value');
 
96
      $('g.network').remove();
 
97
      $.cookie('ntp_draw_mode',self.draw_mode);
 
98
      self.data_convert();
 
99
    });
 
100
 
 
101
    $(window)
 
102
      .on('message',function(e){
 
103
        var message = JSON.parse(e.originalEvent.data);
 
104
        if (self.previous_message != message.message) {
 
105
          horizon.alert(message.type, message.message);
 
106
          horizon.autoDismissAlerts();
 
107
          self.previous_message = message.message;
 
108
          self.delete_post_message(message.iframe_id);
 
109
          self.load_network_info();
 
110
          setTimeout(function() {
 
111
            self.previous_message = null;
 
112
          },10000);
 
113
        }
 
114
      });
 
115
 
 
116
    self.load_network_info();
24
117
    setInterval(function(){
25
 
      self.retrieve_network_info();
 
118
      self.load_network_info();
26
119
    }, self.reload_duration);
27
120
  },
28
 
  retrieve_network_info: function(){
 
121
  load_network_info:function(){
29
122
    var self = this;
30
 
    if($("#networktopology").length === 0) {
31
 
        return;
 
123
    if($('#networktopology').length === 0) {
 
124
      return;
32
125
    }
33
 
    $.getJSON($("#networktopology").data("networktopology"),
 
126
    $.getJSON($('#networktopology').data('networktopology') + '?' + $.now(),
34
127
      function(data) {
35
 
        self.draw_graph(data);
 
128
        self.model = data;
 
129
        self.data_convert();
36
130
      }
37
131
    );
38
132
  },
39
 
  draw_loading: function () {
40
 
    $("#topologyCanvas").spin(horizon.conf.spinner_options.modal);
41
 
  },
42
 
  draw_graph: function(data){
43
 
    var canvas = $("#topologyCanvas");
44
 
    var networks = $("#topologyCanvas > .networks");
45
 
    var nodata = $("#topologyCanvas > .nodata");
46
 
    networks.show();
47
 
    nodata.hide();
48
 
    canvas.spin(false);
49
 
    networks.empty();
50
 
    this.model = data;
51
 
    this.device_last_position = this.device_initial_position;
52
 
    var network_elements = this.draw_networks();
53
 
    var router_elements = this.draw_routers();
54
 
    var server_elements = this.draw_servers();
55
 
    if ((network_elements + router_elements + server_elements) <= 0){
56
 
      networks.hide();
57
 
      nodata.show();
 
133
  select_draw_mode:function() {
 
134
    var self = this;
 
135
    var draw_mode = $.cookie('ntp_draw_mode');
 
136
    if (draw_mode && (draw_mode == 'normal'| draw_mode ==  'small')) {
 
137
      self.draw_mode = draw_mode;
58
138
    } else {
59
 
      canvas.height(
60
 
        Math.max(this.device_last_position + this.topologyCanvas_padding, this.min_network_height)
61
 
      );
62
 
      networks.width(
63
 
        this.model.networks.length * this.network_margin
64
 
      );
65
 
    }
66
 
  },
67
 
  network_color: function(network_id){
68
 
    var max_hue = 360;
69
 
    var num_network = this.model.networks.length;
70
 
    if(num_network <= 0){
71
 
      return;
72
 
    }
73
 
    num_network ++;
74
 
    var hue = Math.floor(
75
 
      max_hue/num_network*(this.network_index(network_id) + 1));
76
 
    return this.hsv2rgb(
77
 
      hue, this.network_saturation, this.network_lightness);
78
 
  },
79
 
  //see http://en.wikipedia.org/wiki/HSL_and_HSV
80
 
  hsv2rgb:function (h, s, v) {
81
 
      var hi = Math.round(h/60) % 6;
82
 
      var f = h/60 - hi;
83
 
      var p = v*(1 - s);
84
 
      var q = v*(1 - f*s);
85
 
      var t = v*(1 - (1 - f)*s);
86
 
      switch(hi){
87
 
        case 0:
88
 
          r = v;
89
 
          g = t;
90
 
          b = p;
91
 
          break;
92
 
        case 1:
93
 
          r = q;
94
 
          g = v;
95
 
          b = p;
96
 
          break;
97
 
        case 2:
98
 
          r = p;
99
 
          g = v;
100
 
          b = t;
101
 
          break;
102
 
        case 3:
103
 
          r = p;
104
 
          g = q;
105
 
          b = v;
106
 
          break;
107
 
        case 4:
108
 
          r = t;
109
 
          g = p;
110
 
          b = v;
111
 
          break;
112
 
        case 5:
113
 
          r = v;
114
 
          g = p;
115
 
          b = q;
116
 
          break;
117
 
      }
118
 
      return "rgb(" + Math.round(r*255) + "," + Math.round(g*255) + "," + Math.round(b*255) + ")";
119
 
  },
120
 
  draw_networks: function(){
 
139
      if (self.model.networks.length * 
 
140
        self.element_properties.normal.network_width >  $('#topologyCanvas').width()) {
 
141
        self.draw_mode = 'small';
 
142
      } else {
 
143
        self.draw_mode = 'normal';
 
144
      }
 
145
      $.cookie('ntp_draw_mode',self.draw_mode);
 
146
    }
 
147
   $('.toggleView > .btn').each(function(){
 
148
      var $this = $(this);
 
149
      if($this.hasClass(self.draw_mode)) {
 
150
        $this.addClass('active');
 
151
      }
 
152
    });
 
153
  },
 
154
  data_convert:function() {
121
155
    var self = this;
122
 
    var networks = $("#topologyCanvas > .networks");
123
 
    $.each(self.model.networks, function(index, network){
124
 
      var label = (network.name != "")? network.name : network.id;
125
 
      if(network['router:external']){
126
 
         label += " (external) ";
127
 
      }
 
156
    var model = self.model;
 
157
    $.each(model.networks, function(index, network) {
128
158
      self.network_index[network.id] = index;
129
 
      var network_html = $("<div class='network' />").attr("id", network.id);
130
 
      var nicname_html = $("<div class='nicname'><h3>" + label +
131
 
        "</h3><span class='ip'>" + self.select_cidr(network.id) + "</span></div>");
132
 
      if (network.url == undefined) {
133
 
        nicname_html.addClass("nourl");
 
159
    });
 
160
    self.select_draw_mode();
 
161
    var element_properties = self.element_properties[self.draw_mode];
 
162
    self.network_height = element_properties.top_margin;
 
163
    $.each([
 
164
      {model:model.routers, type:'router'},
 
165
      {model:model.servers, type:'instance'}
 
166
      ], function(index, devices) {
 
167
      var type = devices.type;
 
168
      var model = devices.model;
 
169
      $.each(model, function(index, device) {
 
170
        device.type = type;
 
171
        device.ports = self.select_port(device.id);
 
172
        var hasports = (device.ports.length <= 0) ? false : true;
 
173
        device.parent_network = (hasports) ?
 
174
          self.select_main_port(device.ports).network_id : self.model.networks[0].id;
 
175
        var height = element_properties.port_margin*(device.ports.length - 1);
 
176
        device.height = 
 
177
          (self.draw_mode == 'normal' && height > element_properties.default_height) ? height :
 
178
          element_properties.default_height;
 
179
        device.pos_y = self.network_height;
 
180
        device.port_height = 
 
181
          (self.draw_mode == 'small' && height > device.height) ? 1 :
 
182
          element_properties.port_height;
 
183
        device.port_margin = 
 
184
          (self.draw_mode == 'small' && height > device.height) ?
 
185
          device.height/device.ports.length :
 
186
          element_properties.port_margin;
 
187
        self.network_height += device.height + element_properties.margin;
 
188
      });
 
189
    });
 
190
    $.each(model.networks, function(index, network) {
 
191
      network.devices = [];
 
192
      $.each([model.routers, model.servers],function(index, devices) {
 
193
        $.each(devices,function(index, device) {
 
194
          if(network.id == device.parent_network) {
 
195
            network.devices.push(device);
 
196
          }
 
197
        });
 
198
      });
 
199
    });
 
200
    self.network_height += element_properties.top_margin;
 
201
    self.network_height = (self.network_height > element_properties.network_min_height) ? 
 
202
      self.network_height : element_properties.network_min_height;
 
203
    self.draw_topology();
 
204
  },
 
205
  draw_topology:function() {
 
206
    var self = this;
 
207
    $(self.svg_container).spin(false);
 
208
    $(self.svg_container).removeClass('noinfo');
 
209
    if (self.model.networks.length <= 0) {
 
210
      $('g.network').remove();
 
211
      $(self.svg_container).addClass('noinfo');
 
212
      return;
 
213
    }
 
214
    var svg = d3.select(self.svg);
 
215
    var element_properties = self.element_properties[self.draw_mode];
 
216
    svg
 
217
      .attr('width',self.model.networks.length*element_properties.network_width)
 
218
      .attr('height',self.network_height);
 
219
 
 
220
    var network = svg.selectAll('g.network')
 
221
      .data(self.model.networks);
 
222
 
 
223
    var network_enter = network.enter()
 
224
      .append('g')
 
225
      .attr('class','network')
 
226
      .each(function(d,i){
 
227
        this.appendChild(d3.select(self.network_tmpl[self.draw_mode]).node().cloneNode(true));
 
228
        var $this = d3.select(this).select('.network-rect');
 
229
        if (d.url) {
 
230
          var $this = d3.select(this).select('.network-rect');
 
231
          $this
 
232
          .on('mouseover',function(){
 
233
            $this.transition().style('fill',
 
234
              function() { return d3.rgb(self.network_color(d.id)).brighter(0.5)});
 
235
          })
 
236
          .on('mouseout',function(){
 
237
            $this.transition().style('fill',
 
238
              function() { return self.network_color(d.id)});
 
239
          })
 
240
          .on('click',function(){
 
241
            window.location.href = d.url;
 
242
          });
 
243
        } else {
 
244
          $this.classed('nourl', true);
 
245
        }
 
246
      });
 
247
 
 
248
    network
 
249
      .attr('id',function(d) { return 'id_' + d.id; })
 
250
      .attr('transform',function(d,i){ 
 
251
        return 'translate(' + element_properties.network_width * i + ',' + 0 + ')'})
 
252
      .select('.network-rect')
 
253
      .attr('height', function(d) { return self.network_height})
 
254
      .style('fill', function(d) { return self.network_color(d.id)});
 
255
    network
 
256
      .select('.network-name')
 
257
      .attr('x', function(d) { return self.network_height/2 })
 
258
      .text(function(d) { return d.name; });
 
259
    network
 
260
      .select('.network-cidr')
 
261
      .attr('x', function(d) { return self.network_height - self.element_properties.cidr_margin })
 
262
      .text(function(d) {
 
263
        var cidr = $.map(d.subnets,function(n, i){
 
264
          return n.cidr;
 
265
        });
 
266
        return cidr.join(', ');
 
267
       });
 
268
 
 
269
    network.exit().remove();
 
270
 
 
271
    var device = network.selectAll('g.device')
 
272
        .data(function(d) { return d.devices; });
 
273
 
 
274
    var device_enter = device.enter()
 
275
      .append("g")
 
276
      .attr('class','device')
 
277
      .each(function(d,i){
 
278
        var device_template = self[d.type + '_tmpl'][self.draw_mode];
 
279
        this.appendChild(d3.select(device_template).node().cloneNode(true));
 
280
      });
 
281
    
 
282
    device_enter.on('mouseenter',function(d){
 
283
      var $this = $(this);
 
284
      self.show_balloon(d,$this);
 
285
    })
 
286
    .on('click',function(){
 
287
      d3.event.stopPropagation();
 
288
    });
 
289
 
 
290
    device
 
291
      .attr('id',function(d) { return 'id_' + d.id; })
 
292
      .attr('transform',function(d,i){
 
293
        return 'translate(' + element_properties.device_x + ',' + d.pos_y  + ')';
 
294
      })
 
295
      .select('.frame')
 
296
      .attr('height',function(d) { return d.height; });
 
297
    device
 
298
      .select('.texts_bg')
 
299
      .attr('y',function(d) {
 
300
        return element_properties.texts_bg_y + d.height - element_properties.default_height;
 
301
      });
 
302
    device
 
303
      .select('.type')
 
304
      .attr('y',function(d) {
 
305
        return element_properties.type_y + d.height - element_properties.default_height;
 
306
      });
 
307
    device
 
308
      .select('.name')
 
309
      .text(function(d) { return self.string_truncate(d.name); });
 
310
    device.each(function(d) {
 
311
      if (d.status == 'BUILD') {
 
312
        d3.select(this).classed('loading',true);
 
313
      } else if (d.task == 'deleting') {
 
314
        d3.select(this).classed('loading',true);
 
315
        if ('bl_' + d.id == self.balloon_id) {
 
316
          self.delete_balloon();
 
317
        }
134
318
      } else {
135
 
        nicname_html.click(function (){
136
 
          window.location.href = network.url;
137
 
        });
138
 
      }
139
 
      nicname_html
140
 
        .css (
141
 
          {'background-color':self.network_color(network.id)})
142
 
        .appendTo(network_html);
143
 
      networks.append(network_html);
144
 
    });
145
 
    return self.model.networks.length;
146
 
  },
147
 
  select_cidr:function(network_id){
148
 
    var cidr = [];
149
 
    $.each(this.model.subnets, function(index, subnet){
150
 
        if(subnet.network_id != network_id){
151
 
            return;
 
319
        d3.select(this).classed('loading',false);
 
320
        if ('bl_' + d.id == self.balloon_id) {
 
321
          var $this = $(this);
 
322
          self.show_balloon(d,$this);
152
323
        }
153
 
        cidr.push(subnet.cidr);
154
 
    });
155
 
    return cidr.join(', ');
156
 
  },
157
 
  draw_devices: function(type){
158
 
    var self = this;
159
 
    $.each(self.model[type + 's'], function(index, device){
160
 
      var id = device.id;
161
 
      var name = (device.name != "")? device.name : device.id;
162
 
      var ports = self.select_port(id);
163
 
      if(ports.length <= 0){
164
 
          return;
165
 
      }
166
 
      var main_port = self.select_main_port(ports);
167
 
      var parent_network = main_port.network_id;
168
 
      var device_html = $("<div class='" + type + "'></div>");
169
 
      device_html
170
 
        .attr('id', device.id)
171
 
        .css({top: self.device_last_position, position: 'absolute'})
172
 
        .append($("<span class='devicename'><i></i>" + type + "</span>"))
173
 
        .click(function (e){
174
 
          e.stopPropagation();
175
 
          window.location.href = device.url;
 
324
      }
 
325
    });
 
326
 
 
327
    device.exit().each(function(d){
 
328
      if ('bl_' + d.id == self.balloon_id) {
 
329
        self.delete_balloon();
 
330
      }
 
331
    }).remove();
 
332
 
 
333
    var port = device.select('g.ports')
 
334
      .selectAll('g.port')
 
335
      .data(function(d) { return d.ports; });
 
336
 
 
337
    var port_enter = port.enter()
 
338
      .append('g')
 
339
      .attr('class','port')
 
340
      .attr('id',function(d) { return 'id_' + d.id; });
 
341
 
 
342
    port_enter
 
343
      .append('line')
 
344
      .attr('class','port_line');
 
345
 
 
346
    port_enter
 
347
      .append('text')
 
348
      .attr('class','port_text');
 
349
 
 
350
    device.select('g.ports').each(function(d,i){
 
351
      this._portdata = {};
 
352
      this._portdata.ports_length = d.ports.length;
 
353
      this._portdata.parent_network = d.parent_network;
 
354
      this._portdata.device_height = d.height;
 
355
      this._portdata.port_height = d.port_height;
 
356
      this._portdata.port_margin = d.port_margin;
 
357
      this._portdata.left = 0;
 
358
      this._portdata.right = 0;
 
359
      $(this).mouseenter(function(e){
 
360
        e.stopPropagation();
 
361
      });
 
362
    });
 
363
 
 
364
    port.each(function(d,i){
 
365
      var index_diff = self.network_index(this.parentNode._portdata.parent_network) -
 
366
        self.network_index(d.network_id);
 
367
      this._index_diff = index_diff = (index_diff >= 0)? ++index_diff : index_diff;
 
368
      this._direction = (this._index_diff < 0)? 'right' : 'left';
 
369
      this._index  = this.parentNode._portdata[this._direction] ++;
 
370
 
 
371
    });
 
372
 
 
373
    port.attr('transform',function(d,i){
 
374
      var x = (this._direction == 'left') ? 0 : element_properties.device_width;
 
375
      var ports_length = this.parentNode._portdata[this._direction];
 
376
      var distance = this.parentNode._portdata.port_margin;
 
377
      var y = (this.parentNode._portdata.device_height -
 
378
        (ports_length -1)*distance)/2 + this._index*distance;
 
379
      return 'translate(' + x + ',' + y + ')';
 
380
    });
 
381
 
 
382
    port
 
383
      .select('.port_line')
 
384
      .attr('stroke-width',function(d,i) {
 
385
        return this.parentNode.parentNode._portdata.port_height;
 
386
      })
 
387
      .attr('stroke',function(d,i) {return self.network_color(d.network_id)})
 
388
      .attr('x1',0).attr('y1',0).attr('y2',0)
 
389
      .attr('x2',function(d,i) {
 
390
        var parent = this.parentNode;
 
391
        var width = (Math.abs(parent._index_diff) - 1)*element_properties.network_width +
 
392
        element_properties.port_width;
 
393
        return (parent._direction == 'left') ? -1*width : width;
 
394
      });
 
395
 
 
396
      port
 
397
        .select('.port_text')
 
398
        .attr('x',function(d) {
 
399
          var parent = this.parentNode;
 
400
          if (parent._direction == 'left') {
 
401
            d3.select(this).classed('left',true);
 
402
            return element_properties.port_text_margin.x*-1;
 
403
          } else {
 
404
            d3.select(this).classed('left',false);
 
405
            return element_properties.port_text_margin.x;
 
406
          }
 
407
        })
 
408
        .attr('y',function(d) { return element_properties.port_text_margin.y })
 
409
        .text(function(d) {
 
410
          var ip_label = [];
 
411
          $.each(d.fixed_ips, function() {
 
412
            ip_label.push(this.ip_address);
 
413
          });
 
414
          return ip_label.join(',');
176
415
        });
177
 
      var name_html = $("<span class='name'></span>")
178
 
        .html(device.name)
179
 
        .attr('title', device.name)
180
 
        .appendTo(device_html);
181
 
      var port_position = self.port_initial_position;
182
 
      $.each(ports, function(){
183
 
          var port = this;
184
 
          var port_html = self.port_html(port);
185
 
          port_position += self.port_margin;
186
 
          self.port_css(port_html, port_position, parent_network, port.network_id);
187
 
          device_html.append(port_html);
188
 
      });
189
 
      port_position += self.port_margin;
190
 
      device_html.css(
191
 
        {height: Math.max(self.device_min_height, port_position) + "px"});
192
 
      self.device_last_position += device_html.height() + self.device_margin;
193
 
      $("#" + parent_network).append(device_html);
194
 
      $('div.port span.ip').each(function(i, ip){
195
 
          $(ip).css('top', '-'+$(ip).height()+'px');
196
 
      });
197
 
    });
198
 
    return self.model[type + 's'].length;
199
 
  },
200
 
  sum_port_length: function(network_id, ports){
201
 
    var self = this;
202
 
    var sum_port_length = 0;
203
 
    var base_index = self.network_index(network_id);
204
 
    $.each(ports, function(index, port){
205
 
      sum_port_length += base_index - self.network_index(port.network_id);
206
 
    });
207
 
    return sum_port_length;
 
416
 
 
417
    port.exit().remove();
 
418
  },
 
419
  network_color: function(network_id) {
 
420
    return this.color(this.network_index(network_id));
 
421
  },
 
422
  network_index: function(network_id) {
 
423
    return this.network_index[network_id];
 
424
  },
 
425
  select_port: function(device_id){
 
426
   return $.map(this.model.ports,function(port, index){
 
427
    if (port.device_id == device_id) {
 
428
      return port;
 
429
    }
 
430
   });
208
431
  },
209
432
  select_main_port: function(ports){
 
433
    var _self = this;
210
434
    var main_port_index = 0;
211
435
    var MAX_INT = 4294967295;
212
436
    var min_port_length = MAX_INT;
213
437
    $.each(ports, function(index, port){
214
 
      port_length = horizon.network_topology.sum_port_length(port.network_id, ports)
 
438
      var port_length = _self.sum_port_length(port.network_id, ports);
215
439
      if(port_length < min_port_length){
216
440
        min_port_length = port_length;
217
441
        main_port_index = index;
218
442
      }
219
 
    })
 
443
    });
220
444
    return ports[main_port_index];
221
445
  },
222
 
  draw_routers: function(){
223
 
      return this.draw_devices('router');
224
 
  },
225
 
  draw_servers: function(){
226
 
      return this.draw_devices('server');
227
 
  },
228
 
  select_port: function(device_id){
229
 
     return $.map(this.model.ports,function(port, index){
230
 
        if (port.device_id == device_id) {
231
 
          return port;
232
 
        }
233
 
     });
234
 
  },
235
 
  port_html: function(port){
236
 
    var self = this;
237
 
    var port_html = $('<div class="port"><div class="dot"></div></div>');
238
 
    var ip_label = "";
239
 
    $.each(port.fixed_ips, function(){
240
 
      ip_label += this.ip_address + "<br />";
 
446
  sum_port_length: function(network_id, ports){
 
447
    var self = this;
 
448
    var sum_port_length = 0;
 
449
    var base_index = self.network_index(network_id);
 
450
    $.each(ports, function(index, port){
 
451
      sum_port_length += base_index - self.network_index(port.network_id);
 
452
    });
 
453
    return sum_port_length;
 
454
  },
 
455
  string_truncate: function(string) {
 
456
    var self = this;
 
457
    var str = string;
 
458
    var max_size = self.element_properties.device_name_max_size;
 
459
    var suffix = self.element_properties.device_name_suffix;
 
460
    var bytes = 0;
 
461
    for (var i = 0;  i < str.length; i++) {
 
462
      bytes += str.charCodeAt(i) <= 255 ? 1 : 2;
 
463
      if (bytes > max_size) {
 
464
        str = str.substr(0, i) + suffix;
 
465
        break;
 
466
      }
 
467
    }
 
468
    return str;
 
469
  },
 
470
  delete_device: function(type, device_id) {
 
471
    var self = this;
 
472
    var message = {id:device_id};
 
473
    self.post_message(device_id,type,message);
 
474
  },
 
475
  delete_port: function(router_id, port_id) {
 
476
    var self = this;
 
477
    var message = {id:port_id};
 
478
    self.post_message(port_id, 'router/' + router_id + '/', message);
 
479
  },
 
480
  show_balloon:function(d,element) {
 
481
    var self = this;
 
482
    var element_properties = self.element_properties[self.draw_mode];
 
483
    if (self.balloon_id) {
 
484
      self.delete_balloon();
 
485
    }
 
486
    var balloon_tmpl = self.balloon_tmpl;
 
487
    var device_tmpl = self.balloon_device_tmpl;
 
488
    var port_tmpl = self.balloon_port_tmpl;
 
489
    var balloon_id = 'bl_' + d.id;
 
490
    var ports = [];
 
491
    $.each(d.ports,function(i, port){
 
492
      var object = {};
 
493
      object.id = port.id;
 
494
      object.router_id = port.device_id;
 
495
      object.url = port.url;
 
496
      object.port_status = port.status;
 
497
      object.port_status_css = (port.status == "ACTIVE")? 'active' : 'down';
 
498
      var ip_address = '';
 
499
      try {
 
500
        ip_address = port.fixed_ips[0].ip_address;
 
501
      }catch(e){
 
502
        ip_address = 'no info';
 
503
      }
 
504
      var device_owner = '';
 
505
      try {
 
506
        device_owner = port.device_owner.replace('network:','');
 
507
      }catch(e){
 
508
        device_owner = 'no info';
 
509
      }
 
510
      object.ip_address = ip_address;
 
511
      object.device_owner = device_owner;
 
512
      object.is_interface = (device_owner == 'router_interface') ? true : false;
 
513
      ports.push(object);
 
514
    });
 
515
    var html_data = {
 
516
      balloon_id:balloon_id,
 
517
      id:d.id,
 
518
      url:d.url,
 
519
      name:d.name,
 
520
      type:d.type,
 
521
      type_capital:d.type.replace(/^\w/, function($0) {return $0.toUpperCase()}),
 
522
      id:d.id,
 
523
      status:d.status,
 
524
      status_class:(d.status == "ACTIVE")? 'active' : 'down'
 
525
    };
 
526
    if (d.type == 'router') {
 
527
      html_data.port = ports;
 
528
      html = balloon_tmpl.render(html_data,{
 
529
        table1:device_tmpl,
 
530
        table2:port_tmpl
 
531
      });
 
532
    } else if (d.type == 'instance') {
 
533
      html_data.console_id = d.id;
 
534
      html = balloon_tmpl.render(html_data,{
 
535
        table1:device_tmpl
 
536
      });
 
537
    } else {
 
538
      return;
 
539
    }
 
540
    $(self.svg_container).append(html);
 
541
    var device_position = element.find('.frame');
 
542
    var x = device_position.position().left + 
 
543
      element_properties.device_width + 
 
544
      element_properties.balloon_margin.x;
 
545
    var y = device_position.position().top + 
 
546
      element_properties.balloon_margin.y;
 
547
    $('#' + balloon_id).css({
 
548
      'left': x + 'px',
 
549
      'top': y + 'px'
241
550
    })
242
 
    var ip_html = $('<span class="ip" />').html(ip_label);
243
 
    port_html
244
 
      .append(ip_html)
245
 
      .css({'background-color':self.network_color(port.network_id)})
246
 
      .click(function (e) {
247
 
        e.stopPropagation();
248
 
        if(port.url != undefined) {
249
 
          window.location.href = port.url;
250
 
        }
251
 
      });
252
 
    if(port.url == undefined) {
253
 
      port_html.addClass("nourl");
254
 
    }
255
 
    return port_html;
256
 
  },
257
 
  port_css: function(port_html, position, network_a, network_b){
258
 
    var self = this;
259
 
    var index_diff = self.network_index(network_a) - self.network_index(network_b);
260
 
    var width = self.network_margin * index_diff;
261
 
    var direction = "left";
262
 
    if(width < 0){
263
 
        direction = "right";
264
 
        width += self.network_margin;
265
 
    }
266
 
    width = Math.abs(width) + self.device_left_position;
267
 
    var port_css = {};
268
 
    port_css['width'] = width + "px";
269
 
    port_css['top'] = position + "px";
270
 
    port_css[direction] = (-width -3) + "px";
271
 
    port_html.addClass(direction).css(port_css);
272
 
  },
273
 
  network_index: function(network_id){
274
 
    return horizon.network_topology.network_index[network_id];
 
551
    .show();
 
552
    var $balloon = $('#' + balloon_id);
 
553
    if ($balloon.offset().left + $balloon.outerWidth() > $(window).outerWidth()) {
 
554
      $balloon
 
555
        .css({
 
556
          'left': 0 + 'px'
 
557
        })
 
558
        .css({
 
559
          'left': device_position.position().left
 
560
            - $balloon.outerWidth()
 
561
            - element_properties.balloon_margin.x + 'px'
 
562
        })
 
563
        .addClass('leftPosition');
 
564
    }
 
565
    $balloon.find('.delete-device').click(function(e){
 
566
      var $this = $(this);
 
567
      $this.addClass('deleting');
 
568
      d3.select('#id_' + $this.data('device-id')).classed('loading',true);
 
569
      self.delete_device($this.data('type'),$this.data('device-id'));
 
570
    });
 
571
    $balloon.find('.delete-port').click(function(e){
 
572
      var $this = $(this);
 
573
      self.delete_port($this.data('router-id'),$this.data('port-id'));
 
574
    });
 
575
    self.balloon_id = balloon_id;
 
576
  },
 
577
  delete_balloon:function() {
 
578
    var self = this;
 
579
    if(self.balloon_id) {
 
580
      $('#' + self.balloon_id).remove()
 
581
      self.balloon_id = null;
 
582
    }
 
583
  },
 
584
  post_message: function(id,url,message) {
 
585
    var self = this;
 
586
    var iframe_id = 'ifr_' + id;
 
587
    var iframe = $('<iframe width="500" height="300" />')
 
588
      .attr('id',iframe_id)
 
589
      .attr('src',url)
 
590
      .appendTo(self.post_messages);
 
591
    iframe.on('load',function() {
 
592
      $(this).get(0).contentWindow.postMessage(
 
593
      JSON.stringify(message, null, 2), '*');
 
594
    });
 
595
  },
 
596
  delete_post_message: function(id) {
 
597
    $('#' + id).remove();
275
598
  }
276
 
}
 
599
};
277
600
 
278
601
horizon.addInitFunction(function () {
279
 
    horizon.network_topology.init();
 
602
  horizon.network_topology.init();
280
603
});