Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ a70fb308

History | View | Annotate | Download (46.2 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
    var action_type = [];
106
    // standard view only
107
    if ($.cookie("view") == '0') {
108
        for (var i=0;i<pending_actions.length;i++){
109
            // show single confirms
110
            if (pending_actions[i][0] == reboot) {
111
                action_type = 'reboot';
112
            } else if (pending_actions[i][0] == shutdown) {
113
                action_type = 'shutdown';
114
            } else if (pending_actions[i][0] == start) {
115
                action_type = 'start';
116
            } else if (pending_actions[i][0] == console) {
117
                action_type = 'console';
118
            } else {
119
                action_type = 'destroy';
120
            }
121
            $("#machines-pane div.machine-container#" + pending_actions[i][1] +
122
            " div.actions div." + action_type + " div.confirm_single").show();
123
        }
124
    }
125

    
126
    // if more than one pending action show multiple confirm box
127
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
128
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
129
        $('#machines-pane div.confirm_multiple').show();
130
    }
131
}
132

    
133
function update_network_confirmations(){
134
    // hide all confirm boxes to begin with
135
    $('#networks-pane div.confirm_multiple').hide();
136

    
137
    for (var i=0;i<pending_actions.length;i++){
138
        // show single confirms depending on the action
139
        if (pending_actions[i][0] == delete_network) {
140
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
141
        } else if (pending_actions[i][0] == remove_server_from_network) {
142
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
143
        } // else {}
144
    }
145

    
146
    // if more than one pending action show multiple confirm box
147
    if (pending_actions.length > 1){
148
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
149
        $('#networks-pane div.confirm_multiple').show();
150
    }
151
}
152

    
153
function list_view() {
154
    changes_since = 0; // to reload full list
155
    pending_actions = []; // clear pending actions
156
    update_confirmations();
157
    clearTimeout(deferred);    // clear old deferred calls
158
    try {
159
        update_request.abort(); // cancel pending ajax updates
160
        load_request.abort();
161
    }catch(err){}
162
    $.cookie("view", '1'); // set list cookie
163
    uri = $("a#list").attr("href");
164
    load_request = $.ajax({
165
        url: uri,
166
        type: "GET",
167
        timeout: TIMEOUT,
168
        dataType: "html",
169
        error: function(jqXHR, textStatus, errorThrown) {
170
            return false;
171
        },
172
        success: function(data, textStatus, jqXHR) {
173
            $("a#list")[0].className += ' activelink';
174
            $("a#standard")[0].className = '';
175
            $("a#single")[0].className = '';
176
            $("div#machinesview").html(data);
177
        }
178
    });
179
    return false;
180
}
181

    
182
function single_view() {
183
    changes_since = 0; // to reload full list
184
    pending_actions = []; // clear pending actions
185
    update_confirmations();
186
    clearTimeout(deferred);    // clear old deferred calls
187
    try {
188
        update_request.abort(); // cancel pending ajax updates
189
        load_request.abort();
190
    }catch(err){}
191
    $.cookie("view", '2'); // set list cookie
192
    uri = $("a#single").attr("href");
193
    load_request = $.ajax({
194
        url: uri,
195
        type: "GET",
196
        timeout: TIMEOUT,
197
        dataType: "html",
198
        error: function(jqXHR, textStatus, errorThrown) {
199
            return false;
200
        },
201
        success: function(data, textStatus, jqXHR) {
202
            $("a#single")[0].className += ' activelink';
203
            $("a#standard")[0].className = '';
204
            $("a#list")[0].className = '';
205
            $("div#machinesview").html(data);
206
        }
207
    });
208
    return false;
209
}
210

    
211
function standard_view() {
212
    changes_since = 0; // to reload full list
213
    pending_actions = []; // clear pending actions
214
    update_confirmations();
215
    clearTimeout(deferred);    // clear old deferred calls
216
    try {
217
        update_request.abort() // cancel pending ajax updates
218
        load_request.abort();
219
    }catch(err){}
220
    $.cookie("view", '0');
221
    uri = $("a#standard").attr("href");
222
    load_request = $.ajax({
223
        url: uri,
224
        type: "GET",
225
        timeout: TIMEOUT,
226
        dataType: "html",
227
        error: function(jqXHR, textStatus, errorThrown) {
228
            return false;
229
        },
230
        success: function(data, textStatus, jqXHR) {
231
            $("a#standard")[0].className += ' activelink';
232
            $("a#list")[0].className = '';
233
            $("a#single")[0].className = '';
234
            $("div#machinesview").html(data);
235
        }
236
    });
237
    return false;
238
}
239

    
240
function choose_view() {
241
    if ($.cookie("view")=='1') {
242
        list_view();
243
    } else if ($.cookie("view")=='2'){
244
        single_view();
245
    } else {
246
        standard_view();
247
    }
248
}
249

    
250
// get and show a list of running and terminated machines
251
function update_vms(interval) {
252
    try{ console.info('updating machines'); } catch(err){}
253
    var uri= API_URL + '/servers/detail';
254

    
255
    if (changes_since != 0)
256
        uri+='?changes-since='+changes_since
257

    
258
    update_request = $.ajax({
259
        cache: false,
260
        url: uri,
261
        type: "GET",
262
        timeout: TIMEOUT,
263
        dataType: "json",
264
        error: function(jqXHR, textStatus, errorThrown) {
265
            // don't forget to try again later
266
            if (interval) {
267
                clearTimeout(deferred);    // clear old deferred calls
268
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
269
            }
270
            // as for now, just show an error message
271
            try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
272
            ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
273
            return false;
274
            },
275
        success: function(data, textStatus, jqXHR) {
276
            // create changes_since string if necessary
277
            if (jqXHR.getResponseHeader('Date') != null){
278
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
279
                changes_since = ISODateString(changes_since_date);
280
            }
281

    
282
            if (interval) {
283
                clearTimeout(deferred);    // clear old deferred calls
284
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
285
            }
286

    
287
            if (jqXHR.status == 200 || jqXHR.status == 203) {
288
                try {
289
                    servers = data.servers.values;
290
                } catch(err) { ajax_error('400', undefined, 'Update VMs', jqXHR.responseText);}
291
                update_machines_view(data);
292
            } else if (jqXHR.status != 304){
293
                try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
294
                /*
295
                FIXME:  Here it should return the error, however Opera does not support 304.
296
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
297
                        304, which should be corrected (Bug #317).
298
                */
299
                //ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
300
            }
301
            return false;
302
        }
303
    });
304
    return false;
305
}
306

    
307
// get a list of running and terminated machines, used in network view
308
function update_networks(interval) {
309
    try{ console.info('updating networks'); } catch(err){}
310
    var uri= API_URL + '/servers/detail';
311

    
312
    if (changes_since != 0)
313
        uri+='?changes-since='+changes_since
314

    
315
    update_request = $.ajax({
316
        cache: false,
317
        url: uri,
318
        type: "GET",
319
        timeout: TIMEOUT,
320
        dataType: "json",
321
        error: function(jqXHR, textStatus, errorThrown) {
322
            // don't forget to try again later
323
            if (interval) {
324
                clearTimeout(deferred);    // clear old deferred calls
325
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
326
            }
327
            // as for now, just show an error message
328
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
329
            ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
330
            return false;
331
            },
332
        success: function(data, textStatus, jqXHR) {
333
            // create changes_since string if necessary
334
            if (jqXHR.getResponseHeader('Date') != null){
335
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
336
                changes_since = ISODateString(changes_since_date);
337
            }
338

    
339
            if (interval) {
340
                clearTimeout(deferred);    // clear old deferred calls
341
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
342
            }
343

    
344
            if (jqXHR.status == 200 || jqXHR.status == 203) {
345
                try {
346
                    servers = data.servers.values;
347
                } catch(err) { ajax_error('400', undefined, 'Update networks', jqXHR.responseText);}
348
                update_network_names(data);
349
            } else if (jqXHR.status == 304) {
350
                update_network_names();
351
            }
352
            else {
353
                try { console.info('update_networks callback:' + jqXHR.status ) } catch(err) {}
354
                /*
355
                FIXME:  Here it should return the error, however Opera does not support 304.
356
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
357
                        304, which should be corrected (Bug #317).
358
                */
359
                //ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
360
                update_network_names();
361
            }
362
            return false;
363
        }
364
    });
365
    return false;
366
}
367

    
368
// get and show a list of public and private networks
369
function update_network_names(servers_data) {
370
    try{ console.info('updating network names'); } catch(err){}
371
    var uri= API_URL + '/networks/detail';
372

    
373
    if (networks_changes_since != 0)
374
        //FIXME: Comment out the following, until metadata do not 304 when changed
375
        uri+='?changes-since=' + networks_changes_since
376

    
377
    update_request = $.ajax({
378
        cache: false,
379
        url: uri,
380
        type: "GET",
381
        timeout: TIMEOUT,
382
        dataType: "json",
383
        error: function(jqXHR, textStatus, errorThrown) {
384
            // as for now, just show an error message
385
            try {
386
                console.info('update_network names errback:' + jqXHR.status )
387
            } catch(err) {}
388
            ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
389
            return false;
390
            },
391
        success: function(data, textStatus, jqXHR) {
392
            // create changes_since string if necessary
393
            if (jqXHR.getResponseHeader('Date') != null){
394
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
395
                networks_changes_since = ISODateString(changes_since_date);
396
            }
397

    
398
            if (jqXHR.status == 200 || jqXHR.status == 203) {
399
                try {
400
                    networks = data.networks.values;
401
                } catch(err) {
402
                    ajax_error('400', undefined, 'Update network names', jqXHR.responseText);
403
                }
404
                update_networks_view(servers_data, data);
405
            } else if (jqXHR.status == 304) {
406
                update_networks_view(servers_data);
407
            } else if (jqXHR.status != 304){
408
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
409
                /*
410
                FIXME:  Here it should return the error, however Opera does not support 304.
411
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
412
                        304, which should be corrected (Bug #317).
413
                */
414
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
415
                update_networks_view(servers_data);
416
            }
417
            return false;
418
        }
419
    });
420
    return false;
421
}
422

    
423
// get and show a list of available standard and custom images
424
function update_images() {
425
    $.ajax({
426
        url: API_URL + '/images/detail',
427
        type: "GET",
428
        //async: false,
429
        dataType: "json",
430
        timeout: TIMEOUT,
431
        error: function(jqXHR, textStatus, errorThrown) {
432
                    ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
433
                    },
434
        success: function(data, textStatus, jqXHR) {
435
            try {
436
                images = data.images.values;
437
                update_wizard_images();
438
            } catch(err){
439
                ajax_error("NO_IMAGES");
440
            }
441
        }
442
    });
443
    return false;
444
}
445

    
446
function update_wizard_images() {
447
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
448
        $.each(images, function(i,image){
449
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
450
            img.find("label").attr('for',"img-radio-" + image.id);
451
            img.find(".image-title").text(image.name);
452
            if (image.metadata) {
453
                if (image.metadata.values.description != undefined) {
454
                    img.find(".description").text(image.metadata.values.description);
455
                }
456
                if (image.metadata.values.size != undefined) {
457
                    img.find("#size").text(image.metadata.values.size);
458
                }
459
            }
460
            img.find("input.radio").attr('id',"img-radio-" + image.id);
461
            if (i==0) img.find("input.radio").attr("checked","checked");
462
            var image_logo = os_icon(image.metadata);
463
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
464
            if (image.metadata) {
465
                if (image.metadata.values.serverId != undefined) {
466
                    img.appendTo("ul#custom-images");
467
                } else {
468
                    img.appendTo("ul#standard-images");
469
                }
470
            } else {
471
                img.appendTo("ul#standard-images");
472
            }
473
        });
474
    }
475
}
476

    
477
function update_wizard_flavors(){
478
    // sliders for selecting VM flavor
479
    $("#cpu:range").rangeinput({min:0,
480
                               value:0,
481
                               step:1,
482
                               progress: true,
483
                               max:cpus.length-1});
484

    
485
    $("#storage:range").rangeinput({min:0,
486
                               value:0,
487
                               step:1,
488
                               progress: true,
489
                               max:disks.length-1});
490

    
491
    $("#ram:range").rangeinput({min:0,
492
                               value:0,
493
                               step:1,
494
                               progress: true,
495
                               max:ram.length-1});
496
    $("#small").click();
497

    
498
    // update the indicators when sliding
499
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
500
        $("#cpu-indicator")[0].value = cpus[Number(value)];
501
        $("#cpu-indicator").addClass('selectedrange');
502
    });
503
    $("#cpu:range").data().rangeinput.change(function(event,value){
504
        $("#cpu-indicator")[0].value = cpus[Number(value)];
505
        $("#custom").click();
506
        $("#cpu-indicator").removeClass('selectedrange');
507
    });
508
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
509
        $("#ram-indicator")[0].value = ram[Number(value)];
510
        $("#ram-indicator").addClass('selectedrange');
511
    });
512
    $("#ram:range").data().rangeinput.change(function(event,value){
513
        $("#ram-indicator")[0].value = ram[Number(value)];
514
        $("#custom").click();
515
        $("#ram-indicator").removeClass('selectedrange');
516
    });
517
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
518
        $("#storage-indicator")[0].value = disks[Number(value)];
519
        $("#storage-indicator").addClass('selectedrange');
520
    });
521
    $("#storage:range").data().rangeinput.change(function(event,value){
522
        $("#storage-indicator")[0].value = disks[Number(value)];
523
        $("#custom").click();
524
        $("#storage-indicator").removeClass('selectedrange');
525
    });
526
}
527

    
528
Array.prototype.unique = function () {
529
    var r = new Array();
530
    o:for(var i = 0, n = this.length; i < n; i++)
531
    {
532
        for(var x = 0, y = r.length; x < y; x++)
533
        {
534
            if(r[x]==this[i])
535
            {
536
                continue o;
537
            }
538
        }
539
        r[r.length] = this[i];
540
    }
541
    return r;
542
}
543

    
544
// get and configure flavor selection
545
function update_flavors() {
546
    $.ajax({
547
        url: API_URL + '/flavors/detail',
548
        type: "GET",
549
        //async: false,
550
        dataType: "json",
551
        timeout: TIMEOUT,
552
        error: function(jqXHR, textStatus, errorThrown) {
553
            try {
554
                ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
555
            } catch (err) {
556
                ajax_error(err);
557
            }
558
            // start updating vm list
559
            update_vms(UPDATE_INTERVAL);
560
        },
561
        success: function(data, textStatus, jqXHR) {
562
            flavors = data.flavors.values;
563
            $.each(flavors, function(i, flavor) {
564
                cpus[i] = flavor['cpu'];
565
                disks[i] = flavor['disk'];
566
                ram[i] = flavor['ram'];
567
            });
568
            cpus = cpus.unique();
569
            disks = disks.unique();
570
            ram = ram.unique();
571
            update_wizard_flavors();
572
            // start updating vm list
573
            update_vms(UPDATE_INTERVAL);
574
        }
575
    });
576
    return false;
577
}
578

    
579
// return flavorRef from cpu, disk, ram values
580
function identify_flavor(cpu, disk, ram){
581
    for (i=0;i<flavors.length;i++){
582
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
583
            return flavors[i]['id']
584
        }
585
    }
586
    return 0;
587
}
588

    
589
// return image entry from imageRef
590
function get_image(imageRef) {
591
    for (i=0;i<images.length;i++){
592
        if (images[i]['id'] == imageRef) {
593
            return images[i];
594
        }
595
    }
596
    return 0;
597
}
598

    
599
// return machine entry from serverID
600
function get_machine(serverID) {
601
    for (i=0;i<servers.length;i++){
602
        if (servers[i]['id'] == serverID) {
603
            return servers[i];
604
        }
605
    }
606
    return 0;
607
}
608

    
609
// update the actions in icon view, per server
610
function update_iconview_actions(serverID, server_status) {
611
    // remove .disable from all actions to begin with
612
    $('#machinesview-icon.standard #' + serverID + ' div.actions').children().removeClass('disabled');
613
    // decide which actions should be disabled
614
    for (current_action in actions) {
615
        if (actions[current_action].indexOf(server_status) == -1 ) {
616
            $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
617
        }
618
    }
619
}
620

    
621
// update the actions in list view
622
function update_listview_actions() {
623
    var states = [];
624
    var on = [];
625
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
626
    // disable all actions to begin with
627
    $('#machinesview .list div.actions').children().removeClass('enabled');
628

    
629
    // are there multiple machines selected?
630
    if (checked.length>1)
631
        states[0] = 'multiple';
632

    
633
    // check the states of selected machines
634
    checked.each(function(i,checkbox) {
635
        states[states.length] = checkbox.className;
636
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
637
        if (ip.replace('undefined','').length)
638
            states[states.length] = 'network';
639
    });
640

    
641
    // decide which actions should be enabled
642
    for (a in actions) {
643
        var enabled = false;
644
        for (var s =0; s<states.length; s++) {
645
            if (actions[a].indexOf(states[s]) != -1 ) {
646
                enabled = true;
647
            } else {
648
                enabled = false;
649
                break;
650
            }
651
        }
652
        if (enabled)
653
            on[on.length]=a;
654
    }
655
    // enable those actions
656
    for (action in on) {
657
        $("#action-" + on[action]).addClass('enabled');
658
    }
659
}
660

    
661
//create server action
662
function create_vm(machineName, imageRef, flavorRef){
663
    var image_logo = os_icon(get_image(imageRef).metadata);
664
    var uri = API_URL + '/servers';
665
    var payload = {
666
        "server": {
667
            "name": machineName,
668
            "imageRef": imageRef,
669
            "flavorRef" : flavorRef,
670
            "metadata" : {
671
                "OS" : image_logo
672
            }
673
        }
674
    };
675

    
676
    $.ajax({
677
    url: uri,
678
    type: "POST",
679
    contentType: "application/json",
680
    dataType: "json",
681
    data: JSON.stringify(payload),
682
    timeout: TIMEOUT,
683
    error: function(jqXHR, textStatus, errorThrown) {
684
                ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
685
           },
686
    success: function(data, textStatus, jqXHR) {
687
                if ( jqXHR.status == '202') {
688
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
689
                } else {
690
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
691
                }
692
            }
693
    });
694
}
695

    
696
// reboot action
697
function reboot(serverIDs){
698
    if (!serverIDs.length){
699
        //ajax_success('DEFAULT');
700
        return false;
701
    }
702
    // ajax post reboot call
703
    var payload = {
704
        "reboot": {"type" : "HARD"}
705
    };
706

    
707
    var serverID = serverIDs.pop();
708

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

    
736
// shutdown action
737
function shutdown(serverIDs) {
738
    if (!serverIDs.length){
739
        //ajax_success('DEFAULT');
740
        return false;
741
    }
742
    // ajax post shutdown call
743
    var payload = {
744
        "shutdown": {}
745
    };
746

    
747
    var serverID = serverIDs.pop();
748

    
749
    $.ajax({
750
        url: API_URL + '/servers/' + serverID + '/action',
751
        type: "POST",
752
        contentType: "application/json",
753
        dataType: "json",
754
        data: JSON.stringify(payload),
755
        timeout: TIMEOUT,
756
        error: function(jqXHR, textStatus, errorThrown) {
757
                    display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText)
758
                    },
759
        success: function(data, textStatus, jqXHR) {
760
                    if ( jqXHR.status == '202') {
761
                        try {
762
                            console.info('suspended ' + serverID);
763
                        } catch(err) {}
764
                        // indicate that the action succeeded
765
                        display_success(serverID);
766
                        // continue with the rest of the servers
767
                        shutdown(serverIDs);
768
                    } else {
769
                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
770
                    }
771
                }
772
    });
773
    return false;
774
}
775

    
776
// destroy action
777
function destroy(serverIDs) {
778
    if (!serverIDs.length){
779
        //ajax_success('DEFAULT');
780
        return false;
781
    }
782
    // ajax post destroy call can have an empty request body
783
    var payload = {};
784

    
785
    var serverID = serverIDs.pop();
786

    
787
    $.ajax({
788
        url: API_URL + '/servers/' + serverID,
789
        type: "DELETE",
790
        contentType: "application/json",
791
        dataType: "json",
792
        data: JSON.stringify(payload),
793
        timeout: TIMEOUT,
794
        error: function(jqXHR, textStatus, errorThrown) {
795
                    display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText)
796
                    },
797
        success: function(data, textStatus, jqXHR) {
798
                    if ( jqXHR.status == '204') {
799
                        try {
800
                            console.info('destroyed ' + serverID);
801
                        } catch (err) {}
802
                        // indicate that the action succeeded
803
                        display_success(serverID);
804
                        // continue with the rest of the servers
805
                        destroy(serverIDs);
806
                    } else {
807
                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
808
                    }
809
                }
810
    });
811
    return false;
812
}
813

    
814
// start action
815
function start(serverIDs){
816
    if (!serverIDs.length){
817
        //ajax_success('DEFAULT');
818
        return false;
819
    }
820
    // ajax post start call
821
    var payload = {
822
        "start": {}
823
    };
824

    
825
    var serverID = serverIDs.pop();
826

    
827
    $.ajax({
828
        url: API_URL + '/servers/' + serverID + '/action',
829
        type: "POST",
830
        contentType: "application/json",
831
        dataType: "json",
832
        data: JSON.stringify(payload),
833
        timeout: TIMEOUT,
834
        error: function(jqXHR, textStatus, errorThrown) {
835
                    display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText)
836
                    },
837
        success: function(data, textStatus, jqXHR) {
838
                    if ( jqXHR.status == '202') {
839
                        try {
840
                            console.info('started ' + serverID);
841
                        } catch(err) {}
842
                        // indicate that the action succeeded
843
                        display_success(serverID);
844
                        // continue with the rest of the servers
845
                        start(serverIDs);
846
                    } else {
847
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
848
                    }
849
                }
850
    });
851
    return false;
852
}
853

    
854
// Show VNC console
855
function vnc_attachment(host, port, password) {
856
    // FIXME: Must be made into parameters, in settings.py
857
    //vnc = open("", "displayWindow",
858
    //    "status=yes,toolbar=yes,menubar=yes");
859
    vd = document.open("application/x-vnc");
860

    
861
    vd.writeln("[connection]");
862
    vd.writeln("host=" + host);
863
    vd.writeln("port=" + port);
864
    vd.writeln("password=" + password);
865

    
866
    vd.close();
867
}
868

    
869
// Show VNC console
870
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
871
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
872
    var params_window = 'scrollbars=no,' +
873
                        'menubar=no,' +
874
                        'toolbar=no,' +
875
                        'status=no,' +
876
                        'top=0,' +
877
                        'left=0,' +
878
                        'height=' + screen.height + ',' +
879
                        'width=' + screen.width + ',' +
880
                        'fullscreen=yes';
881

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

    
884
    // Restore os icon in list view
885
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
886
    osIcon.attr('src',osIcon.attr('os'));
887
    return false;
888
}
889

    
890
// console action
891
function open_console(serverIDs){
892
    if (!serverIDs.length){
893
        //ajax_success('DEFAULT');
894
        return false;
895
    }
896
    // ajax post start call
897
    var payload = {
898
        "console": {"type": "vnc"}
899
    };
900

    
901
    var serverID = serverIDs.pop();
902

    
903
    var machine = get_machine(serverID);
904
    var serverName = machine.name;
905
    var serverIP = machine.addresses.values[0].values[0].addr;
906

    
907
    $.ajax({
908
        url: API_URL + '/servers/' + serverID + '/action',
909
        type: "POST",
910
        contentType: "application/json",
911
        dataType: "json",
912
        data: JSON.stringify(payload),
913
        timeout: TIMEOUT,
914
        error: function(jqXHR, textStatus, errorThrown) {
915
                    display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText)
916
                    },
917
        success: function(data, textStatus, jqXHR) {
918
                    if ( jqXHR.status == '200') {
919
                        try {
920
                            console.info('got_console ' + serverID);
921
                        } catch(err) {}
922
                        // indicate that the action succeeded
923
                        //show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
924
show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
925
                        display_success(serverID);
926
                        // hide spinner
927
                        $('#' + serverID + ' .spinner').hide();
928
                        // continue with the rest of the servers
929
                        open_console(serverIDs);
930
                    } else {
931
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
932
                    }
933
                }
934
    });
935
    return false;
936
}
937

    
938
// rename server
939
function rename(serverID, serverName){
940
    if (!serverID.length){
941
        //ajax_success('DEFAULT');
942
        return false;
943
    }
944
    // ajax post rename call
945
    var payload = {
946
        "server": {"name": serverName}
947
    };
948

    
949
    $.ajax({
950
        url: API_URL + '/servers/' + serverID,
951
        type: "PUT",
952
        contentType: "application/json",
953
        dataType: "json",
954
        data: JSON.stringify(payload),
955
        timeout: TIMEOUT,
956
        error: function(jqXHR, textStatus, errorThrown) {
957
                    display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText)
958
                    },
959
        success: function(data, textStatus, jqXHR) {
960
                    if ( jqXHR.status == '204') {
961
                        try {
962
                            console.info('renamed ' + serverID);
963
                        } catch(err) {}
964
                        // indicate that the action succeeded
965
                        display_success(serverID);
966
                    } else {
967
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
968
                    }
969
                }
970
    });
971
    return false;
972
}
973

    
974
// get server metadata
975
function get_metadata(serverID, keys_only) {
976
    $.ajax({
977
        url: API_URL + '/servers/' + serverID + '/meta',
978
        type: "GET",
979
        //async: false,
980
        dataType: "json",
981
        timeout: TIMEOUT,
982
        error: function(jqXHR, textStatus, errorThrown) {
983
            try {
984
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
985
            } catch (err) {
986
                ajax_error(err);
987
            }
988
        },
989
        success: function(data, textStatus, jqXHR) {
990
            // to list the new results in the edit dialog
991
            if (keys_only) {
992
                list_metadata_keys(serverID, data.metadata.values);
993
            } else {
994
                list_metadata(data);
995
                list_metadata_keys(serverID, data.metadata.values);
996
            }
997
            //hide spinner
998
            $('#metadata-wizard .large-spinner').hide();
999
        }
1000
    });
1001
    return false;
1002
}
1003

    
1004
// delete metadata key-value pair
1005
function delete_metadata(serverID, meta_key) {
1006
    $.ajax({
1007
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1008
        type: "DELETE",
1009
        //async: false,
1010
        dataType: "json",
1011
        timeout: TIMEOUT,
1012
        error: function(jqXHR, textStatus, errorThrown) {
1013
            try {
1014
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1015
            } catch (err) {
1016
                ajax_error(err);
1017
            }
1018
        },
1019
        success: function(data, textStatus, jqXHR) {
1020
            // success: Do nothing, the UI is already updated
1021
        }
1022
    });
1023
    return false;
1024
}
1025

    
1026
// add metadata key-value pair
1027
function update_metadata(serverID, meta_key, meta_value) {
1028
    var payload = {
1029
        "meta": {
1030
        }
1031
    };
1032
    payload["meta"][meta_key] = meta_value;
1033

    
1034
    $.ajax({
1035
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1036
        type: "PUT",
1037
        contentType: "application/json",
1038
        dataType: "json",
1039
        data: JSON.stringify(payload),
1040
        timeout: TIMEOUT,
1041
        error: function(jqXHR, textStatus, errorThrown) {
1042
            try {
1043
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1044
            } catch (err) {
1045
                ajax_error(err);
1046
            }
1047
        },
1048
        success: function(data, textStatus, jqXHR) {
1049
            // success: Update icons if meta key is OS
1050
            if (meta_key == "OS") {
1051
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1052
                $("#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');
1053
            }
1054
        }
1055
    });
1056
    return false;
1057
}
1058

    
1059
// create network
1060
function create_network(networkName){
1061
    // ajax post start call
1062
    var payload = {
1063
        "network": { "name": networkName }
1064
    };
1065

    
1066
    $.ajax({
1067
        url: API_URL + '/networks',
1068
        type: "POST",
1069
        contentType: "application/json",
1070
        dataType: "json",
1071
        data: JSON.stringify(payload),
1072
        timeout: TIMEOUT,
1073
        error: function(jqXHR, textStatus, errorThrown) {
1074
            try {
1075
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1076
            } catch (err) {
1077
                ajax_error(err);
1078
            }
1079
        },
1080
        success: function(data, textStatus, jqXHR) {
1081
            if ( jqXHR.status == '202') {
1082
                try {
1083
                    console.info('created network ' + networkName);
1084
                } catch(err) {}
1085
                update_networks(UPDATE_INTERVAL);
1086
                $("a#networkscreate").overlay().close();
1087
            } else {
1088
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1089
            }
1090
        }
1091
    });
1092
    return false;
1093
}
1094

    
1095
// rename network
1096
function rename_network(networkID, networkName){
1097
    if (!networkID.length){
1098
        //ajax_success('DEFAULT');
1099
        return false;
1100
    }
1101
    // prepare payload
1102
    var payload = {
1103
        "network": {"name": networkName}
1104
    };
1105
    // ajax call
1106
    $.ajax({
1107
        url: API_URL + '/networks/' + networkID,
1108
        type: "PUT",
1109
        contentType: "application/json",
1110
        dataType: "json",
1111
        data: JSON.stringify(payload),
1112
        timeout: TIMEOUT,
1113
        error: function(jqXHR, textStatus, errorThrown) {
1114
            try {
1115
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1116
            } catch (err) {
1117
                ajax_error(err);
1118
            }
1119
        },
1120
        success: function(data, textStatus, jqXHR) {
1121
            if ( jqXHR.status == '204') {
1122
                try {
1123
                    console.info('renamed network' + networkID);
1124
                } catch(err) {}
1125
            } else {
1126
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1127
            }
1128
        }
1129
    });
1130
    return false;
1131
}
1132

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

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

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

    
1259
// show the welcome screen
1260
function showWelcome() {
1261
    $("#view-select").fadeOut("fast");
1262
    $("#emptymachineslist").fadeIn("fast");
1263
    $("#machinesview").hide();
1264
}
1265

    
1266
// hide the welcome screen
1267
function hideWelcome() {
1268
    $("#emptymachineslist").fadeOut("fast");
1269
    $("#view-select").fadeIn("fast");
1270
    $("div#view-select").show();
1271
    $("#machinesview").show();
1272
}
1273

    
1274
function log_server_status_change(server_entry, new_status) {
1275
    // firebug console logging
1276
    try {
1277
        if ($("#machinesview-single").length > 0) {
1278
            console.info(server_entry.find("div.machine-details div.name").text() +
1279
                        ' from ' + server_entry.find(".state-label").text() +
1280
                        ' to ' + STATUSES[new_status]);
1281
        } else {
1282
            console.info(server_entry.find("div.name span.name").text() +
1283
                        ' from ' + server_entry.find(".status").text() +
1284
                        ' to ' + STATUSES[new_status]);
1285
        }
1286
    } catch(err) {}
1287
}
1288

    
1289
function get_flavor_params(flavorRef) {
1290
    var cpus, ram, disk;
1291
    if ( flavors.length > 0 ) {
1292
        var current_flavor = '';
1293
        for (i=0; i<flavors.length; i++) {
1294
            if (flavors[i]['id'] == flavorRef) {
1295
                current_flavor = flavors[i];
1296
            }
1297
        }
1298
        cpus = current_flavor['cpu'];
1299
        ram = current_flavor['ram'];
1300
        disk = current_flavor['disk'];
1301
    } else {
1302
        cpus = 'undefined';
1303
        ram = 'undefined';
1304
        disk = 'undefined';
1305
    }
1306
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1307
}
1308

    
1309
function get_image_params(imageRef) {
1310
    var image_name, image_size;
1311
    if ( images.length > 0 ) {
1312
        var current_image = '';
1313
        for (i=0; i<images.length; i++) {
1314
            if (images[i]['id'] == imageRef) {
1315
                current_image = images[i];
1316
            }
1317
        }
1318
        image_name = current_image['name'];
1319
        image_size = current_image['metadata']['values']['size'];
1320
    } else {
1321
        image_name = 'undefined';
1322
        image_size = 'undefined';
1323
    }
1324
    return {'name': image_name,'size': image_size};
1325
}
1326

    
1327
function get_public_ips(server) {
1328
    var ip4, ip6;
1329
    try {
1330
        if (server.addresses.values) {
1331
            $.each (server.addresses.values, function(i, value) {
1332
                if (value.id == 'public') {
1333
                    try {
1334
                        $.each (value.values, function(i, ip) {
1335
                            if (ip.version == '4') {
1336
                                ip4 = ip.addr;
1337
                            } else if (ip.version == '6') {
1338
                                ip6 = ip.addr;
1339
                            } else {
1340
                                ip4 = 'undefined';
1341
                                ip6 = 'undefined';
1342
                            }
1343
                        });
1344
                    } catch (err){
1345
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1346
                        ip4 = 'undefined';
1347
                        ip6 = 'undefined';
1348
                    }
1349
                }
1350
            });
1351
        }
1352
    } catch (err) {
1353
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1354
        ip4 = 'undefined';
1355
        ip6 = 'undefined';
1356
    }
1357
    return {'ip4': ip4, 'ip6': ip6};
1358
}
1359

    
1360
function get_private_ips(server) {
1361

    
1362
}