Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_networks_view.js @ 213a8396

History | View | Annotate | Download (35.2 kB)

1
// Copyright 2013 GRNET S.A. All rights reserved.
2
// 
3
// Redistribution and use in source and binary forms, with or
4
// without modification, are permitted provided that the following
5
// conditions are met:
6
// 
7
//   1. Redistributions of source code must retain the above
8
//      copyright notice, this list of conditions and the following
9
//      disclaimer.
10
// 
11
//   2. Redistributions in binary form must reproduce the above
12
//      copyright notice, this list of conditions and the following
13
//      disclaimer in the documentation and/or other materials
14
//      provided with the distribution.
15
// 
16
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
// POSSIBILITY OF SUCH DAMAGE.
28
// 
29
// The views and conclusions contained in the software and
30
// documentation are those of the authors and should not be
31
// interpreted as representing official policies, either expressed
32
// or implied, of GRNET S.A.
33
// 
34

    
35
;(function(root){
36
    
37
    // root
38
    var root = root;
39
    
40
    // setup namepsaces
41
    var snf = root.synnefo = root.synnefo || {};
42
    var models = snf.models = snf.models || {}
43
    var storage = snf.storage = snf.storage || {};
44
    var ui = snf.ui = snf.ui || {};
45
    var util = snf.util || {};
46
    var views = snf.views = snf.views || {}
47

    
48
    // shortcuts
49
    var bb = root.Backbone;
50
    
51
    // logging
52
    var logger = new snf.logging.logger("SNF-VIEWS");
53
    var debug = _.bind(logger.debug, logger);
54
    
55
    views.NetworkCreateView = views.Overlay.extend({
56
        view_id: "network_create_view",
57
        content_selector: "#networks-create-content",
58
        css_class: 'overlay-networks-create overlay-info',
59
        overlay_id: "network-create-overlay",
60

    
61
        title: "Create new private network",
62
        subtitle: "Networks",
63

    
64
        initialize: function(options) {
65
            views.NetworkCreateView.__super__.initialize.apply(this);
66

    
67
            this.create_button = this.$("form .form-action.create");
68
            this.text = this.$(".network-create-name");
69
            this.form = this.$("form");
70

    
71
            this.dhcp_select = this.$("#network-create-dhcp");
72
            this.type_select = this.$("#network-create-type");
73
            this.subnet_select = this.$("#network-create-subnet");
74
            this.subnet_custom = this.$("#network-create-subnet-custom");
75
                
76
            this.dhcp_form = this.$("#network-create-dhcp-fields");
77
            
78
            this.subnet_select.find(".subnet").remove();
79
            _.each(synnefo.config.network_suggested_subnets, function(subnet){
80
                this.subnet_select.append($('<option value='+subnet+' class="subnet">'+subnet+'</option>'));
81
            }, this);
82

    
83
            this.type_select.find(".subnet").remove();
84
            _.each(synnefo.config.network_available_types, function(name, value){
85
                this.type_select.append($('<option value='+value+' class="subnet">'+name+'</option>'));
86
            }, this);
87
            
88
            if (_.keys(synnefo.config.network_available_types).length <= 1) {
89
                this.type_select.closest(".form-field").hide();
90
            }
91

    
92
            this.check_dhcp_form();
93
            this.init_handlers();
94
        },
95

    
96
        reset_dhcp_form: function() {
97
          this.subnet_select.find("option")[0].selected = 1;
98
          this.subnet_custom.val("");
99
        },
100

    
101
        check_dhcp_form: function() {
102
            if (this.dhcp_select.is(":checked")) {
103
                this.dhcp_form.show();
104
            } else {
105
                this.dhcp_form.hide();
106
            }
107
            
108
            if (this.subnet_select.val() == "custom") {
109
                this.subnet_custom.show();
110
            } else {
111
                this.subnet_custom.hide();
112
            }
113
        },
114

    
115
        init_handlers: function() {
116

    
117
            this.dhcp_select.click(_.bind(function(e){
118
                this.check_dhcp_form();
119
                this.reset_dhcp_form();
120
            }, this));
121

    
122
            this.subnet_select.change(_.bind(function(e){
123
                this.check_dhcp_form();
124
                if (this.subnet_custom.is(":visible")) {
125
                    this.subnet_custom.focus();
126
                }
127
            }, this));
128

    
129
            this.create_button.click(_.bind(function(e){
130
                this.submit();
131
            }, this));
132

    
133
            this.form.submit(_.bind(function(e){
134
                e.preventDefault();
135
                this.submit;
136
                return false;
137
            }, this))
138

    
139
            this.text.keypress(_.bind(function(e){
140
                if (e.which == 13) {this.submit()};
141
            },this))
142
        },
143

    
144
        submit: function() {
145
            if (this.validate()) {
146
                this.create();
147
            };
148
        },
149
        
150
        validate: function() {
151
            // sanitazie
152
            var t = this.text.val();
153
            t = t.replace(/^\s+|\s+$/g,"");
154
            this.text.val(t);
155

    
156
            if (this.text.val() == "") {
157
                this.text.closest(".form-field").addClass("error");
158
                this.text.focus();
159
                return false;
160
            } else {
161
                this.text.closest(".form-field").removeClass("error");
162
            }
163
            
164
            if (this.dhcp_select.is(":checked")) {
165
                if (this.subnet_select.val() == "custom") {
166
                    var sub = this.subnet_custom.val();
167
                    sub = sub.replace(/^\s+|\s+$/g,"");
168
                    this.subnet_custom.val(sub);
169
                        
170
                    if (!synnefo.util.IP_REGEX.exec(this.subnet_custom.val())) {
171
                        this.subnet_custom.closest(".form-field").prev().addClass("error");
172
                        return false;
173
                    } else {
174
                        this.subnet_custom.closest(".form-field").prev().removeClass("error");
175
                    }
176
                };
177
            }
178

    
179
            return true;
180
        },
181
        
182
        get_next_available_subnet: function() {
183
            var auto_tpl = synnefo.config.automatic_network_range_format;
184
            if (!auto_tpl) {
185
                return null
186
            }
187
            var index = 0;
188
            var subnet = auto_tpl.format(index);
189
            var networks = synnefo.storage.networks;
190
            var check_existing = function(n) { return n.get('cidr') == subnet }
191
            while (networks.filter(check_existing).length > 0 && index <= 255) {
192
                index++;
193
                subnet = auto_tpl.format(index); 
194
            }
195
            return subnet;
196
        },
197

    
198
        create: function() {
199
            this.create_button.addClass("in-progress");
200

    
201
            var name = this.text.val();
202
            var dhcp = this.dhcp_select.is(":checked");
203
            var subnet = null;
204
            var type = this.type_select.val();
205

    
206
            if (dhcp) {
207
                if (this.subnet_select.val() == "custom") {
208
                    subnet = this.subnet_custom.val();
209
                } else if (this.subnet_select.val() == "auto") {
210
                    subnet = this.get_next_available_subnet()
211
                } else {
212
                    subnet = this.subnet_select.val();
213
                }
214
                
215
            }
216

    
217
            snf.storage.networks.create(name, type, subnet, dhcp, _.bind(function(){
218
                this.hide();
219
            }, this));
220
        },
221

    
222
        beforeOpen: function() {
223
            this.create_button.removeClass("in-progress")
224
            this.text.closest(".form-field").removeClass("error");
225
            this.text.val("");
226
            this.text.show();
227
            this.text.focus();
228
            this.subnet_custom.val("");
229
            this.subnet_select.val("auto");
230
            this.dhcp_select.attr("checked", true);
231
            this.type_select.val(_.keys(synnefo.config.network_available_types)[0]);
232
            this.check_dhcp_form();
233
        },
234

    
235
        onOpen: function() {
236
            this.text.focus();
237
        }
238
    });
239

    
240
    views.NetworkPortView = views.ext.ModelView.extend({
241
      tpl: '#network-port-view-tpl',
242
      
243
      vm_logo_url: function(vm) {
244
        if (!this.model.get('vm')) { return '' }
245
        return synnefo.ui.helpers.vm_icon_path(this.model.get('vm'), 'medium');
246
      },
247
      
248
      set_confirm: function() {
249
        var parent = this.parent_view.parent_view.el;
250
        parent.addClass("subactionpending");
251
      },
252

    
253
      unset_confirm: function() {
254
        var parent = this.parent_view.parent_view.el;
255
        parent.removeClass("subactionpending");
256
      },
257

    
258
      post_init_element: function() {
259
        this.in_progress = false;
260
        this.firewall = this.$(".firewall-content").hide();
261
        this.firewall_toggler = this.$(".firewall-toggle");
262
        this.firewall_apply = this.$(".firewall-apply");
263
        this.firewall_legends = this.firewall.find(".checkbox-legends");
264
        this.firewall_inputs = this.firewall.find("input");
265
        this.firewall_apply = this.firewall.find("button");
266
        this.firewall_visible = false;
267

    
268
        this.firewall_toggler.click(_.bind(function() {
269
          this.toggle_firewall();
270
        }, this));
271

    
272
        this.firewall.find(".checkbox-legends, input").click(
273
          _.bind(this.handle_firewall_choice_click, this));
274
        this.update_firewall();
275
      },
276
      
277
      toggle_firewall: function(e, hide, cb) {
278
          hide = hide === undefined ? false : hide;
279
          if (hide) {
280
            this.firewall.stop().hide();
281
          } else {
282
            if (!cb) { cb = function() {}}
283
            this.firewall.slideToggle(cb);
284
          }
285
          this.firewall_toggler.toggleClass("open");
286
          this.firewall_visible = this.firewall_toggler.hasClass("open");
287
          if (!this.firewall_visible) {
288
            this.firewall_apply.fadeOut(50);
289
          }
290
          this.update_firewall();
291
      },
292
    
293
      post_hide: function() {
294
        views.NetworkPortView.__super__.post_hide.apply(this);
295
        if (this.firewall_visible) {
296
          this.toggle_firewall({}, true);
297
        }
298
      },
299

    
300
      handle_firewall_choice_click: function(e) {
301
          var el = $(e.currentTarget);
302
          if (el.get(0).tagName == "INPUT") {
303
            el = el.next();
304
          }
305
          var current = this.model.get("firewall_status");
306
          var selected = el.prev().val();
307

    
308
          el.parent().find("input").attr("checked", false);
309
          el.prev().attr("checked", true);
310

    
311
          if (selected != current) {
312
            this.firewall_apply.show();
313
          } else {
314
            this.firewall_apply.hide();
315
          }
316
      },
317
      
318
      disconnect_port: function(model, e) {
319
        e && e.stopPropagation();
320
        var network = this.model.get("network");
321
        this.model.actions.reset_pending();
322
        this.model.disconnect(_.bind(this.disconnect_port_complete, this));
323
      },
324

    
325
      disconnect_port_complete: function() {
326
      },
327

    
328
      set_firewall: function() {
329
        var value = this.get_selected_value();
330
        this.firewall_apply.addClass("in-progress");
331
        this.model.set({'pending_firewall': value});
332
        this.model.set_firewall(value, this.set_firewall_complete, 
333
                                       this.set_firewall_complete);
334
        this.in_progress = true;
335
      },
336
      
337
      set_firewall_complete: function() {
338
        this.in_progress = false;
339
        this.toggle_firewall({}, false, _.bind(function() {
340
          this.firewall_apply.removeClass("in-progress").show();
341
        }, this));
342
      },
343

    
344
      get_selected_value: function() {
345
        return this.firewall_inputs.filter(":checked").val();
346
      },
347

    
348
      update_firewall: function() {
349
        var value = this.model.get("firewall_status");
350
        var value_selector = "[value=" + value + "]"
351
        var status_span = this.firewall_toggler.find("span span");
352
        var current_choice = this.firewall_inputs.filter(value_selector);
353

    
354
        if (_.contains(["PROTECTED", "ENABLED"], value)) {
355
          status_span.removeClass("firewall-off").addClass("firewall-on");
356
          status_span.text("On");
357
        } else {
358
          status_span.removeClass("firewall-on").addClass("firewall-off");
359
          status_span.text("Off");
360
        }
361
        
362
        this.firewall_inputs.attr("checked", false);
363
        this.firewall_legends.removeClass("current");
364
        current_choice.attr("checked", true)
365
        current_choice.next().addClass("current");
366
      },
367

    
368
      show_vm_details: function() {
369
        var vm = this.model.get('vm');
370
        if (vm) { snf.ui.main.show_vm_details(vm) }
371
      }
372
    });
373

    
374
    views.NetworkPortCollectionView = views.ext.CollectionView.extend({
375
      tpl: '#network-port-collection-view-tpl',
376
      model_view_cls: views.NetworkPortView,
377
      rivets_view: true,
378
      get_rivet_object: function() {
379
        return {
380
          model: this.collection.network
381
        }
382
      },
383
      resolve_storage_object: function() {
384
        return this.collection
385
      },
386

    
387
      show_connect_vms_overlay: function() {
388
        this.parent_view.show_connect_vms_overlay();
389
      }
390
    });
391

    
392
    views.NetworkView = views.ext.ModelView.extend({
393
      tpl: '#network-view-tpl',
394
      auto_bind: ['connect_vm'],
395
      post_init_element: function() {
396
        this.ports = this.$(".ports.nested-model-list");
397
        this.ports.hide();
398
        this.ports_toggler = this.$(".network-ports-toggler");
399
        this.ports_toggler.click(this.toggle_ports);
400
        this.ports_visible = false;
401
      },
402

    
403
      toggle_ports: function(e, hide) {
404
        hide = hide === undefined ? false : hide;
405
        if (hide) {
406
          this.ports.stop().hide();
407
        } else {
408
          this.ports.stop().slideToggle();
409
        }
410
        this.ports_toggler.find(".cont-toggler").toggleClass("open");
411
        this.ports_visible = this.ports_toggler.find(".cont-toggler").hasClass("open");
412
        if (this.ports_visible) {
413
          $(this.el).addClass("hovered");
414
        } else {
415
          $(this.el).removeClass("hovered");
416
        }
417
      },
418
      
419
      get_network_icon: function() {
420
        var ico = this.model.get('is_public') ? 'internet.png' : 'network.png';
421
        return synnefo.config.media_url + 'images/' + ico;
422
      },
423

    
424
      post_hide: function() {
425
        views.NetworkView.__super__.post_hide.apply(this);
426
        if (this.ports_visible) {
427
          this.toggle_ports({}, true);
428
        }
429
      },
430
      
431
      status_map: {
432
        'ACTIVE': 'Active',
433
        'CONNECTING': 'Connecting',
434
        'DISCONNECTING': 'Disconnecting',
435
        'REMOVING': 'Destroying'
436
      },
437

    
438
      status_cls_map: {
439
        'ACTIVE': 'status-active',
440
        'DISCONNECTING': 'status-progress',
441
        'CONNECTING': 'status-progress',
442
        'REMOVING': 'status-progress'
443
      },
444
      
445
      status_cls: function(status) {    
446
        return this.status_cls_map[this.model.get('ext_status')]
447
      },
448

    
449
      status_display: function(status) {
450
        var status;
451
        var cidr = this.model.get('cidr');
452
        var status = this.model.get('ext_status');
453
        if (status != 'REMOVING' && cidr) {
454
          return cidr
455
        }
456
        if (this.model.id == "snf-combined-public-network" && !_.contains(
457
          ["CONNECTING", "DISCONNECTING"], status)) {
458
          return "Public connectivity"
459
        }
460

    
461
        return this.status_map[status];
462
      },
463
      
464
      connect_vms: function(vms, cb) {
465
        var finished = 0;
466
        var completed = function() {
467
          finished++;
468
          if (finished == vms.length) {
469
            cb();
470
          }
471
        }
472
        _.each(vms, function(vm) {
473
          this.model.connect_vm(vm, completed);
474
        }, this);
475
      },
476
      
477
      remove: function(model, e) {
478
        e && e.stopPropagation();
479
        this.model.actions.reset_pending();
480
        this.model.destroy({
481
          success: _.bind(function() {
482
            this.model.set({status: 'REMOVING'});
483
            this.model.set({ext_status: 'REMOVING'});
484
            // force status display update
485
            this.model.set({cidr: 'REMOVING'});
486
          }, this),
487
          silent: true
488
        });
489
      },
490

    
491
      show_connect_vms_overlay: function() {
492
        var view = new views.NetworkConnectVMsOverlay();
493
        vms = this.model.connectable_vms;
494
        var cb = _.bind(function(vms) {
495
          view.set_in_progress();
496
          var cbinner = function() {
497
            view.hide();
498
            delete view;
499
          }
500
          this.connect_vms(vms, cbinner);
501
        }, this);
502
        view.show_vms(this.model, vms, [], cb, "subtitle");
503
      }
504

    
505
    });
506
    
507
    views.NetworksCollectionView = views.ext.CollectionView.extend({
508
      collection: storage.networks,
509
      collection_name: 'networks',
510
      model_view_cls: views.NetworkView,
511
      create_view_cls: views.NetworkCreateView,
512
      
513
      init: function() {
514
        this.public_added = false;
515
        views.NetworksCollectionView.__super__.init.apply(this, arguments);
516
      },
517
      
518
      check_empty: function() {
519
        views.NetworksCollectionView.__super__.check_empty.apply(this, arguments);
520
        //if (this.$(".private").children().length == 0) {
521
          //this.$(".private").hide();
522
        //} else {
523
          //this.$(".private").show();
524
        //}
525
      },
526

    
527
      add_model: function(m) {
528
        if (m.get('is_public') && !this.public_added) {
529
          this.combined_public = new models.networks.CombinedPublicNetwork();
530
          this.combined_public_view = new views.NetworkView({
531
            model: this.combined_public
532
          });
533
          this.add_model_view(this.combined_public_view, this.combined_public, 0);
534
          this.public_added = true;
535
        }
536
        return views.NetworksCollectionView.__super__.add_model.call(this, m);
537
      },
538

    
539
      remove_model: function(m) {
540
        if (m.id == 'snf-combined-public-network') {
541
          return;
542
        } else {
543
          return views.NetworksCollectionView.__super__.remove_model.call(this, m);
544
        }
545
      },
546

    
547
      get_model_view_cls: function(m) {
548
        if (!this.public_added) {
549
        }
550
        if (m.get('is_public')) {
551
          return false;
552
        }
553
        return views.NetworksCollectionView.__super__.get_model_view_cls.apply(this, [m]);
554
      },
555
      
556
      parent_for_model: function(m) {
557
        if (m.get('is_public')) {
558
          return this.list_el.find(".public");
559
        } else {
560
          return this.list_el.find(".private");
561
        }
562
      }
563
    });
564

    
565
    views.NetworksPaneView = views.ext.PaneView.extend({
566
      id: "pane",
567
      el: '#networks-pane',
568
      collection_view_cls: views.NetworksCollectionView,
569
      collection_view_selector: '#networks-list-view'
570
    });
571
    
572
    views.VMSelectView = views.ext.SelectModelView.extend({
573
      tpl: '#vm-select-model-tpl',
574
      get_vm_icon: function() {
575
        return $(snf.ui.helpers.vm_icon_tag(this.model, "small")).attr("src")
576
      },
577
      status_cls: function() {
578
        return (views.IconView.STATE_CLASSES[this.model.get("state")] || []).join(" ") + " status clearfix"
579
      },
580
      status_display: function() {
581
        return STATE_TEXTS[this.model.get("state")]
582
      }
583
    });
584

    
585
    views.VMSelectView = views.ext.CollectionView.extend({
586
      init: function() {
587
        views.VMSelectView.__super__.init.apply(this);
588
      },
589
      tpl: '#vm-select-collection-tpl',
590
      model_view_cls: views.VMSelectView,
591
      
592
      trigger_select: function(view, select) {
593
        this.trigger("change:select", view, select);
594
      },
595

    
596
      post_add_model_view: function(view) {
597
        view.bind("change:select", this.trigger_select, this);
598
        if (!this.options.allow_multiple) {
599
          view.input.prop("type", "radio");
600
        }
601
      },
602

    
603
      post_remove_model_view: function(view) {
604
        view.unbind("change:select", this.trigger_select, this);
605
      },
606

    
607
      deselect_all: function(except) {
608
        _.each(this._subviews, function(view) {
609
          if (view != except) { view.deselect() }
610
        });
611
      },
612

    
613
      get_selected: function() {
614
        return _.filter(_.map(this._subviews, function(view) {
615
          if (view.selected) {
616
            return view.model;
617
          }
618
        }), function(m) { return m });
619
      }
620
    });
621

    
622
    views.NetworkConnectVMsOverlay = views.Overlay.extend({
623
        title: "Connect machine",
624
        overlay_id: "overlay-select-vms",
625
        content_selector: "#network-vms-select-content",
626
        css_class: "overlay-info",
627
        allow_multiple: true,
628

    
629
        initialize: function() {
630
            views.NetworkConnectVMsOverlay.__super__.initialize.apply(this);
631
            this.list = this.$(".vms-list ul");
632
            this.empty_message = this.$(".empty-message");
633
            // flag for submit handler to avoid duplicate bindings
634
            this.submit_handler_set = false;
635
            this.in_progress = false;
636

    
637
        },
638
        
639
        init_collection_view: function(collection) {
640
            this.collection_view = new views.VMSelectView({
641
              collection: collection,
642
              el: this.list,
643
              allow_multiple: this.allow_multiple
644
            });
645
            this.collection_view.show(true);
646
            this.list.append($(this.collection_view.el));
647
            if (!this.allow_multiple) {
648
              this.collection_view.bind("change:select", 
649
                                        function(view, selected) {
650
                if (!selected) { return }
651
                this.collection_view.deselect_all(view);
652
              }, this);
653
            }
654
        },
655

    
656
        handle_vm_click: function(el) {
657
            if (!this.allow_multiple) {
658
              $(el).closest("ul").find(".selected").removeClass("selected");
659
              $(el).addClass("selected");
660
            } else {
661
              $(el).toggleClass("selected");
662
            }
663
        },
664

    
665
        init_handlers: function() {
666
            var self = this;
667
            
668
            if (!this.submit_handler_set) {
669
                // avoid duplicate submits
670
                this.el.find(".create, .assign").click(_.bind(function() {
671
                  if (!this.in_progress) {
672
                    this.submit();
673
                  }
674
                }, this));
675
                this.submit_handler_set = true;
676
            }
677
        },
678
        
679
        reset: function() {},
680
        beforeOpen: function() {
681
            this.reset();
682
            this.update_layout();
683
        },
684
        
685
        get_selected: function() {
686
          return this.collection_view.get_selected();
687
        },
688

    
689
        update_layout: function() {
690
            this.unset_in_progress();
691
            this.in_progress = false;
692

    
693
            if (this.vms.length == 0) {
694
                this.empty_message.show();
695
            } else {
696
                this.empty_message.hide();
697
            }
698

    
699
            this.init_handlers();
700
        },
701

    
702
        set_in_progress: function() {
703
          this.$(".form-action").addClass("in-progress");
704
          this.in_progress = true;
705
        },
706

    
707
        unset_in_progress: function() {
708
          this.$(".form-action").removeClass("in-progress");
709
          this.in_progress = false;
710
        },
711

    
712
        show_vms: function(network, vms, selected, callback, subtitle) {
713
            this.init_collection_view(vms);
714
            this.network = network;
715
            this.reset();
716
            if (network) {
717
              this.set_subtitle(network.escape("name"));
718
            } else {
719
              this.set_subtitle(subtitle);
720
            }
721
            this.vms = vms;
722
            this.selected = selected;
723
            this.cb = callback;
724
            this.unset_in_progress();
725
            this.show(true);
726
        },
727
        
728
        onClose: function() {
729
          this.collection_view.hide(true);
730
          delete this.collection_view;
731
        },
732

    
733
        submit: function() {
734
            if (!this.get_selected().length) { return }
735
            this.cb(this.get_selected());
736
        }
737
    });
738
    
739
    views.NetworkSelectModelView = views.ext.SelectModelView.extend({});
740

    
741
    views.NetworkSelectNetworkTypeModelView = views.NetworkSelectModelView.extend({
742
      get_network_icon: function() {
743
        var ico = this.model.get('is_public') ? 'internet-small.png' : 'network-small.png';
744
        return synnefo.config.media_url + 'images/' + ico;
745
      },
746
      forced_title: 'You machine will be automatically connected ' +
747
                    'to this network.'
748
    });
749

    
750
    views.NetworkSelectPublicNetwork = views.NetworkSelectNetworkTypeModelView.extend({
751
      tpl: '#networks-select-public-item-tpl',
752
      classes: 'public-network',
753
      post_init_element: function() {
754
        views.NetworkSelectPublicNetwork.__super__.post_init_element.apply(this);
755
      }
756
    });
757

    
758
    views.NetworkSelectPrivateNetwork = views.NetworkSelectNetworkTypeModelView.extend({
759
      tpl: '#networks-select-private-item-tpl',
760
      classes: 'private-network'
761
    });
762
    
763
    views.NetworkSelectTypeView = views.ext.CollectionView.extend({});
764
    views.NetworkSelectPublicNetworks = views.NetworkSelectTypeView.extend({
765
      tpl: '#networks-select-public-tpl',
766
      model_view_cls: views.NetworkSelectPublicNetwork,
767
      get_floating_ips: function() {
768
        var ips = [];
769
        _.each(this._subviews, function(view) {
770
          _.each(view._subviews, function(view) {
771
            if (view.selected_ips) {
772
              _.each(view.selected_ips, function(m) {
773
                ips.push(m.id);
774
              }, this);
775
            }
776
          }, this);
777
        }, this);
778
        return ips;
779
      }
780
    });
781
    
782
    views.NetworkSelectFloatingIpView = views.NetworkSelectModelView.extend({
783
      tpl: '#networks-select-floating-ip-tpl'
784
    });
785

    
786
    views.NetworkSelectFloatingIpsView = views.ext.CollectionView.extend({
787
      tpl: '#networks-select-floating-ips-tpl',
788
      model_view_cls: views.NetworkSelectFloatingIpView,
789

    
790
      select_if_available: function() {
791
        var selected = false;
792
        if (this._subviews[0]) {
793
          this._subviews[0].select();
794
        }
795
      },
796

    
797
      deselect_all: function() {
798
        this.each_ip_view(function(v) { v.deselect() });
799
      },
800

    
801
      each_ip_view: function(cb) {
802
        _.each(this._subviews, function(view) {
803
          if (view instanceof views.NetworkSelectFloatingIpView) {
804
            cb(view);
805
          }
806
        })
807
      },
808

    
809
      post_init: function() {
810
        var parent = this.parent_view;
811
        var self = this;
812

    
813
        this.quota = synnefo.storage.quotas.get("cyclades.floating_ip");
814
        this.selected_ips = [];
815
        this.handle_ip_select = _.bind(this.handle_ip_select, this);
816
        this.create = this.$(".floating-ip.create");
817
        
818
        this.quota.bind("change", _.bind(this.update_available, this));
819
        this.collection.bind("change", _.bind(this.update_available, this))
820
        this.collection.bind("add", _.bind(this.update_available, this))
821
        this.collection.bind("remove", _.bind(this.update_available, this))
822

    
823
        parent.bind("change:select", function(view, selected) {
824
          if (selected) { this.show_parent() } else { this.hide_parent() }
825
        }, this);
826

    
827
        this.create.click(function(e) {
828
          e.preventDefault();
829
          self.create_ip();
830
        });
831
        this.reset_creating();
832
      },
833
      
834
      hide_parent: function() {
835
        this.parent_view.item.removeClass("selected");
836
        this.parent_view.input.attr("checked", false);
837
        this.parent_view.selected = false;
838
        this.deselect_all();
839
        this.hide(true);
840
      },
841

    
842
      show_parent: function() {
843
        var left = this.quota.get_available();
844
        var available = this.collection.length || left;
845
        if (!available) { 
846
          this.hide_parent();
847
          return;
848
        }
849
        this.parent_view.item.addClass("selected");
850
        this.parent_view.input.attr("checked", true);
851
        this.parent_view.selected = true;
852
        this.show(true);
853
        this.select_if_available();
854
      },
855

    
856
      update_available: function() {
857
        var left = this.quota.get_available();
858
        var available = this.collection.length || left;
859
        var available_el = this.parent_view.$(".available");
860
        var no_available_el = this.parent_view.$(".no-available");
861
        var create = this.$(".create.model-item");
862
        var create_link = this.$(".create a");
863
        var create_no_available = this.$(".create .no-available");
864

    
865
        if (!available) {
866
          // no ip's available to select
867
          this.hide_parent();
868
          available_el.hide();
869
          no_available_el.show();
870
        } else {
871
          // available floating ip
872
          var available_text = "".format(
873
            this.collection.length + this.quota.get_available());
874
          available_el.removeClass("hidden").text(available_text).show();
875
          available_el.show();
876
          no_available_el.hide();
877
        }
878

    
879
        if (left) {
880
          // available quota
881
          create.removeClass("no-available");
882
          create.show();
883
          create_link.show();
884
          create_no_available.hide();
885
        } else {
886
          // no available quota
887
          create.addClass("no-available");
888
          create.hide();
889
          create_link.hide();
890
          //create_no_available.show();
891
        }
892
        this.update_selected();
893
      },
894
      
895
      update_selected: function() {
896
        _.each(this.selected_ips.length, function(ip) {
897
          if (!this.collection.get(ip.id)) {
898
            this.selected_ips = _.without(this.selected_ips, ip);
899
          }
900
        }, this);
901

    
902
        if (this.selected_ips.length) {
903
          this.parent_view.input.attr("checked", true);
904
          this.parent_view.item.addClass("selected");
905
          this.parent_view.item.selected = true;
906
        } else {
907
          this.parent_view.input.attr("checked", false);
908
          this.parent_view.item.removeClass("selected");
909
          this.parent_view.item.selected = false;
910
        }
911
      },
912

    
913
      post_remove_model_view: function(view) {
914
        view.deselect();
915
        view.unbind("change:select", this.handle_ip_select)
916
      },
917

    
918
      handle_create_error: function() {},
919
      
920
      set_creating: function() {
921
        var create_link = this.$(".create a");
922
        var create_no_available = this.$(".create .no-available");
923
        var loading = this.$(".create .loading");
924
        create_link.hide();
925
        loading.show();
926
      },
927

    
928
      reset_creating: function() {
929
        var loading = this.$(".create .loading");
930
        loading.hide();
931
        this.update_available();
932
      },
933

    
934
      create_ip: function() {
935
        if (!this.quota.get_available()) { return }
936
        var self = this;
937
        this.set_creating();
938
        synnefo.storage.floating_ips.create({floatingip:{}}, {
939
          error: _.bind(this.handle_create_error, this),
940
          complete: function() {
941
            synnefo.storage.quotas.fetch();
942
            self.reset_creating();
943
          },
944
          skip_api_error: true
945
        });
946
      },
947

    
948
      post_add_model_view: function(view, model) {
949
        view.bind("change:select", this.handle_ip_select)
950
        if (!this.selected_ips.length && this._subviews.length == 1) {
951
          this._subviews[0].select();
952
          if (!_.contains(this.selected_ips, model)) {
953
            this.selected_ips.push(model);
954
          }
955
        }
956
      },
957

    
958
      handle_ip_select: function(view) {
959
        if (view.selected) {
960
          if (!_.contains(this.selected_ips, view.model)) {
961
            this.selected_ips.push(view.model);
962
          }
963
        } else {
964
          this.selected_ips = _.without(this.selected_ips, view.model);
965
        }
966
        this.update_selected();
967
      },
968
      
969
      post_show: function() {
970
        this.update_available();
971
      },
972

    
973
      get_floating_ips: function() {
974
        return this.selected_ips;
975
      }
976
    });
977

    
978
    views.NetworkSelectPrivateNetworks = views.NetworkSelectTypeView.extend({
979
      tpl: '#networks-select-private-tpl',
980
      model_view_cls: views.NetworkSelectPrivateNetwork,
981
      get_networks: function() {
982
        return _.filter(_.map(this._subviews, function(view) {
983
          if (view.selected) { return view.model.id }
984
        }), function(id) { return id });
985
      }
986

    
987
    });
988

    
989
    views.NetworkSelectView = views.ext.ModelView.extend({
990
      rivets_view: true,
991
      tpl: '#networks-select-view-tpl',
992
      select_public: true,
993
      
994
      forced_values_title_map: {
995
        "SNF:ANY_PUBLIC_IPV6": "Internet (public IPv6)",
996
        "SNF:ANY_PUBLIC_IPV4": "Internet (public IPv4)"
997
      },
998

    
999
      initialize: function(options) {
1000
        this.quotas = synnefo.storage.quotas.get('cyclades.private_network');
1001
        options = options || {};
1002
        options.model = options.model || new models.Model();
1003
        this.private_networks = new Backbone.FilteredCollection(undefined, {
1004
          collection: synnefo.storage.networks,
1005
          collectionFilter: function(m) {
1006
            return !m.get('is_public')
1007
        }});
1008

    
1009
        this.public_networks = new Backbone.Collection();
1010
        this.public_networks.comparator = function(m) {
1011
          if (m.get('forced')) {
1012
            return -1
1013
          }  
1014
          return 100;
1015
        }
1016
        
1017
        if (synnefo.config.forced_server_networks.length) {
1018
          _.each(synnefo.config.forced_server_networks, function(network) {
1019
            var forced = synnefo.storage.networks.get(network);
1020
            if (!forced) {
1021
              var name = this.forced_values_title_map[network];
1022
              if (!name) { name = "Forced network ({0})".format(network)}
1023
              forced = new models.networks.Network({
1024
                id: network,
1025
                name: name, 
1026
                subnets: [],
1027
                is_public: true,
1028
                forced: true
1029
              });
1030
            } else {
1031
              forced.set({'forced': true});
1032
            }
1033
            this.public_networks.add(forced);
1034
          }, this);
1035
        }
1036

    
1037
        // combined public
1038
        this.combined_public = new models.networks.CombinedPublicNetwork();
1039
        this.combined_public.set({noselect: true, 
1040
                                  name: 'Internet (public IPv4)', 
1041
                                  forced: false});
1042
        this.public_networks.add(this.combined_public);
1043

    
1044
        model_attrs = {
1045
          public_collection: this.public_networks,
1046
          private_collection: this.private_networks,
1047
          floating_selected: true
1048
        }
1049

    
1050
        options.model.set(model_attrs);
1051
        this._configure(options);
1052
        return views.NetworkSelectView.__super__.initialize.call(this, options);
1053
      },
1054

    
1055
      get_selected_floating_ips: function() {
1056
        var ips = [];
1057
        _.each(this._subviews, function(view) {
1058
          if (view.get_floating_ips) {
1059
            ips = _.union(ips, view.get_floating_ips());
1060
          }
1061
        }, this);
1062
        return _.filter(
1063
          _.map(ips, function(ipid) { 
1064
          return synnefo.storage.floating_ips.get(parseInt(ipid))
1065
        }), function(ip) { return ip });
1066
      },
1067

    
1068
      get_selected_networks: function() {
1069
        var networks = [];
1070
        _.each(this._subviews, function(view) {
1071
          if (view.get_networks) {
1072
            networks = _.union(networks, view.get_networks());
1073
          }
1074
        }, this);
1075
        return _.filter(
1076
          _.map(networks, function(netid) { 
1077
          return synnefo.storage.networks.get(netid)
1078
        }), function(net) { return net });
1079
      }
1080
    });
1081
 
1082
})(this);