Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_single_view.js @ 21d2d544

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
            });
274
            this.ports_views[vm.id] = ports_view
275
            ports_view.show();
276
            ports_view.el.hide();
277
            
278
            if (storage.vms.models.length > 1) { this.vm(vm).hide(); };
279
        },
280

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

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

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

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

    
313

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

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

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

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

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

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

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

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

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

    
398
            this.update_status_message(vm);
399

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

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

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

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

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

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

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

    
457
})(this);