Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ deb9d144

History | View | Annotate | Download (43.8 kB)

1
var API_URL = "/api/v1.1";
2
var changes_since = 0, deferred = 0, update_request = false, load_request = false, pending_actions = [];
3
var flavors = [], images = [], servers = [], disks = [], cpus = [], ram = [];
4
var networks = [], networks_changes_since = 0;
5

    
6

    
7
//FIXME: authentication
8
//if cookie with value X-Auth-Token exists, set the value on the headers.
9
    $.ajaxSetup({
10
        'beforeSend': function(xhr) {
11
            //if ($.cookie("X-Auth-Token") != null) {
12
              xhr.setRequestHeader("X-Auth-Token", $.cookie("X-Auth-Token"));
13
            //} else {
14
            //    $.cookie("X-Auth-Token", "46e427d657b20defe352804f0eb6f8a2"); // set X-Auth-Token cookie
15
            //}
16
        }
17
    });
18

    
19
function ISODateString(d){
20
    //return a date in an ISO 8601 format using UTC.
21
    //do not include time zone info (Z) at the end
22
    //taken from the Mozilla Developer Center
23
    function pad(n){ return n<10 ? '0'+n : n }
24
    return  d.getUTCFullYear()+ '-' +
25
            pad(d.getUTCMonth()+1) + '-' +
26
            pad(d.getUTCDate()) + 'T' +
27
            pad(d.getUTCHours()) + ':' +
28
            pad(d.getUTCMinutes()) + ':' +
29
            pad(d.getUTCSeconds()) +'Z'
30
}
31

    
32
function parse_error(responseText, errorCode){
33
    var errors = [];
34
    try {
35
        responseObj = JSON.parse(responseText);
36
    }
37
    catch(err) {
38
        errors[0] = {'code': errorCode};
39
        return errors;
40
    }
41
    for (var err in responseObj){
42
        errors[errors.length] = responseObj[err];
43
    }
44
    return errors;
45
}
46

    
47
// indexOf prototype for IE
48
if (!Array.prototype.indexOf) {
49
  Array.prototype.indexOf = function(elt /*, from*/) {
50
    var len = this.length;
51
    var from = Number(arguments[1]) || 0;
52
    from = (from < 0)
53
         ? Math.ceil(from)
54
         : Math.floor(from);
55
    if (from < 0)
56
      from += len;
57

    
58
    for (; from < len; from++) {
59
      if (from in this &&
60
          this[from] === elt)
61
        return from;
62
    }
63
    return -1;
64
  };
65
}
66

    
67
function update_confirmations(){
68
    // hide all confirm boxes to begin with
69
    $('#machines-pane div.confirm_single').hide();
70
    $('#machines-pane div.confirm_multiple').hide();
71
    // standard view only
72
    if ($.cookie("view") == '0') {
73
        for (var i=0;i<pending_actions.length;i++){
74
            // show single confirms
75
            $("#machines-pane div.machine-container#"+pending_actions[i][1]+' .confirm_single').show();
76
        }
77
    }
78

    
79
    // if more than one pending action show multiple confirm box
80
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
81
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
82
        $('#machines-pane div.confirm_multiple').show();
83
    }
84
}
85

    
86
function update_network_confirmations(){
87
    // hide all confirm boxes to begin with
88
    $('#networks-pane div.confirm_multiple').hide();
89

    
90
    for (var i=0;i<pending_actions.length;i++){
91
        // show single confirms depending on the action
92
        if (pending_actions[i][0] == delete_network) {
93
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
94
        } else if (pending_actions[i][0] == remove_server_from_network) {
95
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
96
        } // else {}
97
    }
98

    
99
    // if more than one pending action show multiple confirm box
100
    if (pending_actions.length > 1){
101
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
102
        $('#networks-pane div.confirm_multiple').show();
103
    }
104
}
105

    
106
function list_view() {
107
    changes_since = 0; // to reload full list
108
    pending_actions = []; // clear pending actions
109
    update_confirmations();
110
    clearTimeout(deferred);    // clear old deferred calls
111
    try {
112
        update_request.abort(); // cancel pending ajax updates
113
        load_request.abort();
114
    }catch(err){}
115
    $.cookie("view", '1'); // set list cookie
116
    uri = $("a#list").attr("href");
117
    load_request = $.ajax({
118
        url: uri,
119
        type: "GET",
120
        timeout: TIMEOUT,
121
        dataType: "html",
122
        error: function(jqXHR, textStatus, errorThrown) {
123
            return false;
124
        },
125
        success: function(data, textStatus, jqXHR) {
126
            $("a#list")[0].className += ' activelink';
127
            $("a#standard")[0].className = '';
128
            $("a#single")[0].className = '';
129
            $("div#machinesview").html(data);
130
        }
131
    });
132
    return false;
133
}
134

    
135
function single_view() {
136
    changes_since = 0; // to reload full list
137
    pending_actions = []; // clear pending actions
138
    update_confirmations();
139
    clearTimeout(deferred);    // clear old deferred calls
140
    try {
141
        update_request.abort(); // cancel pending ajax updates
142
        load_request.abort();
143
    }catch(err){}
144
    $.cookie("view", '2'); // set list cookie
145
    uri = $("a#single").attr("href");
146
    load_request = $.ajax({
147
        url: uri,
148
        type: "GET",
149
        timeout: TIMEOUT,
150
        dataType: "html",
151
        error: function(jqXHR, textStatus, errorThrown) {
152
            return false;
153
        },
154
        success: function(data, textStatus, jqXHR) {
155
            $("a#single")[0].className += ' activelink';
156
            $("a#standard")[0].className = '';
157
            $("a#list")[0].className = '';
158
            $("div#machinesview").html(data);
159
        }
160
    });
161
    return false;
162
}
163

    
164
function standard_view() {
165
    changes_since = 0; // to reload full list
166
    pending_actions = []; // clear pending actions
167
    update_confirmations();
168
    clearTimeout(deferred);    // clear old deferred calls
169
    try {
170
        update_request.abort() // cancel pending ajax updates
171
        load_request.abort();
172
    }catch(err){}
173
    $.cookie("view", '0');
174
    uri = $("a#standard").attr("href");
175
    load_request = $.ajax({
176
        url: uri,
177
        type: "GET",
178
        timeout: TIMEOUT,
179
        dataType: "html",
180
        error: function(jqXHR, textStatus, errorThrown) {
181
            return false;
182
        },
183
        success: function(data, textStatus, jqXHR) {
184
            $("a#standard")[0].className += ' activelink';
185
            $("a#list")[0].className = '';
186
            $("a#single")[0].className = '';
187
            $("div#machinesview").html(data);
188
        }
189
    });
190
    return false;
191
}
192

    
193
function choose_view() {
194
    if ($.cookie("view")=='1') {
195
        list_view();
196
    } else if ($.cookie("view")=='2'){
197
        single_view();
198
    } else {
199
        standard_view();
200
    }
201
}
202

    
203
// get and show a list of running and terminated machines
204
function update_vms(interval) {
205
    try{ console.info('updating machines'); } catch(err){}
206
    var uri= API_URL + '/servers/detail';
207

    
208
    if (changes_since != 0)
209
        uri+='?changes-since='+changes_since
210

    
211
    update_request = $.ajax({
212
        cache: false,
213
        url: uri,
214
        type: "GET",
215
        timeout: TIMEOUT,
216
        dataType: "json",
217
        error: function(jqXHR, textStatus, errorThrown) {
218
            // don't forget to try again later
219
            if (interval) {
220
                clearTimeout(deferred);    // clear old deferred calls
221
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
222
            }
223
            // as for now, just show an error message
224
            try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
225
            ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
226
            return false;
227
            },
228
        success: function(data, textStatus, jqXHR) {
229
            // create changes_since string if necessary
230
            if (jqXHR.getResponseHeader('Date') != null){
231
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
232
                changes_since = ISODateString(changes_since_date);
233
            }
234

    
235
            if (interval) {
236
                clearTimeout(deferred);    // clear old deferred calls
237
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
238
            }
239

    
240
            if (jqXHR.status == 200 || jqXHR.status == 203) {
241
                try {
242
                    servers = data.servers.values;
243
                } catch(err) { ajax_error('400', undefined, 'Update VMs', jqXHR.responseText);}
244
                update_machines_view(data);
245
            } else if (jqXHR.status != 304){
246
                try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
247
                /*
248
                FIXME:  Here it should return the error, however Opera does not support 304.
249
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
250
                        304, which should be corrected (Bug #317).
251
                */
252
                //ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
253
            }
254
            return false;
255
        }
256
    });
257
    return false;
258
}
259

    
260
// get a list of running and terminated machines, used in network view
261
function update_networks(interval) {
262
    try{ console.info('updating networks'); } catch(err){}
263
    var uri= API_URL + '/servers/detail';
264

    
265
    if (changes_since != 0)
266
        uri+='?changes-since='+changes_since
267

    
268
    update_request = $.ajax({
269
        cache: false,
270
        url: uri,
271
        type: "GET",
272
        timeout: TIMEOUT,
273
        dataType: "json",
274
        error: function(jqXHR, textStatus, errorThrown) {
275
            // don't forget to try again later
276
            if (interval) {
277
                clearTimeout(deferred);    // clear old deferred calls
278
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
279
            }
280
            // as for now, just show an error message
281
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
282
            ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
283
            return false;
284
            },
285
        success: function(data, textStatus, jqXHR) {
286
            // create changes_since string if necessary
287
            if (jqXHR.getResponseHeader('Date') != null){
288
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
289
                changes_since = ISODateString(changes_since_date);
290
            }
291

    
292
            if (interval) {
293
                clearTimeout(deferred);    // clear old deferred calls
294
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
295
            }
296

    
297
            if (jqXHR.status == 200 || jqXHR.status == 203) {
298
                try {
299
                    servers = data.servers.values;
300
                } catch(err) { ajax_error('400', undefined, 'Update networks', jqXHR.responseText);}
301
                update_network_names(data);
302
            } else if (jqXHR.status == 304) {
303
                update_network_names();
304
            }
305
            else {
306
                try { console.info('update_networks callback:' + jqXHR.status ) } catch(err) {}
307
                /*
308
                FIXME:  Here it should return the error, however Opera does not support 304.
309
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
310
                        304, which should be corrected (Bug #317).
311
                */
312
                //ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
313
                update_network_names();
314
            }
315
            return false;
316
        }
317
    });
318
    return false;
319
}
320

    
321
// get and show a list of public and private networks
322
function update_network_names(servers_data) {
323
    try{ console.info('updating network names'); } catch(err){}
324
    var uri= API_URL + '/networks/detail';
325

    
326
    if (networks_changes_since != 0)
327
        //FIXME: Comment out the following, until metadata do not 304 when changed
328
        uri+='?changes-since=' + networks_changes_since
329

    
330
    update_request = $.ajax({
331
        cache: false,
332
        url: uri,
333
        type: "GET",
334
        timeout: TIMEOUT,
335
        dataType: "json",
336
        error: function(jqXHR, textStatus, errorThrown) {
337
            // as for now, just show an error message
338
            try {
339
                console.info('update_network names errback:' + jqXHR.status )
340
            } catch(err) {}
341
            ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
342
            return false;
343
            },
344
        success: function(data, textStatus, jqXHR) {
345
            // create changes_since string if necessary
346
            if (jqXHR.getResponseHeader('Date') != null){
347
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
348
                networks_changes_since = ISODateString(changes_since_date);
349
            }
350

    
351
            if (jqXHR.status == 200 || jqXHR.status == 203) {
352
                try {
353
                    networks = data.networks.values;
354
                } catch(err) {
355
                    ajax_error('400', undefined, 'Update network names', jqXHR.responseText);
356
                }
357
                update_networks_view(servers_data, data);
358
            } else if (jqXHR.status == 304) {
359
                update_networks_view(servers_data);
360
            } else if (jqXHR.status != 304){
361
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
362
                /*
363
                FIXME:  Here it should return the error, however Opera does not support 304.
364
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
365
                        304, which should be corrected (Bug #317).
366
                */
367
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
368
                update_networks_view(servers_data);
369
            }
370
            return false;
371
        }
372
    });
373
    return false;
374
}
375

    
376
// get and show a list of available standard and custom images
377
function update_images() {
378
    $.ajax({
379
        url: API_URL + '/images/detail',
380
        type: "GET",
381
        //async: false,
382
        dataType: "json",
383
        timeout: TIMEOUT,
384
        error: function(jqXHR, textStatus, errorThrown) {
385
                    ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
386
                    },
387
        success: function(data, textStatus, jqXHR) {
388
            try {
389
                images = data.images.values;
390
                update_wizard_images();
391
            } catch(err){
392
                ajax_error("NO_IMAGES");
393
            }
394
        }
395
    });
396
    return false;
397
}
398

    
399
function update_wizard_images() {
400
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
401
        $.each(images, function(i,image){
402
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
403
            img.find("label").attr('for',"img-radio-" + image.id);
404
            img.find(".image-title").text(image.name);
405
            if (image.metadata) {
406
                if (image.metadata.values.description != undefined) {
407
                    img.find(".description").text(image.metadata.values.description);
408
                }
409
                if (image.metadata.values.size != undefined) {
410
                    img.find("#size").text(image.metadata.values.size);
411
                }
412
            }
413
            img.find("input.radio").attr('id',"img-radio-" + image.id);
414
            if (i==0) img.find("input.radio").attr("checked","checked");
415
            var image_logo = os_icon(image.metadata);
416
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
417
            if (image.metadata) {
418
                if (image.metadata.values.serverId != undefined) {
419
                    img.appendTo("ul#custom-images");
420
                } else {
421
                    img.appendTo("ul#standard-images");
422
                }
423
            } else {
424
                img.appendTo("ul#standard-images");
425
            }
426
        });
427
    }
428
}
429

    
430
function update_wizard_flavors(){
431
    // sliders for selecting VM flavor
432
    $("#cpu:range").rangeinput({min:0,
433
                               value:0,
434
                               step:1,
435
                               progress: true,
436
                               max:cpus.length-1});
437

    
438
    $("#storage:range").rangeinput({min:0,
439
                               value:0,
440
                               step:1,
441
                               progress: true,
442
                               max:disks.length-1});
443

    
444
    $("#ram:range").rangeinput({min:0,
445
                               value:0,
446
                               step:1,
447
                               progress: true,
448
                               max:ram.length-1});
449
    $("#small").click();
450

    
451
    // update the indicators when sliding
452
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
453
        $("#cpu-indicator")[0].value = cpus[Number(value)];
454
        $("#cpu-indicator").addClass('selectedrange');
455
    });
456
    $("#cpu:range").data().rangeinput.change(function(event,value){
457
        $("#cpu-indicator")[0].value = cpus[Number(value)];
458
        $("#custom").click();
459
        $("#cpu-indicator").removeClass('selectedrange');
460
    });
461
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
462
        $("#ram-indicator")[0].value = ram[Number(value)];
463
        $("#ram-indicator").addClass('selectedrange');
464
    });
465
    $("#ram:range").data().rangeinput.change(function(event,value){
466
        $("#ram-indicator")[0].value = ram[Number(value)];
467
        $("#custom").click();
468
        $("#ram-indicator").removeClass('selectedrange');
469
    });
470
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
471
        $("#storage-indicator")[0].value = disks[Number(value)];
472
        $("#storage-indicator").addClass('selectedrange');
473
    });
474
    $("#storage:range").data().rangeinput.change(function(event,value){
475
        $("#storage-indicator")[0].value = disks[Number(value)];
476
        $("#custom").click();
477
        $("#storage-indicator").removeClass('selectedrange');
478
    });
479
}
480

    
481
Array.prototype.unique = function () {
482
    var r = new Array();
483
    o:for(var i = 0, n = this.length; i < n; i++)
484
    {
485
        for(var x = 0, y = r.length; x < y; x++)
486
        {
487
            if(r[x]==this[i])
488
            {
489
                continue o;
490
            }
491
        }
492
        r[r.length] = this[i];
493
    }
494
    return r;
495
}
496

    
497
// get and configure flavor selection
498
function update_flavors() {
499
    $.ajax({
500
        url: API_URL + '/flavors/detail',
501
        type: "GET",
502
        //async: false,
503
        dataType: "json",
504
        timeout: TIMEOUT,
505
        error: function(jqXHR, textStatus, errorThrown) {
506
            try {
507
                ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
508
            } catch (err) {
509
                ajax_error(err);
510
            }
511
            // start updating vm list
512
            update_vms(UPDATE_INTERVAL);
513
        },
514
        success: function(data, textStatus, jqXHR) {
515
            flavors = data.flavors.values;
516
            $.each(flavors, function(i, flavor) {
517
                cpus[i] = flavor['cpu'];
518
                disks[i] = flavor['disk'];
519
                ram[i] = flavor['ram'];
520
            });
521
            cpus = cpus.unique();
522
            disks = disks.unique();
523
            ram = ram.unique();
524
            update_wizard_flavors();
525
            // start updating vm list
526
            update_vms(UPDATE_INTERVAL);
527
        }
528
    });
529
    return false;
530
}
531

    
532
// return flavorRef from cpu, disk, ram values
533
function identify_flavor(cpu, disk, ram){
534
    for (i=0;i<flavors.length;i++){
535
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
536
            return flavors[i]['id']
537
        }
538
    }
539
    return 0;
540
}
541

    
542
// return image entry from imageRef
543
function get_image(imageRef) {
544
    for (i=0;i<images.length;i++){
545
        if (images[i]['id'] == imageRef) {
546
            return images[i];
547
        }
548
    }
549
    return 0;
550
}
551

    
552
// return machine entry from serverID
553
function get_machine(serverID) {
554
    for (i=0;i<servers.length;i++){
555
        if (servers[i]['id'] == serverID) {
556
            return servers[i];
557
        }
558
    }
559
    return 0;
560
}
561

    
562
// update the actions in icon view, per server
563
function update_iconview_actions(serverID, server_status) {
564
    // remove .disable from all actions to begin with
565
    $('#machinesview-icon.standard #' + serverID + ' div.actions').children().removeClass('disabled');
566
    // decide which actions should be disabled
567
    for (current_action in actions) {
568
        if (actions[current_action].indexOf(server_status) == -1 ) {
569
            $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
570
        }
571
    }
572
}
573

    
574
// update the actions in list view
575
function update_listview_actions() {
576
    var states = [];
577
    var on = [];
578
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
579
    // disable all actions to begin with
580
    $('#machinesview .list div.actions').children().removeClass('enabled');
581

    
582
    // are there multiple machines selected?
583
    if (checked.length>1)
584
        states[0] = 'multiple';
585

    
586
    // check the states of selected machines
587
    checked.each(function(i,checkbox) {
588
        states[states.length] = checkbox.className;
589
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
590
        if (ip.replace('undefined','').length)
591
            states[states.length] = 'network';
592
    });
593

    
594
    // decide which actions should be enabled
595
    for (a in actions) {
596
        var enabled = false;
597
        for (var s =0; s<states.length; s++) {
598
            if (actions[a].indexOf(states[s]) != -1 ) {
599
                enabled = true;
600
            } else {
601
                enabled = false;
602
                break;
603
            }
604
        }
605
        if (enabled)
606
            on[on.length]=a;
607
    }
608
    // enable those actions
609
    for (action in on) {
610
        $("#action-" + on[action]).addClass('enabled');
611
    }
612
}
613

    
614
//create server action
615
function create_vm(machineName, imageRef, flavorRef){
616
    var image_logo = os_icon(get_image(imageRef).metadata);
617
    var uri = API_URL + '/servers';
618
    var payload = {
619
        "server": {
620
            "name": machineName,
621
            "imageRef": imageRef,
622
            "flavorRef" : flavorRef,
623
            "metadata" : {
624
                "OS" : image_logo
625
            }
626
        }
627
    };
628

    
629
    $.ajax({
630
    url: uri,
631
    type: "POST",
632
    contentType: "application/json",
633
    dataType: "json",
634
    data: JSON.stringify(payload),
635
    timeout: TIMEOUT,
636
    error: function(jqXHR, textStatus, errorThrown) {
637
                ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
638
           },
639
    success: function(data, textStatus, jqXHR) {
640
                if ( jqXHR.status == '202') {
641
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
642
                } else {
643
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
644
                }
645
            }
646
    });
647
}
648

    
649
// reboot action
650
function reboot(serverIDs){
651
    if (!serverIDs.length){
652
        //ajax_success('DEFAULT');
653
        return false;
654
    }
655
    // ajax post reboot call
656
    var payload = {
657
        "reboot": {"type" : "HARD"}
658
    };
659

    
660
    var serverID = serverIDs.pop();
661

    
662
    $.ajax({
663
        url: API_URL + '/servers/' + serverID + '/action',
664
        type: "POST",
665
        contentType: "application/json",
666
        dataType: "json",
667
        data: JSON.stringify(payload),
668
        timeout: TIMEOUT,
669
        error: function(jqXHR, textStatus, errorThrown) {
670
                    display_failure(jqXHR.status, serverID, 'Reboot', jqXHR.responseText)
671
                },
672
        success: function(data, textStatus, jqXHR) {
673
                    if ( jqXHR.status == '202') {
674
                        try {
675
                            console.info('rebooted ' + serverID);
676
                        } catch(err) {}
677
                        // indicate that the action succeeded
678
                        display_success(serverID);
679
                        // continue with the rest of the servers
680
                        reboot(serverIDs);
681
                    } else {
682
                        ajax_error(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
683
                    }
684
                }
685
    });
686
    return false;
687
}
688

    
689
// shutdown action
690
function shutdown(serverIDs) {
691
    if (!serverIDs.length){
692
        //ajax_success('DEFAULT');
693
        return false;
694
    }
695
    // ajax post shutdown call
696
    var payload = {
697
        "shutdown": {}
698
    };
699

    
700
    var serverID = serverIDs.pop();
701

    
702
    $.ajax({
703
        url: API_URL + '/servers/' + serverID + '/action',
704
        type: "POST",
705
        contentType: "application/json",
706
        dataType: "json",
707
        data: JSON.stringify(payload),
708
        timeout: TIMEOUT,
709
        error: function(jqXHR, textStatus, errorThrown) {
710
                    display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText)
711
                    },
712
        success: function(data, textStatus, jqXHR) {
713
                    if ( jqXHR.status == '202') {
714
                        try {
715
                            console.info('suspended ' + serverID);
716
                        } catch(err) {}
717
                        // indicate that the action succeeded
718
                        display_success(serverID);
719
                        // continue with the rest of the servers
720
                        shutdown(serverIDs);
721
                    } else {
722
                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
723
                    }
724
                }
725
    });
726
    return false;
727
}
728

    
729
// destroy action
730
function destroy(serverIDs) {
731
    if (!serverIDs.length){
732
        //ajax_success('DEFAULT');
733
        return false;
734
    }
735
    // ajax post destroy call can have an empty request body
736
    var payload = {};
737

    
738
    var serverID = serverIDs.pop();
739

    
740
    $.ajax({
741
        url: API_URL + '/servers/' + serverID,
742
        type: "DELETE",
743
        contentType: "application/json",
744
        dataType: "json",
745
        data: JSON.stringify(payload),
746
        timeout: TIMEOUT,
747
        error: function(jqXHR, textStatus, errorThrown) {
748
                    display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText)
749
                    },
750
        success: function(data, textStatus, jqXHR) {
751
                    if ( jqXHR.status == '204') {
752
                        try {
753
                            console.info('destroyed ' + serverID);
754
                        } catch (err) {}
755
                        // indicate that the action succeeded
756
                        display_success(serverID);
757
                        // continue with the rest of the servers
758
                        destroy(serverIDs);
759
                    } else {
760
                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
761
                    }
762
                }
763
    });
764
    return false;
765
}
766

    
767
// start action
768
function start(serverIDs){
769
    if (!serverIDs.length){
770
        //ajax_success('DEFAULT');
771
        return false;
772
    }
773
    // ajax post start call
774
    var payload = {
775
        "start": {}
776
    };
777

    
778
    var serverID = serverIDs.pop();
779

    
780
    $.ajax({
781
        url: API_URL + '/servers/' + serverID + '/action',
782
        type: "POST",
783
        contentType: "application/json",
784
        dataType: "json",
785
        data: JSON.stringify(payload),
786
        timeout: TIMEOUT,
787
        error: function(jqXHR, textStatus, errorThrown) {
788
                    display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText)
789
                    },
790
        success: function(data, textStatus, jqXHR) {
791
                    if ( jqXHR.status == '202') {
792
                        try {
793
                            console.info('started ' + serverID);
794
                        } catch(err) {}
795
                        // indicate that the action succeeded
796
                        display_success(serverID);
797
                        // continue with the rest of the servers
798
                        start(serverIDs);
799
                    } else {
800
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
801
                    }
802
                }
803
    });
804
    return false;
805
}
806

    
807
// Show VNC console
808
function vnc_attachment(host, port, password) {
809
    // FIXME: Must be made into parameters, in settings.py
810
    //vnc = open("", "displayWindow",
811
    //    "status=yes,toolbar=yes,menubar=yes");
812
    vd = document.open("application/x-vnc");
813

    
814
    vd.writeln("[connection]");
815
    vd.writeln("host=" + host);
816
    vd.writeln("port=" + port);
817
    vd.writeln("password=" + password);
818

    
819
    vd.close();
820
}
821

    
822
// Show VNC console
823
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
824
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
825
    var params_window = 'scrollbars=no,' +
826
                        'menubar=no,' +
827
                        'toolbar=no,' +
828
                        'status=no,' +
829
                        'top=0,' +
830
                        'left=0,' +
831
                        'height=' + screen.height + ',' +
832
                        'width=' + screen.width + ',' +
833
                        'fullscreen=yes';
834

    
835
    window.open('machines/console' + params_url, 'formresult' + serverID, params_window);
836

    
837
    // Restore os icon in list view
838
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
839
    osIcon.attr('src',osIcon.attr('os'));
840
    return false;
841
}
842

    
843
// console action
844
function open_console(serverIDs){
845
    if (!serverIDs.length){
846
        //ajax_success('DEFAULT');
847
        return false;
848
    }
849
    // ajax post start call
850
    var payload = {
851
        "console": {"type": "vnc"}
852
    };
853

    
854
    var serverID = serverIDs.pop();
855

    
856
    var machine = get_machine(serverID);
857
    var serverName = machine.name;
858
    var serverIP = machine.addresses.values[0].values[0].addr;
859

    
860
    $.ajax({
861
        url: API_URL + '/servers/' + serverID + '/action',
862
        type: "POST",
863
        contentType: "application/json",
864
        dataType: "json",
865
        data: JSON.stringify(payload),
866
        timeout: TIMEOUT,
867
        error: function(jqXHR, textStatus, errorThrown) {
868
                    display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText)
869
                    },
870
        success: function(data, textStatus, jqXHR) {
871
                    if ( jqXHR.status == '200') {
872
                        try {
873
                            console.info('got_console ' + serverID);
874
                        } catch(err) {}
875
                        // indicate that the action succeeded
876
                        //show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
877
show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
878
                        display_success(serverID);
879
                        // hide spinner
880
                        $('#' + serverID + ' .spinner').hide();
881
                        // continue with the rest of the servers
882
                        open_console(serverIDs);
883
                    } else {
884
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
885
                    }
886
                }
887
    });
888
    return false;
889
}
890

    
891
// rename server
892
function rename(serverID, serverName){
893
    if (!serverID.length){
894
        //ajax_success('DEFAULT');
895
        return false;
896
    }
897
    // ajax post rename call
898
    var payload = {
899
        "server": {"name": serverName}
900
    };
901

    
902
    $.ajax({
903
        url: API_URL + '/servers/' + serverID,
904
        type: "PUT",
905
        contentType: "application/json",
906
        dataType: "json",
907
        data: JSON.stringify(payload),
908
        timeout: TIMEOUT,
909
        error: function(jqXHR, textStatus, errorThrown) {
910
                    display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText)
911
                    },
912
        success: function(data, textStatus, jqXHR) {
913
                    if ( jqXHR.status == '204') {
914
                        try {
915
                            console.info('renamed ' + serverID);
916
                        } catch(err) {}
917
                        // indicate that the action succeeded
918
                        display_success(serverID);
919
                    } else {
920
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
921
                    }
922
                }
923
    });
924
    return false;
925
}
926

    
927
// get server metadata
928
function get_metadata(serverID, keys_only) {
929
    $.ajax({
930
        url: API_URL + '/servers/' + serverID + '/meta',
931
        type: "GET",
932
        //async: false,
933
        dataType: "json",
934
        timeout: TIMEOUT,
935
        error: function(jqXHR, textStatus, errorThrown) {
936
            try {
937
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
938
            } catch (err) {
939
                ajax_error(err);
940
            }
941
        },
942
        success: function(data, textStatus, jqXHR) {
943
            // to list the new results in the edit dialog
944
            if (keys_only) {
945
                list_metadata_keys(serverID, data.metadata.values);
946
            } else {
947
                list_metadata(data);
948
                list_metadata_keys(serverID, data.metadata.values);
949
            }
950
            //hide spinner
951
            $('#metadata-wizard .large-spinner').hide();
952
        }
953
    });
954
    return false;
955
}
956

    
957
// delete metadata key-value pair
958
function delete_metadata(serverID, meta_key) {
959
    $.ajax({
960
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
961
        type: "DELETE",
962
        //async: false,
963
        dataType: "json",
964
        timeout: TIMEOUT,
965
        error: function(jqXHR, textStatus, errorThrown) {
966
            try {
967
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
968
            } catch (err) {
969
                ajax_error(err);
970
            }
971
        },
972
        success: function(data, textStatus, jqXHR) {
973
            // success: Do nothing, the UI is already updated
974
        }
975
    });
976
    return false;
977
}
978

    
979
// add metadata key-value pair
980
function update_metadata(serverID, meta_key, meta_value) {
981
    var payload = {
982
        "meta": {
983
        }
984
    };
985
    payload["meta"][meta_key] = meta_value;
986

    
987
    $.ajax({
988
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
989
        type: "PUT",
990
        contentType: "application/json",
991
        dataType: "json",
992
        data: JSON.stringify(payload),
993
        timeout: TIMEOUT,
994
        error: function(jqXHR, textStatus, errorThrown) {
995
            try {
996
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
997
            } catch (err) {
998
                ajax_error(err);
999
            }
1000
        },
1001
        success: function(data, textStatus, jqXHR) {
1002
            // success: Update icons if meta key is OS
1003
            if (meta_key == "OS") {
1004
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1005
                $("#machinesview-icon").find("div#" + serverID).find("img.logo").attr("src", "static/icons/machines/medium/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1006
            }
1007
        }
1008
    });
1009
    return false;
1010
}
1011

    
1012
// create network
1013
function create_network(networkName){
1014
    // ajax post start call
1015
    var payload = {
1016
        "network": { "name": networkName }
1017
    };
1018

    
1019
    $.ajax({
1020
        url: API_URL + '/networks',
1021
        type: "POST",
1022
        contentType: "application/json",
1023
        dataType: "json",
1024
        data: JSON.stringify(payload),
1025
        timeout: TIMEOUT,
1026
        error: function(jqXHR, textStatus, errorThrown) {
1027
            try {
1028
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1029
            } catch (err) {
1030
                ajax_error(err);
1031
            }
1032
        },
1033
        success: function(data, textStatus, jqXHR) {
1034
            if ( jqXHR.status == '202') {
1035
                try {
1036
                    console.info('created network ' + networkName);
1037
                } catch(err) {}
1038
                update_networks(UPDATE_INTERVAL);
1039
                $("a#networkscreate").overlay().close();
1040
            } else {
1041
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1042
            }
1043
        }
1044
    });
1045
    return false;
1046
}
1047

    
1048
// rename network
1049
function rename_network(networkID, networkName){
1050
    if (!networkID.length){
1051
        //ajax_success('DEFAULT');
1052
        return false;
1053
    }
1054
    // prepare payload
1055
    var payload = {
1056
        "network": {"name": networkName}
1057
    };
1058
    // ajax call
1059
    $.ajax({
1060
        url: API_URL + '/networks/' + networkID,
1061
        type: "PUT",
1062
        contentType: "application/json",
1063
        dataType: "json",
1064
        data: JSON.stringify(payload),
1065
        timeout: TIMEOUT,
1066
        error: function(jqXHR, textStatus, errorThrown) {
1067
            try {
1068
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1069
            } catch (err) {
1070
                ajax_error(err);
1071
            }
1072
        },
1073
        success: function(data, textStatus, jqXHR) {
1074
            if ( jqXHR.status == '204') {
1075
                try {
1076
                    console.info('renamed network' + networkID);
1077
                } catch(err) {}
1078
            } else {
1079
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1080
            }
1081
        }
1082
    });
1083
    return false;
1084
}
1085

    
1086
function delete_network(networkIDs){
1087
    if (!networkIDs.length){
1088
        //ajax_success('DEFAULT');
1089
        return false;
1090
    }
1091
    // get a network
1092
    var networkID = networkIDs.pop();
1093
    // ajax post destroy call can have an empty request body
1094
    var payload = {};
1095
    // ajax call
1096
    $.ajax({
1097
        url: API_URL + '/networks/' + networkID,
1098
        type: "DELETE",
1099
        contentType: "application/json",
1100
        dataType: "json",
1101
        data: JSON.stringify(payload),
1102
        timeout: TIMEOUT,
1103
        error: function(jqXHR, textStatus, errorThrown) {
1104
            try {
1105
                ajax_error(jqXHR.status, undefined, 'Delete network', jqXHR.responseText);
1106
            } catch (err) {
1107
                ajax_error(err);
1108
            }
1109
        },
1110
        success: function(data, textStatus, jqXHR) {
1111
            if ( jqXHR.status == '204') {
1112
                try {
1113
                    console.info('deleted network ' + networkID);
1114
                } catch(err) {}
1115
                // continue with the rest of the servers
1116
                delete_network(networkIDs);
1117
            } else {
1118
                ajax_error(jqXHR.status, undefined, 'Delete network', jqXHR.responseText);
1119
            }
1120
        }
1121
    });
1122
    return false;
1123
}
1124

    
1125
function add_server_to_network(networkID, serverIDs) {
1126
    if (!serverIDs.length){
1127
        //ajax_success('DEFAULT');
1128
        update_networks(UPDATE_INTERVAL);
1129
        $("a#add-machines-overlay").overlay().close();
1130
        return false;
1131
    }
1132
    // get a server
1133
    var serverID = serverIDs.pop();
1134
    // prepare payload
1135
    var payload = {
1136
            "add": { "serverRef": serverID }
1137
        };
1138
    // prepare ajax call
1139
    $.ajax({
1140
        url: API_URL + '/networks/' + networkID + '/action',
1141
        type: "POST",
1142
        contentType: "application/json",
1143
        dataType: "json",
1144
        data: JSON.stringify(payload),
1145
        timeout: TIMEOUT,
1146
        error: function(jqXHR, textStatus, errorThrown) {
1147
            try {
1148
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1149
            } catch (err) {
1150
                ajax_error(err);
1151
            }
1152
        },
1153
        success: function(data, textStatus, jqXHR) {
1154
            if ( jqXHR.status == '202') {
1155
                try {
1156
                    console.info('added server ' + serverID + ' to network ' + networkID);
1157
                } catch(err) {}
1158
                // continue with the rest of the servers
1159
                add_server_to_network(networkID, serverIDs);
1160
            } else {
1161
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1162
            }
1163
        }
1164
    });
1165
    return false;
1166
}
1167

    
1168
function remove_server_from_network(networkIDs, serverIDs) {
1169
    if (!networkIDs.length){
1170
        //ajax_success('DEFAULT');
1171
        return false;
1172
    }
1173
    // get a network and a server
1174
    var networkID = networkIDs.pop();
1175
    var serverID = serverIDs.pop();
1176
    // prepare payload
1177
    var payload = {
1178
            "remove": { "serverRef": serverID }
1179
        };
1180
    // prepare ajax call
1181
    $.ajax({
1182
        url: API_URL + '/networks/' + networkID + '/action',
1183
        type: "POST",
1184
        contentType: "application/json",
1185
        dataType: "json",
1186
        data: JSON.stringify(payload),
1187
        timeout: TIMEOUT,
1188
        error: function(jqXHR, textStatus, errorThrown) {
1189
            try {
1190
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1191
            } catch (err) {
1192
                ajax_error(err);
1193
            }
1194
        },
1195
        success: function(data, textStatus, jqXHR) {
1196
            if ( jqXHR.status == '202') {
1197
                try {
1198
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1199
                } catch(err) {}
1200
                // remove it from DOM
1201
                $('#net-' + networkID + '-server-' + serverID).fadeOut('slow').remove();
1202
                // continue with the rest of the servers
1203
                remove_server_form_network(networkIDs, serverIDs);
1204
            } else {
1205
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1206
            }
1207
        }
1208
    });
1209
    return false;
1210
}
1211

    
1212
// show the welcome screen
1213
function showWelcome() {
1214
    $("#view-select").fadeOut("fast");
1215
    $("#emptymachineslist").fadeIn("fast");
1216
    $("#machinesview").hide();
1217
}
1218

    
1219
// hide the welcome screen
1220
function hideWelcome() {
1221
    $("#emptymachineslist").fadeOut("fast");
1222
    $("#view-select").fadeIn("fast");
1223
    $("div#view-select").show();
1224
    $("#machinesview").show();
1225
}
1226

    
1227
function log_server_status_change(server_entry, new_status) {
1228
    // firebug console logging
1229
    try {
1230
        console.info(server_entry.find("div.name span.name").text() +
1231
                    ' from ' + server_entry.find(".status").text() +
1232
                    ' to ' + STATUSES[new_status]);
1233
    } catch(err) {}
1234
}
1235

    
1236
function get_flavor_params(flavorRef) {
1237
    var cpus, ram, disk;
1238
    if ( flavors.length > 0 ) {
1239
        var current_flavor = '';
1240
        for (i=0; i<flavors.length; i++) {
1241
            if (flavors[i]['id'] == flavorRef) {
1242
                current_flavor = flavors[i];
1243
            }
1244
        }
1245
        cpus = current_flavor['cpu'];
1246
        ram = current_flavor['ram'];
1247
        disk = current_flavor['disk'];
1248
    } else {
1249
        cpus = 'undefined';
1250
        ram = 'undefined';
1251
        disk = 'undefined';
1252
    }
1253
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1254
}
1255

    
1256
function get_image_params(imageRef) {
1257
    var image_name, image_size;
1258
    if ( images.length > 0 ) {
1259
        var current_image = '';
1260
        for (i=0; i<images.length; i++) {
1261
            if (images[i]['id'] == imageRef) {
1262
                current_image = images[i];
1263
            }
1264
        }
1265
        image_name = current_image['name'];
1266
        image_size = current_image['metadata']['values']['size'];
1267
    } else {
1268
        image_name = 'undefined';
1269
        image_size = 'undefined';
1270
    }
1271
    return {'name': image_name,'size': image_size};
1272
}
1273

    
1274
function get_public_ips(server) {
1275
    var ip4, ip6;
1276
    try {
1277
        if (server.addresses.values) {
1278
            $.each (server.addresses.values, function(i, value) {
1279
                if (value.id == 'public') {
1280
                    try {
1281
                        $.each (value.values, function(i, ip) {
1282
                            if (ip.version == '4') {
1283
                                ip4 = ip.addr;
1284
                            } else if (ip.version == '6') {
1285
                                ip6 = ip.addr;
1286
                            } else {
1287
                                ip4 = 'undefined';
1288
                                ip6 = 'undefined';
1289
                            }
1290
                        });
1291
                    } catch (err){
1292
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1293
                        ip4 = 'undefined';
1294
                        ip6 = 'undefined';
1295
                    }
1296
                }
1297
            });
1298
        }
1299
    } catch (err) {
1300
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1301
        ip4 = 'undefined';
1302
        ip6 = 'undefined';
1303
    }
1304
    return {'ip4': ip4, 'ip6': ip6};
1305
}
1306

    
1307
function get_private_ips(server) {
1308

    
1309
}