Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ 09828cb5

History | View | Annotate | Download (45.6 kB)

1
//
2
// Copyright 2011 GRNET S.A. All rights reserved.
3
// 
4
// Redistribution and use in source and binary forms, with or
5
// without modification, are permitted provided that the following
6
// conditions are met:
7
// 
8
//   1. Redistributions of source code must retain the above
9
//      copyright notice, this list of conditions and the following
10
//      disclaimer.
11
// 
12
//   2. Redistributions in binary form must reproduce the above
13
//      copyright notice, this list of conditions and the following
14
//      disclaimer in the documentation and/or other materials
15
//      provided with the distribution.
16
// 
17
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
// POSSIBILITY OF SUCH DAMAGE.
29
// 
30
// The views and conclusions contained in the software and
31
// documentation are those of the authors and should not be
32
// interpreted as representing official policies, either expressed
33
// or implied, of GRNET S.A.
34
//
35
var API_URL = "/api/v1.1";
36
var changes_since = 0, deferred = 0, update_request = false, load_request = false, pending_actions = [];
37
var flavors = [], images = [], servers = [], disks = [], cpus = [], ram = [];
38
var networks = [], networks_changes_since = 0;
39

    
40

    
41
//FIXME: authentication
42
//if cookie with value X-Auth-Token exists, set the value on the headers.
43
    $.ajaxSetup({
44
        'beforeSend': function(xhr) {
45
            //if ($.cookie("X-Auth-Token") != null) {
46
              xhr.setRequestHeader("X-Auth-Token", $.cookie("X-Auth-Token"));
47
            //} else {
48
            //    $.cookie("X-Auth-Token", "46e427d657b20defe352804f0eb6f8a2"); // set X-Auth-Token cookie
49
            //}
50
        }
51
    });
52

    
53
function ISODateString(d){
54
    //return a date in an ISO 8601 format using UTC.
55
    //do not include time zone info (Z) at the end
56
    //taken from the Mozilla Developer Center
57
    function pad(n){ return n<10 ? '0'+n : n }
58
    return  d.getUTCFullYear()+ '-' +
59
            pad(d.getUTCMonth()+1) + '-' +
60
            pad(d.getUTCDate()) + 'T' +
61
            pad(d.getUTCHours()) + ':' +
62
            pad(d.getUTCMinutes()) + ':' +
63
            pad(d.getUTCSeconds()) +'Z'
64
}
65

    
66
function parse_error(responseText, errorCode){
67
    var errors = [];
68
    try {
69
        responseObj = JSON.parse(responseText);
70
    }
71
    catch(err) {
72
        errors[0] = {'code': errorCode};
73
        return errors;
74
    }
75
    for (var err in responseObj){
76
        errors[errors.length] = responseObj[err];
77
    }
78
    return errors;
79
}
80

    
81
// indexOf prototype for IE
82
if (!Array.prototype.indexOf) {
83
  Array.prototype.indexOf = function(elt /*, from*/) {
84
    var len = this.length;
85
    var from = Number(arguments[1]) || 0;
86
    from = (from < 0)
87
         ? Math.ceil(from)
88
         : Math.floor(from);
89
    if (from < 0)
90
      from += len;
91

    
92
    for (; from < len; from++) {
93
      if (from in this &&
94
          this[from] === elt)
95
        return from;
96
    }
97
    return -1;
98
  };
99
}
100

    
101
function update_confirmations(){
102
    // hide all confirm boxes to begin with
103
    $('#machines-pane div.confirm_single').hide();
104
    $('#machines-pane div.confirm_multiple').hide();
105
    // standard view only
106
    if ($.cookie("view") == '0') {
107
        for (var i=0;i<pending_actions.length;i++){
108
            // show single confirms
109
            $("#machines-pane div.machine-container#"+pending_actions[i][1]+' .confirm_single').show();
110
        }
111
    }
112

    
113
    // if more than one pending action show multiple confirm box
114
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
115
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
116
        $('#machines-pane div.confirm_multiple').show();
117
    }
118
}
119

    
120
function update_network_confirmations(){
121
    // hide all confirm boxes to begin with
122
    $('#networks-pane div.confirm_multiple').hide();
123

    
124
    for (var i=0;i<pending_actions.length;i++){
125
        // show single confirms depending on the action
126
        if (pending_actions[i][0] == delete_network) {
127
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
128
        } else if (pending_actions[i][0] == remove_server_from_network) {
129
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
130
        } // else {}
131
    }
132

    
133
    // if more than one pending action show multiple confirm box
134
    if (pending_actions.length > 1){
135
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
136
        $('#networks-pane div.confirm_multiple').show();
137
    }
138
}
139

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

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

    
198
function standard_view() {
199
    changes_since = 0; // to reload full list
200
    pending_actions = []; // clear pending actions
201
    update_confirmations();
202
    clearTimeout(deferred);    // clear old deferred calls
203
    try {
204
        update_request.abort() // cancel pending ajax updates
205
        load_request.abort();
206
    }catch(err){}
207
    $.cookie("view", '0');
208
    uri = $("a#standard").attr("href");
209
    load_request = $.ajax({
210
        url: uri,
211
        type: "GET",
212
        timeout: TIMEOUT,
213
        dataType: "html",
214
        error: function(jqXHR, textStatus, errorThrown) {
215
            return false;
216
        },
217
        success: function(data, textStatus, jqXHR) {
218
            $("a#standard")[0].className += ' activelink';
219
            $("a#list")[0].className = '';
220
            $("a#single")[0].className = '';
221
            $("div#machinesview").html(data);
222
        }
223
    });
224
    return false;
225
}
226

    
227
function choose_view() {
228
    if ($.cookie("view")=='1') {
229
        list_view();
230
    } else if ($.cookie("view")=='2'){
231
        single_view();
232
    } else {
233
        standard_view();
234
    }
235
}
236

    
237
// get and show a list of running and terminated machines
238
function update_vms(interval) {
239
    try{ console.info('updating machines'); } catch(err){}
240
    var uri= API_URL + '/servers/detail';
241

    
242
    if (changes_since != 0)
243
        uri+='?changes-since='+changes_since
244

    
245
    update_request = $.ajax({
246
        cache: false,
247
        url: uri,
248
        type: "GET",
249
        timeout: TIMEOUT,
250
        dataType: "json",
251
        error: function(jqXHR, textStatus, errorThrown) {
252
            // don't forget to try again later
253
            if (interval) {
254
                clearTimeout(deferred);    // clear old deferred calls
255
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
256
            }
257
            // as for now, just show an error message
258
            try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
259
            ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
260
            return false;
261
            },
262
        success: function(data, textStatus, jqXHR) {
263
            // create changes_since string if necessary
264
            if (jqXHR.getResponseHeader('Date') != null){
265
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
266
                changes_since = ISODateString(changes_since_date);
267
            }
268

    
269
            if (interval) {
270
                clearTimeout(deferred);    // clear old deferred calls
271
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
272
            }
273

    
274
            if (jqXHR.status == 200 || jqXHR.status == 203) {
275
                try {
276
                    servers = data.servers.values;
277
                } catch(err) { ajax_error('400', undefined, 'Update VMs', jqXHR.responseText);}
278
                update_machines_view(data);
279
            } else if (jqXHR.status != 304){
280
                try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
281
                /*
282
                FIXME:  Here it should return the error, however Opera does not support 304.
283
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
284
                        304, which should be corrected (Bug #317).
285
                */
286
                //ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
287
            }
288
            return false;
289
        }
290
    });
291
    return false;
292
}
293

    
294
// get a list of running and terminated machines, used in network view
295
function update_networks(interval) {
296
    try{ console.info('updating networks'); } catch(err){}
297
    var uri= API_URL + '/servers/detail';
298

    
299
    if (changes_since != 0)
300
        uri+='?changes-since='+changes_since
301

    
302
    update_request = $.ajax({
303
        cache: false,
304
        url: uri,
305
        type: "GET",
306
        timeout: TIMEOUT,
307
        dataType: "json",
308
        error: function(jqXHR, textStatus, errorThrown) {
309
            // don't forget to try again later
310
            if (interval) {
311
                clearTimeout(deferred);    // clear old deferred calls
312
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
313
            }
314
            // as for now, just show an error message
315
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
316
            ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
317
            return false;
318
            },
319
        success: function(data, textStatus, jqXHR) {
320
            // create changes_since string if necessary
321
            if (jqXHR.getResponseHeader('Date') != null){
322
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
323
                changes_since = ISODateString(changes_since_date);
324
            }
325

    
326
            if (interval) {
327
                clearTimeout(deferred);    // clear old deferred calls
328
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
329
            }
330

    
331
            if (jqXHR.status == 200 || jqXHR.status == 203) {
332
                try {
333
                    servers = data.servers.values;
334
                } catch(err) { ajax_error('400', undefined, 'Update networks', jqXHR.responseText);}
335
                update_network_names(data);
336
            } else if (jqXHR.status == 304) {
337
                update_network_names();
338
            }
339
            else {
340
                try { console.info('update_networks callback:' + jqXHR.status ) } catch(err) {}
341
                /*
342
                FIXME:  Here it should return the error, however Opera does not support 304.
343
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
344
                        304, which should be corrected (Bug #317).
345
                */
346
                //ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
347
                update_network_names();
348
            }
349
            return false;
350
        }
351
    });
352
    return false;
353
}
354

    
355
// get and show a list of public and private networks
356
function update_network_names(servers_data) {
357
    try{ console.info('updating network names'); } catch(err){}
358
    var uri= API_URL + '/networks/detail';
359

    
360
    if (networks_changes_since != 0)
361
        //FIXME: Comment out the following, until metadata do not 304 when changed
362
        uri+='?changes-since=' + networks_changes_since
363

    
364
    update_request = $.ajax({
365
        cache: false,
366
        url: uri,
367
        type: "GET",
368
        timeout: TIMEOUT,
369
        dataType: "json",
370
        error: function(jqXHR, textStatus, errorThrown) {
371
            // as for now, just show an error message
372
            try {
373
                console.info('update_network names errback:' + jqXHR.status )
374
            } catch(err) {}
375
            ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
376
            return false;
377
            },
378
        success: function(data, textStatus, jqXHR) {
379
            // create changes_since string if necessary
380
            if (jqXHR.getResponseHeader('Date') != null){
381
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
382
                networks_changes_since = ISODateString(changes_since_date);
383
            }
384

    
385
            if (jqXHR.status == 200 || jqXHR.status == 203) {
386
                try {
387
                    networks = data.networks.values;
388
                } catch(err) {
389
                    ajax_error('400', undefined, 'Update network names', jqXHR.responseText);
390
                }
391
                update_networks_view(servers_data, data);
392
            } else if (jqXHR.status == 304) {
393
                update_networks_view(servers_data);
394
            } else if (jqXHR.status != 304){
395
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
396
                /*
397
                FIXME:  Here it should return the error, however Opera does not support 304.
398
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
399
                        304, which should be corrected (Bug #317).
400
                */
401
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
402
                update_networks_view(servers_data);
403
            }
404
            return false;
405
        }
406
    });
407
    return false;
408
}
409

    
410
// get and show a list of available standard and custom images
411
function update_images() {
412
    $.ajax({
413
        url: API_URL + '/images/detail',
414
        type: "GET",
415
        //async: false,
416
        dataType: "json",
417
        timeout: TIMEOUT,
418
        error: function(jqXHR, textStatus, errorThrown) {
419
                    ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
420
                    },
421
        success: function(data, textStatus, jqXHR) {
422
            try {
423
                images = data.images.values;
424
                update_wizard_images();
425
            } catch(err){
426
                ajax_error("NO_IMAGES");
427
            }
428
        }
429
    });
430
    return false;
431
}
432

    
433
function update_wizard_images() {
434
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
435
        $.each(images, function(i,image){
436
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
437
            img.find("label").attr('for',"img-radio-" + image.id);
438
            img.find(".image-title").text(image.name);
439
            if (image.metadata) {
440
                if (image.metadata.values.description != undefined) {
441
                    img.find(".description").text(image.metadata.values.description);
442
                }
443
                if (image.metadata.values.size != undefined) {
444
                    img.find("#size").text(image.metadata.values.size);
445
                }
446
            }
447
            img.find("input.radio").attr('id',"img-radio-" + image.id);
448
            if (i==0) img.find("input.radio").attr("checked","checked");
449
            var image_logo = os_icon(image.metadata);
450
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
451
            if (image.metadata) {
452
                if (image.metadata.values.serverId != undefined) {
453
                    img.appendTo("ul#custom-images");
454
                } else {
455
                    img.appendTo("ul#standard-images");
456
                }
457
            } else {
458
                img.appendTo("ul#standard-images");
459
            }
460
        });
461
    }
462
}
463

    
464
function update_wizard_flavors(){
465
    // sliders for selecting VM flavor
466
    $("#cpu:range").rangeinput({min:0,
467
                               value:0,
468
                               step:1,
469
                               progress: true,
470
                               max:cpus.length-1});
471

    
472
    $("#storage:range").rangeinput({min:0,
473
                               value:0,
474
                               step:1,
475
                               progress: true,
476
                               max:disks.length-1});
477

    
478
    $("#ram:range").rangeinput({min:0,
479
                               value:0,
480
                               step:1,
481
                               progress: true,
482
                               max:ram.length-1});
483
    $("#small").click();
484

    
485
    // update the indicators when sliding
486
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
487
        $("#cpu-indicator")[0].value = cpus[Number(value)];
488
        $("#cpu-indicator").addClass('selectedrange');
489
    });
490
    $("#cpu:range").data().rangeinput.change(function(event,value){
491
        $("#cpu-indicator")[0].value = cpus[Number(value)];
492
        $("#custom").click();
493
        $("#cpu-indicator").removeClass('selectedrange');
494
    });
495
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
496
        $("#ram-indicator")[0].value = ram[Number(value)];
497
        $("#ram-indicator").addClass('selectedrange');
498
    });
499
    $("#ram:range").data().rangeinput.change(function(event,value){
500
        $("#ram-indicator")[0].value = ram[Number(value)];
501
        $("#custom").click();
502
        $("#ram-indicator").removeClass('selectedrange');
503
    });
504
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
505
        $("#storage-indicator")[0].value = disks[Number(value)];
506
        $("#storage-indicator").addClass('selectedrange');
507
    });
508
    $("#storage:range").data().rangeinput.change(function(event,value){
509
        $("#storage-indicator")[0].value = disks[Number(value)];
510
        $("#custom").click();
511
        $("#storage-indicator").removeClass('selectedrange');
512
    });
513
}
514

    
515
Array.prototype.unique = function () {
516
    var r = new Array();
517
    o:for(var i = 0, n = this.length; i < n; i++)
518
    {
519
        for(var x = 0, y = r.length; x < y; x++)
520
        {
521
            if(r[x]==this[i])
522
            {
523
                continue o;
524
            }
525
        }
526
        r[r.length] = this[i];
527
    }
528
    return r;
529
}
530

    
531
// get and configure flavor selection
532
function update_flavors() {
533
    $.ajax({
534
        url: API_URL + '/flavors/detail',
535
        type: "GET",
536
        //async: false,
537
        dataType: "json",
538
        timeout: TIMEOUT,
539
        error: function(jqXHR, textStatus, errorThrown) {
540
            try {
541
                ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
542
            } catch (err) {
543
                ajax_error(err);
544
            }
545
            // start updating vm list
546
            update_vms(UPDATE_INTERVAL);
547
        },
548
        success: function(data, textStatus, jqXHR) {
549
            flavors = data.flavors.values;
550
            $.each(flavors, function(i, flavor) {
551
                cpus[i] = flavor['cpu'];
552
                disks[i] = flavor['disk'];
553
                ram[i] = flavor['ram'];
554
            });
555
            cpus = cpus.unique();
556
            disks = disks.unique();
557
            ram = ram.unique();
558
            update_wizard_flavors();
559
            // start updating vm list
560
            update_vms(UPDATE_INTERVAL);
561
        }
562
    });
563
    return false;
564
}
565

    
566
// return flavorRef from cpu, disk, ram values
567
function identify_flavor(cpu, disk, ram){
568
    for (i=0;i<flavors.length;i++){
569
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
570
            return flavors[i]['id']
571
        }
572
    }
573
    return 0;
574
}
575

    
576
// return image entry from imageRef
577
function get_image(imageRef) {
578
    for (i=0;i<images.length;i++){
579
        if (images[i]['id'] == imageRef) {
580
            return images[i];
581
        }
582
    }
583
    return 0;
584
}
585

    
586
// return machine entry from serverID
587
function get_machine(serverID) {
588
    for (i=0;i<servers.length;i++){
589
        if (servers[i]['id'] == serverID) {
590
            return servers[i];
591
        }
592
    }
593
    return 0;
594
}
595

    
596
// update the actions in icon view, per server
597
function update_iconview_actions(serverID, server_status) {
598
    // remove .disable from all actions to begin with
599
    $('#machinesview-icon.standard #' + serverID + ' div.actions').children().removeClass('disabled');
600
    // decide which actions should be disabled
601
    for (current_action in actions) {
602
        if (actions[current_action].indexOf(server_status) == -1 ) {
603
            $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
604
        }
605
    }
606
}
607

    
608
// update the actions in list view
609
function update_listview_actions() {
610
    var states = [];
611
    var on = [];
612
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
613
    // disable all actions to begin with
614
    $('#machinesview .list div.actions').children().removeClass('enabled');
615

    
616
    // are there multiple machines selected?
617
    if (checked.length>1)
618
        states[0] = 'multiple';
619

    
620
    // check the states of selected machines
621
    checked.each(function(i,checkbox) {
622
        states[states.length] = checkbox.className;
623
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
624
        if (ip.replace('undefined','').length)
625
            states[states.length] = 'network';
626
    });
627

    
628
    // decide which actions should be enabled
629
    for (a in actions) {
630
        var enabled = false;
631
        for (var s =0; s<states.length; s++) {
632
            if (actions[a].indexOf(states[s]) != -1 ) {
633
                enabled = true;
634
            } else {
635
                enabled = false;
636
                break;
637
            }
638
        }
639
        if (enabled)
640
            on[on.length]=a;
641
    }
642
    // enable those actions
643
    for (action in on) {
644
        $("#action-" + on[action]).addClass('enabled');
645
    }
646
}
647

    
648
//create server action
649
function create_vm(machineName, imageRef, flavorRef){
650
    var image_logo = os_icon(get_image(imageRef).metadata);
651
    var uri = API_URL + '/servers';
652
    var payload = {
653
        "server": {
654
            "name": machineName,
655
            "imageRef": imageRef,
656
            "flavorRef" : flavorRef,
657
            "metadata" : {
658
                "OS" : image_logo
659
            }
660
        }
661
    };
662

    
663
    $.ajax({
664
    url: uri,
665
    type: "POST",
666
    contentType: "application/json",
667
    dataType: "json",
668
    data: JSON.stringify(payload),
669
    timeout: TIMEOUT,
670
    error: function(jqXHR, textStatus, errorThrown) {
671
                ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
672
           },
673
    success: function(data, textStatus, jqXHR) {
674
                if ( jqXHR.status == '202') {
675
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
676
                } else {
677
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
678
                }
679
            }
680
    });
681
}
682

    
683
// reboot action
684
function reboot(serverIDs){
685
    if (!serverIDs.length){
686
        //ajax_success('DEFAULT');
687
        return false;
688
    }
689
    // ajax post reboot call
690
    var payload = {
691
        "reboot": {"type" : "HARD"}
692
    };
693

    
694
    var serverID = serverIDs.pop();
695

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

    
723
// shutdown action
724
function shutdown(serverIDs) {
725
    if (!serverIDs.length){
726
        //ajax_success('DEFAULT');
727
        return false;
728
    }
729
    // ajax post shutdown call
730
    var payload = {
731
        "shutdown": {}
732
    };
733

    
734
    var serverID = serverIDs.pop();
735

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

    
763
// destroy action
764
function destroy(serverIDs) {
765
    if (!serverIDs.length){
766
        //ajax_success('DEFAULT');
767
        return false;
768
    }
769
    // ajax post destroy call can have an empty request body
770
    var payload = {};
771

    
772
    var serverID = serverIDs.pop();
773

    
774
    $.ajax({
775
        url: API_URL + '/servers/' + serverID,
776
        type: "DELETE",
777
        contentType: "application/json",
778
        dataType: "json",
779
        data: JSON.stringify(payload),
780
        timeout: TIMEOUT,
781
        error: function(jqXHR, textStatus, errorThrown) {
782
                    display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText)
783
                    },
784
        success: function(data, textStatus, jqXHR) {
785
                    if ( jqXHR.status == '204') {
786
                        try {
787
                            console.info('destroyed ' + serverID);
788
                        } catch (err) {}
789
                        // indicate that the action succeeded
790
                        display_success(serverID);
791
                        // continue with the rest of the servers
792
                        destroy(serverIDs);
793
                    } else {
794
                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
795
                    }
796
                }
797
    });
798
    return false;
799
}
800

    
801
// start action
802
function start(serverIDs){
803
    if (!serverIDs.length){
804
        //ajax_success('DEFAULT');
805
        return false;
806
    }
807
    // ajax post start call
808
    var payload = {
809
        "start": {}
810
    };
811

    
812
    var serverID = serverIDs.pop();
813

    
814
    $.ajax({
815
        url: API_URL + '/servers/' + serverID + '/action',
816
        type: "POST",
817
        contentType: "application/json",
818
        dataType: "json",
819
        data: JSON.stringify(payload),
820
        timeout: TIMEOUT,
821
        error: function(jqXHR, textStatus, errorThrown) {
822
                    display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText)
823
                    },
824
        success: function(data, textStatus, jqXHR) {
825
                    if ( jqXHR.status == '202') {
826
                        try {
827
                            console.info('started ' + serverID);
828
                        } catch(err) {}
829
                        // indicate that the action succeeded
830
                        display_success(serverID);
831
                        // continue with the rest of the servers
832
                        start(serverIDs);
833
                    } else {
834
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
835
                    }
836
                }
837
    });
838
    return false;
839
}
840

    
841
// Show VNC console
842
function vnc_attachment(host, port, password) {
843
    // FIXME: Must be made into parameters, in settings.py
844
    //vnc = open("", "displayWindow",
845
    //    "status=yes,toolbar=yes,menubar=yes");
846
    vd = document.open("application/x-vnc");
847

    
848
    vd.writeln("[connection]");
849
    vd.writeln("host=" + host);
850
    vd.writeln("port=" + port);
851
    vd.writeln("password=" + password);
852

    
853
    vd.close();
854
}
855

    
856
// Show VNC console
857
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
858
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
859
    var params_window = 'scrollbars=no,' +
860
                        'menubar=no,' +
861
                        'toolbar=no,' +
862
                        'status=no,' +
863
                        'top=0,' +
864
                        'left=0,' +
865
                        'height=' + screen.height + ',' +
866
                        'width=' + screen.width + ',' +
867
                        'fullscreen=yes';
868

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

    
871
    // Restore os icon in list view
872
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
873
    osIcon.attr('src',osIcon.attr('os'));
874
    return false;
875
}
876

    
877
// console action
878
function open_console(serverIDs){
879
    if (!serverIDs.length){
880
        //ajax_success('DEFAULT');
881
        return false;
882
    }
883
    // ajax post start call
884
    var payload = {
885
        "console": {"type": "vnc"}
886
    };
887

    
888
    var serverID = serverIDs.pop();
889

    
890
    var machine = get_machine(serverID);
891
    var serverName = machine.name;
892
    var serverIP = machine.addresses.values[0].values[0].addr;
893

    
894
    $.ajax({
895
        url: API_URL + '/servers/' + serverID + '/action',
896
        type: "POST",
897
        contentType: "application/json",
898
        dataType: "json",
899
        data: JSON.stringify(payload),
900
        timeout: TIMEOUT,
901
        error: function(jqXHR, textStatus, errorThrown) {
902
                    display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText)
903
                    },
904
        success: function(data, textStatus, jqXHR) {
905
                    if ( jqXHR.status == '200') {
906
                        try {
907
                            console.info('got_console ' + serverID);
908
                        } catch(err) {}
909
                        // indicate that the action succeeded
910
                        //show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
911
show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
912
                        display_success(serverID);
913
                        // hide spinner
914
                        $('#' + serverID + ' .spinner').hide();
915
                        // continue with the rest of the servers
916
                        open_console(serverIDs);
917
                    } else {
918
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
919
                    }
920
                }
921
    });
922
    return false;
923
}
924

    
925
// rename server
926
function rename(serverID, serverName){
927
    if (!serverID.length){
928
        //ajax_success('DEFAULT');
929
        return false;
930
    }
931
    // ajax post rename call
932
    var payload = {
933
        "server": {"name": serverName}
934
    };
935

    
936
    $.ajax({
937
        url: API_URL + '/servers/' + serverID,
938
        type: "PUT",
939
        contentType: "application/json",
940
        dataType: "json",
941
        data: JSON.stringify(payload),
942
        timeout: TIMEOUT,
943
        error: function(jqXHR, textStatus, errorThrown) {
944
                    display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText)
945
                    },
946
        success: function(data, textStatus, jqXHR) {
947
                    if ( jqXHR.status == '204') {
948
                        try {
949
                            console.info('renamed ' + serverID);
950
                        } catch(err) {}
951
                        // indicate that the action succeeded
952
                        display_success(serverID);
953
                    } else {
954
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
955
                    }
956
                }
957
    });
958
    return false;
959
}
960

    
961
// get server metadata
962
function get_metadata(serverID, keys_only) {
963
    $.ajax({
964
        url: API_URL + '/servers/' + serverID + '/meta',
965
        type: "GET",
966
        //async: false,
967
        dataType: "json",
968
        timeout: TIMEOUT,
969
        error: function(jqXHR, textStatus, errorThrown) {
970
            try {
971
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
972
            } catch (err) {
973
                ajax_error(err);
974
            }
975
        },
976
        success: function(data, textStatus, jqXHR) {
977
            // to list the new results in the edit dialog
978
            if (keys_only) {
979
                list_metadata_keys(serverID, data.metadata.values);
980
            } else {
981
                list_metadata(data);
982
                list_metadata_keys(serverID, data.metadata.values);
983
            }
984
            //hide spinner
985
            $('#metadata-wizard .large-spinner').hide();
986
        }
987
    });
988
    return false;
989
}
990

    
991
// delete metadata key-value pair
992
function delete_metadata(serverID, meta_key) {
993
    $.ajax({
994
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
995
        type: "DELETE",
996
        //async: false,
997
        dataType: "json",
998
        timeout: TIMEOUT,
999
        error: function(jqXHR, textStatus, errorThrown) {
1000
            try {
1001
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1002
            } catch (err) {
1003
                ajax_error(err);
1004
            }
1005
        },
1006
        success: function(data, textStatus, jqXHR) {
1007
            // success: Do nothing, the UI is already updated
1008
        }
1009
    });
1010
    return false;
1011
}
1012

    
1013
// add metadata key-value pair
1014
function update_metadata(serverID, meta_key, meta_value) {
1015
    var payload = {
1016
        "meta": {
1017
        }
1018
    };
1019
    payload["meta"][meta_key] = meta_value;
1020

    
1021
    $.ajax({
1022
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1023
        type: "PUT",
1024
        contentType: "application/json",
1025
        dataType: "json",
1026
        data: JSON.stringify(payload),
1027
        timeout: TIMEOUT,
1028
        error: function(jqXHR, textStatus, errorThrown) {
1029
            try {
1030
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1031
            } catch (err) {
1032
                ajax_error(err);
1033
            }
1034
        },
1035
        success: function(data, textStatus, jqXHR) {
1036
            // success: Update icons if meta key is OS
1037
            if (meta_key == "OS") {
1038
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1039
                $("#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');
1040
            }
1041
        }
1042
    });
1043
    return false;
1044
}
1045

    
1046
// create network
1047
function create_network(networkName){
1048
    // ajax post start call
1049
    var payload = {
1050
        "network": { "name": networkName }
1051
    };
1052

    
1053
    $.ajax({
1054
        url: API_URL + '/networks',
1055
        type: "POST",
1056
        contentType: "application/json",
1057
        dataType: "json",
1058
        data: JSON.stringify(payload),
1059
        timeout: TIMEOUT,
1060
        error: function(jqXHR, textStatus, errorThrown) {
1061
            try {
1062
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1063
            } catch (err) {
1064
                ajax_error(err);
1065
            }
1066
        },
1067
        success: function(data, textStatus, jqXHR) {
1068
            if ( jqXHR.status == '202') {
1069
                try {
1070
                    console.info('created network ' + networkName);
1071
                } catch(err) {}
1072
                update_networks(UPDATE_INTERVAL);
1073
                $("a#networkscreate").overlay().close();
1074
            } else {
1075
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1076
            }
1077
        }
1078
    });
1079
    return false;
1080
}
1081

    
1082
// rename network
1083
function rename_network(networkID, networkName){
1084
    if (!networkID.length){
1085
        //ajax_success('DEFAULT');
1086
        return false;
1087
    }
1088
    // prepare payload
1089
    var payload = {
1090
        "network": {"name": networkName}
1091
    };
1092
    // ajax call
1093
    $.ajax({
1094
        url: API_URL + '/networks/' + networkID,
1095
        type: "PUT",
1096
        contentType: "application/json",
1097
        dataType: "json",
1098
        data: JSON.stringify(payload),
1099
        timeout: TIMEOUT,
1100
        error: function(jqXHR, textStatus, errorThrown) {
1101
            try {
1102
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1103
            } catch (err) {
1104
                ajax_error(err);
1105
            }
1106
        },
1107
        success: function(data, textStatus, jqXHR) {
1108
            if ( jqXHR.status == '204') {
1109
                try {
1110
                    console.info('renamed network' + networkID);
1111
                } catch(err) {}
1112
            } else {
1113
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1114
            }
1115
        }
1116
    });
1117
    return false;
1118
}
1119

    
1120
function delete_network(networkIDs){
1121
    if (!networkIDs.length){
1122
        //ajax_success('DEFAULT');
1123
        return false;
1124
    }
1125
    // get a network
1126
    var networkID = networkIDs.pop();
1127
    // ajax post destroy call can have an empty request body
1128
    var payload = {};
1129
    // ajax call
1130
    $.ajax({
1131
        url: API_URL + '/networks/' + networkID,
1132
        type: "DELETE",
1133
        contentType: "application/json",
1134
        dataType: "json",
1135
        data: JSON.stringify(payload),
1136
        timeout: TIMEOUT,
1137
        error: function(jqXHR, textStatus, errorThrown) {
1138
            try {
1139
                ajax_error(jqXHR.status, undefined, 'Delete network', jqXHR.responseText);
1140
            } catch (err) {
1141
                ajax_error(err);
1142
            }
1143
        },
1144
        success: function(data, textStatus, jqXHR) {
1145
            if ( jqXHR.status == '204') {
1146
                try {
1147
                    console.info('deleted network ' + networkID);
1148
                } catch(err) {}
1149
                // continue with the rest of the servers
1150
                delete_network(networkIDs);
1151
            } else {
1152
                ajax_error(jqXHR.status, undefined, 'Delete network', jqXHR.responseText);
1153
            }
1154
        }
1155
    });
1156
    return false;
1157
}
1158

    
1159
function add_server_to_network(networkID, serverIDs) {
1160
    if (!serverIDs.length){
1161
        //ajax_success('DEFAULT');
1162
        update_networks(UPDATE_INTERVAL);
1163
        $("a#add-machines-overlay").overlay().close();
1164
        return false;
1165
    }
1166
    // get a server
1167
    var serverID = serverIDs.pop();
1168
    // prepare payload
1169
    var payload = {
1170
            "add": { "serverRef": serverID }
1171
        };
1172
    // prepare ajax call
1173
    $.ajax({
1174
        url: API_URL + '/networks/' + networkID + '/action',
1175
        type: "POST",
1176
        contentType: "application/json",
1177
        dataType: "json",
1178
        data: JSON.stringify(payload),
1179
        timeout: TIMEOUT,
1180
        error: function(jqXHR, textStatus, errorThrown) {
1181
            try {
1182
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1183
            } catch (err) {
1184
                ajax_error(err);
1185
            }
1186
        },
1187
        success: function(data, textStatus, jqXHR) {
1188
            if ( jqXHR.status == '202') {
1189
                try {
1190
                    console.info('added server ' + serverID + ' to network ' + networkID);
1191
                } catch(err) {}
1192
                // continue with the rest of the servers
1193
                add_server_to_network(networkID, serverIDs);
1194
            } else {
1195
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1196
            }
1197
        }
1198
    });
1199
    return false;
1200
}
1201

    
1202
function remove_server_from_network(networkIDs, serverIDs) {
1203
    if (!networkIDs.length){
1204
        //ajax_success('DEFAULT');
1205
        return false;
1206
    }
1207
    // get a network and a server
1208
    var networkID = networkIDs.pop();
1209
    var serverID = serverIDs.pop();
1210
    // prepare payload
1211
    var payload = {
1212
            "remove": { "serverRef": serverID }
1213
        };
1214
    // prepare ajax call
1215
    $.ajax({
1216
        url: API_URL + '/networks/' + networkID + '/action',
1217
        type: "POST",
1218
        contentType: "application/json",
1219
        dataType: "json",
1220
        data: JSON.stringify(payload),
1221
        timeout: TIMEOUT,
1222
        error: function(jqXHR, textStatus, errorThrown) {
1223
            try {
1224
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1225
            } catch (err) {
1226
                ajax_error(err);
1227
            }
1228
        },
1229
        success: function(data, textStatus, jqXHR) {
1230
            if ( jqXHR.status == '202') {
1231
                try {
1232
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1233
                } catch(err) {}
1234
                // remove it from DOM
1235
                $('#net-' + networkID + '-server-' + serverID).fadeOut('slow').remove();
1236
                // continue with the rest of the servers
1237
                remove_server_form_network(networkIDs, serverIDs);
1238
            } else {
1239
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1240
            }
1241
        }
1242
    });
1243
    return false;
1244
}
1245

    
1246
// show the welcome screen
1247
function showWelcome() {
1248
    $("#view-select").fadeOut("fast");
1249
    $("#emptymachineslist").fadeIn("fast");
1250
    $("#machinesview").hide();
1251
}
1252

    
1253
// hide the welcome screen
1254
function hideWelcome() {
1255
    $("#emptymachineslist").fadeOut("fast");
1256
    $("#view-select").fadeIn("fast");
1257
    $("div#view-select").show();
1258
    $("#machinesview").show();
1259
}
1260

    
1261
function log_server_status_change(server_entry, new_status) {
1262
    // firebug console logging
1263
    try {
1264
        if ($("#machinesview-single").length > 0) {
1265
            console.info(server_entry.find("div.machine-details div.name").text() +
1266
                        ' from ' + server_entry.find(".state-label").text() +
1267
                        ' to ' + STATUSES[new_status]);
1268
        } else {
1269
            console.info(server_entry.find("div.name span.name").text() +
1270
                        ' from ' + server_entry.find(".status").text() +
1271
                        ' to ' + STATUSES[new_status]);
1272
        }
1273
    } catch(err) {}
1274
}
1275

    
1276
function get_flavor_params(flavorRef) {
1277
    var cpus, ram, disk;
1278
    if ( flavors.length > 0 ) {
1279
        var current_flavor = '';
1280
        for (i=0; i<flavors.length; i++) {
1281
            if (flavors[i]['id'] == flavorRef) {
1282
                current_flavor = flavors[i];
1283
            }
1284
        }
1285
        cpus = current_flavor['cpu'];
1286
        ram = current_flavor['ram'];
1287
        disk = current_flavor['disk'];
1288
    } else {
1289
        cpus = 'undefined';
1290
        ram = 'undefined';
1291
        disk = 'undefined';
1292
    }
1293
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1294
}
1295

    
1296
function get_image_params(imageRef) {
1297
    var image_name, image_size;
1298
    if ( images.length > 0 ) {
1299
        var current_image = '';
1300
        for (i=0; i<images.length; i++) {
1301
            if (images[i]['id'] == imageRef) {
1302
                current_image = images[i];
1303
            }
1304
        }
1305
        image_name = current_image['name'];
1306
        image_size = current_image['metadata']['values']['size'];
1307
    } else {
1308
        image_name = 'undefined';
1309
        image_size = 'undefined';
1310
    }
1311
    return {'name': image_name,'size': image_size};
1312
}
1313

    
1314
function get_public_ips(server) {
1315
    var ip4, ip6;
1316
    try {
1317
        if (server.addresses.values) {
1318
            $.each (server.addresses.values, function(i, value) {
1319
                if (value.id == 'public') {
1320
                    try {
1321
                        $.each (value.values, function(i, ip) {
1322
                            if (ip.version == '4') {
1323
                                ip4 = ip.addr;
1324
                            } else if (ip.version == '6') {
1325
                                ip6 = ip.addr;
1326
                            } else {
1327
                                ip4 = 'undefined';
1328
                                ip6 = 'undefined';
1329
                            }
1330
                        });
1331
                    } catch (err){
1332
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1333
                        ip4 = 'undefined';
1334
                        ip6 = 'undefined';
1335
                    }
1336
                }
1337
            });
1338
        }
1339
    } catch (err) {
1340
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1341
        ip4 = 'undefined';
1342
        ip6 = 'undefined';
1343
    }
1344
    return {'ip4': ip4, 'ip6': ip6};
1345
}
1346

    
1347
function get_private_ips(server) {
1348

    
1349
}