Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (36 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
                // trigger parent view create handler
220
                this.parent_view.post_create();
221
            }, this));
222
        },
223

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

    
237
        onOpen: function() {
238
            this.text.focus();
239
        }
240
    });
241

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

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

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

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

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

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

    
312
          el.parent().find("input").attr("checked", false);
313
          el.prev().attr("checked", true);
314

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

    
329
      disconnect_port_complete: function() {
330
      },
331

    
332
      set_firewall: function() {
333
        var value = this.get_selected_value();
334
        this.firewall_apply.addClass("in-progress");
335
        var vm = this.model.get('vm');
336
        if (!vm) { return }
337
        this.model.set({'pending_firewall': value});
338
        vm.set_firewall(this.model, value, this.set_firewall_success,
339
                        this.set_firewall_error)
340
        this.in_progress = true;
341
      },
342
      
343
      set_firewall_success: function() {
344
        this.set_firewall_complete();
345
      },
346

    
347
      set_firewall_error: function() {
348
        this.model.set({'pending_firewall': undefined});
349
        this.set_firewall_complete();
350
      },
351

    
352
      set_firewall_complete: function() {
353
        this.in_progress = false;
354
        this.toggle_firewall({}, false, _.bind(function() {
355
          this.firewall_apply.removeClass("in-progress").show();
356
        }, this));
357
      },
358

    
359
      get_selected_value: function() {
360
        return this.firewall_inputs.filter(":checked").val();
361
      },
362

    
363
      update_firewall: function() {
364
        var value = this.model.get("firewall_status");
365
        var value_selector = "[value=" + value + "]"
366
        var status_span = this.firewall_toggler.find("span span");
367
        var current_choice = this.firewall_inputs.filter(value_selector);
368

    
369
        if (_.contains(["PROTECTED", "ENABLED"], value)) {
370
          status_span.removeClass("firewall-off").addClass("firewall-on");
371
          status_span.text("On");
372
        } else {
373
          status_span.removeClass("firewall-on").addClass("firewall-off");
374
          status_span.text("Off");
375
        }
376
        
377
        this.firewall_inputs.attr("checked", false);
378
        this.firewall_legends.removeClass("current");
379
        current_choice.attr("checked", true)
380
        current_choice.next().addClass("current");
381
      },
382

    
383
      show_vm_details: function() {
384
        var vm = this.model.get('vm');
385
        if (vm) { snf.ui.main.show_vm_details(vm) }
386
      }
387
    });
388

    
389
    views.NetworkPortCollectionView = views.ext.CollectionView.extend({
390
      tpl: '#network-port-collection-view-tpl',
391
      model_view_cls: views.NetworkPortView,
392
      rivets_view: true,
393
      get_rivet_object: function() {
394
        return {
395
          model: this.collection.network
396
        }
397
      },
398
      resolve_storage_object: function() {
399
        return this.collection
400
      },
401

    
402
      show_connect_vms_overlay: function() {
403
        this.parent_view.show_connect_vms_overlay();
404
      }
405
    });
406

    
407
    views.NetworkView = views.ext.ModelView.extend({
408
      tpl: '#network-view-tpl',
409
      auto_bind: ['connect_vm'],
410
      post_init_element: function() {
411
        this.ports = this.$(".ports.nested-model-list");
412
        this.ports.hide();
413
        this.ports_toggler = this.$(".network-ports-toggler");
414
        this.ports_toggler.click(this.toggle_ports);
415
        this.ports_visible = false;
416
      },
417

    
418
      toggle_ports: function(e, hide) {
419
        $(window).trigger("resize");
420
        hide = hide === undefined ? false : hide;
421
        if (hide) {
422
          this.ports.stop().hide();
423
        } else {
424
          this.ports.stop().slideToggle(function() {
425
              $(window).trigger("resize");
426
            });
427
        }
428
        this.ports_toggler.find(".cont-toggler").toggleClass("open");
429
        this.ports_visible = this.ports_toggler.find(".cont-toggler").hasClass("open");
430
        if (this.ports_visible) {
431
          $(this.el).addClass("hovered");
432
        } else {
433
          $(this.el).removeClass("hovered");
434
        }
435
      },
436
      
437
      get_network_icon: function() {
438
        var ico = this.model.get('is_public') ? 'internet.png' : 'network.png';
439
        return synnefo.config.media_url + 'images/' + ico;
440
      },
441

    
442
      post_hide: function() {
443
        views.NetworkView.__super__.post_hide.apply(this);
444
        if (this.ports_visible) {
445
          this.toggle_ports({}, true);
446
        }
447
      },
448
      
449
      status_map: {
450
        'ACTIVE': 'Active',
451
        'CONNECTING': 'Connecting',
452
        'DISCONNECTING': 'Disconnecting',
453
        'REMOVING': 'Destroying'
454
      },
455

    
456
      status_cls_map: {
457
        'ACTIVE': 'status-active',
458
        'DISCONNECTING': 'status-progress',
459
        'CONNECTING': 'status-progress',
460
        'REMOVING': 'status-progress'
461
      },
462
      
463
      status_cls: function(status) {    
464
        return this.status_cls_map[this.model.get('ext_status')]
465
      },
466

    
467
      status_display: function(status) {
468
        var status;
469
        var cidr = this.model.get('cidr');
470
        var status = this.model.get('ext_status');
471
        if (status != 'REMOVING' && cidr) {
472
          return cidr
473
        }
474
        if (this.model.id == "snf-combined-public-network" && !_.contains(
475
          ["CONNECTING", "DISCONNECTING"], status)) {
476
          return "Public"
477
        }
478

    
479
        return this.status_map[status];
480
      },
481
      
482
      connect_vms: function(vms, cb) {
483
        var finished = 0;
484
        var completed = function() {
485
          finished++;
486
          if (finished == vms.length) {
487
            cb();
488
          }
489
        }
490
        _.each(vms, function(vm) {
491
          this.model.connect_vm(vm, completed);
492
        }, this);
493
      },
494
      
495
      remove: function(model, e) {
496
        e && e.stopPropagation();
497
        this.model.actions.reset_pending();
498
        this.model.destroy({
499
          success: _.bind(function() {
500
            this.model.set({status: 'REMOVING'});
501
            this.model.set({ext_status: 'REMOVING'});
502
            // force status display update
503
            this.model.set({cidr: 'REMOVING'});
504
          }, this),
505
          silent: true
506
        });
507
      },
508

    
509
      show_connect_vms_overlay: function() {
510
        var view = new views.NetworkConnectVMsOverlay();
511
        vms = this.model.connectable_vms;
512
        var cb = _.bind(function(vms) {
513
          view.set_in_progress();
514
          var cbinner = function() {
515
            view.hide();
516
            delete view;
517
          }
518
          this.connect_vms(vms, cbinner);
519
        }, this);
520
        view.show_vms(this.model, vms, [], cb, "subtitle", this);
521
      }
522

    
523
    });
524
    
525
    views.NetworksCollectionView = views.ext.CollectionView.extend({
526
      collection: storage.networks,
527
      collection_name: 'networks',
528
      model_view_cls: views.NetworkView,
529
      create_view_cls: views.NetworkCreateView,
530
      quota_key: 'cyclades.network.private',
531
      
532
      init: function() {
533
        this.public_added = false;
534
        views.NetworksCollectionView.__super__.init.apply(this, arguments);
535
      },
536
      
537
      check_empty: function() {
538
        views.NetworksCollectionView.__super__.check_empty.apply(this, arguments);
539
        if (this.collection.filter(function(n){ return !n.is_public()}).length == 0) {
540
          this.$(".private").hide();  
541
        } else {
542
          this.$(".private").show();  
543
        }
544
      },
545

    
546
      add_model: function(m) {
547
        if (m.get('is_public') && !this.public_added) {
548
          this.combined_public = new models.networks.CombinedPublicNetwork();
549
          this.combined_public_view = new views.NetworkView({
550
            model: this.combined_public
551
          });
552
          this.add_model_view(this.combined_public_view, this.combined_public, 0);
553
          this.public_added = true;
554
        }
555
        return views.NetworksCollectionView.__super__.add_model.call(this, m);
556
      },
557

    
558
      remove_model: function(m) {
559
        if (m.id == 'snf-combined-public-network') {
560
          return;
561
        } else {
562
          return views.NetworksCollectionView.__super__.remove_model.call(this, m);
563
        }
564
      },
565

    
566
      get_model_view_cls: function(m) {
567
        if (!this.public_added) {
568
        }
569
        if (m.get('is_public')) {
570
          return false;
571
        }
572
        return views.NetworksCollectionView.__super__.get_model_view_cls.apply(this, [m]);
573
      },
574
      
575
      parent_for_model: function(m) {
576
        if (m.get('is_public')) {
577
          return this.list_el.find(".public");
578
        } else {
579
          return this.list_el.find(".private");
580
        }
581
      }
582
    });
583

    
584
    views.NetworksPaneView = views.ext.PaneView.extend({
585
      id: "pane",
586
      el: '#networks-pane',
587
      collection_view_cls: views.NetworksCollectionView,
588
      collection_view_selector: '#networks-list-view'
589
    });
590
    
591
    views.VMSelectView = views.ext.SelectModelView.extend({
592
      tpl: '#vm-select-model-tpl',
593
      get_vm_icon: function() {
594
        return $(snf.ui.helpers.vm_icon_tag(this.model, "small")).attr("src")
595
      },
596
      status_cls: function() {
597
        return (views.IconView.STATE_CLASSES[this.model.get("state")] || []).join(" ") + " status clearfix"
598
      },
599
      status_display: function() {
600
        return STATE_TEXTS[this.model.get("state")]
601
      }
602
    });
603

    
604
    views.VMSelectView = views.ext.CollectionView.extend({
605
      init: function() {
606
        views.VMSelectView.__super__.init.apply(this);
607
      },
608
      tpl: '#vm-select-collection-tpl',
609
      model_view_cls: views.VMSelectView,
610
      
611
      trigger_select: function(view, select) {
612
        this.trigger("change:select", view, select);
613
      },
614

    
615
      post_add_model_view: function(view) {
616
        view.bind("change:select", this.trigger_select, this);
617
        if (!this.options.allow_multiple) {
618
          view.input.prop("type", "radio");
619
        }
620
      },
621

    
622
      post_remove_model_view: function(view) {
623
        view.unbind("change:select", this.trigger_select, this);
624
      },
625

    
626
      deselect_all: function(except) {
627
        _.each(this._subviews, function(view) {
628
          if (view != except) { view.deselect() }
629
        });
630
      },
631

    
632
      get_selected: function() {
633
        return _.filter(_.map(this._subviews, function(view) {
634
          if (view.selected) {
635
            return view.model;
636
          }
637
        }), function(m) { return m });
638
      }
639
    });
640

    
641
    views.NetworkConnectVMsOverlay = views.Overlay.extend({
642
        title: "Connect machine",
643
        overlay_id: "overlay-select-vms",
644
        content_selector: "#network-vms-select-content",
645
        css_class: "overlay-info",
646
        allow_multiple: true,
647

    
648
        initialize: function() {
649
            views.NetworkConnectVMsOverlay.__super__.initialize.apply(this);
650
            this.list = this.$(".vms-list ul");
651
            this.empty_message = this.$(".empty-message");
652
            // flag for submit handler to avoid duplicate bindings
653
            this.submit_handler_set = false;
654
            this.in_progress = false;
655

    
656
        },
657
        
658
        init_collection_view: function(collection) {
659
            this.collection_view = new views.VMSelectView({
660
              collection: collection,
661
              el: this.list,
662
              allow_multiple: this.allow_multiple
663
            });
664
            this.collection_view.show(true);
665
            this.list.append($(this.collection_view.el));
666
            if (!this.allow_multiple) {
667
              this.collection_view.bind("change:select", 
668
                                        function(view, selected) {
669
                if (!selected) { return }
670
                this.collection_view.deselect_all(view);
671
              }, this);
672
            }
673
        },
674

    
675
        handle_vm_click: function(el) {
676
            if (!this.allow_multiple) {
677
              $(el).closest("ul").find(".selected").removeClass("selected");
678
              $(el).addClass("selected");
679
            } else {
680
              $(el).toggleClass("selected");
681
            }
682
        },
683

    
684
        init_handlers: function() {
685
            var self = this;
686
            
687
            if (!this.submit_handler_set) {
688
                // avoid duplicate submits
689
                this.el.find(".create, .assign").click(_.bind(function() {
690
                  if (!this.in_progress) {
691
                    this.submit();
692
                  }
693
                }, this));
694
                this.submit_handler_set = true;
695
            }
696
        },
697
        
698
        reset: function() {},
699
        beforeOpen: function() {
700
            this.reset();
701
            this.update_layout();
702
        },
703
        
704
        get_selected: function() {
705
          return this.collection_view.get_selected();
706
        },
707

    
708
        update_layout: function() {
709
            this.unset_in_progress();
710
            this.in_progress = false;
711

    
712
            if (this.vms.length == 0) {
713
                this.empty_message.show();
714
            } else {
715
                this.empty_message.hide();
716
            }
717

    
718
            this.init_handlers();
719
        },
720

    
721
        set_in_progress: function() {
722
          this.$(".form-action").addClass("in-progress");
723
          this.in_progress = true;
724
        },
725

    
726
        unset_in_progress: function() {
727
          this.$(".form-action").removeClass("in-progress");
728
          this.in_progress = false;
729
        },
730

    
731
        show_vms: function(network, vms, selected, callback, subtitle) {
732
            this.init_collection_view(vms);
733
            this.network = network;
734
            this.reset();
735
            if (network) {
736
              this.set_subtitle(network.escape("name"));
737
            } else {
738
              this.set_subtitle(subtitle);
739
            }
740
            this.vms = vms;
741
            this.selected = selected;
742
            this.cb = callback;
743
            this.unset_in_progress();
744
            this.show(true);
745
        },
746
        
747
        onClose: function() {
748
          this.collection_view.hide(true);
749
          delete this.collection_view;
750
        },
751

    
752
        submit: function() {
753
            if (!this.get_selected().length) { return }
754
            this.cb(this.get_selected());
755
        }
756
    });
757
    
758
    views.NetworkSelectModelView = views.ext.SelectModelView.extend({});
759

    
760
    views.NetworkSelectNetworkTypeModelView = views.NetworkSelectModelView.extend({
761
      get_network_icon: function() {
762
        var ico = this.model.get('is_public') ? 'internet-small.png' : 'network-small.png';
763
        return synnefo.config.media_url + 'images/' + ico;
764
      },
765
      forced_title: 'You machine will be automatically connected ' +
766
                    'to this network.'
767
    });
768

    
769
    views.NetworkSelectPublicNetwork = views.NetworkSelectNetworkTypeModelView.extend({
770
      tpl: '#networks-select-public-item-tpl',
771
      classes: 'public-network',
772
      post_init_element: function() {
773
        views.NetworkSelectPublicNetwork.__super__.post_init_element.apply(this);
774
      }
775
    });
776

    
777
    views.NetworkSelectPrivateNetwork = views.NetworkSelectNetworkTypeModelView.extend({
778
      tpl: '#networks-select-private-item-tpl',
779
      classes: 'private-network'
780
    });
781
    
782
    views.NetworkSelectTypeView = views.ext.CollectionView.extend({});
783
    views.NetworkSelectPublicNetworks = views.NetworkSelectTypeView.extend({
784
      tpl: '#networks-select-public-tpl',
785
      model_view_cls: views.NetworkSelectPublicNetwork,
786
      get_floating_ips: function() {
787
        var ips = [];
788
        _.each(this._subviews, function(view) {
789
          _.each(view._subviews, function(view) {
790
            if (view.selected_ips) {
791
              _.each(view.selected_ips, function(m) {
792
                ips.push(m.id);
793
              }, this);
794
            }
795
          }, this);
796
        }, this);
797
        return ips;
798
      }
799
    });
800
    
801
    views.NetworkSelectFloatingIpView = views.NetworkSelectModelView.extend({
802
      tpl: '#networks-select-floating-ip-tpl'
803
    });
804

    
805
    views.NetworkSelectFloatingIpsView = views.ext.CollectionView.extend({
806
      tpl: '#networks-select-floating-ips-tpl',
807
      model_view_cls: views.NetworkSelectFloatingIpView,
808

    
809
      deselect_all: function() {
810
        this.each_ip_view(function(v) { v.deselect() });
811
      },
812

    
813
      each_ip_view: function(cb) {
814
        _.each(this._subviews, function(view) {
815
          if (view instanceof views.NetworkSelectFloatingIpView) {
816
            cb(view);
817
          }
818
        })
819
      },
820

    
821
      post_init: function() {
822
        var parent = this.parent_view;
823
        var self = this;
824

    
825
        this.quota = synnefo.storage.quotas.get("cyclades.floating_ip");
826
        this.selected_ips = [];
827
        this.handle_ip_select = _.bind(this.handle_ip_select, this);
828
        this.create = this.$(".floating-ip.create");
829
        
830
        this.quota.bind("change", _.bind(this.update_available, this));
831
        this.collection.bind("change", _.bind(this.update_available, this))
832
        this.collection.bind("add", _.bind(this.update_available, this))
833
        this.collection.bind("remove", _.bind(this.update_available, this))
834

    
835
        parent.bind("change:select", function(view, selected) {
836
          if (selected) { this.show_parent() } else { this.hide_parent() }
837
        }, this);
838

    
839
        this.create.click(function(e) {
840
          e.preventDefault();
841
          self.create_ip();
842
        });
843
        this.reset_creating();
844
      },
845
      
846
      hide_parent: function() {
847
        this.parent_view.item.removeClass("selected");
848
        this.parent_view.input.attr("checked", false);
849
        this.parent_view.selected = false;
850
        this.deselect_all();
851
        this.hide(true);
852
      },
853

    
854
      show_parent: function() {
855
        var left = this.quota.get_available();
856
        var available = this.collection.length || left;
857
        if (!available) { 
858
          this.hide_parent();
859
          return;
860
        }
861
        this.select_first();
862
        this.parent_view.item.addClass("selected");
863
        this.parent_view.input.attr("checked", true);
864
        this.parent_view.selected = true;
865
        this.show(true);
866
      },
867

    
868
      update_available: function() {
869
        var left = this.quota.get_available();
870
        var available = this.collection.length || left;
871
        var available_el = this.parent_view.$(".available");
872
        var no_available_el = this.parent_view.$(".no-available");
873
        var parent_check = this.parent_view.$("input[type=checkbox]");
874
        var create = this.$(".create.model-item");
875
        var create_link = this.$(".create a");
876
        var create_no_available = this.$(".create .no-available");
877

    
878
        if (!available) {
879
          // no ip's available to select
880
          this.hide_parent();
881
          available_el.hide();
882
          no_available_el.show();
883
          parent_check.attr("disabled", true);
884
        } else {
885
          // available floating ip
886
          var available_text = "".format(
887
            this.collection.length + this.quota.get_available());
888
          available_el.removeClass("hidden").text(available_text).show();
889
          available_el.show();
890
          no_available_el.hide();
891
          parent_check.attr("disabled", false);
892
        }
893

    
894
        if (left) {
895
          // available quota
896
          create.removeClass("no-available");
897
          create.show();
898
          create_link.show();
899
          create_no_available.hide();
900
        } else {
901
          // no available quota
902
          create.addClass("no-available");
903
          create.hide();
904
          create_link.hide();
905
          //create_no_available.show();
906
        }
907
        this.update_selected();
908
      },
909
      
910
      update_selected: function() {
911
        // reset missing entries
912
        _.each(this.selected_ips.length, function(ip) {
913
          if (!this.collection.get(ip.id)) {
914
            this.selected_ips = _.without(this.selected_ips, ip);
915
          }
916
        }, this);
917

    
918
        if (this.selected_ips.length) {
919
          this.parent_view.input.attr("checked", true);
920
          this.parent_view.item.addClass("selected");
921
          this.parent_view.selected = true;
922
        } else {
923
          this.parent_view.input.attr("checked", false);
924
          this.parent_view.item.removeClass("selected");
925
          this.parent_view.selected = false;
926
        }
927
      },
928

    
929
      post_remove_model_view: function(view) {
930
        view.deselect();
931
        view.unbind("change:select", this.handle_ip_select)
932
      },
933

    
934
      handle_create_error: function() {},
935
      
936
      set_creating: function() {
937
        var create_link = this.$(".create a");
938
        var create_no_available = this.$(".create .no-available");
939
        var loading = this.$(".create .loading");
940
        create_link.hide();
941
        loading.show();
942
      },
943

    
944
      reset_creating: function() {
945
        var loading = this.$(".create .loading");
946
        loading.hide();
947
        this.update_available();
948
      },
949

    
950
      create_ip: function() {
951
        if (!this.quota.get_available()) { return }
952
        var self = this;
953
        this.set_creating();
954
        synnefo.storage.floating_ips.create({floatingip:{}}, {
955
          error: _.bind(this.handle_create_error, this),
956
          complete: function() {
957
            synnefo.storage.quotas.fetch();
958
            self.reset_creating();
959
          }
960
        });
961
      },
962
      
963
      select_first: function() {
964
        if (this.selected_ips.length > 0) { return }
965
        if (this._subviews.length == 0) { return }
966
        this._subviews[0].select();
967
        if (!_.contains(this.selected_ips, this._subviews[0].model)) {
968
          this.selected_ips.push(this._subviews[0].model);
969
        }
970
      },
971

    
972
      post_add_model_view: function(view, model) {
973
        view.bind("change:select", this.handle_ip_select)
974
        if (!this.selected_ips.length && this._subviews.length == 1) {
975
          this.select_first();
976
        }
977
      },
978

    
979
      handle_ip_select: function(view) {
980
        if (view.selected) {
981
          if (!_.contains(this.selected_ips, view.model)) {
982
            this.selected_ips.push(view.model);
983
          }
984
        } else {
985
          this.selected_ips = _.without(this.selected_ips, view.model);
986
        }
987
        this.update_selected();
988
      },
989
      
990
      post_show: function() {
991
        this.update_available();
992
      },
993

    
994
      get_floating_ips: function() {
995
        return this.selected_ips;
996
      }
997
    });
998

    
999
    views.NetworkSelectPrivateNetworks = views.NetworkSelectTypeView.extend({
1000
      tpl: '#networks-select-private-tpl',
1001
      model_view_cls: views.NetworkSelectPrivateNetwork,
1002
      get_networks: function() {
1003
        return _.filter(_.map(this._subviews, function(view) {
1004
          if (view.selected) { return view.model.id }
1005
        }), function(id) { return id });
1006
      }
1007

    
1008
    });
1009

    
1010
    views.NetworkSelectView = views.ext.ModelView.extend({
1011
      rivets_view: true,
1012
      tpl: '#networks-select-view-tpl',
1013
      select_public: true,
1014
      
1015
      forced_values_title_map: {
1016
        "SNF:ANY_PUBLIC_IPV6": "Internet (public IPv6)",
1017
        "SNF:ANY_PUBLIC_IPV4": "Internet (public IPv4)"
1018
      },
1019

    
1020
      initialize: function(options) {
1021
        this.quotas = synnefo.storage.quotas.get('cyclades.private_network');
1022
        options = options || {};
1023
        options.model = options.model || new models.Model();
1024
        this.private_networks = new Backbone.FilteredCollection(undefined, {
1025
          collection: synnefo.storage.networks,
1026
          collectionFilter: function(m) {
1027
            return !m.get('is_public')
1028
        }});
1029

    
1030
        this.public_networks = new Backbone.Collection();
1031
        this.public_networks.comparator = function(m) {
1032
          if (m.get('forced')) {
1033
            return -1
1034
          }  
1035
          return 100;
1036
        }
1037
        
1038
        if (synnefo.config.forced_server_networks.length) {
1039
          _.each(synnefo.config.forced_server_networks, function(network) {
1040
            var forced = synnefo.storage.networks.get(network);
1041
            if (!forced) {
1042
              var name = this.forced_values_title_map[network];
1043
              if (!name) { name = "Forced network ({0})".format(network)}
1044
              forced = new models.networks.Network({
1045
                id: network,
1046
                name: name, 
1047
                subnets: [],
1048
                is_public: true,
1049
                forced: true
1050
              });
1051
            } else {
1052
              forced.set({'forced': true});
1053
            }
1054
            this.public_networks.add(forced);
1055
          }, this);
1056
        }
1057

    
1058
        // combined public
1059
        this.combined_public = new models.networks.CombinedPublicNetwork();
1060
        this.combined_public.set({noselect: true, 
1061
                                  name: 'Internet (public IPv4)', 
1062
                                  forced: false});
1063
        this.public_networks.add(this.combined_public);
1064

    
1065
        model_attrs = {
1066
          public_collection: this.public_networks,
1067
          private_collection: this.private_networks,
1068
          floating_selected: true
1069
        }
1070

    
1071
        options.model.set(model_attrs);
1072
        this._configure(options);
1073
        return views.NetworkSelectView.__super__.initialize.call(this, options);
1074
      },
1075

    
1076
      get_selected_floating_ips: function() {
1077
        var ips = [];
1078
        _.each(this._subviews, function(view) {
1079
          if (view.get_floating_ips) {
1080
            ips = _.union(ips, view.get_floating_ips());
1081
          }
1082
        }, this);
1083
        return _.filter(
1084
          _.map(ips, function(ipid) { 
1085
          return synnefo.storage.floating_ips.get(parseInt(ipid))
1086
        }), function(ip) { return ip });
1087
      },
1088

    
1089
      get_selected_networks: function() {
1090
        var networks = [];
1091
        _.each(this._subviews, function(view) {
1092
          if (view.get_networks) {
1093
            networks = _.union(networks, view.get_networks());
1094
          }
1095
        }, this);
1096
        return _.filter(
1097
          _.map(networks, function(netid) { 
1098
          return synnefo.storage.networks.get(netid)
1099
        }), function(net) { return net });
1100
      }
1101
    });
1102
 
1103
})(this);