Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_single_view.js @ 807a0305

History | View | Annotate | Download (16.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 = snf.util || {};
46

    
47
    var views = snf.views = snf.views || {}
48

    
49
    // shortcuts
50
    var bb = root.Backbone;
51
    var hasKey = Object.prototype.hasOwnProperty;
52
    
53
    views.VMSinglePortListView = views.VMPortListView.extend({
54
      
55
      init: function() {
56
        views.VMSinglePortListView.__super__.init.apply(this);
57
        this.open = false;
58
        this.vm_el = $(this.options.vm_view);
59
        this.tags_toggler = this.vm_el.find(".tags-header");
60
        this.tags_content = this.vm_el.find(".tags-content");
61
        this.toggler = this.vm_el.find(".toggler-header.ips");
62
        this.toggler_content = this.vm_el.find(".ips-content");
63
        this.toggler_content.hide();
64
        $(this.el).show();
65
        
66
        var self = this;
67
        this.toggler.click(function() {
68
          if(self.toggler.parent().parent().hasClass("disabled")) {
69
            return;
70
          }
71
          self.toggle();
72
        });
73

    
74
        this.tags_toggler.click(function() {
75
          self.toggler.find(".toggler").removeClass("open");
76
          var f = function() { self.hide(true) }
77
          self.toggler_content.slideUp(f);
78
        });
79
      },
80

    
81
      toggle: function() {
82
        var self = this;
83
        this.open = !this.open;
84

    
85
        if (this.open) {
86
          this.show(true);
87
          this.tags_toggler.find(".toggler").removeClass("open");
88
          this.tags_content.slideUp();
89
          this.toggler.find(".toggler").addClass("open");
90
          this.toggler_content.removeClass(".hidden").slideDown();
91
        } else {
92
          this.toggler.find(".toggler").removeClass("open");
93
          var f = function() { self.hide(true) }
94
          this.toggler_content.removeClass(".hidden").slideUp();
95
        }
96
      }
97
    });
98

    
99
    views.SingleDetailsView = views.VMDetailsView.extend({
100
    
101
        view_id: "vm_details_single",
102
        el_sel: '.machine-details',
103
        
104
        selectors: {
105
            'cpu': '.machine-detail.cpus',
106
            'ram': '.machine-detail.ram',
107
            'disk': '.machine-detail.disk',
108
            'image_name': '.machine-detail.image-name',
109
            'image_size': '.machine-detail.image-size'
110
        },
111
    
112
    })
113

    
114
    // VMs single view
115
    views.SingleView = views.VMListView.extend({
116
        
117
        // view id (this could be used to identify 
118
        // the view object from global context
119
        view_id: 'vm_single',
120

    
121
        el: '#machinesview-single',
122
        id_tpl: 'single-vm-',
123
        link_id_tpl: 'single-vm-at-',
124

    
125
        hide_actions: false,
126

    
127
        selectors: {
128
            'vms': '.single-container',
129
            'vm': '#single-vm-',
130
            'view': '#machinesview-single',
131
            'tpl': '.single-container-template',
132
            'spinner': '.large-spinner',
133
            'vm_spinner': '#single-vm-{0} .state .spinner',
134
            'vm_wave': '#single-vm-{0} img.wave',
135
            'vm_cont_active': '#machinesview-single',
136
            'vm_cont_terminated': '#machinesview-single'
137
        },
138
        
139
        initialize: function() {
140
            this.current_vm = 0;
141
            
142
            // button selectors
143
            this.prev_button = this.$(".controls .previous");
144
            this.next_button = this.$(".controls .next");
145
            this.menu = $("#single-servers-list");
146

    
147
            views.SingleView.__super__.initialize.apply(this, arguments);
148
            this.update_current_vm();
149
        },
150

    
151
        // overload show function
152
        show: function() {
153
            views.SingleView.__super__.show.apply(this, arguments);
154
            this.log.debug("showing");
155
            this.$(".column3").show();
156
            this.show_vm_menu();
157
            this.show_current();
158
        },
159

    
160
        show_vm: function(vm) {
161
            if (!vm) { return };
162
            this.current_vm_instance = vm;
163
            this.show_vm_menu();
164
            this.show_current();
165
            this.update_layout();
166
        },
167

    
168
        // identify vm model instance id based on DOM element
169
        vm_id_for_element: function(el) {
170
            return el.attr('id').replace("single-vm-", "");
171
        },
172
        
173
        // set generic view handlers
174
        set_handlers: function() {
175
            this.prev_button.click(_.bind(function(ev){
176
                storage.vms.reset_pending_actions();
177
                ev.preventDefault();
178
                this.show_prev();
179
            }, this));
180

    
181
            this.next_button.click(_.bind(function(ev){
182
                storage.vms.reset_pending_actions();
183
                ev.preventDefault();
184
                this.show_next();
185
            }, this));
186
        },  
187

    
188
        update_current_vm: function() {
189
            try {
190
                this.current_vm_instance = storage.vms.at(this.current_vm);
191
                this.current_vm_instance.start_stats_update(true);
192
                storage.vms.stop_stats_update([this.current_vm_instance]);
193
            } catch (err) {
194
                this.log.debug("Cannot select current vm instance for: {0}".format(this.current_vm));
195
                this.current_vm_instance = undefined;
196
                this.current_vm = 0;
197
            }
198
        },
199

    
200
        show_next: function() {
201
            this.current_vm++;
202
            if (this.current_vm >= storage.vms.models.length) {
203
                this.current_vm = 0;
204
            }
205
            
206
            this.update_current_vm();
207

    
208
            // this might fail when vms get empty
209
            // catch the exception
210
            try {
211
                snf.router.vm_details_view(this.current_vm_instance.id);
212
            } catch (err) {};
213
        },
214

    
215
        show_prev: function() {
216
            this.current_vm--;
217
            if (this.current_vm < 0) {
218
                this.current_vm = storage.vms.length - 1;
219
            }
220

    
221
            this.update_current_vm();
222
            snf.router.vm_details_view(this.current_vm_instance.id);
223
        },
224

    
225
        post_remove_vm: function(vm) {
226
            // current vm removed or does not exist after an update
227
            this.show_vm_menu();
228
            if (!this.current_vm_instance || this.current_vm_instance.id == vm.id) {
229
                this.current_vm++;
230
                if (this.current_vm >= storage.vms.models.length) {
231
                    this.current_vm = 0;
232
                }
233
                this.update_current_vm();
234
                this.show_current();
235
            // this might fail when vms get empty
236
            // catch the exception
237
            } else {
238
                this.show_current();
239
            }
240
        },
241
        
242
        // stuff to do when a new vm has been created.
243
        // - create vm subviews
244
        post_add: function(vm) {
245
            this.vm(vm).removeClass("single-container-template");
246
            this.show_vm_menu();
247
            this.show_current();
248

    
249
            // rename views index
250
            this.stats_views = this.stats_views || {};
251
            this.connect_views = this.connect_views || {};
252
            this.tags_views = this.tags_views || {};
253
            this.details_views = this.details_views || {};
254
            this.action_views = this.action_views || {};
255
            this.action_error_views = this.action_error_views || {};
256
            this.ports_views = this.ports_views || {};
257

    
258
            //this.stats_views[vm.id] = new views.IconStatsView(vm, this);
259

    
260
            // same as icon view
261
            this.action_views[vm.id] = new views.VMActionsView(vm, this, this.vm(vm), this.hide_actions);
262
            this.stats_views[vm.id] = new views.VMStatsView(vm, this, {stats_type: 'series'});
263
            this.connect_views[vm.id] = new views.IconVMConnectView(vm, this);
264
            this.tags_views[vm.id] = new views.VMTagsView(vm, this, true, 20, 10, 35);
265
            this.details_views[vm.id] = new views.SingleDetailsView(vm, this);
266
            this.action_error_views[vm.id] = new views.VMActionErrorView(vm, this);
267

    
268
            var ports_container = this.vm(vm).find(".ips-content");
269
            var ports_toggler = this.vm(vm).find(".toggler-header.ips");
270
            
271
            var ports_view = new views.VMSinglePortListView({
272
              vm_view: this.vm(vm),
273
              collection: vm.ports, 
274
              container: ports_container,
275
              parent: this,
276
              truncate: 50
277
            });
278
            this.ports_views[vm.id] = ports_view
279
            ports_view.show();
280
            ports_view.el.hide();
281
            
282
            if (storage.vms.models.length > 1) { this.vm(vm).hide(); };
283
        },
284

    
285
        post_update_vm: function(vm) {
286
        },
287
        
288
        // vm specific event handlers
289
        set_vm_handlers: function(vm) {
290
        },
291
        
292
        // handle selected vm
293
        show_current: function() {
294
            var index = this.current_vm;
295
            var vm = storage.vms.at(index);
296

    
297
            this.$(".server-name").removeClass("column3-selected");
298
            
299
            if (vm) {
300
                this.vm(vm).show();
301
            };
302

    
303
            _.each(storage.vms.models, function(vmo){
304
                if (vm && (vm.id != vmo.id)) {
305
                    if (!hasKey.call(this._vm_els, vmo.id)) { return };
306
                    this.vm(vmo).hide();
307
                }
308
            }, this)
309

    
310
            if (!vm) {
311
                // empty list
312
                this.$(".column3").hide();
313
                return;
314
            }
315
            this.$(".column3").show();
316

    
317

    
318
            $("#" + this.link_id_tpl + this.current_vm).addClass("column3-selected");
319
            try {
320
                this.update_details(vm);
321
            } catch (err) {};
322
        },
323

    
324
        show_vm_menu: function() {
325
            this.menu.find(".server-name").remove();
326

    
327
            _.each(storage.vms.models, function(vm, index) {
328
                var el = $('<div class="server-name" id="'+this.link_id_tpl + index +'">' + 
329
                               util.truncate(vm.escape("name"),16)+'</div>')
330
                this.menu.append(el);
331

    
332
                vm.bind("change:name", function(){
333
                  el.html(util.truncate(vm.escape("name"), 16));
334
                })
335

    
336
                if (this.current_vm_instance && vm.id == this.current_vm_instance.id) {
337
                    this.current_vm = index;
338
                }
339
            }, this);
340
            
341
            var self = this;
342
            this.menu.find(".server-name").click(function(ev) {
343
                ev.preventDefault();
344
                var id = $(this).attr("id").replace("single-vm-at-", "");
345
                if (self.current_vm != id) {
346
                    storage.vms.reset_pending_actions();
347
                }
348
                self.current_vm = id;
349
                self.update_current_vm();
350
                snf.router.vm_details_view(self.current_vm_instance.id);
351
            })
352
        },
353

    
354
        // generic stuff to do on each view update
355
        // called once after each vm has been updated
356
        update_layout: function() {
357
            this.update_current_vm();
358
            fix_v6_addresses();
359
        },
360

    
361
        update_status_message: function(vm) {
362
            var el = this.vm(vm);
363
            var message = vm.get_status_message();
364
            if (message) {
365
                // update bulding progress
366
                el.find("div.machine-ips").hide();
367
                el.find("div.build-progress").show();
368
                el.find("div.build-progress .message").text(message);
369
                if (vm.in_error_state()) {
370
                    el.find("div.build-progress .btn").show();
371
                } else {
372
                    el.find("div.build-progress .btn").hide();
373
                }
374
            } else {
375
                // hide building progress
376
                el.find("div.machine-ips").show()
377
                el.find("div.build-progress").hide();
378
                el.find("div.build-progress .btn").hide();
379
            }
380
        },
381

    
382
        // update vm details
383
        update_details: function(vm) {
384
            var el = this.vm(vm);
385
            if (vm != this.current_vm_instance) { return };
386

    
387
            // truncate name
388
            el.find(".machine-detail.name").text(util.truncate(vm.get("name"), 53));
389
            el.find(".fqdn").text(vm.get("fqdn"));
390
            // set the state (i18n ??)
391
            el.find(".state-label").text(STATE_TEXTS[vm.state()]);
392
            // set state class
393
            el.find(".state").removeClass().addClass(views.SingleView.STATE_CLASSES[vm.state()].join(" "));
394
            // os icon
395
            el.find(".single-image").css({'background-image': "url(" + this.get_vm_icon_path(vm, "medium") + ")"});
396
            
397
            el.removeClass("connectable");
398
            if (vm.is_connectable()) {
399
                el.addClass("connectable");
400
            }
401

    
402
            this.update_status_message(vm);
403

    
404
            icon_state = vm.is_active() ? "on" : "off";
405
            set_machine_os_image(el, "single", icon_state, this.get_vm_icon_os(vm));
406
            
407
            // update subviews
408
            this.action_views[vm.id].update_layout();
409
            this.stats_views[vm.id].update_layout();
410
            this.connect_views[vm.id].update_layout();
411
            this.tags_views[vm.id].update_layout();
412
            this.details_views[vm.id].update_layout();
413
        },
414
            
415
        get_vm_icon_os: function(vm) {
416
            var os = vm.get_os();
417
            var icons = window.os_icons || views.SingleView.VM_OS_ICONS;
418
            if (icons.indexOf(os) == -1) {
419
                os = "unknown";
420
            }
421
            return os;
422
        },
423

    
424
        // TODO: move to views.utils (the method and the VM_OS_ICON vars)
425
        get_vm_icon_path: function(vm, icon_type) {
426
            var os = vm.get_os();
427
            var icons = window.os_icons || views.SingleView.VM_OS_ICONS;
428

    
429
            if (icons.indexOf(os) == -1) {
430
                os = "unknown";
431
            }
432

    
433
            return views.SingleView.VM_OS_ICON_TPLS()[icon_type].format(os);
434
        }
435
    })
436

    
437
    views.SingleView.VM_OS_ICON_TPLS = function() {
438
        return {
439
            "medium": snf.config.machines_icons_url + "large/{0}-sprite.png"
440
        }
441
    }
442

    
443
    views.SingleView.VM_OS_ICONS = window.os_icons || [];
444

    
445
    views.SingleView.STATE_CLASSES = {
446
        'UNKNOWN':          ['state', 'error-state'],
447
        'BUILD':            ['state', 'build-state'],
448
        'REBOOT':           ['state', 'rebooting-state'],
449
        'STOPPED':          ['state', 'terminated-state'],
450
        'ACTIVE':           ['state', 'running-state'],
451
        'ERROR':            ['state', 'error-state'],
452
        'DELETED':          ['state', 'destroying-state'],
453
        'DESTROY':          ['state', 'destroying-state'],
454
        'SHUTDOWN':         ['state', 'shutting-state'],
455
        'START':            ['state', 'starting-state'],
456
        'CONNECT':          ['state', 'connecting-state'],
457
        'DISCONNECT':       ['state', 'disconnecting-state'],
458
        'RESIZE':           ['state', 'rebooting-state']
459
    };
460

    
461
})(this);