Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_single_view.js @ 5be51d4f

History | View | Annotate | Download (16.4 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
          var disabled = self.toggler.parent().find(".cont-toggler-wrapper").hasClass("disabled");
69
          if (disabled) { return; }
70
          self.toggle();
71
        });
72

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

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

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

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

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

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

    
124
        hide_actions: false,
125

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
316

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

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

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

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

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

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

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

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

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

    
401
            this.update_status_message(vm);
402

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

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

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

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

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

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

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

    
460
})(this);