Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (46.3 kB)

1
// Copyright 2011 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
    
56
    views.NetworkConnectVMsOverlay = views.Overlay.extend({
57
        title: "Connect machine",
58
        overlay_id: "overlay-select-vms",
59
        content_selector: "#network-vms-select-content",
60
        css_class: "overlay-info",
61

    
62
        initialize: function() {
63
            views.NetworkConnectVMsOverlay.__super__.initialize.apply(this);
64
            this.list = this.$(".vms-list ul");
65
            this.empty_message = this.$(".empty-message");
66

    
67
            // flag for submit handler to avoid duplicate bindings
68
            this.submit_handler_set = false;
69
        },
70
        
71
        init_handlers: function() {
72
            var self = this;
73
            this.list.find("li").click(function(){
74
                $(this).toggleClass("selected");
75
            });
76
            
77
            if (!this.submit_handler_set) {
78
                // avoid duplicate submits
79
                this.el.find(".create").click(_.bind(function() {
80
                    this.submit();
81
                }, this));
82
                this.submit_handler_set = true;
83
            }
84
        },
85

    
86
        reset: function() {
87
            this.list.find("li").remove();
88
        },
89

    
90
        beforeOpen: function() {
91
            this.reset();
92
            this.update_layout();
93
        },
94
        
95
        vm: function(vm) {
96
            if (vm.id) { var id = vm.id } else {var id = vm}
97
            return this.list.find(".vm-" + id);
98
        },
99

    
100
        get_selected: function() {
101
            return this.list.find(".selected").map(function() {return $(this).data('vm')})
102
        },
103

    
104
        update_layout: function() {
105
            if (this.vms.length == 0) {
106
                this.empty_message.show();
107
            } else {
108
                this.empty_message.hide();
109
            }
110

    
111
            _.each(this.vms, _.bind(function(vm){
112
                
113
                var html = '<li class="vm option options-object vm-{0}">' +
114
                           '<div class="options-object-cont">' +
115
                           '{2}' + 
116
                           '<span class="title">{1}</span>' + 
117
                           '<span class="value">{3}</span></div>' + 
118
                           '</li>';
119
                var el = $(html.format(vm.id, 
120
                                       util.truncate(_.escape(vm.get("name")), 23), 
121
                                       snf.ui.helpers.vm_icon_tag(vm, "small", {'class':'os'}),
122
                                       _.escape(vm.get_os())
123
                                      ))
124
                el.data({vm:vm, vm_id:vm.id})
125
                this.list.append(el);
126

    
127
                vm.bind("remove", function(){ el.remove()})
128
                vm.bind("change:name", function(i,v){el.find(".title").text(v)})
129
            }, this));
130
            
131
            this.init_handlers();
132
            this.set_selected();
133
        },
134

    
135
        set_selected: function() {
136
            _.each(this.selected, _.bind(function(el){
137
                this.vm(el).addClass("selected");
138
            }, this));
139
        },
140

    
141
        show_vms: function(network, vms, selected, callback) {
142
            this.network = network;
143
            this.reset();
144
            this.set_subtitle(network.escape("name"));
145
            this.vms = vms;
146
            if (!synnefo.config.network_allow_duplicate_vm_nics) {
147
                this.vms = _.filter(this.vms, function(vm) {
148
                    return !vm.connected_to(this.network);
149
                }, this);
150
            }
151

    
152
            this.selected = selected;
153
            this.cb = callback;
154
            this.show();
155
        },
156

    
157
        submit: function() {
158
            this.cb(this.get_selected());
159
        }
160
    })
161

    
162
    views.NetworkActionsView = views.View.extend({
163
        
164
        initialize: function(view, net, el, opts) {
165
            this.parent = view
166
            this.network = net;
167
            this.el = el;
168
            
169
            this.actions = this.$(".actions");
170
            this.selected = undefined;
171

    
172
            this.destroy = this.$(".actions .destroy a");
173
            this.connect = this.$(".actions .add");
174

    
175
            this.init_handlers();
176
            this.update_layout();
177
        },
178

    
179
        init_handlers: function() {
180
            this.connect.click(_.bind(function(e){
181
                e.preventDefault();
182
            }))
183
        },
184

    
185
        update_layout: function() {
186
        }
187
    });
188

    
189
    views.NetworkCreateView = views.Overlay.extend({
190
        view_id: "network_create_view",
191
        content_selector: "#networks-create-content",
192
        css_class: 'overlay-networks-create overlay-info',
193
        overlay_id: "network-create-overlay",
194

    
195
        title: "Create new private network",
196
        subtitle: "Networks",
197

    
198
        initialize: function(options) {
199
            views.NetworkCreateView.__super__.initialize.apply(this);
200

    
201
            this.create_button = this.$("form .form-action.create");
202
            this.text = this.$(".network-create-name");
203
            this.form = this.$("form");
204

    
205
            this.dhcp_select = this.$("#network-create-dhcp");
206
            this.type_select = this.$("#network-create-type");
207
            this.subnet_select = this.$("#network-create-subnet");
208
            this.subnet_custom = this.$("#network-create-subnet-custom");
209
                
210
            this.dhcp_form = this.$("#network-create-dhcp-fields");
211
            
212
            this.subnet_select.find(".subnet").remove();
213
            _.each(synnefo.config.network_suggested_subnets, function(subnet){
214
                this.subnet_select.append($('<option value='+subnet+' class="subnet">'+subnet+'</option>'));
215
            }, this);
216

    
217
            this.type_select.find(".subnet").remove();
218
            _.each(synnefo.config.network_available_types, function(name, value){
219
                this.type_select.append($('<option value='+value+' class="subnet">'+name+'</option>'));
220
            }, this);
221
            
222
            if (_.keys(synnefo.config.network_available_types).length <= 1) {
223
                this.type_select.closest(".form-field").hide();
224
            }
225

    
226
            this.check_dhcp_form();
227
            this.init_handlers();
228
        },
229

    
230
        reset_dhcp_form: function() {
231
          this.subnet_select.find("option")[0].selected = 1;
232
          this.subnet_custom.val("");
233
        },
234

    
235
        check_dhcp_form: function() {
236
            if (this.dhcp_select.is(":checked")) {
237
                this.dhcp_form.show();
238
            } else {
239
                this.dhcp_form.hide();
240
            }
241
            
242
            if (this.subnet_select.val() == "custom") {
243
                this.subnet_custom.show();
244
            } else {
245
                this.subnet_custom.hide();
246
            }
247
        },
248

    
249
        init_handlers: function() {
250

    
251
            this.dhcp_select.click(_.bind(function(e){
252
                this.check_dhcp_form();
253
                this.reset_dhcp_form();
254
            }, this));
255

    
256
            this.subnet_select.change(_.bind(function(e){
257
                this.check_dhcp_form();
258
                if (this.subnet_custom.is(":visible")) {
259
                    this.subnet_custom.focus();
260
                }
261
            }, this));
262

    
263
            this.create_button.click(_.bind(function(e){
264
                this.submit();
265
            }, this));
266

    
267
            this.form.submit(_.bind(function(e){
268
                e.preventDefault();
269
                this.submit;
270
                return false;
271
            }, this))
272

    
273
            this.text.keypress(_.bind(function(e){
274
                if (e.which == 13) {this.submit()};
275
            },this))
276
        },
277

    
278
        submit: function() {
279
            if (this.validate()) {
280
                this.create();
281
            };
282
        },
283
        
284
        validate: function() {
285
            // sanitazie
286
            var t = this.text.val();
287
            t = t.replace(/^\s+|\s+$/g,"");
288
            this.text.val(t);
289

    
290
            if (this.text.val() == "") {
291
                this.text.closest(".form-field").addClass("error");
292
                this.text.focus();
293
                return false;
294
            } else {
295
                this.text.closest(".form-field").removeClass("error");
296
            }
297
            
298
            if (this.dhcp_select.is(":checked")) {
299
                if (this.subnet_select.val() == "custom") {
300
                    var sub = this.subnet_custom.val();
301
                    sub = sub.replace(/^\s+|\s+$/g,"");
302
                    this.subnet_custom.val(sub);
303
                        
304
                    if (!synnefo.util.IP_REGEX.exec(this.subnet_custom.val())) {
305
                        this.subnet_custom.closest(".form-field").prev().addClass("error");
306
                        return false;
307
                    } else {
308
                        this.subnet_custom.closest(".form-field").prev().removeClass("error");
309
                    }
310
                };
311
            }
312

    
313
            return true;
314
        },
315
        
316
        get_next_available_subnet: function() {
317
            var auto_tpl = synnefo.config.automatic_network_range_format;
318
            if (!auto_tpl) {
319
                return null
320
            }
321
            var index = 0;
322
            var subnet = auto_tpl.format(index);
323
            var networks = synnefo.storage.networks;
324
            var check_existing = function(n) { return n.get('cidr') == subnet }
325
            while (networks.filter(check_existing).length > 0 && index <= 255) {
326
                index++;
327
                subnet = auto_tpl.format(index); 
328
            }
329
            return subnet;
330
        },
331

    
332
        create: function() {
333
            this.create_button.addClass("in-progress");
334

    
335
            var name = this.text.val();
336
            var dhcp = this.dhcp_select.is(":checked");
337
            var subnet = null;
338
            var type = this.type_select.val();
339

    
340
            if (dhcp) {
341
                if (this.subnet_select.val() == "custom") {
342
                    subnet = this.subnet_custom.val();
343
                } else if (this.subnet_select.val() == "auto") {
344
                    subnet = this.get_next_available_subnet()
345
                } else {
346
                    subnet = this.subnet_select.val();
347
                }
348
                
349
            }
350

    
351
            snf.storage.networks.create(name, type, subnet, dhcp, _.bind(function(){
352
                this.hide();
353
            }, this));
354
        },
355

    
356
        beforeOpen: function() {
357
            this.create_button.removeClass("in-progress")
358
            this.text.closest(".form-field").removeClass("error");
359
            this.text.val("");
360
            this.text.show();
361
            this.text.focus();
362
            this.subnet_custom.val("");
363
            this.subnet_select.val("auto");
364
            this.dhcp_select.attr("checked", true);
365
            this.type_select.val(_.keys(synnefo.config.network_available_types)[0]);
366
            this.check_dhcp_form();
367
        },
368

    
369
        onOpen: function() {
370
            this.text.focus();
371
        }
372
    });
373

    
374
    views.NetworkNICView = views.View.extend({
375

    
376
        initialize: function(nic, parent, firewall_controls, el) {
377
            this.firewall_controls = firewall_controls || false;
378
            this.nic = nic;
379
            this.vm = nic.get_vm();
380
            // parent view di
381
            this.parent = parent;
382
            // TODO make it better
383
            this.el = el || this.parent.get_nic_view(nic);
384

    
385
            this.init_layout();
386
            this.update_layout();
387

    
388
            this.disconnect = this.$(".action-disconnect");
389
            this.confirm_el = this.$(".confirm_single");
390
            this.cancel = this.$("button.no");
391
            this.confirm = this.$("button.yes");
392
            this.details = this.$(".action-details");
393
            this.vm_connect = this.$(".machine-connect");
394
            this.actions = this.$(".net-vm-actions");
395

    
396
            this.init_handlers();
397
            this.connect_overlay = new views.VMConnectView();
398
            
399
            this.firewall_view = undefined;
400
            if (this.firewall_controls) {
401
                this.firewall_view = new views.FirewallEditView(this.nic, this.parent.network, this);
402
            }
403

    
404
        },
405
        
406
        reset_all_net_actions: function(act_types) {
407
            synnefo.storage.networks.each(function(n){
408
                var actions = n.get('actions');
409
                _.each(act_types, function(type){
410
                    actions.remove_all(type);
411
                })
412
            })
413
        },
414

    
415
        init_handlers: function() {
416
            if (!this.parent.network.is_public()) {
417
                this.disconnect.click(_.bind(function(e){
418
                    e.preventDefault();
419
                    this.reset_all_net_actions(['destroy','disconnect']);
420
                    this.parent.network.get("actions").remove_all("disconnect");
421
                    this.parent.network.get("actions").add("disconnect", this.nic.id);
422
                    this.parent.network.get("actions").remove("destroy");
423
                }, this));
424
                this.cancel.click(_.bind(function(e){
425
                    this.parent.network.get("actions").remove("disconnect", this.nic.id);
426
                    e.preventDefault()
427
                }, this));
428

    
429
                this.confirm.click(_.bind(function(e){
430
                    e.preventDefault()
431
                    this.disconnect_nic();
432
                    this.confirm_el.hide();
433
                    this.disconnect.removeClass("selected");
434
                    this.actions.find("a").removeClass("visible");
435
                }, this));
436

    
437
                snf.ui.main.bind("view:change", _.bind(function(v) {
438
                    if (v == "networks" ){ return }
439
                    this.confirm_el.hide();
440
                    this.disconnect.removeClass("selected");
441
                }, this));
442

    
443
                this.$(".remove-icon").click(_.bind(function(){
444
                    this.reset_all_net_actions(['destroy','disconnect']);
445
                    this.parent.network.get("actions").remove_all("disconnect");
446
                    this.parent.network.get("actions").add("disconnect", this.nic.id);
447
                    this.parent.network.get("actions").remove("destroy");
448
                }, this));
449

    
450
                this.vm_connect.click(_.bind(function() {
451
                    this.connect_overlay.show(this.vm);
452
                }, this));
453
                
454
                this.parent.network.bind("change:actions", _.bind(function(model, action){
455
                    if (this.parent.network.get("actions").contains("disconnect", this.nic.id)) {
456
                        this.confirm_disconnect();
457
                    } else {
458
                        this.cancel_disconnect();
459
                    }
460
                }, this));
461
            }
462
            
463
            var vm = this.vm;
464
            this.details.click(function(e){
465
                e.preventDefault();
466
                snf.ui.main.show_vm_details(vm);
467
            });
468

    
469
        },
470

    
471
        cancel_disconnect: function() {
472
            this.confirm_el.hide();
473
            this.disconnect.removeClass("selected");
474
            this.$(".net-vm-actions a").removeClass("visible");
475
        },
476

    
477
        confirm_disconnect: function() {
478
            this.confirm_el.show();
479
            this.disconnect.addClass("selected");
480
            this.$(".net-vm-actions a").addClass("visible");
481
        },
482

    
483
        init_layout: function() {
484
            if (!this.firewall_controls) { return };
485
        },
486

    
487
        update_layout: function() {
488
            this.$(".vm-name").text(snf.util.truncate(this.vm.get("name"), 40));
489
            this.$("img.logo").attr("src", ui.helpers.vm_icon_path(this.vm, "medium"));
490

    
491
            if (this.firewall_view) {
492
                this.$(".ipv4-text").text(this.nic.get_v4_address());
493
                this.$(".ipv6-text").text(this.nic.get_v6_address());
494
            }
495

    
496
            if (this.firewall_view) {
497
                this.firewall_view.update_layout();
498
            }
499

    
500
            if (!this.firewall_view) {
501
                this.$(".ip4-container").hide();
502
                this.$(".ip6-container").hide();
503
                
504
                if (this.nic.get("ipv4")) {
505
                    this.$(".ipv4-text").text(this.nic.get_v4_address());
506
                    this.$(".ip4-container").show();
507
                    this.$(".machine-connect .content").hide();
508
                } else if (this.nic.get("ipv6")) {
509
                    this.$(".ipv6-text").text(this.nic.get_v6_address());
510
                    this.$(".ip6-container").show();
511
                    this.$(".machine-connect .content").hide();
512
                } else {
513
                    this.$(".machine-connect .content").show();
514
                }
515
            } else {
516
            }
517
        },
518

    
519
        disconnect_nic: function() {
520
            this.$("a.selected").removeClass("selected");
521
            this.nic.get_network().remove_nic(this.nic);
522
        }
523
    })
524

    
525
    views.NetworkModelRenameView = views.View.extend({
526
        initialize: function(parent, network) {
527
            this.parent = parent;
528
            this.network = network;
529
            this.el = this.parent.el.find(".name-div");
530

    
531
            this.icon = this.$(".rename-network");
532
            this.save = this.$("span.save");
533
            this.cancel = this.$("span.cancel");
534
            this.buttons = this.$(".editbuttons");
535
            this.name = this.$("span.name");
536
            this.editing = false;
537
            this.init_handlers();
538
            this.update_layout();
539
        },
540

    
541
        init_handlers: function() {
542
            this.icon.click(_.bind(function(){
543
                this.editing = true;
544
                this.update_layout();
545
            }, this));
546
            this.cancel.click(_.bind(function(){
547
                this.editing = false;
548
                this.update_layout();
549
            }, this));
550
            this.save.click(_.bind(function(){
551
                this.submit();
552
            }, this))
553
        },
554
        
555
        submit: function() {
556
            var value = _(this.input.val()).trim();
557
            if (value == "") { return }
558

    
559
            this.network.rename(value, _.bind(function(){
560
                this.editing = false;
561
                this.update_layout();
562
            }, this));
563
        },
564

    
565
        create_input: function() {
566
            this.input = $('<input type="text" class="network-rename-input" />');
567
            this.input.val(this.network.get("name"));
568
            this.el.append(this.input);
569
            this.input.focus();
570
            this.input.bind("keydown", _.bind(function(ev){
571
                ev.keyCode = ev.keyCode || ev.which;
572
                if (ev.keyCode == 13) { this.submit(); };
573
                if (ev.keyCode == 27) {this.editing = false; this.update_layout()};
574
            }, this));
575
        },
576

    
577
        remove_input: function() {
578
            if (!this.input) { return }
579
            this.input.remove();
580
        },
581

    
582
        update_layout: function() {
583
            if (this.editing) {
584
                if (this.buttons.is(":visible")) { return }
585
                this.icon.hide();
586
                this.buttons.show();
587
                this.create_input();
588
                this.name.hide();
589
            } else {
590
                this.buttons.hide();
591
                this.remove_input();
592
                this.name.show();
593
                this.icon.show();
594
            }
595
        }
596
    })
597

    
598
    views.FirewallEditView = views.View.extend({
599

    
600
        initialize: function(nic, network, parent) {
601
            this.parent = parent;
602
            this.vm = nic.get_vm();
603
            this.nic = nic;
604
            this.network = network;
605
            this.el = this.parent.el;
606

    
607
            views.FirewallEditView.__super__.initialize.apply(this);
608

    
609
            // elements
610
            this.toggler = this.$(".firewall-toggle");
611
            this.indicator = this.$(".machines-label span");
612
            this.progress = this.$(".network-progress-indicator");
613
            this.content = this.$(".firewall-content");
614
            this.inputs = this.$("input[type=radio]");
615
            this.labels = this.$("span.checkbox-legends, label.checkbox-legends");
616
            this.apply = this.$(".firewall-apply");
617

    
618
            this.$(".firewall-content").hide();
619
            this.$(".firewall-content input[type=radio]").attr("name", "firewall-opt-for-{0}".format(this.vm.id))
620
            var mode = this.vm.get_firewall_profile();
621
            this.$(".firewall-content input[value={0}]".format(mode)).attr("checked", true);
622

    
623
            this.init_handlers();
624
            this.update_layout();
625

    
626
            var self = this;
627
            this.nic.bind("change:pending_firewall_sending", function(nic, value) {
628
                if (value) {
629
                    self.apply.addClass("in-progress");       
630
                    self.progress.show();
631
                } else {
632
                    self.apply.removeClass("in-progress");       
633
                    self.progress.hide();
634
                    self.toggler.click();
635
                }
636
            });
637

    
638
            this.nic.bind("change:firewallProfile", function(nic){
639
                self.update_layout();
640
                self.reset_value();
641
            })
642

    
643
        },
644
        
645
        _get_selected: function() {
646
            return this.inputs.filter(":checked");
647
        },
648

    
649
        reset_selected: function() {
650
        },
651

    
652
        submit: function() {
653
        },
654

    
655
        reset_value: function() {
656
            this.inputs.filter("[value={0}]".format(
657
              this.nic.get('firewallProfile'))).attr("checked", true);
658
        },
659

    
660
        init_handlers: function() {
661
            this.toggler.click(_.bind(function(){
662
                cont = this.content;
663
                if (cont.is(":visible")) {
664
                    this.hide_firewall();
665
                    this.reset_value();
666
                } else {
667
                    this.show_firewall();
668
                }
669

    
670
                $(window).trigger("resize");
671
            }, this))
672
            
673
            this.apply.click(_.bind(function(){
674
                this.nic.set_firewall(this.value());
675
            }, this))
676

    
677
            this.inputs.change(_.bind(function(){
678
                this.update_selected();
679
            }, this))
680
            
681
            var self = this;
682
            this.$(".checkbox-legends").click(function(el) {
683
                var el = $(this);
684
                el.prev().click();
685
                self.update_selected();
686
            })
687
        },
688

    
689
        update_selected: function() {
690
            this.update_layout();
691
        },
692

    
693
        show_firewall: function() {
694
            this.content.slideDown(100, function(){$(window).trigger("resize")});
695
            this.toggler.addClass("open");
696
        },
697

    
698
        hide_firewall: function() {
699
            this.content.slideUp(100, function(){$(window).trigger("resize")});
700
            this.toggler.removeClass("open");
701
        },
702

    
703
        value: function() {
704
            return this._get_selected().val();
705
        },
706

    
707
        update_layout: function() {
708
            if (this.value() == this.vm.get_firewall_profile()) {
709
                this.apply.hide();
710
            } else {
711
                this.apply.show();
712
            }
713

    
714
            var profile = this.vm.get_firewall_profile();
715
            if (this.vm.has_firewall(this.network.id)) {
716
                this.$(".firewall-toggle .label span").text("On");
717
                this.$(".firewall-toggle .label span").removeClass("firewall-off");
718
                this.$(".firewall-toggle .label span").addClass("firewall-on");
719
            } else {
720
                this.$(".firewall-toggle .label span").text("Off");
721
                this.$(".firewall-toggle .label span").removeClass("firewall-on");
722
                this.$(".firewall-toggle .label span").addClass("firewall-off");
723
            }
724
            
725
            this.$("span.checkbox-legends").removeClass("current");
726
            this.inputs.filter("[value={0}]".format(profile)).next().addClass("current");
727
            
728
        }
729
    })
730

    
731
    views.NetworkModelView = views.View.extend({
732
        
733
        firewall: false,
734

    
735
        initialize: function(network, view) {
736
            this.parent_view = view;
737
            this.network = network;
738
            this.main_view_id = this.main_view_id ? this.main_view_id : "networks_view_" + network.id;
739
            this.is_public = network.is_public();
740

    
741
            this.init_nics_handlers();
742
            
743
            this.view_id = "networks_view_" + network.id;
744
            views.NetworkModelView.__super__.initialize.call(this);
745

    
746
            this.nics_views = {};
747

    
748
            this.el = this.create_el();
749

    
750
            // element helpers
751
            this.nics_list = this.$(".machines-list");
752
            this.nics_list_toggler = this.$(".list-toggle");
753
            
754
            this.init_handlers();
755
            this.init_toggler_handlers();
756
            this.update_nics();
757
            this.update_layout();
758

    
759
            this.hide_nics_list();
760
            this.nics_list.hide();
761

    
762
            this.rename_view = undefined;
763
            if (!this.network.is_public()) {
764
                // allow network rename for non public networks only
765
                this.rename_view = new views.NetworkModelRenameView(this, network);
766
            }
767
            
768
            var self = this;
769
            this.network.bind('change:status', function() {
770
                self.update_layout();
771
            });
772

    
773
        },
774

    
775
        init_nics_handlers: function() {
776
            storage.nics.bind("add", _.bind(this.nic_added_handler, this, "add"));
777
            storage.nics.bind("change", _.bind(this.nic_changed_handler, this, "change"));
778
            storage.nics.bind("reset", _.bind(this.nic_changed_handler, this, "reset"));
779
            storage.nics.bind("remove", _.bind(this.nic_removed_handler, this, "remove"));
780
        },
781

    
782

    
783
        show_nics_list: function() {
784
            //if (this.nics_empty()) { return }
785
            var self = this;
786
            this.nics_list_toggler.addClass("open");
787
            this.nics_list.slideDown(function(){
788
                $(window).trigger("resize");
789
            }).closest(".network").addClass("expand");
790
            this.$(".empty-network-slot").slideDown();
791
            this.nics_visible = true;
792
        },
793

    
794
        hide_nics_list: function() {
795
            this.nics_list_toggler.removeClass("open");
796
            this.nics_list.slideUp(function(){
797
                $(window).trigger("resize");
798
            }).closest(".network").removeClass("expand");
799
            this.$(".empty-network-slot").slideUp();
800
            this.nics_visible = false;
801
        },
802
        
803
        init_toggler_handlers: function() {
804
            this.nics_list_toggler.click(_.bind(function(){
805
                if (this.nics_list.is(":visible")) {
806
                    this.hide_nics_list();
807
                } else {
808
                    this.show_nics_list();
809
                    this.fix_left_border();
810
                }
811

    
812
            }, this));
813
            $(window).bind("resize", _.bind(function() {
814
                this.fix_left_border();
815
            }, this));
816
        },
817

    
818
        init_handlers: function() {
819
            var self = this;
820

    
821

    
822
            this.$(".action-add").click(_.bind(function(e){
823
                e.preventDefault();
824
                this.network.get("actions").remove("destroy");
825
                this.show_connect_vms();
826
            }, this))
827

    
828
            this.$(".add-icon").click(_.bind(function(e){
829
                e.preventDefault();
830
                this.show_connect_vms();
831
            }, this))
832

    
833
            this.$(".net-actions .destroy a").click(_.bind(function(e){
834
                e.preventDefault();
835
                synnefo.storage.networks.each(function(n) {
836
                    n.get('actions').remove_all("disconnect");
837
                    if (!synnefo.config.network_allow_multiple_destory) {
838
                        n.get('actions').remove_all("destroy");
839
                    }
840
                });
841
                self.network.get("actions").add("destroy");
842
                self.network.get("actions").remove_all("disconnect");
843
            }, this));
844

    
845
            self.network.bind("change:actions", _.bind(function(net, action) {
846
                if (this.network.get("actions").contains("destroy")) {
847
                    this.confirm_destroy();
848
                } else {
849
                    this.cancel_destroy();
850
                }
851
            }, this));
852
            
853

    
854
            // reset pending destory action after successful removal
855
            self.network.bind("remove", _.bind(function(net){
856
                net.get("actions").remove_all("destroy");
857
            }));
858

    
859
            this.$(".net-actions button.no").click(function(e){
860
                e.preventDefault();
861
                self.network.get("actions").remove("destroy");
862
            });
863

    
864
            this.$(".net-actions button.yes").click(function(e){
865
                e.preventDefault();
866
                var el = $(this);
867
                el.closest(".confirm_single").hide();
868
                el.parent().parent().find(".selected").removeClass("selected");
869
                self.network.call('destroy', {}, function(){
870
                    el.closest(".confirm_single").removeClass("in-progress");
871
                });
872
                el.closest(".confirm_single").addClass("in-progress");
873
            });
874

    
875
            snf.ui.main.bind("view:change", _.bind(function(v) {
876
                if (v == "networks" ){ return }
877
                this.$(".confirm_single").hide();
878
                this.$("a.selected").removeClass("selected");
879
            }, this));
880
            
881
            this.$(".empty-network-slot").hide();
882
        },
883

    
884
        show_connect_vms: function() {
885
            this.$(".confirm_single").hide();
886
            this.$("a.selected").removeClass("selected");
887
            var vms = this.network.get_connectable_vms();
888
            this.parent_view.connect_machines_view.show_vms(this.network,
889
                                                            vms, [], 
890
                                                            _.bind(this.connect_vms, this));
891
        },
892

    
893
        cancel_destroy: function() {
894
            this.$(".net-actions .destroy .confirm_single").hide();
895
            this.$(".net-actions .destroy a.selected").removeClass("selected");
896
            this.$(".net-actions a").removeClass("visible");
897
        },
898

    
899
        confirm_destroy: function() {
900
            this.$(".destroy .confirm_single").show();
901
            this.$(".destroy a").addClass("selected");
902
            this.$(".net-actions a").addClass("visible");
903
        },
904

    
905
        connect_vms: function(vms) {
906
            _.each(vms, _.bind(function(vm){
907
                this.network.add_vm(vm);
908
            }, this));
909

    
910
            this.parent_view.connect_machines_view.hide();
911
        },
912

    
913
        create_el: function() {
914
            return this.$(this.tpl).clone().attr("id", this.main_view_id);
915
        },
916

    
917
        get_nic_id: function(nic) {
918
            return this.nic_id_tpl.format(nic.id);
919
        },
920

    
921
        get_nic_view: function(nic) {
922
            return $(this.get_nic_id(nic));
923
        },
924
        
925
        nic_in_network: function(nic) {
926
          return nic.get_network().id == this.network.id;
927
        },
928

    
929
        nic_added_handler: function(action, nic) {
930
            if (!this.nic_in_network(nic)) { return };
931
            this.add_or_update_nic(nic);
932
            this.update_layout();
933
            this.fix_left_border();
934
        },
935

    
936
        nic_changed_handler: function(action, nics, model, changes) {
937
            var nics = nics || [];
938

    
939
            // reset or update
940
            if (action == "reset") {
941
                nics = nics;
942
            } else {
943
                if (!_.isArray(nics)) {
944
                    nics = [nics]
945
                }
946
            }
947
            
948
            _.each(nics, _.bind(function(nic) {
949
                if (!this.nic_in_network(nic)) { return };
950
                this.add_or_update_nic(nic);
951
            }, this));
952

    
953
            this.update_layout();
954
        },
955

    
956
        nic_removed_handler: function(action, nic, model) {
957
            if (!this.nic_in_network(nic)) { return };
958
            this.fix_left_border();
959
            this.remove_nic(nic);
960
            this.update_layout();
961
        },
962

    
963
        remove_nic: function(nic) {
964
            var nic_view = this.get_nic_view(nic);
965
            if (nic_view.length) {
966
                nic_view.remove();
967
                try {
968
                    delete this.nics_views[nic.id]
969
                } catch (err) {
970
                }
971
            }
972
        },
973
        
974
        create_nic_view: function(nic) {
975
            var nic_el = $(this.nic_tpl).clone().attr({
976
                id: this.get_nic_id(nic).replace("#","")
977
            });
978
            this.nics_list.append(nic_el);
979
            this.post_nic_add(nic);
980

    
981
            if (!this.nics_views[nic.id]) {
982
                var nic_view = this.nics_views[nic.id] = new views.NetworkNICView(nic, this, this.firewall, nic_el);
983
            }
984
        },
985

    
986
        add_or_update_nic: function(nic) {
987
            if (!nic) { return };
988
                
989
            var nic_el = this.get_nic_view(nic);
990
            var nic_view = this.nics_views[nic.id];
991

    
992
            if (nic_el.length == 0) {
993
                nic_view = this.create_nic_view(nic);
994
            }
995
            
996
            if (nic_view) { nic_view.update_layout() };
997

    
998
            this.update_nic(nic);
999
            this.post_nic_update(nic);
1000
        },
1001

    
1002
        update_nic: function(vm){},
1003
        post_nic_add: function(vm){},
1004
        post_nic_update: function(vm){},
1005
        
1006
        get_nics: function() {
1007
          return this.network.get_nics();
1008
        },
1009

    
1010
        update_nics: function(nics) {
1011
            if (!nics) { nics = this.get_nics() };
1012
            _.each(nics, _.bind(function(nic){
1013
                this.add_or_update_nic(nic);
1014
            }, this));
1015
        },
1016

    
1017
        check_empty_nics: function() {
1018
            if (this.get_nics().length == 0) {
1019
                this.hide_nics_list();
1020
            }
1021
        },
1022

    
1023
        nics_empty: function() {
1024
            return this.get_nics().length == 0;
1025
        },
1026

    
1027
        remove: function() {
1028
            $(this.el).remove();
1029
        },
1030
        
1031
        get_name: function() {
1032
          var net_name = this.network.get('name');
1033
          if (net_name == "public") { net_name = "Internet" };
1034
          return net_name;
1035
        },
1036

    
1037
        update_layout: function() {
1038
            // has vms ???
1039
            this.check_empty_nics();
1040

    
1041
            // is expanded ???
1042
            //
1043
            // whats the network status ???
1044
            //
1045
            this.$(".machines-count").text(this.get_nics().length);
1046

    
1047
            var net_name = this.get_name();
1048
            this.$(".name-div span.name").text(net_name);
1049

    
1050
            if (this.rename_view) {
1051
                this.rename_view.update_layout();
1052
            }
1053
            
1054
            this.$(".net-status").text(this.network.state_message());
1055

    
1056
            if (this.network.in_progress())  {
1057
                this.$(".spinner").show();
1058
                this.$(".network-indicator").addClass("in-progress");
1059
            } else {
1060
                this.$(".spinner").hide();
1061
                this.$(".network-indicator").removeClass("in-progress");
1062
            }
1063
                
1064
            if (this.network.get('state') == 'PENDING') {
1065
                this.el.addClass("pending");
1066
            } else {
1067
                this.el.removeClass("pending");
1068
            }
1069

    
1070
            if (this.network.get('state') == 'ERROR') {
1071
                this.el.addClass("in-error");
1072
                this.$(".network-indicator").addClass("error-state");
1073
            } else {
1074
                this.el.removeClass("in-error");
1075
                this.$(".network-indicator").removeClass("error-state");
1076
            }
1077

    
1078
            if (synnefo.config.network_strict_destroy) {
1079
                if (this.get_nics().length == 0 && 
1080
                        !this.network.in_progress()) {
1081
                    this.el.removeClass("disable-destroy");
1082
                } else {
1083
                    this.el.addClass("disable-destroy");
1084
                }
1085
            }
1086

    
1087
            if (this.network.get("state") == "DESTROY") {
1088
                this.$(".spinner").show();
1089
                this.$(".state").addClass("destroying-state");
1090
                this.$(".actions").hide();
1091
            } else {
1092
                this.$(".state").removeClass("destroying-state");
1093
                this.$(".actions").show();
1094
                this.$(".actions a").removeClass("visible");
1095
            }
1096
        },
1097

    
1098
        // fix left border position
1099
        fix_left_border: function(force) {
1100
            if (!this.nics_visible && !force) { return };
1101
            
1102
            var imgheight = 2783;
1103
            var opened_vm_height = 133 + 18;
1104
            var closed_vm_height = 61 + 20;
1105
            var additional_height = 25;
1106

    
1107
            if (!this.is_public) { 
1108
                imgheight = 2700;
1109
                additional_height = 65;
1110
            };
1111
            
1112
            var contents = this.$(".network-contents");
1113
            var last_vm = this.$(".network-machine:last .cont-toggler.open").length;
1114
            var last_vm_height = closed_vm_height;
1115
            if (last_vm > 0){
1116
                last_vm_height = opened_vm_height;
1117
            }
1118

    
1119
            var nics_opened = this.$(".network-machine .cont-toggler.open").length;
1120
            var nics_closed = this.$(".network-machine").length - nics_opened;
1121

    
1122
            var calc_height = (nics_opened * opened_vm_height) + (nics_closed * closed_vm_height) + additional_height; 
1123
            var bgpos = imgheight - calc_height + last_vm_height - 30;
1124
            this.$(".network-contents").css({'background-position':'33px ' + (-bgpos) + 'px'});
1125
        }
1126
    })
1127

    
1128
    views.PublicNetworkView = views.NetworkModelView.extend({
1129
        firewall: true,
1130
        tpl: "#public-template",
1131
        nic_tpl: "#public-nic-template",
1132
        nic_id_tpl: "#nic-{0}",
1133
        
1134
        initialize: function(network, view) {
1135
          views.PublicNetworkView.__super__.initialize.call(this, network, view);
1136
          this.fix_left_border(1);
1137
        },
1138

    
1139
        init_handlers: function(vm) {
1140
          $(window).bind("resize", _.bind(function() {
1141
              this.fix_left_border();
1142
          }, this));
1143
        }
1144
    });
1145

    
1146
    views.GroupedPublicNetworkView = views.PublicNetworkView.extend({
1147
        main_view_id: "grouped-public",
1148

    
1149
        initialize: function(network, view) {
1150
          this.networks = {};
1151
          this.add_network(network);
1152
          views.GroupedPublicNetworkView.__super__.initialize.call(this, 
1153
                                                                   network, 
1154
                                                                   view);
1155
        },
1156
          
1157
        get_name: function() {
1158
          return synnefo.config.grouped_public_network_name || views.GroupedPublicNetworkView.__super__.get_name.call(this);
1159
        },
1160

    
1161
        nic_in_network: function(nic) {
1162
          var nic_net  = nic.get_network();
1163
          return _.filter(this.networks, function(n) { 
1164
            return nic_net.id == n.id;
1165
          }).length > 0;
1166
        },
1167

    
1168
        get_nics: function() {
1169
          var n = _.flatten(_.map(this.networks, function(n){ return n.get_nics(); }));
1170
          return n
1171
        },
1172

    
1173
        add_network: function(net) {
1174
          this.networks[net.id] = net;
1175
        },
1176

    
1177
        remove_network: function(net) {
1178
          delete this.networks[net.id];
1179
          this.update_nics();
1180
        }
1181

    
1182
    })
1183
    
1184
    views.PrivateNetworkView = views.NetworkModelView.extend({
1185
        tpl: "#private-template",
1186
        nic_tpl: "#private-nic-template",
1187
        nic_id_tpl: "#nic-{0}"
1188
    })
1189

    
1190
    views.NetworksView = views.View.extend({
1191
        
1192
        view_id: "networks",
1193
        pane: "#networks-pane",
1194
        el: "#networks-pane",
1195

    
1196
        initialize: function() {
1197
            // elements shortcuts
1198
            this.create_cont = this.$("#networks-createcontainer");
1199
            this.container = this.$("#networks-container");
1200
            this.public_list = this.$(".public-networks");
1201
            this.private_list = this.$(".private-networks");
1202
            views.NetworksView.__super__.initialize.call(this);
1203
            this.init_handlers();
1204
            this.network_views = {};
1205
            this.public_network = false;
1206
            this.update_networks(storage.networks.models);
1207
            this.create_view = new views.NetworkCreateView();
1208
            this.connect_machines_view = new views.NetworkConnectVMsOverlay();
1209
        },
1210
        
1211
        exists: function(net) {
1212
            return this.network_views[net.id];
1213
        },
1214

    
1215
        add_or_update: function(net) {
1216
            var nv = this.exists(net);
1217
            if (!nv) {
1218
                if (net.is_public()){
1219
                  if (synnefo.config.group_public_networks) {
1220
                    if (!this.public_network) {
1221
                      // grouped public not initialized
1222
                      this.public_network = this.create_network_view(net);
1223
                    } else {
1224
                      // grouped public initialized, append
1225
                      this.public_network.add_network(net);
1226
                    }
1227
                    nv = this.public_network;
1228
                  } else {
1229
                    // no grouped view asked, fallback to default create
1230
                    nv = this.create_network_view(net);
1231
                  }
1232
                } else {
1233
                  nv = this.create_network_view(net);
1234
                }
1235

    
1236
                this.network_views[net.id] = nv;
1237
                
1238
                if (net.is_public()) {
1239
                    this.public_list.append(nv.el);
1240
                    this.public_list.show();
1241
                } else {
1242
                    this.private_list.append(nv.el);
1243
                    this.private_list.show();
1244
                }
1245
            }
1246

    
1247
            // update vms
1248
            // for cases where network servers list
1249
            // get updated after vm addition and
1250
            // vm_added_handler fails to append the
1251
            // vm to the list
1252
            nv.update_nics();
1253
            nv.update_layout();
1254
        },
1255
        
1256
        create_network_view: function(net) {
1257
            if (net.is_public()) {
1258
                if (synnefo.config.group_public_networks) {
1259
                  if (self.public_network) { return self.public_network }
1260
                  return new views.GroupedPublicNetworkView(net, this);
1261
                } else {
1262
                  return new views.PublicNetworkView(net, this);
1263
                }
1264
            }
1265
            return new views.PrivateNetworkView(net, this);
1266
        },
1267
        
1268
        init_handlers: function() {
1269
            storage.networks.bind("add", _.bind(this.network_added_handler, this, "add"));
1270
            storage.networks.bind("change", _.bind(this.network_changed_handler, this, "change"));
1271
            storage.networks.bind("reset", _.bind(this.network_changed_handler, this, "reset"));
1272
            storage.networks.bind("remove", _.bind(this.network_removed_handler, this, "remove"));
1273

    
1274
            this.$("#networkscreate").click(_.bind(function(e){
1275
              e.preventDefault();
1276
              if ($(this.$("#networkscreate")).hasClass("disabled")) { return }
1277
              this.create_view.show();
1278
            }, this));
1279
            
1280
            var self = this;
1281
            storage.networks.bind("quota_reached", function(){
1282
              self.$("#networkscreate").addClass("disabled").attr("title", 
1283
                                                            "Networks limit reached");
1284
            });
1285
            storage.networks.bind("quota_free", function(){
1286
              self.$("#networkscreate").removeClass("disabled").attr("title", 
1287
                                                            "");
1288
            });
1289
        },
1290

    
1291
        update_networks: function(nets) {
1292
            _.each(nets, _.bind(function(net){
1293
                if (net.get("status") == "DELETED") { return };
1294
                view = this.add_or_update(net);
1295
            }, this));
1296
        },
1297

    
1298
        show: function() {
1299
            this.container.show();
1300
            $(this.el).show();
1301
        },
1302

    
1303
        network_added_handler: function(type, net) {
1304
            this.update_networks([net]);
1305
        },
1306

    
1307
        network_changed_handler: function(type, models) {
1308
            var nets = [];
1309
            if (type == "change") {
1310
                nets = [models]
1311
            } else {
1312
                nets = models.models;
1313
            }
1314

    
1315
            this.update_networks(nets)
1316
        },
1317

    
1318
        network_removed_handler: function(type, net) {
1319
            this.remove_net(net)
1320
            if (this.private_list.find(".network").length == 0) {
1321
                this.private_list.hide();
1322
            }
1323
            
1324
        },
1325

    
1326
        network_added: function(net) {
1327
            return this.network_views[net.id];
1328
        },
1329

    
1330
        get_network_view: function(net) {
1331
            return this.network_views[net.id];
1332
        },
1333

    
1334
        remove_net: function(net) {
1335
            if (this.network_added(net)) {
1336
                var view = this.get_network_view(net);
1337
                if (view == this.public_network) {
1338
                  this.public_network.remove_network(net);
1339
                } else {
1340
                  view.remove();
1341
                }
1342
                delete this.network_views[net.id];
1343
            }
1344
        },
1345

    
1346
        __update_layout: function() {
1347
        }
1348
    });
1349

    
1350
})(this);