Statistics
| Branch: | Tag: | Revision:

root / ui / templates / standard.html @ 1027802d

History | View | Annotate | Download (18 kB)

1
{% load i18n %}
2

    
3
<!-- the standard view -->
4
<div id="machinesview" class="standard">
5
    <div id="spinner"></div>
6
    <div class="machine" id="machine-template" style="display:none">
7
        <div class="actions">
8
            <a href="#" class="action-start">{% trans "Start" %}</a>
9
            <a href="#" class="action-reboot">{% trans "Reboot" %}</a>
10
            <a href="#" class="action-shutdown">{% trans "Shutdown" %}</a>
11
            <a href="#" class="action-destroy">{% trans "Destroy" %}</a>
12
        </div>        
13
        <div class="state">
14
            <div class="status">{% trans "Running" %}</div>
15
            <div class="indicator"></div>
16
            <div class="indicator"></div>
17
            <div class="indicator"></div>
18
            <div class="indicator"></div>
19
            <img class="spinner" style="display:none" src="/static/progress.gif" />
20
            <img class="wave" style="display:none" src="/static/wave.gif" />
21
        </div>
22
        <img class="logo" src="" />
23
        <a href="#" class="name">
24
            <h5>{% trans "Name: " %}<span class="name">node.name</span><span class="rename"></span></h5>
25
        </a>
26
        <a href="#" class="ip">
27
            <h5>{% trans "IP: " %}<span class="public">node.public_ip</span></h5>
28
        </a>
29
        <h5 class="settings">
30
            {% trans "Show:" %} 
31
            <a class="show-disks"href="#">{% trans "disks" %}</a> | 
32
            <a class="show-networks"href="#">{% trans "networks" %}</a> | 
33
            <a class="show-group"href="#">{% trans "group" %}</a> | 
34
            <a class="show-metadata" href="#">{% trans "metadata" %}</a>
35
        </h5>
36
        <div class="confirm_single">
37
            <button class="yes">{% trans "Confirm" %}</button>
38
            <button class="no">{% trans "Cancel" %}</button>
39
        </div>
40
        <div class="action_error" align="center">
41
            {% trans "<span class='orange'>Error</span> on" %} <span class="action">{% trans "error action" %}</span>
42
            <span class="code"></span>            
43
            <span class="message"></span>
44
            <button class="details">{% trans "Details" %}</button>
45
        </div>
46
        <div class="separator"></div>
47
    </div>
48
    <div class="running"></div>
49
    <div id="mini" class="separator"></div>
50
    <div class="terminated"></div>
51
</div>
52

    
53
<script>
54

55
// intercept metadata click
56
$("a.show-metadata").live('click', function() {
57
    // get server name and server ID
58
    var serverID = $(this).parent().parent().attr("id");
59
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
60
    // set server name in box's title
61
    $("#editor-1 h3 span").text(serverName);
62
    var triggers = $("a#meta-editor-1").overlay({
63
        // some mask tweaks suitable for modal dialogs
64
        mask: '#000',
65
        effect: 'default',
66
        top: '10%',
67
        closeOnClick: false,
68
        oneInstance: false,
69
        load: false
70
    });
71
    $("a#meta-editor-1").data('overlay').load();
72
    return false; 
73
});
74

75
// intercept create new metadata entry click
76
$("a.show-group").live('click', function() {
77
    // get server name and server ID
78
    var serverID = $(this).parent().parent().attr("id");
79
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
80
    // set server name in box's title
81
    $("#editor-2 h3 span").text(serverName);
82
    var triggers = $("a#meta-editor-2").overlay({
83
        // some mask tweaks suitable for modal dialogs
84
        mask: '#000',
85
        effect: 'default',
86
        top: '10%',
87
        closeOnClick: false,
88
        oneInstance: false,
89
        load: false
90
    });
91
    $("a#meta-editor-2").data('overlay').load();
92
    return false; 
93
});
94

95
// intercept reboot click 
96
$("div.actions a.action-reboot").live('click', function(){
97
    var serverID = $(this).parent().parent().attr("id");
98
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
99
    var found = false;
100
    
101
    $(this).parent().children('a').removeClass('selected');
102
    $(this).addClass('selected');
103
    $(this).parent().addClass('display');
104
    $(this).parent().parent().find('.action_error').hide();
105
    for (i=0;i<pending_actions.length;i++){ // if there is already a pending action for this server replace it
106
        if (pending_actions[i][1]==serverID){
107
            pending_actions[i][0] = reboot;
108
            found = true
109
        }
110
    }
111
    if (!found) // no pending action for this server was found, so let's just add it to the list
112
        pending_actions.push([reboot, serverID, serverName])
113
    update_confirmations();
114
    return false;
115
});
116

117
// intercept shutdown click
118
$("div.actions a.action-shutdown").live('click', function(){ 
119
    var serverID = $(this).parent().parent().attr("id");
120
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
121
    var found = false;
122
    $(this).parent().children('a').removeClass('selected');
123
    $(this).addClass('selected');
124
    $(this).parent().addClass('display')
125
    $(this).parent().parent().find('.action_error').hide();
126

127
    for (i=0;i<pending_actions.length;i++){ // if there is already a pending action for this server replace it
128
        if (pending_actions[i][1]==serverID){
129
            pending_actions[i][0] = shutdown;
130
            found = true
131
        }
132
    }
133
    if (!found) // no pending action for this server was found, so let's just add it to the list 
134
        pending_actions.push([shutdown, serverID, serverName])
135
    update_confirmations();
136
    return false;
137
});
138

139
// intercept start click
140
$("div.actions a.action-start").live('click', function(){ 
141
    var serverID = $(this).parent().parent().attr("id");
142
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
143
    var found = false;
144
    $(this).parent().children('a').removeClass('selected');
145
    $(this).addClass('selected');
146
    $(this).parent().addClass('display')
147
    $(this).parent().parent().find('.action_error').hide();
148

149
    for (i=0;i<pending_actions.length;i++){ // if there is already a pending action for this server replace it
150
        if (pending_actions[i][1]==serverID){
151
            pending_actions[i][0] = start;
152
            found = true
153
        }
154
    }
155
    if (!found) // no pending action for this server was found, so let's just add it to the list
156
        pending_actions.push([start, serverID, serverName])
157
    update_confirmations();    
158
    return false;
159
});
160

161
// intercept destroy click
162
$("div.actions a.action-destroy").live('click', function(){ 
163
    var serverID = $(this).parent().parent().attr("id");
164
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
165
    var found = false;
166
    $(this).parent().children('a').removeClass('selected');
167
    $(this).addClass('selected');
168
    $(this).parent().addClass('display')
169
    $(this).parent().parent().find('.action_error').hide();
170

171
    for (i=0;i<pending_actions.length;i++){ // if there is already a pending action for this server replace it
172
        if (pending_actions[i][1]==serverID){
173
            pending_actions[i][0] = destroy;
174
            found = true
175
        }
176
    }
177
    if (!found) // no pending action for this server was found, so let's just add it to the list
178
        pending_actions.push([destroy, serverID, serverName])
179
    update_confirmations();    
180
    return false;
181
});
182

183
$("div.confirm_single .yes").live('click', function(){
184
    var serverID = $(this).parent().parent().attr("id");
185
    for (i=0;i<pending_actions.length;i++){ // if there is a pending action for this server execute it
186
        if (pending_actions[i][1]==serverID){
187
            action = pending_actions.splice(i,1)[0]; // extract action
188
            // change the status text in cases where no api state exists
189
            if (action[0] == start) {
190
                $(this).parent().parent().find('.status').text('Starting');
191
                $(this).parent().parent().find('.spinner').show();
192
            } else if (action[0] == shutdown) {
193
                $(this).parent().parent().find('.status').text('Shutting down');
194
                $(this).parent().parent().find('.spinner').show();
195
            } else if (action[0] == reboot) {
196
                $(this).parent().parent().find('.status').text('Rebooting');
197
                $(this).parent().parent().find('.spinner').show();
198
            }  else if (action[0] == destroy) {
199
                $(this).parent().parent().find('.status').text('Destroying');
200
                $(this).parent().parent().find('.spinner').show();
201
            }                    
202
            action[0]([action[1]]); // execute action
203
        }
204
    }
205
    $(this).parent().hide();
206
    $(this).parent().parent().children('div.actions').children('a').removeClass('selected');
207
    $(this).parent().parent().children('.state').children('.spinner').show()
208
    $(this).parent().parent().children('div.actions').removeClass('display');
209
    update_confirmations(); 
210
    return false;
211
});
212

213
$("div.confirm_single .no").live('click', function(){
214
    // remove the action from the pending list
215
    var serverID = $(this).parent().parent().attr("id");
216
    
217
    $(this).parent().parent().children('div.actions').children('a').removeClass('selected');
218
    $(this).parent().parent().children('div.actions').removeClass('display');    
219
    for (i=0;i<pending_actions.length;i++){ // if there is a pending action for this server remove it
220
        if (pending_actions[i][1]==serverID){
221
            pending_actions.splice(i,1);
222
        }
223
    }
224
    $(this).parent().hide();
225
    update_confirmations();    
226
    return false;
227
});
228

229
$("div.action_error .details").live('click', function(){
230
    // remove the action from the pending list
231
    ajax_error($(this).parent().children('.code').text(), undefined, $(this).parent().children('.action').text(), $(this).parent().children('.message').text());
232
    $(this).parent().hide();
233
});
234

235

236
// update the servers list
237
function update_machines_view(data){
238
    /* 
239
    Go through the servers in the input data. Update existing entries, add
240
    new ones to the list
241
    */     
242
           
243
    $.each(data.servers.values, function(i,server){
244
        existing = $('#' + server.id);
245
        
246
        // if multiple machines exist in the DOM, delete all but one
247
        // defensive coding - that shouldn't happen normally
248
        while (existing.length > 1){
249
            existing.remove();
250
        }
251

252
        var server_image = os_icon(server.metadata);
253

254

255
        // server already exists in DOM
256
        if (existing.length){
257
            $("div.machine:last-child").find("div.separator").show();                
258

259
            //  if the status is deleted, delete it from the DOM
260
            if (server.status == 'DELETED') {
261
                existing.remove();            
262
                try {
263
                    console.info(existing.find("a.name span.name").text() + ' removed');
264
                } catch(err) {}            
265
            } else if (existing.find(".status").text() != STATUS_MESSAGES[server.status]) {
266

267
                try { // firebug console logging
268
                    console.info(existing.find("a.name span.name").text() + ' from ' 
269
                                + existing.find(".status").text() + ' to ' + STATUS_MESSAGES[server.status]);
270
                } catch(err) {}
271
                //show reboot and shutdown actions
272
                if (server.status == 'ACTIVE' || server.status == 'REBOOT') {
273
                    $('div.#' + server.id + ' a.action-reboot').show();
274
                    $('div.#' + server.id + ' a.action-shutdown').show();        
275
                    $('div.#' + server.id + ' a.action-destroy').removeClass('destroy-padding');        
276
                }
277
                if (['BUILD','ACTIVE','REBOOT'].indexOf(server.status) >= 0 &&
278
                    [STATUS_MESSAGES['STOPPED'], STATUS_MESSAGES['ERROR'],
279
                     'Starting'].indexOf(existing.find(".status").text()) >= 0) {
280
                    // from stopped, on error or starting to building, active or rebooting
281
                    // starting is not an api state, it means the vm is stopped or on error
282
                    moved = existing.clone().appendTo(".running");
283
                    moved.find("img.logo").attr("src","static/machines/" + server_image + '-on.png');
284
                    existing.remove();
285
                    existing = moved;
286
                    existing.find(".status").text(STATUS_MESSAGES[server.status]); 
287
                } else if (['STOPPED','ERROR'].indexOf(server.status) >= 0 &&
288
                           [STATUS_MESSAGES['ACTIVE'], STATUS_MESSAGES['BUILD'], STATUS_MESSAGES['REBOOT'],
289
                            'Shutting down'].indexOf(existing.find(".status").text()) >= 0) {
290
                    // from active, building, rebooting, or shutting down to stopped or on error
291
                    // shutting down is not an api state, it means the server is active
292
                    moved = existing.clone().appendTo(".terminated");
293
                    moved.find("img.logo").attr("src","static/machines/" + server_image + '-off.png');
294
                    existing.remove();
295
                    existing = moved;
296
                    existing.find(".status").text(STATUS_MESSAGES[server.status]); 
297
                } else if (['BUILD','ACTIVE','REBOOT'].indexOf(server.status) >= 0 && 
298
                            [STATUS_MESSAGES['ACTIVE'], STATUS_MESSAGES['BUILD'],
299
                             STATUS_MESSAGES['REBOOT']].indexOf(existing.find(".status").text()) >= 0) {
300
                    // the server changes status, but remains in running list
301
                    existing.find(".status").text(STATUS_MESSAGES[server.status]); 
302
                } else if (['STOPPED','ERROR'].indexOf(server.status) >= 0 &&
303
                    [STATUS_MESSAGES['STOPPED'], 
304
                     STATUS_MESSAGES['ERROR']].indexOf(existing.find(".status").text()) >= 0) {
305
                    // the server changes status, but remains in terminated list
306
                    existing.find(".status").text(STATUS_MESSAGES[server.status]); 
307
                }        
308
                existing.find('.spinner').hide();
309
                existing.find(' .wave').attr('src','static/wave.gif').show();
310
                setTimeout("$('#" + server.id +" .wave').attr('src','').hide()", 3000);
311
                // show spinner while the server is rebooting, starting or shutting down  
312
                if ([STATUS_MESSAGES['REBOOT'], 
313
                    'Starting', 'Shutting down'].indexOf(existing.find(".status").text()) >= 0 ) {
314
                    existing.find(' .wave').hide();
315
                    existing.find('.spinner').show();
316
                }                            
317
            }
318
            existing.find("a.name span.name").text(server.name);
319
            existing.find("a.ip span.public").text(String(server.addresses.values[0].values[0].addr).replace(',',' '));
320
        } else if (server.status != 'DELETED') {
321
            // If it does not exist and it's not deleted, we should create it
322
            var machine = $("#machine-template").clone().attr("id", server.id).fadeIn("slow");
323
            machine.find("a.name span.name").text(server.name);
324
            machine.find("img.logo").attr("src","static/machines/"+server_image+'-on.png');
325
            machine.find("span.imagetag").text(server_image);
326
            machine.find("a.ip span.public").text(String(server.addresses.values[0].values[0].addr).replace(',',' '));            
327
            machine.find(".status").text(STATUS_MESSAGES[server.status]);
328
            if (['BUILD', 'ACTIVE', 'REBOOT'].indexOf(server.status) >= 0){
329
                machine.appendTo(".running");
330
            } else {
331
                machine.find("img.logo").attr("src","static/machines/"+server_image+'-off.png');
332
                machine.appendTo(".terminated");
333
            }
334
            //show spinner while machine is building
335
            if (server.status == 'BUILD' || 
336
                ['Starting', 'Shutting down'].indexOf(existing.find(".status").text()) >= 0 ) { 
337
                machine.find('.spinner').show();
338
            }   
339
            //allow destroy action only while machine is building
340
            if (server.status == 'BUILD') {
341
                $('div.#' + server.id + ' a.action-reboot').hide();
342
                $('div.#' + server.id + ' a.action-shutdown').hide();        
343
                $('div.#' + server.id + ' a.action-destroy').addClass('destroy-padding');        
344
            }
345
        }
346
    });
347

348
    $("#spinner").hide();
349
    // show all separators
350
    $("div.machine div.separator").show();
351
    // hide the last one
352
    $("div.machine:last-child").find("div.separator").hide();
353
    // the separator shows only when running and terminated machines are available
354
    if ($(".terminated a.name").length > 0 && $(".running a.name").length > 0) {
355
        $("#mini.separator").fadeIn("slow");
356
    } else {
357
        $("#mini.separator").fadeOut("slow");
358
    }     
359

360
    // show message in case user has no servers!
361
    if (servers.length == 0) {
362
        showWelcome()
363
    } else {
364
        hideWelcome()
365
    }  
366
    
367
    // set confirm box position
368
    if (window.innerHeight - 220 < $('#machinesview').height())
369
        $('.confirm_multiple').addClass('fixed');
370
    else
371
        $('.confirm_multiple').removeClass('fixed');
372
}
373

374
// indicate that the requested action was succesfully completed
375
function display_success(serverID) {
376

377
}
378

379
// indicate that the requested action was not completed
380
function display_failure(status, serverID, action, responseText) {
381
    $('#'+serverID+ ' .spinner').hide();
382
    $('#'+serverID+ ' .action_error .action').text(action);
383
    $('#'+serverID+ ' .action_error .code').text(status);
384
    $('#'+serverID+ ' .action_error .message').text(responseText);
385
    $('#'+serverID+ ' .action_error').show();    
386
}
387

388
// basic functions executed on page load
389
if (images.length == 0) {
390
    // populate image list
391
    update_images();
392
}
393
if (flavors.length == 0) {
394
    // configure flavors
395
    update_flavors(); 
396
}
397
// set the label of the multiple buttons 
398
$('div.confirm_multiple button.yes').text('Confirm All');
399
$('div.confirm_multiple button.no').text('Cancel All');
400
// reposition multiple confirmation box on window resize
401
$(window).resize(function(){
402
    if (this.innerHeight - 220 < $('#machinesview').height())
403
        $('.confirm_multiple').addClass('fixed');
404
    else
405
        $('.confirm_multiple').removeClass('fixed');
406
});
407
// start updating vm list
408
update_vms(UPDATE_INTERVAL);
409
</script>