Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (14.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
    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.SingleDetailsView = views.VMDetailsView.extend({
54
    
55
        view_id: "vm_details_single",
56
        el_sel: '.machine-details',
57
        
58
        selectors: {
59
            'cpu': '.machine-detail.cpus',
60
            'ram': '.machine-detail.ram',
61
            'disk': '.machine-detail.disk',
62
            'image_name': '.machine-detail.image-name',
63
            'image_size': '.machine-detail.image-size'
64
        }
65
    
66
    })
67

    
68
    // VMs single view
69
    views.SingleView = views.VMListView.extend({
70
        
71
        // view id (this could be used to identify 
72
        // the view object from global context
73
        view_id: 'vm_single',
74

    
75
        el: '#machinesview-single',
76
        id_tpl: 'single-vm-',
77
        link_id_tpl: 'single-vm-at-',
78

    
79
        hide_actions: false,
80

    
81
        selectors: {
82
            'vms': '.single-container',
83
            'vm': '#single-vm-',
84
            'view': '#machinesview-single',
85
            'tpl': '.single-container-template',
86
            'spinner': '.large-spinner',
87
            'vm_spinner': '#single-vm-{0} .state .spinner',
88
            'vm_wave': '#single-vm-{0} img.wave',
89
            'vm_cont_active': '#machinesview-single',
90
            'vm_cont_terminated': '#machinesview-single'
91
        },
92
        
93
        initialize: function() {
94
            this.current_vm = 0;
95
            
96
            // button selectors
97
            this.prev_button = this.$(".controls .previous");
98
            this.next_button = this.$(".controls .next");
99
            this.menu = $("#single-servers-list");
100

    
101
            views.SingleView.__super__.initialize.apply(this, arguments);
102
            this.update_current_vm();
103
        },
104

    
105
        // overload show function
106
        show: function() {
107
            views.SingleView.__super__.show.apply(this, arguments);
108
            this.log.debug("showing");
109
            this.$(".column3").show();
110
            this.show_vm_menu();
111
            this.show_current();
112
        },
113

    
114
        show_vm: function(vm) {
115
            if (!vm) { return };
116
            this.current_vm_instance = vm;
117
            this.show_vm_menu();
118
            this.show_current();
119
            this.update_layout();
120
        },
121

    
122
        // identify vm model instance id based on DOM element
123
        vm_id_for_element: function(el) {
124
            return el.attr('id').replace("single-vm-", "");
125
        },
126
        
127
        // set generic view handlers
128
        set_handlers: function() {
129
            this.prev_button.click(_.bind(function(ev){
130
                storage.vms.reset_pending_actions();
131
                ev.preventDefault();
132
                this.show_prev();
133
            }, this));
134

    
135
            this.next_button.click(_.bind(function(ev){
136
                storage.vms.reset_pending_actions();
137
                ev.preventDefault();
138
                this.show_next();
139
            }, this));
140
        },  
141

    
142
        update_current_vm: function() {
143
            try {
144
                this.current_vm_instance = storage.vms.at(this.current_vm);
145
                this.current_vm_instance.start_stats_update(true);
146
                storage.vms.stop_stats_update([this.current_vm_instance]);
147
            } catch (err) {
148
                this.log.debug("Cannot select current vm instance for: {0}".format(this.current_vm));
149
                this.current_vm_instance = undefined;
150
                this.current_vm = 0;
151
            }
152
        },
153

    
154
        show_next: function() {
155
            this.current_vm++;
156
            if (this.current_vm >= storage.vms.models.length) {
157
                this.current_vm = 0;
158
            }
159
            
160
            this.update_current_vm();
161

    
162
            // this might fail when vms get empty
163
            // catch the exception
164
            try {
165
                snf.router.vm_details_view(this.current_vm_instance.id);
166
            } catch (err) {};
167
        },
168

    
169
        show_prev: function() {
170
            this.current_vm--;
171
            if (this.current_vm < 0) {
172
                this.current_vm = storage.vms.length - 1;
173
            }
174

    
175
            this.update_current_vm();
176
            snf.router.vm_details_view(this.current_vm_instance.id);
177
        },
178

    
179
        post_remove_vm: function(vm) {
180
            // current vm removed or does not exist after an update
181
            this.show_vm_menu();
182
            if (!this.current_vm_instance || this.current_vm_instance.id == vm.id) {
183
                this.current_vm++;
184
                if (this.current_vm >= storage.vms.models.length) {
185
                    this.current_vm = 0;
186
                }
187
                this.update_current_vm();
188
                this.show_current();
189
            // this might fail when vms get empty
190
            // catch the exception
191
            } else {
192
                this.show_current();
193
            }
194
        },
195
        
196
        // stuff to do when a new vm has been created.
197
        // - create vm subviews
198
        post_add: function(vm) {
199
            this.vm(vm).removeClass("single-container-template");
200
            this.show_vm_menu();
201
            this.show_current();
202

    
203
            // rename views index
204
            this.stats_views = this.stats_views || {};
205
            this.connect_views = this.connect_views || {};
206
            this.tags_views = this.tags_views || {};
207
            this.details_views = this.details_views || {};
208
            this.action_views = this.action_views || {};
209
            this.action_error_views = this.action_error_views || {};
210

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

    
213
            // same as icon view
214
            this.action_views[vm.id] = new views.VMActionsView(vm, this, this.vm(vm), this.hide_actions);
215
            this.stats_views[vm.id] = new views.VMStatsView(vm, this, {stats_type: 'series'});
216
            this.connect_views[vm.id] = new views.IconVMConnectView(vm, this);
217
            this.tags_views[vm.id] = new views.VMTagsView(vm, this, true, 20, 10, 35);
218
            this.details_views[vm.id] = new views.SingleDetailsView(vm, this);
219
            this.action_error_views[vm.id] = new views.VMActionErrorView(vm, this);
220
            
221
            if (storage.vms.models.length > 1) { this.vm(vm).hide(); };
222
        },
223

    
224
        post_update_vm: function(vm) {
225
        },
226
        
227
        // vm specific event handlers
228
        set_vm_handlers: function(vm) {
229
        },
230
        
231
        // handle selected vm
232
        show_current: function() {
233
            var index = this.current_vm;
234
            var vm = storage.vms.at(index);
235

    
236
            this.$(".server-name").removeClass("column3-selected");
237
            
238
            if (vm) {
239
                this.vm(vm).show();
240
            };
241

    
242
            _.each(storage.vms.models, function(vmo){
243
                if (vm && (vm.id != vmo.id)) {
244
                    if (!hasKey.call(this._vm_els, vmo.id)) { return };
245
                    this.vm(vmo).hide();
246
                }
247
            }, this)
248

    
249
            if (!vm) {
250
                // empty list
251
                this.$(".column3").hide();
252
                return;
253
            }
254
            this.$(".column3").show();
255

    
256

    
257
            $("#" + this.link_id_tpl + this.current_vm).addClass("column3-selected");
258
            try {
259
                this.update_details(vm);
260
            } catch (err) {};
261
        },
262

    
263
        show_vm_menu: function() {
264
            this.menu.find(".server-name").remove();
265

    
266
            _.each(storage.vms.models, function(vm, index) {
267
                var el = $('<div class="server-name" id="'+this.link_id_tpl + index +'">' + 
268
                               util.truncate(vm.escape("name"),16)+'</div>')
269
                this.menu.append(el);
270

    
271
                vm.bind("change:name", function(){
272
                  el.html(util.truncate(vm.escape("name"), 16));
273
                })
274

    
275
                if (this.current_vm_instance && vm.id == this.current_vm_instance.id) {
276
                    this.current_vm = index;
277
                }
278
            }, this);
279
            
280
            var self = this;
281
            this.menu.find(".server-name").click(function(ev) {
282
                ev.preventDefault();
283
                var id = $(this).attr("id").replace("single-vm-at-", "");
284
                if (self.current_vm != id) {
285
                    storage.vms.reset_pending_actions();
286
                }
287
                self.current_vm = id;
288
                self.update_current_vm();
289
                snf.router.vm_details_view(self.current_vm_instance.id);
290
            })
291
        },
292

    
293
        // generic stuff to do on each view update
294
        // called once after each vm has been updated
295
        update_layout: function() {
296
            this.update_current_vm();
297
            fix_v6_addresses();
298
        },
299

    
300
        update_status_message: function(vm) {
301
            var el = this.vm(vm);
302
            var message = vm.get_status_message();
303
            if (message) {
304
                // update bulding progress
305
                el.find("div.machine-ips").hide();
306
                el.find("div.build-progress").show();
307
                el.find("div.build-progress .message").text(message);
308
                if (vm.in_error_state()) {
309
                    el.find("div.build-progress .btn").show();
310
                } else {
311
                    el.find("div.build-progress .btn").hide();
312
                }
313
            } else {
314
                // hide building progress
315
                el.find("div.machine-ips").show()
316
                el.find("div.build-progress").hide();
317
                el.find("div.build-progress .btn").hide();
318
            }
319
        },
320

    
321
        // update vm details
322
        update_details: function(vm) {
323
            var el = this.vm(vm);
324
            if (vm != this.current_vm_instance) { return };
325

    
326
            // truncate name
327
            el.find(".machine-detail.name").text(util.truncate(vm.get("name"), 35));
328
            // set ips
329
            el.find(".machine-detail.ipv4.ipv4-text").text(vm.get_addresses().ip4 || "not set");
330
            // TODO: fix ipv6 truncates and tooltip handler
331
            el.find(".machine-detail.ipv6.ipv6-text").text(vm.get_addresses().ip6 || "not set");
332
            // set the state (i18n ??)
333
            el.find(".state-label").text(STATE_TEXTS[vm.state()]);
334
            // set state class
335
            el.find(".state").removeClass().addClass(views.SingleView.STATE_CLASSES[vm.state()].join(" "));
336
            // os icon
337
            el.find(".single-image").css({'background-image': "url(" + this.get_vm_icon_path(vm, "medium") + ")"});
338
            
339
            el.removeClass("connectable");
340
            if (vm.is_connectable()) {
341
                el.addClass("connectable");
342
            }
343

    
344
            this.update_status_message(vm);
345

    
346
            icon_state = vm.is_active() ? "on" : "off";
347
            set_machine_os_image(el, "single", icon_state, this.get_vm_icon_os(vm));
348
            
349
            // update subviews
350
            this.action_views[vm.id].update_layout();
351
            this.stats_views[vm.id].update_layout();
352
            this.connect_views[vm.id].update_layout();
353
            this.tags_views[vm.id].update_layout();
354
            this.details_views[vm.id].update_layout();
355
        },
356
            
357
        get_vm_icon_os: function(vm) {
358
            var os = vm.get_os();
359
            var icons = window.os_icons || views.SingleView.VM_OS_ICONS;
360
            if (icons.indexOf(os) == -1) {
361
                os = "unknown";
362
            }
363
            return os;
364
        },
365

    
366
        // TODO: move to views.utils (the method and the VM_OS_ICON vars)
367
        get_vm_icon_path: function(vm, icon_type) {
368
            var os = vm.get_os();
369
            var icons = window.os_icons || views.SingleView.VM_OS_ICONS;
370

    
371
            if (icons.indexOf(os) == -1) {
372
                os = "unknown";
373
            }
374

    
375
            return views.SingleView.VM_OS_ICON_TPLS()[icon_type].format(os);
376
        }
377
    })
378

    
379
    views.SingleView.VM_OS_ICON_TPLS = function() {
380
        return {
381
            "medium": snf.config.machines_icons_url + "large/{0}-sprite.png"
382
        }
383
    }
384

    
385
    views.SingleView.VM_OS_ICONS = window.os_icons || [];
386

    
387
    views.SingleView.STATE_CLASSES = {
388
        'UNKNOWN':          ['state', 'error-state'],
389
        'BUILD':            ['state', 'build-state'],
390
        'REBOOT':           ['state', 'rebooting-state'],
391
        'STOPPED':          ['state', 'terminated-state'],
392
        'ACTIVE':           ['state', 'running-state'],
393
        'ERROR':            ['state', 'error-state'],
394
        'DELETED':          ['state', 'destroying-state'],
395
        'DESTROY':          ['state', 'destroying-state'],
396
        'SHUTDOWN':         ['state', 'shutting-state'],
397
        'START':            ['state', 'starting-state'],
398
        'CONNECT':          ['state', 'connecting-state'],
399
        'DISCONNECT':       ['state', 'disconnecting-state'],
400
        'RESIZE':           ['state', 'rebooting-state']
401
    };
402

    
403
})(this);