Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ 260d6bb3

History | View | Annotate | Download (66.3 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

    
54
// jquery show/hide events
55
var _oldshow = $.fn.show;
56
$.fn.show = function(speed, callback) {
57
    $(this).trigger('show');
58
    return _oldshow.apply(this,arguments);
59
}
60
var _oldhide = $.fn.hide;
61
$.fn.hide = function(speed, callback) {
62
    $(this).trigger('hide');
63
    return _oldhide.apply(this,arguments);
64
}
65

    
66
function ISODateString(d){
67
    //return a date in an ISO 8601 format using UTC.
68
    //do not include time zone info (Z) at the end
69
    //taken from the Mozilla Developer Center
70
    function pad(n){ return n<10 ? '0'+n : n }
71
    return  d.getUTCFullYear()+ '-' +
72
            pad(d.getUTCMonth()+1) + '-' +
73
            pad(d.getUTCDate()) + 'T' +
74
            pad(d.getUTCHours()) + ':' +
75
            pad(d.getUTCMinutes()) + ':' +
76
            pad(d.getUTCSeconds()) +'Z'
77
}
78

    
79
function parse_error(responseText, errorCode){
80
    var errors = [];
81
    try {
82
        responseObj = JSON.parse(responseText);
83
    }
84
    catch(err) {
85
        errors[0] = {'code': errorCode};
86
        return errors;
87
    }
88
    for (var err in responseObj){
89
        errors[errors.length] = responseObj[err];
90
    }
91
    return errors;
92
}
93

    
94
// indexOf prototype for IE
95
if (!Array.prototype.indexOf) {
96
  Array.prototype.indexOf = function(elt /*, from*/) {
97
    var len = this.length;
98
    var from = Number(arguments[1]) || 0;
99
    from = (from < 0)
100
         ? Math.ceil(from)
101
         : Math.floor(from);
102
    if (from < 0)
103
      from += len;
104

    
105
    for (; from < len; from++) {
106
      if (from in this &&
107
          this[from] === elt)
108
        return from;
109
    }
110
    return -1;
111
  };
112
}
113

    
114
// trim prototype for IE
115
if(typeof String.prototype.trim !== 'function') {
116
    String.prototype.trim = function() {
117
        return this.replace(/^\s+|\s+$/g, '');
118
    }
119
}
120

    
121
function update_confirmations() {
122
    // hide all confirm boxes to begin with
123
    $('#machines-pane div.confirm_single').hide();
124
    $('#machines-pane div.confirm_multiple').hide();
125
    var action_type = [];
126
    // standard view or single view
127
    if ($.cookie("view") == '0' || $.cookie("view") == '2') {
128
        for (var i=0; i<pending_actions.length; i++) {
129
            // show single confirms
130
            if (pending_actions[i][0] == reboot) {
131
                action_type = "reboot";
132
            } else if (pending_actions[i][0] == shutdown) {
133
                action_type = "shutdown";
134
            } else if (pending_actions[i][0] == start) {
135
                action_type = "start";
136
            } else if (pending_actions[i][0] == open_console) {
137
                action_type = "console";
138
            } else {
139
                action_type = "destroy";
140
            }
141
            $("#machines-pane #" + pending_actions[i][1] +
142
            " div.action-container." + action_type + " div.confirm_single").show();
143
        }
144
    }
145
    // if more than one pending action show multiple confirm box
146
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
147
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
148
        $('#machines-pane div.confirm_multiple').show();
149
    }
150
}
151

    
152
function update_network_confirmations(){
153
    // hide all confirm boxes to begin with
154
    $('#networks-pane div.confirm_multiple').hide();
155

    
156
    for (var i=0;i<pending_actions.length;i++){
157
        // show single confirms depending on the action
158
        if (pending_actions[i][0] == delete_network) {
159
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
160
        } else if (pending_actions[i][0] == remove_server_from_network) {
161
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
162
        } // else {}
163
    }
164

    
165
    // if more than one pending action show multiple confirm box
166
    if (pending_actions.length > 1){
167
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
168
        $('#networks-pane div.confirm_multiple').show();
169
    }
170
}
171

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

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

    
230
function standard_view() {
231
    changes_since = 0; // to reload full list
232
    pending_actions = []; // clear pending actions
233
    update_confirmations();
234
    clearTimeout(deferred);    // clear old deferred calls
235
    try {
236
        update_request.abort() // cancel pending ajax updates
237
        load_request.abort();
238
    }catch(err){}
239
    $.cookie("view", '0');
240
    uri = $("a#standard").attr("href");
241
    load_request = $.ajax({
242
        url: uri,
243
        type: "GET",
244
        timeout: TIMEOUT,
245
        dataType: "html",
246
        error: function(jqXHR, textStatus, errorThrown) {
247
            return false;
248
        },
249
        success: function(data, textStatus, jqXHR) {
250
            $("a#standard")[0].className += ' activelink';
251
            $("a#list")[0].className = '';
252
            $("a#single")[0].className = '';
253
            $("div#machinesview").html(data);
254
        }
255
    });
256
    return false;
257
}
258

    
259
function choose_view() {
260
    if ($.cookie("view")=='1') {
261
        list_view();
262
    } else if ($.cookie("view")=='2'){
263
        single_view();
264
    } else {
265
        standard_view();
266
    }
267
}
268

    
269
// return value from metadata key "OS", if it exists
270
function os_icon(metadata) {
271
    if (!metadata) {
272
        return 'unknown';
273
    }
274
    if (metadata.values.OS == undefined || metadata.values.OS == '') {
275
        return 'unknown';
276
    } else {
277
        if (os_icons.indexOf(metadata.values.OS) == -1) {
278
            return 'unknown';
279
        } else {
280
            return metadata.values.OS;
281
        }
282
    }
283
}
284

    
285
function os_icon_from_value(metadata) {
286
    if (!metadata) {
287
        return 'unknown';
288
    }
289
if (metadata == undefined || metadata == '') {
290
        return 'unknown';
291
    } else {
292
        if (os_icons.indexOf(metadata) == -1) {
293
            return 'unknown';
294
        } else {
295
            return metadata;
296
        }
297
    }
298
}
299

    
300
// get and show a list of running and terminated machines
301
function update_vms(interval) {
302
    try{ console.info('updating machines'); } catch(err){}
303
    var uri= API_URL + '/servers/detail';
304

    
305
    if (changes_since != 0)
306
        uri+='?changes-since='+changes_since
307

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

    
336
            if (interval) {
337
                clearTimeout(deferred);    // clear old deferred calls
338
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
339
            }
340

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

    
362
// get a list of running and terminated machines, used in network view
363
function update_networks(interval) {
364
    try{ console.info('updating networks'); } catch(err){}
365
    var uri= API_URL + '/servers/detail';
366

    
367
    if (changes_since != 0)
368
        uri+='?changes-since='+changes_since
369

    
370
    update_request = $.ajax({
371
        cache: false,
372
        url: uri,
373
        type: "GET",
374
        timeout: TIMEOUT,
375
        dataType: "json",
376
        error: function(jqXHR, textStatus, errorThrown) {
377
            // don't forget to try again later
378
            if (interval) {
379
                clearTimeout(deferred);    // clear old deferred calls
380
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
381
            }
382
            // as for now, just show an error message
383
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
384
            try {
385
                ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
386
            } catch(err) {
387
                ajax_error(0, undefined, 'Update networks', jqXHR.responseText);
388
            }
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
                changes_since = ISODateString(changes_since_date);
396
            }
397

    
398
            if (interval) {
399
                clearTimeout(deferred);    // clear old deferred calls
400
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
401
            }
402

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

    
428
// get and show a list of public and private networks
429
function update_network_names(servers_data) {
430
    try{ console.info('updating network names'); } catch(err){}
431
    var uri= API_URL + '/networks/detail';
432

    
433
    if (networks_changes_since != 0)
434
        //FIXME: Comment out the following, until metadata do not 304 when changed
435
        uri+='?changes-since=' + networks_changes_since
436

    
437
    update_request = $.ajax({
438
        cache: false,
439
        url: uri,
440
        type: "GET",
441
        timeout: TIMEOUT,
442
        dataType: "json",
443
        error: function(jqXHR, textStatus, errorThrown) {
444
            // as for now, just show an error message
445
            try {
446
                console.info('update_network names errback:' + jqXHR.status )
447
            } catch(err) {}
448
            try {
449
                ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
450
            } catch(err) {
451
                ajax_error(0, undefined, 'Update network names', jqXHR.responseText);
452
            }
453
            return false;
454
            },
455
        success: function(data, textStatus, jqXHR) {
456
            // create changes_since string if necessary
457
            if (jqXHR.getResponseHeader('Date') != null){
458
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
459
                networks_changes_since = ISODateString(changes_since_date);
460
            }
461

    
462
            if (jqXHR.status == 200 || jqXHR.status == 203) {
463
                try {
464
                    networks = data.networks.values;
465
                    jQuery.parseJSON(data);
466
                    update_networks_view(servers_data, data);
467
                } catch(err) {
468
                    ajax_error('400', undefined, 'Update network names', jqXHR.responseText);
469
                }
470
            } else if (jqXHR.status == 304) {
471
                update_networks_view(servers_data);
472
            } else if (jqXHR.status != 304){
473
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
474
                /*
475
                FIXME:  Here it should return the error, however Opera does not support 304.
476
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
477
                        304, which should be corrected (Bug #317).
478
                */
479
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
480
                update_networks_view(servers_data);
481
            }
482
            return false;
483
        }
484
    });
485
    return false;
486
}
487

    
488
// get and show a list of available standard and custom images
489
function update_images() {
490
    $.ajax({
491
        url: API_URL + '/images/detail',
492
        type: "GET",
493
        //async: false,
494
        dataType: "json",
495
        timeout: TIMEOUT,
496
        error: function(jqXHR, textStatus, errorThrown) {
497
                    try {
498
                        ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
499
                    } catch(err) {
500
                        ajax_error(0, undefined, 'Update Images', jqXHR.responseText);
501
                    }
502
                },
503
        success: function(data, textStatus, jqXHR) {
504
            try {
505
                images = data.images.values;
506
                jQuery.parseJSON(data);
507
                update_wizard_images();
508
            } catch(err){
509
                ajax_error("NO_IMAGES");
510
            }
511
        }
512
    });
513
    return false;
514
}
515

    
516
function update_wizard_images() {
517
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
518
        $.each(images, function(i,image){
519
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
520
            img.find("label").attr('for',"img-radio-" + image.id);
521
            img.find(".image-title").text(image.name);
522
            if (image.metadata) {
523
                if (image.metadata.values.description != undefined) {
524
                    img.find(".description").text(image.metadata.values.description);
525
                }
526
                if (image.metadata.values.size != undefined) {
527
                    img.find("#size").text(image.metadata.values.size);
528
                }
529
            }
530
            img.find("input.radio").attr('id',"img-radio-" + image.id);
531
            if (i==0) img.find("input.radio").attr("checked","checked");
532
            var image_logo = os_icon(image.metadata);
533
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
534
            if (image.metadata) {
535
                if (image.metadata.values.serverId != undefined) {
536
                    img.appendTo("ul#custom-images");
537
                } else {
538
                    img.appendTo("ul#standard-images");
539
                }
540
            } else {
541
                img.appendTo("ul#standard-images");
542
            }
543
        });
544
    }
545
}
546

    
547
function update_wizard_flavors(){
548
    // sliders for selecting VM flavor
549
    $("#cpu:range").rangeinput({min:0,
550
                               value:0,
551
                               step:1,
552
                               progress: true,
553
                               max:cpus.length-1});
554

    
555
    $("#storage:range").rangeinput({min:0,
556
                               value:0,
557
                               step:1,
558
                               progress: true,
559
                               max:disks.length-1});
560

    
561
    $("#ram:range").rangeinput({min:0,
562
                               value:0,
563
                               step:1,
564
                               progress: true,
565
                               max:ram.length-1});
566
    $("#small").click();
567

    
568
    // update the indicators when sliding
569
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
570
        $("#cpu-indicator")[0].value = cpus[Number(value)];
571
        $("#cpu-indicator").addClass('selectedrange');
572
    });
573
    $("#cpu:range").data().rangeinput.change(function(event,value){
574
        $("#cpu-indicator")[0].value = cpus[Number(value)];
575
        $("#custom").click();
576
        $("#cpu-indicator").removeClass('selectedrange');
577
    });
578
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
579
        $("#ram-indicator")[0].value = ram[Number(value)];
580
        $("#ram-indicator").addClass('selectedrange');
581
    });
582
    $("#ram:range").data().rangeinput.change(function(event,value){
583
        $("#ram-indicator")[0].value = ram[Number(value)];
584
        $("#custom").click();
585
        $("#ram-indicator").removeClass('selectedrange');
586
    });
587
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
588
        $("#storage-indicator")[0].value = disks[Number(value)];
589
        $("#storage-indicator").addClass('selectedrange');
590
    });
591
    $("#storage:range").data().rangeinput.change(function(event,value){
592
        $("#storage-indicator")[0].value = disks[Number(value)];
593
        $("#custom").click();
594
        $("#storage-indicator").removeClass('selectedrange');
595
    });
596
}
597

    
598
Array.prototype.unique = function () {
599
    var r = new Array();
600
    o:for(var i = 0, n = this.length; i < n; i++)
601
    {
602
        for(var x = 0, y = r.length; x < y; x++)
603
        {
604
            if(r[x]==this[i])
605
            {
606
                continue o;
607
            }
608
        }
609
        r[r.length] = this[i];
610
    }
611
    return r;
612
}
613

    
614
// get and configure flavor selection
615
function update_flavors() {
616
    $.ajax({
617
        url: API_URL + '/flavors/detail',
618
        type: "GET",
619
        //async: false,
620
        dataType: "json",
621
        timeout: TIMEOUT,
622
        error: function(jqXHR, textStatus, errorThrown) {
623
            try {
624
                ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
625
            } catch (err) {
626
                ajax_error(err);
627
            }
628
            // start updating vm list
629
            update_vms(UPDATE_INTERVAL);
630
        },
631
        success: function(data, textStatus, jqXHR) {
632

    
633
            try {
634
                flavors = data.flavors.values;
635
                jQuery.parseJSON(data);
636
                $.each(flavors, function(i, flavor) {
637
                    cpus[i] = flavor['cpu'];
638
                    disks[i] = flavor['disk'];
639
                    ram[i] = flavor['ram'];
640
                });
641
                cpus = cpus.unique();
642
                disks = disks.unique();
643
                ram = ram.unique();
644
                update_wizard_flavors();
645
            } catch(err){
646
                ajax_error("NO_FLAVORS");
647
            }
648
            // start updating vm list
649
            update_vms(UPDATE_INTERVAL);
650
        }
651
    });
652
    return false;
653
}
654

    
655
// return flavorRef from cpu, disk, ram values
656
function identify_flavor(cpu, disk, ram){
657
    for (i=0;i<flavors.length;i++){
658
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
659
            return flavors[i]['id']
660
        }
661
    }
662
    return 0;
663
}
664

    
665
// return image entry from imageRef
666
function get_image(imageRef) {
667
    for (i=0;i<images.length;i++){
668
        if (images[i]['id'] == imageRef) {
669
            return images[i];
670
        }
671
    }
672
    return 0;
673
}
674

    
675
// return machine entry from serverID
676
function get_machine(serverID) {
677
    for (i=0;i<servers.length;i++){
678
        if (servers[i]['id'] == serverID) {
679
            return servers[i];
680
        }
681
    }
682
    return 0;
683
}
684

    
685
// update the actions in icon view, per server
686
function update_iconview_actions(serverID, server_status) {
687
    if ($.cookie("view")=='2') {
688
        // remove .disable from all actions to begin with
689
        $('#machinesview-single #' + serverID + ' div.single-action').show();
690
        // decide which actions should be disabled
691
        for (current_action in actions) {
692
            if (actions[current_action].indexOf(server_status) == -1 ) {
693
                $('#machinesview-single #' + serverID + ' div.action-' + current_action).hide();
694
            }
695
        }
696
    } else {
697
        // remove .disable from all actions to begin with
698
        $('#machinesview-icon.standard #' + serverID + ' div.actions').find('a').removeClass('disabled');
699
        // decide which actions should be disabled
700
        for (current_action in actions) {
701
            if (actions[current_action].indexOf(server_status) == -1 ) {
702
                $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
703
            }
704
        }
705
    }
706
}
707

    
708
// update the actions in list view
709
function update_listview_actions() {
710
    var states = [];
711
    var on = [];
712
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
713
    // disable all actions to begin with
714
    $('#machinesview .list div.actions').children().removeClass('enabled');
715

    
716
    // are there multiple machines selected?
717
    if (checked.length>1)
718
        states[0] = 'multiple';
719

    
720
    // check the states of selected machines
721
    checked.each(function(i,checkbox) {
722
        states[states.length] = checkbox.className;
723
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
724
        if (ip.replace('undefined','').length)
725
            states[states.length] = 'network';
726
    });
727

    
728
    // decide which actions should be enabled
729
    for (a in actions) {
730
        var enabled = false;
731
        for (var s =0; s<states.length; s++) {
732
            if (actions[a].indexOf(states[s]) != -1 ) {
733
                enabled = true;
734
            } else {
735
                enabled = false;
736
                break;
737
            }
738
        }
739
        if (enabled)
740
            on[on.length]=a;
741
    }
742
    // enable those actions
743
    for (action in on) {
744
        $("#action-" + on[action]).addClass('enabled');
745
    }
746
}
747

    
748
//create server action
749
function create_vm(machineName, imageRef, flavorRef){
750
    var image_logo = os_icon(get_image(imageRef).metadata);
751
    var uri = API_URL + '/servers';
752
    var payload = {
753
        "server": {
754
            "name": machineName,
755
            "imageRef": imageRef,
756
            "flavorRef" : flavorRef,
757
            "metadata" : {
758
                "OS" : image_logo
759
            }
760
        }
761
    };
762

    
763
    $.ajax({
764
    url: uri,
765
    type: "POST",
766
    contentType: "application/json",
767
    dataType: "json",
768
    data: JSON.stringify(payload),
769
    timeout: TIMEOUT,
770
    error: function(jqXHR, textStatus, errorThrown) {
771
                // close wizard and show error box
772
                $('#machines-pane a#create').data('overlay').close();
773
                    try {
774
                        ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
775
                    } catch(err) {
776
                        ajax_error(0, undefined, 'Create VM', jqXHR.responseText);
777
                    }
778
           },
779
    success: function(data, textStatus, jqXHR) {
780
                if ( jqXHR.status == '202') {
781
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
782
                } else {
783
                    // close wizard and show error box
784
                    $('#machines-pane a#create').data('overlay').close();
785
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
786
                }
787
            }
788
    });
789
}
790

    
791
// reboot action
792
function reboot(serverIDs){
793
    if (!serverIDs.length){
794
        //ajax_success('DEFAULT');
795
        return false;
796
    }
797
    // ajax post reboot call
798
    var payload = {
799
        "reboot": {"type" : "HARD"}
800
    };
801

    
802
    var serverID = serverIDs.pop();
803

    
804
    $.ajax({
805
        url: API_URL + '/servers/' + serverID + '/action',
806
        type: "POST",
807
        contentType: "application/json",
808
        dataType: "json",
809
        data: JSON.stringify(payload),
810
        timeout: TIMEOUT,
811
        error: function(jqXHR, textStatus, errorThrown) {
812
                    // in machine views
813
                    if ( $.cookie("pane") == 0) {
814
                        try {
815
                            display_failure(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
816
                        } catch (err) {
817
                            display_failure(0, serverID, 'Reboot', jqXHR.responseText);
818
                        }
819
                    }
820
                    // in network view
821
                    else {
822
                        try {
823
                            display_reboot_failure(jqXHR.status, serverID, jqXHR.responseText);
824
                        } catch (err) {
825
                            display_reboot_failure(0, serverID, jqXHR.responseText);
826
                        }
827
                    }
828
                },
829
        success: function(data, textStatus, jqXHR) {
830
                    if ( jqXHR.status == '202') {
831
                        try {
832
                            console.info('rebooted ' + serverID);
833
                        } catch(err) {}
834
                        // indicate that the action succeeded
835
                        // in machine views
836
                        if ( $.cookie("pane") == 0) {
837
                            display_success(serverID);
838
                        }
839
                        // in network view
840
                        else {
841
                            display_reboot_success(serverID);
842
                        }
843
                        // continue with the rest of the servers
844
                        reboot(serverIDs);
845
                    } else {
846
                        ajax_error(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
847
                    }
848
                }
849
    });
850
    return false;
851
}
852

    
853
// shutdown action
854
function shutdown(serverIDs) {
855
    if (!serverIDs.length){
856
        //ajax_success('DEFAULT');
857
        return false;
858
    }
859
    // ajax post shutdown call
860
    var payload = {
861
        "shutdown": {}
862
    };
863

    
864
    var serverID = serverIDs.pop();
865

    
866
    $.ajax({
867
        url: API_URL + '/servers/' + serverID + '/action',
868
        type: "POST",
869
        contentType: "application/json",
870
        dataType: "json",
871
        data: JSON.stringify(payload),
872
        timeout: TIMEOUT,
873
        error: function(jqXHR, textStatus, errorThrown) {
874
                    try {
875
                        display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
876
                    } catch(err) {
877
                        display_failure(0, serverID, 'Shutdown', jqXHR.responseText);
878
                    }
879
                },
880
        success: function(data, textStatus, jqXHR) {
881
                    if ( jqXHR.status == '202') {
882
                        try {
883
                            console.info('suspended ' + serverID);
884
                        } catch(err) {}
885
                        // indicate that the action succeeded
886
                        display_success(serverID);
887
                        // continue with the rest of the servers
888
                        shutdown(serverIDs);
889
                    } else {
890
                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
891
                    }
892
                }
893
    });
894
    return false;
895
}
896

    
897
// destroy action
898
function destroy(serverIDs) {
899
    if (!serverIDs.length){
900
        //ajax_success('DEFAULT');
901
        return false;
902
    }
903
    // ajax post destroy call can have an empty request body
904
    var payload = {};
905

    
906
    var serverID = serverIDs.pop();
907

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

    
939
// start action
940
function start(serverIDs){
941
    if (!serverIDs.length){
942
        //ajax_success('DEFAULT');
943
        return false;
944
    }
945
    // ajax post start call
946
    var payload = {
947
        "start": {}
948
    };
949

    
950
    var serverID = serverIDs.pop();
951

    
952
    $.ajax({
953
        url: API_URL + '/servers/' + serverID + '/action',
954
        type: "POST",
955
        contentType: "application/json",
956
        dataType: "json",
957
        data: JSON.stringify(payload),
958
        timeout: TIMEOUT,
959
        error: function(jqXHR, textStatus, errorThrown) {
960
                    try {
961
                        display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText);
962
                    } catch(err) {
963
                        display_failure(0, serverID, 'Start', jqXHR.responseText);
964
                    }
965
                },
966
        success: function(data, textStatus, jqXHR) {
967
                    if ( jqXHR.status == '202') {
968
                        try {
969
                            console.info('started ' + serverID);
970
                        } catch(err) {}
971
                        // indicate that the action succeeded
972
                        display_success(serverID);
973
                        // continue with the rest of the servers
974
                        start(serverIDs);
975
                    } else {
976
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
977
                    }
978
                }
979
    });
980
    return false;
981
}
982

    
983
// Show VNC console
984
function vnc_attachment(host, port, password) {
985
    // FIXME: Must be made into parameters, in settings.py
986
    //vnc = open("", "displayWindow",
987
    //    "status=yes,toolbar=yes,menubar=yes");
988
    vd = document.open("application/x-vnc");
989

    
990
    vd.writeln("[connection]");
991
    vd.writeln("host=" + host);
992
    vd.writeln("port=" + port);
993
    vd.writeln("password=" + password);
994

    
995
    vd.close();
996
}
997

    
998
// Show VNC console
999
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
1000
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
1001
    var params_window = 'scrollbars=no,' +
1002
                        'menubar=no,' +
1003
                        'toolbar=no,' +
1004
                        'status=no,' +
1005
                        'top=0,' +
1006
                        'left=0,' +
1007
                        'height=' + screen.height + ',' +
1008
                        'width=' + screen.width + ',' +
1009
                        'fullscreen=yes';
1010

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

    
1013
    // Restore os icon in list view
1014
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1015
    osIcon.attr('src',osIcon.attr('os'));
1016
    return false;
1017
}
1018

    
1019
// console action
1020
function open_console(serverIDs){
1021
    if (!serverIDs.length){
1022
        //ajax_success('DEFAULT');
1023
        return false;
1024
    }
1025
    // ajax post start call
1026
    var payload = {
1027
        "console": {"type": "vnc"}
1028
    };
1029

    
1030
    var serverID = serverIDs.pop();
1031

    
1032
    var machine = get_machine(serverID);
1033
    var serverName = machine.name;
1034
    try {
1035
        var serverIP = machine.addresses.values[0].values[0].addr;
1036
    } catch(err) { var serverIP = 'undefined'; }
1037

    
1038
    $.ajax({
1039
        url: API_URL + '/servers/' + serverID + '/action',
1040
        type: "POST",
1041
        contentType: "application/json",
1042
        dataType: "json",
1043
        data: JSON.stringify(payload),
1044
        timeout: TIMEOUT,
1045
        error: function(jqXHR, textStatus, errorThrown) {
1046
                    try {
1047
                        display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1048
                    } catch(err) {
1049
                        display_failure(0, serverID, 'Console', jqXHR.responseText);
1050
                    }
1051
                },
1052
        success: function(data, textStatus, jqXHR) {
1053
                    if ( jqXHR.status == '200') {
1054
                        try {
1055
                            console.info('got_console ' + serverID);
1056
                        } catch(err) {}
1057
                        // indicate that the action succeeded
1058
                        // show_vnc_console(serverID, serverName, serverIP,
1059
                        // data.console.host,data.console.port,data.console.password);
1060
                        show_vnc_console(serverID, serverName, serverIP,
1061
                                         data.console.host,data.console.port,data.console.password);
1062
                        display_success(serverID);
1063
                        // hide spinner
1064
                        $('#' + serverID + ' .spinner').hide();
1065
                        // continue with the rest of the servers
1066
                        open_console(serverIDs);
1067
                    } else {
1068
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1069
                    }
1070
                }
1071
    });
1072
    return false;
1073
}
1074

    
1075
// connect to machine action
1076
function machine_connect(serverIDs){
1077
    if (!serverIDs.length){
1078
        //ajax_success('DEFAULT');
1079
        return false;
1080
    }
1081
    var serverID = serverIDs.pop();
1082

    
1083
    var machine = get_machine(serverID);
1084
    var serverName = machine.name;
1085
    try {
1086
        var serverIP = machine.addresses.values[0].values[0].addr;
1087
    } catch(err) { var serverIP = 'undefined'; }
1088

    
1089
    try {
1090
        var os = machine.metadata.values.OS;
1091
    } catch(err) { var os = 'undefined'; }
1092

    
1093
    var params_url = '?ip_address=' + serverIP + '&os=' + os;
1094

    
1095
    window.open('machines/connect' + params_url);
1096

    
1097
    // Restore os icon in list view
1098
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1099
    osIcon.attr('src',osIcon.attr('os'));
1100

    
1101
    return false;
1102
}
1103

    
1104

    
1105
// rename server
1106
function rename(serverID, serverName){
1107
    if (!serverID.length){
1108
        //ajax_success('DEFAULT');
1109
        return false;
1110
    }
1111
    // ajax post rename call
1112
    var payload = {
1113
        "server": {"name": serverName}
1114
    };
1115

    
1116
    $.ajax({
1117
        url: API_URL + '/servers/' + serverID,
1118
        type: "PUT",
1119
        contentType: "application/json",
1120
        dataType: "json",
1121
        data: JSON.stringify(payload),
1122
        timeout: TIMEOUT,
1123
        error: function(jqXHR, textStatus, errorThrown) {
1124
                    try {
1125
                        display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1126
                    } catch(err) {
1127
                        display_failure(0, serverID, 'Rename', jqXHR.responseText);
1128
                    }
1129
                },
1130
        success: function(data, textStatus, jqXHR) {
1131
                    if ( jqXHR.status == '204' || jqXHR.status == '1223') {
1132
                        try {
1133
                            console.info('renamed ' + serverID);
1134
                        } catch(err) {}
1135
                        // indicate that the action succeeded
1136
                        display_success(serverID);
1137
                    } else {
1138
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1139
                    }
1140
                }
1141
    });
1142
    return false;
1143
}
1144

    
1145
// get server metadata
1146
function get_metadata(serverID, keys_only) {
1147
    $.ajax({
1148
        url: API_URL + '/servers/' + serverID + '/meta',
1149
        cache: false,
1150
        type: "GET",
1151
        //async: false,
1152
        dataType: "json",
1153
        timeout: TIMEOUT,
1154
        error: function(jqXHR, textStatus, errorThrown) {
1155
            try {
1156
                // close wizard and show error box
1157
                $("a#metadata-scrollable").data('overlay').close();
1158
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
1159
            } catch (err) {
1160
                ajax_error(err);
1161
            }
1162
        },
1163
        success: function(data, textStatus, jqXHR) {
1164
            // to list the new results in the edit dialog
1165
            if (keys_only) {
1166
                list_metadata_keys(serverID, data.metadata.values);
1167
            } else {
1168
                list_metadata(data);
1169
                list_metadata_keys(serverID, data.metadata.values);
1170
            }
1171
            //hide spinner
1172
            $('#metadata-wizard .large-spinner').hide();
1173
        }
1174
    });
1175
    return false;
1176
}
1177

    
1178
// delete metadata key-value pair
1179
function delete_metadata(serverID, meta_key) {
1180
    $.ajax({
1181
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1182
        type: "DELETE",
1183
        //async: false,
1184
        dataType: "json",
1185
        timeout: TIMEOUT,
1186
        error: function(jqXHR, textStatus, errorThrown) {
1187
            try {
1188
                // close wizard and show error box
1189
                $("a#metadata-scrollable").data('overlay').close();
1190
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1191
            } catch (err) {
1192
                ajax_error(err);
1193
            }
1194
        },
1195
        success: function(data, textStatus, jqXHR) {
1196
                    // success: Do nothing, the UI is already updated
1197
        }
1198
    });
1199
    return false;
1200
}
1201

    
1202
// add metadata key-value pair
1203
function update_metadata(serverID, meta_key, meta_value) {
1204
    var payload = {
1205
        "meta": {
1206
        }
1207
    };
1208
    payload["meta"][meta_key] = meta_value;
1209

    
1210
    $.ajax({
1211
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1212
        type: "PUT",
1213
        contentType: "application/json",
1214
        dataType: "json",
1215
        data: JSON.stringify(payload),
1216
        timeout: TIMEOUT,
1217
        error: function(jqXHR, textStatus, errorThrown) {
1218
            try {
1219
                // close wizard and show error box
1220
                $("a#metadata-scrollable").data('overlay').close();
1221
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1222
            } catch (err) {
1223
                ajax_error(err);
1224
            }
1225
        },
1226
        success: function(data, textStatus, jqXHR) {
1227
            // success: Update icons if meta key is OS
1228
            if (meta_key == "OS") {
1229
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1230
                var machine_icon = $("#machinesview-icon").find("div#" + serverID);
1231
                var machine_single = $("#machinesview-single").find("div#" + serverID);
1232

    
1233
                var os = os_icon_from_value(meta_value);
1234
                var state = $("#metadata-wizard div#on-off").text()
1235
                var state_single = $(".state", machine_single).hasClass("terminated-state") ? "off" : "on";
1236
                set_machine_os_image(machine_icon, "icon", state, os);
1237
                set_machine_os_image(machine_single, "single", state_single, os);
1238
            }
1239
        }
1240
    });
1241
    return false;
1242
}
1243

    
1244
// get stats
1245
function get_server_stats(serverID) {
1246
    $.ajax({
1247
        url: API_URL + '/servers/' + serverID + '/stats',
1248
        cache: false,
1249
        type: "GET",
1250
        //async: false,
1251
        dataType: "json",
1252
        timeout: TIMEOUT,
1253
        error: function(jqXHR, textStatus, errorThrown) {
1254
                // report error as text inline
1255
                $('#' + serverID + ' img.busy').hide();
1256
                $('#' + serverID + ' div.stat-error').show();
1257
        },
1258
        success: function(data, textStatus, jqXHR) {
1259
            // in icon view
1260
            if ( $.cookie('view') == 0 ) {
1261
                $('#' + serverID + ' img.busy').removeClass('busy');
1262
                $('#' + serverID + ' img.cpu').attr("src", data.stats.cpuBar);
1263
                $('#' + serverID + ' img.net').attr("src", data.stats.netBar);
1264
            }
1265
            // in single view
1266
            else if ( $.cookie('view') == 2 ) {
1267
                $('#' + serverID + ' div.cpu-graph img.stats').attr("src", data.stats.cpuTimeSeries);
1268
                $('#' + serverID + ' div.network-graph img.stats').attr("src", data.stats.netTimeSeries);
1269
            }
1270
        }
1271
    });
1272
    return false;
1273
}
1274

    
1275
// create network
1276
function create_network(networkName){
1277
    // ajax post start call
1278
    var payload = {
1279
        "network": { "name": networkName }
1280
    };
1281

    
1282
    $.ajax({
1283
        url: API_URL + '/networks',
1284
        type: "POST",
1285
        contentType: "application/json",
1286
        dataType: "json",
1287
        data: JSON.stringify(payload),
1288
        timeout: TIMEOUT,
1289
        error: function(jqXHR, textStatus, errorThrown) {
1290
            try {
1291
                // close wizard and show error box
1292
                $("a#networkscreate").overlay().close();
1293
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1294
            } catch (err) {
1295
                ajax_error(err);
1296
            }
1297
        },
1298
        success: function(data, textStatus, jqXHR) {
1299
            if ( jqXHR.status == '202') {
1300
                try {
1301
                    console.info('created network ' + networkName);
1302
                } catch(err) {}
1303
                /*
1304
                On success of this call nothing happens.
1305
                When the UI gets the first update containing the created server,
1306
                the creation wizard is closed and the new network is inserted
1307
                to the DOM. This is done in update_networks_view()
1308
                */
1309
            } else {
1310
                // close wizard and show error box
1311
                $("a#networkscreate").overlay().close();
1312
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1313
            }
1314
        }
1315
    });
1316
    return false;
1317
}
1318

    
1319
// rename network
1320
function rename_network(networkID, networkName){
1321
    if (!networkID.length){
1322
        //ajax_success('DEFAULT');
1323
        return false;
1324
    }
1325
    // prepare payload
1326
    var payload = {
1327
        "network": {"name": networkName}
1328
    };
1329
    // ajax call
1330
    $.ajax({
1331
        url: API_URL + '/networks/' + networkID,
1332
        type: "PUT",
1333
        contentType: "application/json",
1334
        dataType: "json",
1335
        data: JSON.stringify(payload),
1336
        timeout: TIMEOUT,
1337
        error: function(jqXHR, textStatus, errorThrown) {
1338
            try {
1339
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1340
            } catch (err) {
1341
                ajax_error(0, undefined, 'Rename network', jqXHR.responseText);
1342
            }
1343
        },
1344
        success: function(data, textStatus, jqXHR) {
1345
            if ( jqXHR.status == '204') {
1346
                try {
1347
                    console.info('renamed network' + networkID);
1348
                } catch(err) {}
1349
            } else {
1350
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1351
            }
1352
        }
1353
    });
1354
    return false;
1355
}
1356

    
1357
function delete_network(networkIDs){
1358
    if (!networkIDs.length){
1359
        //ajax_success('DEFAULT');
1360
        return false;
1361
    }
1362
    // get a network
1363
    var networkID = networkIDs.pop();
1364
    // ajax post destroy call can have an empty request body
1365
    var payload = {};
1366
    // ajax call
1367
    $.ajax({
1368
        url: API_URL + '/networks/' + networkID,
1369
        type: "DELETE",
1370
        contentType: "application/json",
1371
        dataType: "json",
1372
        data: JSON.stringify(payload),
1373
        timeout: TIMEOUT,
1374
        error: function(jqXHR, textStatus, errorThrown) {
1375
            try {
1376
                display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1377
            } catch (err) {
1378
                display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1379
            }
1380
        },
1381
        success: function(data, textStatus, jqXHR) {
1382
            if ( jqXHR.status == '204') {
1383
                try {
1384
                    console.info('deleted network ' + networkID);
1385
                } catch(err) {}
1386
                // continue with the rest of the servers
1387
                delete_network(networkIDs);
1388
            } else {
1389
                try {
1390
                    display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1391
                } catch (err) {
1392
                    display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1393
                }
1394
            }
1395
        }
1396
    });
1397
    return false;
1398
}
1399

    
1400
function add_server_to_network(networkID, serverIDs, serverNames, serverStates) {
1401
    if (!serverIDs.length){
1402
        // close the overlay when all the calls are made
1403
        $("a#add-machines-overlay").overlay().close();
1404
        return false;
1405
    }
1406
    // get a server
1407
    var serverID = serverIDs.pop();
1408
    var serverName = serverNames.pop();
1409
    var serverState = serverStates.pop();
1410
    // prepare payload
1411
    var payload = {
1412
            "add": { "serverRef": serverID }
1413
        };
1414
    // prepare ajax call
1415
    $.ajax({
1416
        url: API_URL + '/networks/' + networkID + '/action',
1417
        type: "POST",
1418
        contentType: "application/json",
1419
        dataType: "json",
1420
        data: JSON.stringify(payload),
1421
        timeout: TIMEOUT,
1422
        error: function(jqXHR, textStatus, errorThrown) {
1423
            try {
1424
                // close wizard and show error box
1425
                $("a#add-machines-overlay").data('overlay').close();
1426
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1427
            } catch (err) {
1428
                ajax_error(0, undefined, 'Add server to network', jqXHR.responseText);
1429
            }
1430
        },
1431
        success: function(data, textStatus, jqXHR) {
1432
            if ( jqXHR.status == '202') {
1433
                try {
1434
                    console.info('added server ' + serverID + ' to network ' + networkID);
1435
                } catch(err) {}
1436
                // toggle the reboot dialog
1437
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1438
                // continue with the rest of the servers
1439
                add_server_to_network(networkID, serverIDs, serverNames, serverStates);
1440
            } else {
1441
                // close wizard and show error box
1442
                $("a#add-machines-overlay").data('overlay').close();
1443
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1444
            }
1445
        }
1446
    });
1447
    return false;
1448
}
1449

    
1450
function remove_server_from_network(networkIDs, serverIDs, serverNames, serverStates) {
1451
    if (!networkIDs.length){
1452
        //ajax_success('DEFAULT');
1453
        return false;
1454
    }
1455
    // get a network and a server
1456
    var networkID = networkIDs.pop();
1457
    var serverID = serverIDs.pop();
1458
    var serverName = serverNames.pop();
1459
    var serverState = serverStates.pop();
1460
    // prepare payload
1461
    var payload = {
1462
            "remove": { "serverRef": serverID }
1463
        };
1464
    // prepare ajax call
1465
    $.ajax({
1466
        url: API_URL + '/networks/' + networkID + '/action',
1467
        type: "POST",
1468
        contentType: "application/json",
1469
        dataType: "json",
1470
        data: JSON.stringify(payload),
1471
        timeout: TIMEOUT,
1472
        error: function(jqXHR, textStatus, errorThrown) {
1473
            try {
1474
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1475
            } catch (err) {
1476
                ajax_error(0, undefined, 'Remove server form network', jqXHR.responseText);
1477
            }
1478
        },
1479
        success: function(data, textStatus, jqXHR) {
1480
            if ( jqXHR.status == '202') {
1481
                try {
1482
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1483
                } catch(err) {}
1484
                // toggle the reboot dialog
1485
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1486
                // continue with the rest of the servers
1487
                remove_server_form_network(networkIDs, serverIDs, serverNames, serverStates);
1488
            } else {
1489
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1490
            }
1491
        }
1492
    });
1493
    return false;
1494
}
1495

    
1496
function set_firewall(networkID, serverID, profile) {
1497
    if (!networkID.length || !serverID.length || !profile.length){
1498
        return false;
1499
    }
1500
    // prepare payload
1501
    var payload = {
1502
            "firewallProfile": { "profile": profile }
1503
        };
1504
    // prepare ajax call
1505
    $.ajax({
1506
        url: API_URL + '/servers/' + serverID + '/action',
1507
        type: "POST",
1508
        contentType: "application/json",
1509
        dataType: "json",
1510
        data: JSON.stringify(payload),
1511
        timeout: TIMEOUT,
1512
        error: function(jqXHR, textStatus, errorThrown) {
1513
            try {
1514
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1515
            } catch (err) {
1516
                ajax_error(0, undefined, 'Set firewall profile', jqXHR.responseText);
1517
            }
1518
        },
1519
        success: function(data, textStatus, jqXHR) {
1520
            if ( jqXHR.status == '202') {
1521
                try {
1522
                    console.info('for server ' + serverID + ' set firewall profile to ' + profile);
1523
                } catch(err) {}
1524
                //remove progress gif and toggle the content
1525
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').html(VARIOUS["APPLY"]);
1526
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').attr("disabled", false);
1527
                $('div#net-' + networkID + '-server-' + serverID + ' div.firewall-header').click();
1528
                // toggle the reboot dialog
1529
                var serverName = $('div#net-' + networkID + '-server-' + serverID + ' div.machine-name-div span.name').text();
1530
                var serverState = $('div#net-' + networkID + '-server-' + serverID + ' img.logo').attr('src').split('-')[1];
1531
                serverState = serverState.split('.')[0];
1532
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1533
            } else {
1534
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1535
            }
1536
        }
1537
    });
1538
    return false;
1539
}
1540

    
1541
// show the welcome screen
1542
function showWelcome() {
1543
    $("#view-select").fadeOut("fast");
1544
    $("#emptymachineslist").fadeIn("fast");
1545
    $("#machinesview").hide();
1546
}
1547

    
1548
// hide the welcome screen
1549
function hideWelcome() {
1550
    $("#emptymachineslist").fadeOut("fast");
1551
    $("#view-select").fadeIn("fast");
1552
    $("div#view-select").show();
1553
    $("#machinesview").show();
1554
}
1555

    
1556
function log_server_status_change(server_entry, new_status) {
1557
    // firebug console logging
1558
    try {
1559
        if ($("#machinesview-single").length > 0) {
1560
            console.info(server_entry.find("div.machine-details div.name").text() +
1561
                        ' from ' + server_entry.find(".state-label").text() +
1562
                        ' to ' + STATUSES[new_status]);
1563
        } else {
1564
            console.info(server_entry.find("div.name span.name").text() +
1565
                        ' from ' + server_entry.find(".status").text() +
1566
                        ' to ' + STATUSES[new_status]);
1567
        }
1568
    } catch(err) {}
1569
}
1570

    
1571
function get_flavor_params(flavorRef) {
1572
    var cpus, ram, disk;
1573
    if ( flavors.length > 0 ) {
1574
        var current_flavor = '';
1575
        for (i=0; i<flavors.length; i++) {
1576
            if (flavors[i]['id'] == flavorRef) {
1577
                current_flavor = flavors[i];
1578
            }
1579
        }
1580
        cpus = current_flavor['cpu'];
1581
        ram = current_flavor['ram'];
1582
        disk = current_flavor['disk'];
1583
    } else {
1584
        cpus = 'undefined';
1585
        ram = 'undefined';
1586
        disk = 'undefined';
1587
    }
1588
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1589
}
1590

    
1591
function get_image_params(imageRef) {
1592
    var image_name, image_size;
1593
    if ( images.length > 0 ) {
1594
        var current_image = '';
1595
        for (i=0; i<images.length; i++) {
1596
            if (images[i]['id'] == imageRef) {
1597
                current_image = images[i];
1598
            }
1599
        }
1600
        try {
1601
            image_name = current_image['name'];
1602
        } catch(err) { image_name = 'undefined'; }
1603
        try{
1604
            image_size = current_image['metadata']['values']['size'];
1605
        } catch(err) { image_size = 'undefined'; }
1606
    } else {
1607
        image_name = 'undefined';
1608
        image_size = 'undefined';
1609
    }
1610
    return {'name': image_name,'size': image_size};
1611
}
1612

    
1613
function get_public_ips(server) {
1614
    var ip4, ip6;
1615
    try {
1616
        if (server.addresses.values) {
1617
            $.each (server.addresses.values, function(i, value) {
1618
                if (value.id == 'public') {
1619
                    try {
1620
                        $.each (value.values, function(i, ip) {
1621
                            if (ip.version == '4') {
1622
                                ip4 = ip.addr;
1623
                            } else if (ip.version == '6') {
1624
                                ip6 = ip.addr;
1625
                            } else {
1626
                                ip4 = 'undefined';
1627
                                ip6 = 'undefined';
1628
                            }
1629
                        });
1630
                    } catch (err){
1631
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1632
                        ip4 = 'undefined';
1633
                        ip6 = 'undefined';
1634
                    }
1635
                }
1636
            });
1637
        }
1638
    } catch (err) {
1639
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1640
        ip4 = 'undefined';
1641
        ip6 = 'undefined';
1642
    }
1643
    return {'ip4': ip4, 'ip6': ip6};
1644
}
1645

    
1646
function get_private_ips(server) {
1647

    
1648
}
1649

    
1650
function close_all_overlays() {
1651
        try {
1652
                $("a#networkscreate").overlay().close();
1653
        } catch(err) {}
1654
        try {
1655
                $('a#create').overlay().close();
1656
        } catch(err) {}
1657
        try {
1658
                $("a#add-machines-overlay").overlay().close();
1659
        } catch(err) {}
1660
        try {
1661
                $("a#metadata-scrollable").overlay().close();
1662
        } catch(err) {}
1663
}
1664

    
1665
// logout
1666
function user_session_logout() {
1667
    $.cookie("X-Auth-Token", null);
1668
    if (window.LOGOUT_REDIRECT !== undefined)
1669
    {
1670
        window.location = window.LOGOUT_REDIRECT;
1671
    } else {
1672
        window.location.reload();
1673
    }
1674
}
1675

    
1676
// action indicators
1677
function init_action_indicator_handlers(machines_view)
1678
{
1679
    // init once for each view
1680
    if (window.ACTION_ICON_HANDLERS == undefined)
1681
    {
1682
        window.ACTION_ICON_HANDLERS = {};
1683
    }
1684

    
1685
    if (machines_view in window.ACTION_ICON_HANDLERS)
1686
    {
1687
        return;
1688
    }
1689
    window.ACTION_ICON_HANDLERS[machines_view] = 1;
1690

    
1691
    if (machines_view == "list")
1692
    {
1693
        // totally different logic for list view
1694
        init_action_indicator_list_handlers();
1695
        return;
1696
    }
1697

    
1698
    function update_action_icon_indicators(force)
1699
    {
1700
        function show(el, action) {
1701
            $(".action-indicator", $(el)).attr("class", "action-indicator " + action);
1702
            $(".action-indicator", $(el)).show();
1703
        }
1704

    
1705
        function hide(el) {
1706
            $(".action-indicator", $(el)).hide();
1707
        }
1708

    
1709
        function get_pending_actions(el) {
1710
            return $(".confirm_single:visible", $(el));
1711
        }
1712

    
1713
        function other_indicators(el) {
1714
           return $("img.wave:visible, img.spinner:visible", $(el))
1715
        }
1716

    
1717
        $("div.machine:visible, div.single-container").each(function(index, el){
1718
            var el = $(el);
1719
            var pending = get_pending_actions(el);
1720
            var other = other_indicators(el);
1721
            var action = undefined;
1722
            var force_action = force;
1723
            var visible = $(el).css("display") == "block";
1724

    
1725
            if (force_action !==undefined && force_action.el !== el[0]) {
1726
                // force action for other vm
1727
                // skipping force action
1728
                force_action = undefined;
1729
            }
1730

    
1731
            if (force_action !==undefined && force_action.el === el[0]) {
1732
                action = force_action.action;
1733
            }
1734

    
1735
            if (other.length >= 1) {
1736
                return;
1737
            }
1738

    
1739
            if (pending.length >= 1 && force_action === undefined) {
1740
                action = $(pending.parent()).attr("class").replace("action-container","");
1741
            }
1742

    
1743
            if (action in {'console':''}) {
1744
                return;
1745
            }
1746

    
1747
            if (action !== undefined) {
1748
                show(el, action);
1749
            } else {
1750
                try {
1751
                    if (el.attr('id') == pending_actions[0][1])
1752
                    {
1753
                        return;
1754
                    }
1755
                } catch (err) {
1756
                }
1757
                hide(el);
1758
            }
1759

    
1760
        });
1761
    }
1762

    
1763
    // action indicators
1764
    $(".action-container").live('mouseover', function(evn) {
1765
        force_action = {'el': $(evn.currentTarget).parent().parent()[0], 'action':$(evn.currentTarget).attr("class").replace("action-container","")};
1766
        // single view case
1767
        if ($(force_action.el).attr("class") == "upper")
1768
        {
1769
            force_action.el = $(evn.currentTarget).parent().parent().parent()[0]
1770
        };
1771
        update_action_icon_indicators(force_action);
1772
    });
1773

    
1774
    $("img.spinner, img.wave").live('hide', function(){
1775
        update_action_icon_indicators();
1776
    });
1777
    // register events where icons should get updated
1778

    
1779
    // hide action indicator image on mouse out, spinner appear, wave appear
1780
    $(".action-container").live("mouseout", function(evn){
1781
        update_action_icon_indicators();
1782
    });
1783

    
1784
    $(".confirm_single").live("click", function(evn){
1785
        update_action_icon_indicators();
1786
    });
1787

    
1788
    $("img.spinner, img.wave").live('show', function(){
1789
        $("div.action-indicator").hide();
1790
    });
1791

    
1792
    $(".confirm_single button.no").live('click', function(evn){
1793
        $("div.action-indicator", $(evn.currentTarget).parent().parent()).hide();
1794
    });
1795

    
1796
    $(".confirm_multiple button.no").click(function(){
1797
        $("div.action-indicator").hide();
1798
    });
1799

    
1800
    $(".confirm_multiple button.yes").click(function(){
1801
        $("div.action-indicator").hide();
1802
    });
1803
}
1804

    
1805
function init_action_indicator_list_handlers()
1806
{
1807
    var skip_actions = { 'console':'','connect':'','details':'' };
1808

    
1809
    var has_pending_confirmation = function()
1810
    {
1811
        return $(".confirm_multiple:visible").length >= 1
1812
    }
1813

    
1814
    function update_action_indicator_icons(force_action, skip_pending)
1815
    {
1816
        // pending action based on the element class
1817
        var pending_action = $(".selected", $(".actions"))[0];
1818
        var selected = get_list_view_selected_machine_rows();
1819

    
1820
        // reset previous state
1821
        list_view_hide_action_indicators();
1822

    
1823
        if (pending_action == undefined && !force_action)
1824
        {
1825
            // no action selected
1826
            return;
1827
        }
1828

    
1829
        if (force_action != undefined)
1830
        {
1831
            // user forced action choice
1832
            var action_class = force_action;
1833
        } else {
1834
            // retrieve action name (reboot, stop, etc..)
1835
            var action_class = $(pending_action).attr("id").replace("action-","");
1836
        }
1837

    
1838
        selected.each(function(index, el) {
1839
            if (has_pending_confirmation() && skip_pending)
1840
            {
1841
                return;
1842
            }
1843
            var el = $(el);
1844
            var logo = $("img.list-logo", el);
1845
            $(".action-indicator", el).remove();
1846
            var cls = "action-indicator " + action_class;
1847
            // add icon div
1848
            logo.after('<div class="' + cls + '"></div>');
1849
            // hide os logo
1850
            $("img.list-logo", el).hide();
1851
        });
1852
    }
1853

    
1854
    // on mouseover we force the images to the hovered action
1855
    $(".actions a").live("mouseover", function(evn) {
1856
        var el = $(evn.currentTarget);
1857
        if (!el.hasClass("enabled"))
1858
        {
1859
            return;
1860
        }
1861
        var action_class = el.attr("id").replace("action-","");
1862
        if (action_class in skip_actions)
1863
        {
1864
            return;
1865
        }
1866
        update_action_indicator_icons(action_class, false);
1867
    });
1868

    
1869

    
1870
    // register events where icons should get updated
1871
    $(".actions a.enabled").live("click", function(evn) {
1872
        // clear previous selections
1873
        $("a.selected").removeClass("selected");
1874

    
1875
        var el = $(evn.currentTarget);
1876
        el.addClass("selected");
1877
        update_action_indicator_icons(undefined, false);
1878
    });
1879

    
1880
    $(".actions a").live("mouseout", function(evn) {
1881
        update_action_indicator_icons(undefined, false);
1882
    });
1883

    
1884
    $(".confirm_multiple button.no").click(function(){
1885
        list_view_hide_action_indicators();
1886
    });
1887

    
1888
    $(".confirm_multiple button.yes").click(function(){
1889
        list_view_hide_action_indicators();
1890
    });
1891

    
1892
    $("input[type=checkbox]").live('change', function(){
1893
        // pending_actions will become empty on every checkbox click/change
1894
        // line 154 machines_list.html
1895
        pending_actions = [];
1896
        if (pending_actions.length == 0)
1897
        {
1898
            $(".confirm_multiple").hide();
1899
            $("a.selected").each(function(index, el){$(el).removeClass("selected")});
1900
        }
1901
        update_action_indicator_icons(undefined, false);
1902
    });
1903

    
1904
}
1905

    
1906
function list_view_hide_action_indicators()
1907
{
1908
    $("tr td .action-indicator").remove();
1909
    $("tr td img.list-logo").show();
1910
}
1911

    
1912
function get_list_view_selected_machine_rows()
1913
{
1914
    var table = $("table.list-machines");
1915
    var rows = $("tr:has(input[type=checkbox]:checked)",table);
1916
    return rows;
1917
}
1918

    
1919
// machines images utils
1920
function set_machine_os_image(machine, machines_view, state, os, skip_reset_states, remove_state) {
1921
    var views_map = {'single': '.single-image', 'icon': '.logo'};
1922
    var states_map = {'on': 'state1', 'off': 'state3', 'hover': 'state4', 'click': 'state2'}
1923
    var sizes_map = {'single': 'large', 'icon': 'medium'}
1924

    
1925
    var size = sizes_map[machines_view];
1926
    var img_selector = views_map[machines_view];
1927
    var cls = states_map[state];
1928

    
1929
    if (os === "unknown") { os = "okeanos" } ;
1930
    var new_img = 'url("./static/icons/machines/' + size + '/' + os + '-sprite.png")';
1931

    
1932
    var el = $(img_selector, machine);
1933
    var current_img = el.css("backgroundImage");
1934
    if (os == undefined){
1935
        new_img = current_img;
1936
    }
1937

    
1938
    // os changed
1939
    el.css("backgroundImage", new_img);
1940

    
1941
    // reset current state
1942
    if (skip_reset_states === undefined)
1943
    {
1944
        el.removeClass("single-image-state1");
1945
        el.removeClass("single-image-state2");
1946
        el.removeClass("single-image-state3");
1947
        el.removeClass("single-image-state4");
1948
    }
1949

    
1950
    if (remove_state !== undefined)
1951
    {
1952
        remove_state = "single-image-" + states_map[remove_state];
1953
        el.removeClass(remove_state);
1954
        return;
1955
    }
1956
    
1957
    // set proper state
1958
    el.addClass("single-image-" + cls);
1959
}
1960