Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_single_view.js @ 6201f0e3

History | View | Annotate | Download (16.2 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
          self.toggle();
69
        });
70

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

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

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

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

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

    
118
        el: '#machinesview-single',
119
        id_tpl: 'single-vm-',
120
        link_id_tpl: 'single-vm-at-',
121

    
122
        hide_actions: false,
123

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

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

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

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

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

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

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

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

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

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

    
218
            this.update_current_vm();
219
            snf.router.vm_details_view(this.current_vm_instance.id);
220
        },
221

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

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

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

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

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

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

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

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

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

    
314

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

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

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

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

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

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

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

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

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

    
399
            this.update_status_message(vm);
400

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

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

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

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

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

    
440
    views.SingleView.VM_OS_ICONS = window.os_icons || [];
441

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

    
458
})(this);