Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_vms_base_view.js @ efb056c0

History | View | Annotate | Download (30.5 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

    
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
    var hasKey = Object.prototype.hasOwnProperty;
56

    
57
    // base class for views that contain/handle VMS
58
    views.VMListView = views.View.extend({
59

    
60
        // just a flag to identify that
61
        // views of this type handle vms
62
        vms_view: true,
63

    
64
        selectors: {},
65
        hide_actions: true,
66
        pane: "#machines-pane",
67
        metadata_view: undefined,
68

    
69
        initialize: function() {
70
            views.VMListView.__super__.initialize.call(this);
71
            this._vm_els = {};
72
            this.set_storage_handlers();
73
            this.set_handlers();
74
            this.vms_updated_handler();
75
            this.connect_overlay = new views.VMConnectView();
76
        },
77

    
78
        // display vm diagnostics detail overlay view, update based on
79
        // diagnostics_update_interval config value.
80
        show_build_details_for_vm: function(vm) {
81
            var cont = null;
82
            var success = function(data) {
83
                var message = "";
84
                var title = vm.get('name');
85
                var info = '<em>Status log messages:</em>';
86

    
87
                var list_el = $('<div class="diagnostics-list">');
88
                list_el.append('<div class="empty">No data available</div>');
89

    
90
                cont = list_el;
91
                var messages = _.clone(data);
92

    
93
                update_overlay_diagnostics(data, cont);
94
                synnefo.ui.main.details_view.show(title, info, cont);
95
            }
96

    
97
            var update_overlay_diagnostics = function(data) {
98
                var existing = cont.find(".msg-log-entry");
99
                var messages = _.clone(data);
100
                
101
                var to_append = messages.slice(0, messages.length - existing.length);
102
                to_append.reverse();
103

    
104
                var appending = to_append.length != messages.length;
105
                
106
                if (to_append.length) { cont.find(".empty").hide() } else {
107
                    if (!messages.length) { cont.find(".empty").show() }
108
                }
109
                _.each(to_append, function(msg){
110
                    var el = $('<div class="clearfix msg-log-entry ' + msg.level.toLowerCase() + '">');
111
                    if (msg.details) {
112
                      el.addClass("with-details");
113
                    }
114
                    var display_source_date = synnefo.config.diagnostics_display_source_date;
115
                    var source_date = "";
116
                    if (display_source_date) {
117
                        source_date = '('+synnefo.util.formatDate(new Date(msg.source_date))+')';
118
                    }
119

    
120
                    el.append('<span class="date">' + synnefo.util.formatDate(new Date(msg.created)) + source_date + '</span>');
121
                    el.append('<span class="src">' + _.escape(msg.source) + '</span>');
122
                    el.append('<span class="msg">' + _.escape(msg.message) + '</span>');
123
                    if (msg.details) {
124
                        el.append('<pre class="details">' + _.escape(msg.details) + '</pre>');
125
                    }
126
                    if (appending) { el.hide(0); el.css({'display':'none'})}
127
                    cont.prepend(el);
128
                    el.click(function(el) {
129
                        $(this).find(".details").slideToggle();
130
                        $(this).toggleClass("expanded");
131
                    });
132

    
133
                    if (appending) { el.fadeIn(800); }
134
                });
135
                
136
                window.setTimeout(function(){
137
                    if (cont.is(":visible")) {
138
                        vm.get_diagnostics(update_overlay_diagnostics);
139
                    }
140
                }, synnefo.config.diagnostics_update_interval);
141
            }
142

    
143
            vm.get_diagnostics(success);
144
        },
145

    
146
        // Helpers
147
        //
148
        // get element based on this.selectors key/value pairs
149
        sel: function(id, params) {
150
            if (!this.selectors[id]){ return };
151
            return $(this.selectors[id].format(params));
152
        },
153
        
154
        // vm element based on vm model instance provided
155
        vm: function(vm) {
156
            if (hasKey.call(this._vm_els, vm.id)) {
157
                ret = this._vm_els[vm.id];
158
            } else {
159
                return $([]);
160
            }
161

    
162
            return ret;
163
        },
164
        
165
        // get vm model instance from DOM element
166
        vm_for_element: function(el) {
167
            return storage.vms.sel(this.vm_id_for_element(el));
168
        },
169
        
170

    
171
        // Event binding and stuff like that
172
        //
173
        set_storage_handlers: function() {
174
            storage.vms.bind("add", _.bind(this.vms_updated_handler, this, "add"));
175
            storage.vms.bind("change", _.bind(this.vms_updated_handler, this, "change"));
176
            storage.vms.bind("reset", _.bind(this.vms_updated_handler, this, "reset"));
177
            storage.vms.bind("remove", _.bind(this.vms_updated_handler, this, "remove"));
178
        },
179
        
180
        // vms updated triggered, update view vms
181
        vms_updated_handler: function (method, model, arg2, arg3) {
182
            var updated = storage.vms.models;
183
            if (method == "add") { updated = [model] };
184
            if (method == "change") { updated = [model] };
185
            if (method == "remove") { updated = [model] };
186

    
187
            if (method == "remove") {
188
                this.remove_vm(model)
189
                return;
190
            }
191
            
192
            this.update_vms(updated);
193
        },
194

    
195
        // create vm
196
        // append it on proper view container
197
        create_vm: function(vm) {
198
            // create dom element
199
            var vm_view = this.create_vm_element(vm);
200
            vm_view.find(".vm-actions").attr("id", this.view_id+"-actions-" + vm.id);
201
            this._vm_els[vm.id] = vm_view;
202
            var container = this.get_vm_container(vm);
203
            container.append(vm_view);
204
            vm_view.find(".action-indicator").text("");
205
            if (this.visible()) {
206
                container.show()
207
            }
208

    
209
            // initialize vm specific event handlers 
210
            this.__set_vm_handlers(vm);
211
            vm_view.find(".suspended-notice").click(function(){
212
              synnefo.ui.main.suspended_view.show(vm);
213
            })
214
            return vm_view;
215
        },
216
        
217
        // create vm dom element
218
        create_vm_element: function(vm) {
219
            // clone template
220
            return this.sel('tpl').clone().attr("id", this.id_tpl + vm.id)
221
        },
222

    
223
        // get proper vm container
224
        get_vm_container: function(vm) {
225
            if (vm.is_active()) {
226
                return this.sel("vm_cont_active");
227
            } else {
228
                return this.sel("vm_cont_terminated");
229
            }
230
        },
231

    
232
        // create and append inside the proper container the vm model
233
        // if it doesn't exist update vm data and make it visible
234
        add: function(vm) {
235
            // create if it does not exist
236
            if (!hasKey.call(this._vm_els, vm.id)) {
237
                var el = this.create_vm(vm);
238
                el.show();
239
                this.post_add(vm);
240
                this.init_vm_view_handlers(vm);
241
            }
242

    
243
            return this.vm(vm);
244
        },
245

    
246
        init_vm_view_handlers: function(vm) {
247
            var self = this;
248
            var el = this.vm(vm);
249

    
250
            // hidden feature, double click on indicators to display 
251
            // vm diagnostics.
252
            el.find(".indicators").bind("dblclick", function(){
253
                self.show_build_details_for_vm(vm);
254
            });
255

    
256
            // this button gets visible if vm creation failed.
257
            el.find("div.build-progress .btn").click(function(){
258
                self.show_build_details_for_vm(vm);
259
            });
260
        },
261
        
262
        // helpers for VMListView descendants
263
        post_add: function(vm) { throw "Not implemented" },
264
        set_vm_handlers: function(vm) { throw "Not implemented" },
265
        set_handlers: function() { throw "Not implemented" },
266
        update_layout: function() { throw "Not implemented" },
267
        post_update_vm: function(vm) { throw "Not implemented" },
268
        update_details: function(vm) {},
269
        
270
        // remove vm
271
        remove_vm: function(vm) {
272
            // FIXME: some kind of transiton ??? effect maybe ???
273
            this.vm(vm).remove();
274
            this.post_remove_vm(vm);
275
            if (hasKey.call(this._vm_els, vm.id)) {
276
                delete this._vm_els[vm.id];
277
            }
278
        },
279
        
280
        // remove all vms from view
281
        clear: function() {
282
            this.sel('vms').remove();
283
            this.__update_layout();
284
        },
285

    
286
        show: function() {
287
            views.VMListView.__super__.show.apply(this, arguments);
288
            if (storage.vms.length == 0) { this.hide() };
289
            if (!snf.config.update_hidden_views) {
290
                this.update_vms(storage.vms.models);
291
            }
292
        },
293

    
294
        // do update for provided vms, then update the view layout
295
        update_vms: function(vms) {
296
            if (!this.visible() && !snf.config.update_hidden_views) { return };
297

    
298
            _.each(vms, _.bind(function(vm){
299
                // vm will be removed
300
                // no need to update
301
                if (vm.get("status") == "DELETED") {
302
                    return;
303
                }
304

    
305
                // this won't add it additional times
306
                this.add(vm);
307
                this.update_vm(vm);
308
            }, this))
309
            
310
            // update view stuff
311
            this.__update_layout();
312
        },
313
        
314
        update_toggles_visibility: function(vm) {
315
          if (vm.is_building() || vm.in_error_state() || vm.get("status") == "DESTROY") {
316
            this.vm(vm).find(".cont-toggler-wrapper.ips").addClass("disabled");
317
          } else {
318
            this.vm(vm).find(".cont-toggler-wrapper.ips").removeClass("disabled");
319
          }
320
        },
321

    
322
        // update ui for the given vm
323
        update_vm: function(vm) {
324
            // do not update deleted state vms
325
            if (!vm || vm.get("status") == 'DELETED') { return };
326
            this.check_vm_container(vm);
327

    
328
            this.update_details(vm);
329
            this.update_transition_state(vm);
330
            this.update_toggles_visibility(vm);
331

    
332
            if (this.action_views) {
333
                this.action_views[vm.id].update();
334
                this.action_views[vm.id].update_layout();
335
            }
336
            
337
            var el = this.vm(vm);
338
            if (vm.can_resize()) {
339
              el.addClass("can-resize");
340
            } else {
341
              el.removeClass("can-resize");
342
            }
343

    
344
            if (vm.get('suspended')) {
345
              el.addClass("suspended");
346
            } else {
347
              el.removeClass("suspended");
348
            }
349

    
350
            try {
351
                this.post_update_vm(vm);
352
            } catch (err) {};
353
        },
354

    
355
        // check if vm is placed properly within the view
356
        // container (e.g. some views might have different
357
        // containers for terminated or running machines
358
        check_vm_container: function(vm){
359
            var el = this.vm(vm);
360
            if (!el.length) { return };
361
            var self = this;
362
            var selector = vm.is_active() ? 'vm_cont_active' : 'vm_cont_terminated';
363
            if (el.parent()[0] != this.sel(selector)[0]) {
364
                var cont = this.sel(selector);
365
                var self = this;
366

    
367
                el.hide().appendTo(cont).fadeIn(300);
368
                $(window).trigger('resize');
369

    
370
                //el.fadeOut(200, function() {
371
                    //el.appendTo(cont); 
372
                    //el.fadeIn(200);
373
                    //self.sel(selector).show(function(){
374
                        //$(window).trigger("resize");
375
                    //});
376
                //});
377
            }
378
        },
379

    
380
        __update_layout: function() {
381
            this.update_layout();
382
        },
383
        
384
        // append handlers for vm specific events
385
        __set_vm_handlers: function(vm) {
386
            // show transition on vm status transit
387
            vm.bind('transition', _.bind(function(){this.show_transition(vm)}, this));
388
            this.set_vm_handlers(vm);
389
        },
390
        
391
        // is vm in transition ??? show the progress spinner
392
        update_transition_state: function(vm) {
393
            if (vm.in_transition() && !vm.has_pending_action()){
394
                this.sel('vm_spinner', vm.id).show();
395
            } else {
396
                this.sel('vm_spinner', vm.id).hide();
397
            }
398
        },
399
        
400
        show_indicator: function(vm, action) {
401
            var action = action || vm.pending_action;
402
            this.sel('vm_wave', vm.id).hide();
403
            this.sel('vm_spinner', vm.id).hide();
404
            this.vm(vm).find(".action-indicator").removeClass().addClass(action + " action-indicator").show();
405
        },
406

    
407
        hide_indicator: function(vm) {
408
            this.vm(vm).find(".action-indicator").removeClass().addClass("action-indicator").hide();
409
            this.update_transition_state(vm);
410
        },
411

    
412
        // display transition animations
413
        show_transition: function(vm) {
414
            var wave = this.sel('vm_wave', vm.id);
415
            if (!wave || !wave.length) { return }
416

    
417
            var src = wave.attr('src');
418
            // change src to force gif play from the first frame
419
            // animate for 500 ms then hide
420
            wave.attr('src', "").show();
421
            wave.attr('src', src).fadeIn(200).delay(700).fadeOut(300, function() {
422
                wave.hide();
423
            });
424
        },
425

    
426
        connect_to_console: function(vm) {
427
            // It seems that Safari allows popup windows only if the window.open
428
            // call is made within an html element click event context. 
429
            // Otherwise its behaviour is based on "Block Pop-Up Windows" 
430
            // setting, which when enabled no notification appears for the user. 
431
            // Since there is no easy way to check for the setting value we use
432
            // async:false for the action call so that action success handler 
433
            // which opens the new window is called inside the click event 
434
            // context.
435
            var use_async = true;
436
            if ($.client.browser == "Safari") {
437
                use_async = false;
438
            }
439

    
440
            vm.call("console", function(console_data) {
441
                var url = vm.get_console_url(console_data);
442
                snf.util.open_window(url, "VM_" + vm.get("id") + "_CONSOLE", {'scrollbars': 1, 'fullscreen': 0});
443
            }, undefined, {async: use_async});
444
        }
445

    
446
    });
447
    
448
    // empty message view (just a wrapper to the element containing 
449
    // the empty information message)
450
    views.EmptyView = views.View.extend({
451
        el: '#emptymachineslist'
452
    })
453

    
454
    views.VMActionsView = views.View.extend({
455
        
456
        initialize: function(vm, parent, el, hide) {
457
            this.hide = hide || false;
458
            this.view = parent;
459
            this.vm = vm;
460
            this.vm_el = el;
461
            this.el = $("#" + parent.view_id + "-actions-" + vm.id);
462
            this.all_action_names = _.keys(views.VMActionsView.STATUS_ACTIONS);
463
            
464
            // state params
465
            this.selected_action = false;
466

    
467
            _.bindAll(this);
468
            window.acts = this;
469
            this.view_id = "vm_" + vm.id + "_actions";
470
            views.VMActionsView.__super__.initialize.call(this);
471

    
472
            this.hovered = false;
473
            this.set_hover_handlers();
474
        },
475

    
476
        action: function(name) {
477
            return $(this.el).find(".action-container." + name);
478
        },
479

    
480
        action_link: function(name) {
481
            return this.action(name).find("a");
482
        },
483
        
484
        action_confirm_cont: function(name) {
485
            return this.action_confirm(name).parent();
486
        },
487

    
488
        action_confirm: function(name) {
489
            return this.action(name).find("button.yes");
490
        },
491

    
492
        action_cancel: function(name) {
493
            return this.action(name).find("button.no");
494
        },
495

    
496
        hide_actions: function() {
497
            $(this.el).find("a").css("visibility", "hidden");
498
        },
499
        
500
        set_can_start: function() {
501
          var el = $(this.el).find("a.action-start").parent();
502
          el.removeClass("disabled-visible");
503
        },
504

    
505
        set_cannot_start: function() {
506
          var el = $(this.el).find("a.action-start").parent();
507
          el.addClass("disabled-visible")
508
        },
509

    
510
        // update the actions layout, depending on the selected actions
511
        update_layout: function() {
512
            
513
            if (this.vm.get('status') == 'STOPPED') {
514
              if (this.vm.can_start()) {
515
                this.set_can_start();
516
              } else {
517
                this.set_cannot_start();
518
              }
519
            }
520

    
521
            if (!this.vm_handlers_initialized) {
522
                this.vm = storage.vms.get(this.vm.id);
523
                this.init_vm_handlers();
524
            }
525

    
526
            if (!this.vm) { return }
527
            
528
            if (!this.hovered && !this.vm.has_pending_action() && this.hide && !this.vm.action_error) { 
529
                this.el.hide();
530
                this.view.hide_indicator(this.vm);
531
                return 
532
            };
533

    
534
            if (!this.el.is(":visible") && !this.hide) { return };
535
            if (!this.handlers_initialized) { this.set_handlers(); }
536

    
537

    
538
            // update selected action
539
            if (this.vm.pending_action) {
540
                this.selected_action = this.vm.pending_action;
541
            } else {
542
                this.selected_action = false;
543
            }
544
            
545
            // vm actions tha can be performed
546
            var actions = this.vm.get_available_actions();
547
            
548
            // had pending action but actions changed and now selected action is
549
            // not available, hide it from user
550
            if (this.selected_action && actions.indexOf(this.selected_action) == -1) {
551
                this.reset();
552
            }
553
            
554
            this.el.show();
555
            
556
            if ((this.selected_action || this.hovered) && !this.vm.action_error) {
557
                // show selected action
558
                $(this.el).show();
559
                $(this.el).find("a").css("visibility", "visible");
560
                // show action icon
561
                this.view.show_indicator(this.vm);
562
            } else {
563
                if (this.hide || this.vm.action_error) {
564
                    // view shows actions on machine hover
565
                    $(this.el).find("a").css("visibility", "hidden");
566
                } else {
567
                    if (!this.vm.action_error) {
568
                        // view shows actions always
569
                        $(this.el).find("a").css("visibility", "visible");
570
                        $(this.el).show();
571
                    }
572
                }
573
                
574
                this.view.hide_indicator(this.vm);
575
            }
576
                
577
            // update action link styles and shit
578
            _.each(models.VM.ACTIONS, function(action, index) {
579
                if (actions.indexOf(action) > -1) {
580
                    this.action(action).removeClass("disabled");
581
                    var inactive = models.VM.AVAILABLE_ACTIONS_INACTIVE[action];
582

    
583
                    if (inactive && !_.contains(inactive, this.vm.get('status'))) {
584
                      this.action(action).addClass("inactive");
585
                    } else {
586
                      this.action(action).removeClass("inactive");
587
                    }
588

    
589
                    if (this.selected_action == action) {
590
                        this.action_confirm_cont(action).css('display', 'block');
591
                        this.action_confirm(action).show();
592
                        this.action(action).removeClass("disabled");
593
                        this.action_link(action).addClass("selected");
594
                    } else {
595
                        this.action_confirm_cont(action).hide();
596
                        this.action_confirm(action).hide();
597
                        this.action_link(action).removeClass("selected");
598
                    }
599
                } else {
600
                    this.action().hide();
601
                    this.action(action).addClass("disabled");
602
                    this.action_confirm(action).hide();
603
                }
604
            }, this);
605
        },
606
        
607
        init_vm_handlers: function() {
608
            try {
609
                this.vm.unbind("action:fail", this.update_layout)
610
                this.vm.unbind("action:fail:reset", this.update_layout)
611
            } catch (err) { console.log("Error")};
612
            
613
            this.vm.bind("action:fail", this.update_layout)
614
            this.vm.bind("action:fail:reset", this.update_layout)
615

    
616
            this.vm_handlers_initialized = true;
617
        },
618
        
619
        set_hover_handlers: function() {
620
            // vm container hover (icon view)
621
            this.view.vm(this.vm).hover(_.bind(function() {
622
                this.hovered = true;
623
                this.update_layout();
624

    
625
            }, this),  _.bind(function() {
626
                this.hovered = false;
627
                this.update_layout();
628
            }, this));
629
        },
630

    
631
        // bind event handlers
632
        set_handlers: function() {
633
            var self = this;
634
            var vm = this.vm;
635
            
636
            // initial hide
637
            if (this.hide) { $(this.el).hide() };
638
            
639
            // action links events
640
            _.each(models.VM.ACTIONS, function(action) {
641
                var action = action;
642
                // indicator hovers
643
                this.view.vm(this.vm).find(".action-container."+action+" a").hover(function() {
644
                    self.view.show_indicator(self.vm, action);
645
                }, function() {
646
                    // clear or show selected action indicator
647
                    if (self.vm.pending_action) {
648
                        self.view.show_indicator(self.vm);
649
                    } else {
650
                        self.view.hide_indicator(self.vm);
651
                    }
652
                })
653
                
654
                // action links click events
655
                $(this.el).find(".action-container."+action+" a").click(function(ev) {
656
                    ev.preventDefault();
657
                    if (action == "start" && !self.vm.can_start() && !vm.in_error_state()) {
658
                        ui.main.vm_resize_view.show_with_warning(self.vm);
659
                        return;
660
                    }
661

    
662
                    if (action == "resize") {
663
                        ui.main.vm_resize_view.show(self.vm);
664
                        return;
665
                    } else {
666
                        self.set(action);
667
                    }
668
                }).data("action", action);
669

    
670
                // confirms
671
                $(this.el).find(".action-container."+action+" button.no").click(function(ev) {
672
                    ev.preventDefault();
673
                    self.reset();
674
                });
675

    
676
                // cancels
677
                $(this.el).find(".action-container."+action+" button.yes").click(function(ev) {
678
                    ev.preventDefault();
679
                    // override console
680
                    // ui needs to act (open the console window)
681
                    if (action == "console") {
682
                        self.view.connect_to_console(self.vm);
683
                    } else {
684
                        self.vm.call(action);
685
                    }
686
                    self.reset();
687
                });
688
            }, this);
689

    
690
            this.handlers_initialized = true;
691
        },
692
        
693
        // reset actions
694
        reset: function() {
695
            var prev_action = this.selected_action;
696
            this.selected_action = false;
697
            this.vm.clear_pending_action();
698
            this.trigger("change", {'action': prev_action, 'vm': this.vm, 'view': this, remove: true});
699
            this.trigger("remove", {'action': prev_action, 'vm': this.vm, 'view': this, remove: true});
700
        },
701
        
702
        // set selected action
703
        set: function(action_name) {
704
            if (action_name == "snapshot") { return }
705
            this.selected_action = action_name;
706
            this.vm.update_pending_action(this.selected_action);
707
            this.view.vm(this.vm).find(".action-indicator").show().removeClass().addClass(action_name + " action-indicator");
708
            this.trigger("change", {'action': this.selected_action, 'vm': this.vm, 'view': this});
709
        },
710

    
711
        update: function() {
712
        }
713
    })
714

    
715

    
716
    views.VMActionsView.STATUS_ACTIONS = { 
717
        'reboot':        ['UNKOWN', 'ACTIVE', 'REBOOT'],
718
        'shutdown':      ['UNKOWN', 'ACTIVE', 'REBOOT'],
719
        'console':       ['ACTIVE'],
720
        'start':         ['UNKOWN', 'STOPPED'],
721
        'resize':        ['UNKOWN', 'ACTIVE', 'STOPPED', 'REBOOT', 'ERROR', 'BUILD'],
722
        'snapshot':      ['ACTIVE', 'STOPPED'],
723
        'destroy':       ['UNKOWN', 'ACTIVE', 'STOPPED', 'REBOOT', 'ERROR', 'BUILD']
724
    };
725

    
726
    // UI helpers
727
    var uihelpers = snf.ui.helpers = {};
728
    
729
    // OS icon helpers
730
    var os_icon = uihelpers.os_icon = function(os) {
731
        var icons = window.os_icons;
732
        if (!icons) { return "unknown" }
733
        if (icons.indexOf(os) == -1) {
734
            os = "unknown";
735
        }
736
        return os;
737
    }
738

    
739
    var os_icon_path = uihelpers.os_icon_path = function(os, size, active) {
740
        size = size || "small";
741
        if (active == undefined) { active = true };
742

    
743
        var icon = os_icon(os);
744
        if (active) {
745
            icon = icon + "-on";
746
        } else {
747
            icon = icon + "-off";
748
        }
749

    
750
        return (snf.config.machines_icons_url + "{0}/{1}.png").format(size, icon)
751
    }
752

    
753
    var os_icon_tag = uihelpers.os_icon_tag = function (os, size, active, attrs) {
754
        attrs = attrs || {};
755
        return '<img src="{0}" />'.format(os_icon_path(os, size, active));
756
    }
757

    
758
    // VM Icon helpers
759
    //
760
    // identify icon
761
    var vm_icon = uihelpers.vm_icon = function(vm) {
762
        return os_icon(vm.get_os());
763
    }
764
    
765
    // get icon url
766
    var vm_icon_path = uihelpers.vm_icon_path = function(vm, size) {
767
        return os_icon_path(vm.get_os(), size, vm.is_active());
768
    }
769
    
770
    // get icon IMG tag
771
    var vm_icon_tag = uihelpers.vm_icon_tag = function (vm, size, attrs) {
772
       return os_icon_tag(vm.get_os(), size, vm.is_active(), attrs);
773
    }
774
    
775

    
776
    snf.ui = _.extend(snf.ui, bb.Events);
777
    snf.ui.trigger_error = function(code, msg, error, extra) {
778
        if (msg.match(/Script error/i)) {
779
          // No usefull information to display in this case. Plus it's an
780
          // exception that probably doesn't affect our app.
781
          return 0;
782
        }
783
        snf.ui.trigger("error", { code:code, msg:msg, error:error, extra:extra || {} });
784
    };
785
    
786
    views.VMPortView = views.ext.ModelView.extend({
787
      tpl: '#vm-port-view-tpl',
788
      classes: 'port-item clearfix',
789
      
790
      update_in_progress: function() {
791
        if (this.model.get("in_progress_no_vm")) {
792
          this.set_in_progress();
793
        } else {
794
          this.unset_in_progress();
795
        }
796
      },
797

    
798
      set_in_progress: function() {
799
        this.el.find(".type").hide();
800
        this.el.find(".in-progress").removeClass("hidden").show();
801
      },
802

    
803
      unset_in_progress: function() {
804
        this.el.find(".type").show();
805
        this.el.find(".in-progress").hide();
806
      },
807

    
808
      disconnect_port: function(model, e) {
809
        e && e.stopPropagation();
810
        var network = this.model.get("network");
811
        this.model.actions.reset_pending();
812
        this.model.disconnect(_.bind(this.disconnect_port_complete, this));
813
      },
814

    
815
      disconnect_port_complete: function() {
816
      },
817

    
818
      get_network_name: function() {
819
        var network = this.model.get('network');
820
        var name = network && network.get('name');
821
        if (!name) {
822
          if (network.get('is_public')) {
823
            name = 'Internet'
824
          } else {
825
            name = '(no network name)'
826
          }
827
        }
828
        var truncate_length = this.parent_view.options.truncate || 40;
829
        name = synnefo.util.truncate(name, truncate_length, '...');
830
        return name || 'Loading...';
831
      },
832

    
833
      get_network_icon: function() {
834
        var ico;
835
        var network = this.model.get('network');
836
        if (network) {
837
          ico = network.get('is_public') ? 'internet-small.png' : 'network-small.png';
838
        } else {
839
          ico = 'network-small.png';
840
        }
841
        return synnefo.config.media_url + 'images/' + ico;
842
      },
843
    });
844
    
845
    views.VMPortListView = views.ext.CollectionView.extend({
846
      tpl: '#vm-port-list-view-tpl',
847
      model_view_cls: views.VMPortView
848
    });
849

    
850
    views.VMPortIpView = views.ext.ModelView.extend({
851
      tpl: '#vm-port-ip-tpl',
852
      css_classes: "clearfix"
853
    });
854

    
855
    views.VMPortIpsView = views.ext.CollectionView.extend({
856
      tpl: '#vm-port-ips-tpl',
857
      model_view_cls: views.VMPortIpView
858
    });
859

    
860
})(this);