Revision 42f67a2a ui/templates/machines.html

b/ui/templates/machines.html
7 7

  
8 8
<!-- changing between standard/list view -->
9 9
<div id="view-select">
10
    <a id="standard" class="current" href="/machines">#</a>
10
    <a id="standard" href="/machines/standard">#</a>
11 11
    <span class="view-seperator">|</span>
12 12
    <a id="list" href="/machines/list">=</a>
13 13
</div>
14 14

  
15
<!-- the standard view -->
16
<div id="machinesview" class="standard">
17
    <div id="spinner"></div>
18
    <div class="machine" id="machine-template" style="display:none">
19
        <div class="state">
20
            <div class="status">{% trans "Running" %}</div>
21
            <div class="indicator"></div>
22
            <div class="indicator"></div>
23
            <div class="indicator"></div>
24
            <div class="indicator"></div>
25
        </div>
26
        <img class="logo" src="" />
27
        <a href="#" class="name">
28
            <h5>Νame: <span class="name">node.name</span><span class="rename"></span></h5>
29
        </a>
30
        <a href="#" class="ip">
31
            <h5>IP: <span class="public">node.public_ip</span></h5>
32
        </a>
33
        <h5 class="settings">
34
            {% trans "Show:" %} <a href="#">{% trans "disks" %}</a> | <a href="#">{% trans "networks" %}</a> | <a href="#">{% trans "group" %}</a>
35
        </h5>
36
        <div class="actions">
37
            <a href="#" class="action-start">{% trans "Start" %}</a>
38
            <a href="#" class="action-reboot">{% trans "Reboot" %}</a>
39
            <a href="#" class="action-shutdown">{% trans "Shutdown" %}</a>
40
            <a href="#" class="more">{% trans "more &hellip;" %}</a>
41
        </div>
42
        <div class="seperator"></div>
43
    </div>
44

  
45
    <div class="running"></div>
46
    <div id="mini" class="seperator"></div>
47
    <div class="terminated"></div>
48
</div>
49

  
50
<div id="machines" class="seperator"></div>
51

  
52 15
<!-- the form -->
53 16
<form action="#">
54 17
	<!-- scrollable root element -->
......
201 164
	<button>{% trans "No" %}</button>
202 165
</div>
203 166

  
167
<div id="machinesview"></div>
168

  
204 169
<script type="text/javascript"> 
205 170
var TIMEOUT = {{timeout}};
206 171
</script>
207 172

  
208 173
<script>
174

  
175

  
176
    
209 177
// hardcoded image types
210 178
var image_tags = {
211 179
                1: 'archlinux',
......
223 191
                20: 'ubuntu',
224 192
               };
225 193

  
226
// ajax error checking  
227
function ajax_error(jqXHR) {; 
228
    // prepare the error message
229
    $("#error-success h3").text('{% trans "Error!" %}');
230
    // check the error code
231
    switch (jqXHR.status) {
232
        case 400: // YY error/message
233
            $("#error-success p").text('{% trans "A Bad Request has been made." %}');
234
            break;
235
        case 404: // YY error/message
236
            $("#error-success p").text('{% trans "Your request has failed." %}');
237
            break;
238
        case 501: // XX error/message
239
            $("#error-success p").text('{% trans "There has been an Internal Error. Our administrators have been notified." %}');
240
            break;
241
        case 503: // XX error/message
242
            $("#error-success p").text('{% trans "This service is unavailble right now, please try again later." %}');
243
            break;
244
        default: // XXYY error/message
245
            $("#error-success p").text('{% trans "An Error has happened. Our administrators have been notified." %}');
246
    }
247
    // bring up error notification
248
    var triggers = $("a#notification").overlay({
249
	    // some mask tweaks suitable for modal dialogs
250
	    mask: {
251
		    color: '#ebecff',
252
		    opacity: '0.9'
253
	    },
254
        top: 'center',
255
	    closeOnClick: false,
256
        oneInstance: false,
257
        load: true,
258
        onClose: function(){
259
            $("div.pane#machines-pane").load($("a#standard").attr("href"));
260
        }
261
    });
262
    return false;
263
}
264

  
265
// ajax success checking
266
function ajax_success() {
267
    // prepare the error message
268
    $("#error-success h3").text('{% trans "Success!" %}');
269
    $("#error-success p").text('{% trans "Your request has been succefully executed." %}');
270
    // bring up success notification
271
    var triggers = $("a#notification").overlay({
272
	    // some mask tweaks suitable for modal dialogs
273
	    mask: {
274
		    color: '#ebecff',
275
		    opacity: '0.9'
276
	    },
277
        top: 'center',
278
	    closeOnClick: false,
279
        oneInstance: false,
280
        load: true,
281
        onClose: function(){
282
            $("div.pane#machines-pane").load($("a#standard").attr("href"));
283
        }
284
    });
285
    return false;
286
}
287

  
288
// confirmation overlay generation
289
function confirm_action(action_string, action_function, serverID, serverName) {
290
    $("#yes-no h3").text('You are about to ' + action_string + ' vm ' + serverName);
291
    // action confirmation overlay
292
    var triggers = $("a#confirmation").overlay({
293
	    // some mask tweaks suitable for modal dialogs
294
	    mask: {
295
		    color: '#ebecff',
296
		    opacity: '0.9'
297
	    },
298
        top: 'center',
299
        load: true
300
    });
301
    // yes or no?
302
    var buttons = $("#yes-no button").click(function(e) {
303
	    // get user input
304
	    var yes = buttons.index(this) === 0;
305
        //close the confirmation window
306
        $("a#confirmation").overlay().close(); 
307
        // return true=yes or false=no
308
        if (yes) {
309
            action_function(serverID);
310
        } else {
311
            // reload page
312
            $("div.pane#machines-pane").load($("a#standard").attr("href"));
313
        }
314
    });
315
    return false;
316
}
317

  
318
// get and show a list of running and terminated machines
319
function update_vms() {
320

  
321
    $(".running").text('');
322
    $(".terminated").text('');
323
    $("ul#standard-images").text('');
324
    $("ul#custom-images").text('');
325

  
326
    $.ajax({
327
        url: '/api/v1.0/servers/detail',
328
        type: "GET",
329
        timeout: TIMEOUT,
330
        dataType: "json",
331
        error: function(jqXHR, textStatus, errorThrown) { 
332
                    ajax_error(jqXHR);
333
                    return false;
334
                    },
335
        success: function(data, textStatus, jqXHR) {
336
            if ($(".running a.name").length + $(".terminated a.name").length == 0) {
337
            
338
                $.each(data.servers, function(i,server){
339
                    // if the machine is deleted it should not be included in any list
340
                    if (server.status == 'DELETED') {
341
                        return;
342
                    }
343
                    var machine = $("#machine-template").clone().attr("id", server.id).fadeIn("slow");
344
                    machine.find("input[type='checkbox']").attr("id", "input-" + server.id);
345
                    machine.find("input[type='checkbox']").attr("class", server.status);
346
                    machine.find("a.name span.name").text(server.name);
347
                    machine.find("img.logo").attr("src","static/machines/"+image_tags[server.imageId]+'.png');
348
                    machine.find("img.list-logo").attr("src","static/os_logos/"+image_tags[server.imageId]+'.png');
349
                    machine.find("img.list-logo").attr("title",image_tags[server.imageId]);
350
                    machine.find("span.imagetag").text(image_tags[server.imageId]);
351
    
352
                    machine.find("a.ip span.public").text(String(server.addresses.public.ip.addr).replace(',',' '));            
353
    
354
                    // TODO: handle SHARE_IP, SHARE_IP_NO_CONFIG, DELETE_IP, REBUILD, QUEUE_RESIZE, PREP_RESIZE, RESIZE, VERIFY_RESIZE, PASSWORD, RESCUE
355
                    if (server.status == 'BUILD'){
356
                        machine.find(".status").text('Building');
357
                        machine.appendTo(".running");
358
                    } else if (server.status == 'ACTIVE') {
359
                        machine.find(".status").text('Running');
360
                        machine.appendTo(".running"); 
361
                    } else if (server.status == 'REBOOT' || server.status == 'HARD_REBOOT') {
362
                        machine.find(".status").text('Rebooting');
363
                        machine.appendTo(".running");
364
                    } else if (server.status == 'STOPPED') {
365
                        machine.find(".status").text('Stopped');
366
                        machine.find("img.logo").attr("src","static/machines/"+image_tags[server.imageId]+'-off.png');
367
                        machine.find("img.list-logo").attr("src","static/os_logos/"+image_tags[server.imageId]+'-off.png');
368
                        machine.appendTo(".terminated");
369
                    } else if (server.status == 'ERROR') {
370
                        machine.find(".status").text('Error');
371
                        machine.find("img.logo").attr("src","static/machines/"+image_tags[server.imageId]+'-off.png');
372
                        machine.find("img.list-logo").attr("src","static/os_logos/"+image_tags[server.imageId]+'-off.png');
373
                        machine.appendTo(".terminated");
374
                    } 
375
                    else {
376
                        machine.find(".status").text('Unknown');
377
                        machine.find("img.logo").attr("src","static/machines/"+image_tags[server.imageId]+'-off.png');
378
                        machine.find("img.list-logo").attr("src","static/os_logos/"+image_tags[server.imageId]+'-off.png');
379
                        machine.appendTo(".terminated");
380
                    }
381
                });
382
            }
383
            $("#spinner").hide();
384
            $("div.machine:last-child").find("div.seperator").hide();
385
            // if the terminated list is populated then the seperator must be shown
386
            if ($(".terminated a.name").length > 0) {
387
                $("#mini.seperator").fadeIn("slow");
388
            }
389
            // creating the table in list view, if there are machines to show
390
            if ($("div.list table.list-machines tbody").length > 0) {
391
                $("div.list table.list-machines").dataTable({
392
                    "bInfo": false,
393
                    "bPaginate": false,
394
            		"bAutoWidth": false,
395
            		"bSort": true,    
396
                    "bStateSave": true,
397
                    //"sScrollY": "250px",
398
                    //"sScrollX": "500px",
399
                    //"sScrollXInner": "480px",
400
                    "aoColumnDefs": [
401
                        { "bSortable": false, "aTargets": [ 0 ] }
402
                    ]
403
                });
404
                $("div.list table.list-machines").show();
405
                $("div.list div.actions").show();
406
            }
407
        }
408
    });
409
    return false;
410
}
411

  
412
// get and show a list of anvailable standard and custom images
413
function update_images() { 
414
    $.ajax({
415
        url: '/api/v1.0/images/detail',
416
        type: "GET",
417
        //async: false,
418
        dataType: "json",
419
        timeout: TIMEOUT,
420
        error: function(jqXHR, textStatus, errorThrown) { 
421
                    ajax_error(jqXHR);
422
                    },
423
        success: function(data, textStatus, jqXHR) {
424
            if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
425
                $.each(data.images, function(i,image){
426
                    var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
427
                    img.find("label").attr('for',"img-radio-" + image.id);
428
                    img.find(".image-title").text(image.name);
429
                    img.find(".description").text(image.description);
430
                    img.find(".size").text(image.size);
431
                    img.find("input.radio").attr('id',"img-radio-" + image.id);
432
                    if (i==0) img.find("input.radio").attr("checked","checked"); 
433
                    img.find("img.image-logo").attr('src','static/os_logos/'+image_tags[image.id]+'.png');
434
                    if (image.serverId) {
435
                        img.appendTo("ul#custom-images");
436
                    } else {
437
                        img.appendTo("ul#standard-images");
438
                    }
439
                });
440
            }
441
        }
442
    });
443
    return false;
444
}
445

  
446
var flavors = {}, disks = [], cpus = [], ram = [];
447

  
448
Array.prototype.unique = function () {
449
	var r = new Array();
450
	o:for(var i = 0, n = this.length; i < n; i++)
451
	{
452
		for(var x = 0, y = r.length; x < y; x++)
453
		{
454
			if(r[x]==this[i])
455
			{
456
				continue o;
457
			}
458
		}
459
		r[r.length] = this[i];
460
	}
461
	return r;
462
}
463

  
464
// get and configure flavor selection
465
function update_flavors() { 
466
    $.ajax({
467
        url: '/api/v1.0/flavors/detail',
468
        type: "GET",
469
        //async: false,
470
        dataType: "json",
471
        timeout: TIMEOUT,
472
        error: function(jqXHR, textStatus, errorThrown) { 
473
            ajax_error(jqXHR);
474
        },
475
        success: function(data, textStatus, jqXHR) {
476
            flavors = data.flavors;
477
            $.each(flavors, function(i, flavor) {
478
                cpus[i] = flavor['cpu'];
479
                disks[i] = flavor['disk'];
480
                ram[i] = flavor['ram'];
481
            });
482
            cpus = cpus.unique();
483
            disks = disks.unique();
484
            ram = ram.unique();
485
            // sliders for selecting VM flavor
486
            $("#cpu:range").rangeinput({min:0,
487
                                       value:0,
488
                                       step:1,
489
                                       progress: true,
490
                                       max:cpus.length-1});
491
            
492
            $("#storage:range").rangeinput({min:0,
493
                                       value:0,
494
                                       step:1,
495
                                       progress: true,
496
                                       max:disks.length-1});
497

  
498
            $("#ram:range").rangeinput({min:0,
499
                                       value:0,
500
                                       step:1,
501
                                       progress: true,
502
                                       max:ram.length-1});
503
            $("#small").click();
504
            
505
            // update the indicators when sliding
506
            $("#cpu:range").data().rangeinput.onSlide(function(event,value){
507
                $("#cpu-indicator")[0].value = cpus[Number(value)];
508
                $("#custom").click();
509
            });
510
            $("#ram:range").data().rangeinput.onSlide(function(event,value){
511
                $("#ram-indicator")[0].value = ram[Number(value)];
512
                $("#custom").click();
513
            });
514
            $("#storage:range").data().rangeinput.onSlide(function(event,value){
515
                $("#storage-indicator")[0].value = disks[Number(value)];
516
                $("#custom").click();
517
            });
518
        }
519
    });
520
    return false;
521
}
522
// return flavorId from cpu, disk, ram values
523
function identify_flavor(cpu, disk, ram){
524
    for (i=0;i<flavors.length;i++){
525
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
526
            return flavors[i]['id']
527
        }
528
    }
529
    return 0;
530
}
531

  
532 194
// switch to list view
533
$("#list").click(function(){
534
    $.cookie("list", '1'); // set list cookie
535
    $("div.standard#machinesview").load($("#list").attr("href"));
536
    $("a#standard")[0].className += ' activelink'
537
    this.style.color = '#5f8dd3';
538
    update_vms();
539
    return false;
540
});
541

  
195
$("a#list").click(function(){list_view(); return false;});
542 196
// switch to standard view
543
$("a#standard").click(function(){
544
    $.cookie("list", '0');
545
    href=$("a#standard").attr("href");
546
    $("div.pane#machines-pane").load(href);
547
    return false;
548
});
197
$("a#standard").click(function(){standard_view(); return false;});
549 198

  
550
// redirect to list view if the list cookie is set
551
if ($.cookie("list") == '1') {
552
    $("#list").click();
553
} else {
554
    // execute the update function to populate the list
555
    update_vms();
556
}
557 199

  
558 200
// launch VM creation wizard
559 201
$("a#create").click(function(){
......
563 205
    update_flavors(); 
564 206
    // launch the wizard
565 207
    $("#wizard").scrollable().begin();
208
    $("#wizard").show();
209
    $('a#create').data('overlay').load()    
566 210
});
567 211

  
568 212
// create wizard overlay
......
572 216
        effect: 'default', 
573 217
        top: '5%', 
574 218
        oneInstance: false,
575
        closeOnClick: false
219
        closeOnClick: false,
576 220
    });
577 221
});
578 222

  
579 223
// wizard
580 224
$(function() {
581 225
    var root = $("#wizard").scrollable();
582

  
583
    // some variables that we need
584 226
    var api = root.scrollable();
585

  
586 227
    // rangeinput with default configuration
587 228
    // validation logic is done inside the onBeforeSeek callback
588 229
    api.onBeforeSeek(function(event, i) {
......
607 248
	    }
608 249
	    // update status bar
609 250
	    $("#status li").removeClass("active").eq(i).addClass("active");
610
        
611 251
        // update confirm step
612 252
        if (api.getIndex()==0) {
613 253
            var image = $("input[type=radio][name=image-id]:checked");
......
617 257
                $("#machine_image-label")[0].textContent = imageName;
618 258
                $("input[type=text][name=machine_name]")[0].value = "My " + imageName + " server";
619 259
            }
260
        } else if (api.getIndex()==1) {
620 261
            $("#machine_cpu-label")[0].textContent = $("#cpu-indicator")[0].value;
621 262
            $("#machine_ram-label")[0].textContent = $("#ram-indicator")[0].value;
622 263
            $("#machine_storage-label")[0].textContent = $("#storage-indicator")[0].value;
......
701 342
        }
702 343
    };
703 344

  
345
    $('a#create').data('overlay').close();
704 346
    $.ajax({
705 347
    url: "/api/v1.0/servers",
706 348
    type: "POST",
......
723 365
    $("#wizard").hide();
724 366
});
725 367

  
726
// reboot action
727
function reboot(serverID){
728
    // ajax post reboot call
729
    var payload = {
730
        "reboot": {"type" : "HARD"}
731
    };   
732

  
733
    $.ajax({
734
        url: '/api/v1.0/servers/' + serverID + '/action',
735
        type: "POST",        
736
        dataType: "json",
737
        data: JSON.stringify(payload),
738
        timeout: TIMEOUT,
739
        error: function(jqXHR, textStatus, errorThrown) { 
740
                    ajax_error(jqXHR);
741
                    },
742
        success: function(data, textStatus, jqXHR) {
743
                    if ( jqXHR.status == '202') {
744
                        ajax_success(jqXHR);
745
                    } else {
746
                        ajax_error(jqXHR);
747
                    }}
748
    });
749
    console.warn('rebooting ' + serverID);    
750
    return false;
751
}
752

  
753
// shutdown action
754
function shutdown(serverID) {
755
    // ajax post shutdown call
756
    var payload = {
757
        "shutdown": {"timeout" : "5"}
758
    };   
759

  
760
    $.ajax({
761
	    url: '/api/v1.0/servers/' + serverID + '/action',
762
	    type: "POST",
763
	    dataType: "json",
764
        data: JSON.stringify(payload),
765
        timeout: TIMEOUT,
766
        error: function(jqXHR, textStatus, errorThrown) { 
767
                    ajax_error(jqXHR);
768
                    },
769
        success: function(data, textStatus, jqXHR) {
770
                    if ( jqXHR.status == '202') {
771
                        ajax_success(jqXHR);
772
                    } else {
773
                        ajax_error(jqXHR);
774
                    }}             
775
    });
776
    console.warn('shutting down ' + serverID);        
777
    return false;    
778
}
779

  
780

  
781
// start action
782
function start(serverID){
783
    // ajax post start call
784
    var payload = {
785
        "start": {"type" : "NORMAL"}
786
    };   
787

  
788
    $.ajax({
789
        url: '/api/v1.0/servers/' + serverID + '/action',
790
        type: "POST",
791
        dataType: "json",
792
        data: JSON.stringify(payload),
793
        timeout: TIMEOUT,
794
        error: function(jqXHR, textStatus, errorThrown) { 
795
                    ajax_error(jqXHR);
796
                    },
797
        success: function(data, textStatus, jqXHR) {
798
                    if ( jqXHR.status == '202') {
799
                        ajax_success(jqXHR);
800
                    } else {
801
                        ajax_error(jqXHR);
802
                    }}
803
    });
804
    console.warn('starting ' + serverID);        
805
    return false;
806
}
807 368

  
808 369

  
809 370
// basic functions executed on page load
......
811 372
// create tabs for main menu
812 373
$("ul.tabs").tabs("div.panes ul");
813 374

  
814
// intercept reboot click 
815
$("div.actions a.action-reboot").live('click', function(){ 
816
    var serverID = $(this).parent().parent().attr("id");
817
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
818
    confirm_action('reboot', reboot, serverID, serverName);
819
    return false;
820
});
821 375

  
822
// intercept shutdown click
823
$("div.actions a.action-shutdown").live('click', function(){ 
824
    var serverID = $(this).parent().parent().attr("id");
825
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
826
    confirm_action('shutdown', shutdown, serverID, serverName);
827
    return false;
828
});
829
// intercept start click
830
$("div.actions a.action-start").live('click', function(){ 
831
    var serverID = $(this).parent().parent().attr("id");
832
    var serverName = $(this).parent().prevAll("a.name").find("span.name").text();
833
    confirm_action('start', start, serverID, serverName);
834
    return false;
835
});
376

  
836 377
</script>

Also available in: Unified diff