Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ a583aa29

History | View | Annotate | Download (61.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 hide event
55
var _oldshow = $.fn.show;
56
$.fn.show = function(speed, callback) {
57
    $(this).trigger('show');
58
    return _oldshow.apply(this,arguments);
59
}
60

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

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

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

    
100
    for (; from < len; from++) {
101
      if (from in this &&
102
          this[from] === elt)
103
        return from;
104
    }
105
    return -1;
106
  };
107
}
108

    
109
// trim prototype for IE
110
if(typeof String.prototype.trim !== 'function') {
111
    String.prototype.trim = function() {
112
        return this.replace(/^\s+|\s+$/g, '');
113
    }
114
}
115

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

    
147
function update_network_confirmations(){
148
    // hide all confirm boxes to begin with
149
    $('#networks-pane div.confirm_multiple').hide();
150

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

    
160
    // if more than one pending action show multiple confirm box
161
    if (pending_actions.length > 1){
162
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
163
        $('#networks-pane div.confirm_multiple').show();
164
    }
165
}
166

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

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

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

    
254
function choose_view() {
255
    if ($.cookie("view")=='1') {
256
        list_view();
257
    } else if ($.cookie("view")=='2'){
258
        single_view();
259
    } else {
260
        standard_view();
261
    }
262
}
263

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

    
280
function os_icon_from_value(metadata) {
281
    if (!metadata) {
282
        return 'unknown';
283
    }
284
if (metadata == undefined || metadata == '') {
285
        return 'unknown';
286
    } else {
287
        if (os_icons.indexOf(metadata) == -1) {
288
            return 'unknown';
289
        } else {
290
            return metadata;
291
        }
292
    }
293
}
294

    
295
// get and show a list of running and terminated machines
296
function update_vms(interval) {
297
    try{ console.info('updating machines'); } catch(err){}
298
    var uri= API_URL + '/servers/detail';
299

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

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

    
331
            if (interval) {
332
                clearTimeout(deferred);    // clear old deferred calls
333
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
334
            }
335

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

    
357
// get a list of running and terminated machines, used in network view
358
function update_networks(interval) {
359
    try{ console.info('updating networks'); } catch(err){}
360
    var uri= API_URL + '/servers/detail';
361

    
362
    if (changes_since != 0)
363
        uri+='?changes-since='+changes_since
364

    
365
    update_request = $.ajax({
366
        cache: false,
367
        url: uri,
368
        type: "GET",
369
        timeout: TIMEOUT,
370
        dataType: "json",
371
        error: function(jqXHR, textStatus, errorThrown) {
372
            // don't forget to try again later
373
            if (interval) {
374
                clearTimeout(deferred);    // clear old deferred calls
375
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
376
            }
377
            // as for now, just show an error message
378
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
379
            try {
380
                ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
381
            } catch(err) {
382
                ajax_error(0, undefined, 'Update networks', jqXHR.responseText);
383
            }
384
            return false;
385
            },
386
        success: function(data, textStatus, jqXHR) {
387
            // create changes_since string if necessary
388
            if (jqXHR.getResponseHeader('Date') != null){
389
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
390
                changes_since = ISODateString(changes_since_date);
391
            }
392

    
393
            if (interval) {
394
                clearTimeout(deferred);    // clear old deferred calls
395
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
396
            }
397

    
398
            if (jqXHR.status == 200 || jqXHR.status == 203) {
399
                try {
400
                    servers = data.servers.values;
401
                    jQuery.parseJSON(data);
402
                    update_network_names(data);
403
                } catch(err) { ajax_error('400', undefined, 'Update networks', jqXHR.responseText);}
404
            } else if (jqXHR.status == 304) {
405
                update_network_names();
406
            }
407
            else {
408
                try { console.info('update_networks 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 networks', jqXHR.responseText);
415
                update_network_names();
416
            }
417
            return false;
418
        }
419
    });
420
    return false;
421
}
422

    
423
// get and show a list of public and private networks
424
function update_network_names(servers_data) {
425
    try{ console.info('updating network names'); } catch(err){}
426
    var uri= API_URL + '/networks/detail';
427

    
428
    if (networks_changes_since != 0)
429
        //FIXME: Comment out the following, until metadata do not 304 when changed
430
        uri+='?changes-since=' + networks_changes_since
431

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

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

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

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

    
542
function update_wizard_flavors(){
543
    // sliders for selecting VM flavor
544
    $("#cpu:range").rangeinput({min:0,
545
                               value:0,
546
                               step:1,
547
                               progress: true,
548
                               max:cpus.length-1});
549

    
550
    $("#storage:range").rangeinput({min:0,
551
                               value:0,
552
                               step:1,
553
                               progress: true,
554
                               max:disks.length-1});
555

    
556
    $("#ram:range").rangeinput({min:0,
557
                               value:0,
558
                               step:1,
559
                               progress: true,
560
                               max:ram.length-1});
561
    $("#small").click();
562

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

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

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

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

    
650
// return flavorRef from cpu, disk, ram values
651
function identify_flavor(cpu, disk, ram){
652
    for (i=0;i<flavors.length;i++){
653
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
654
            return flavors[i]['id']
655
        }
656
    }
657
    return 0;
658
}
659

    
660
// return image entry from imageRef
661
function get_image(imageRef) {
662
    for (i=0;i<images.length;i++){
663
        if (images[i]['id'] == imageRef) {
664
            return images[i];
665
        }
666
    }
667
    return 0;
668
}
669

    
670
// return machine entry from serverID
671
function get_machine(serverID) {
672
    for (i=0;i<servers.length;i++){
673
        if (servers[i]['id'] == serverID) {
674
            return servers[i];
675
        }
676
    }
677
    return 0;
678
}
679

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

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

    
711
    // are there multiple machines selected?
712
    if (checked.length>1)
713
        states[0] = 'multiple';
714

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

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

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

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

    
786
// reboot action
787
function reboot(serverIDs){
788
    if (!serverIDs.length){
789
        //ajax_success('DEFAULT');
790
        return false;
791
    }
792
    // ajax post reboot call
793
    var payload = {
794
        "reboot": {"type" : "HARD"}
795
    };
796

    
797
    var serverID = serverIDs.pop();
798

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

    
848
// shutdown action
849
function shutdown(serverIDs) {
850
    if (!serverIDs.length){
851
        //ajax_success('DEFAULT');
852
        return false;
853
    }
854
    // ajax post shutdown call
855
    var payload = {
856
        "shutdown": {}
857
    };
858

    
859
    var serverID = serverIDs.pop();
860

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

    
892
// destroy action
893
function destroy(serverIDs) {
894
    if (!serverIDs.length){
895
        //ajax_success('DEFAULT');
896
        return false;
897
    }
898
    // ajax post destroy call can have an empty request body
899
    var payload = {};
900

    
901
    var serverID = serverIDs.pop();
902

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

    
934
// start action
935
function start(serverIDs){
936
    if (!serverIDs.length){
937
        //ajax_success('DEFAULT');
938
        return false;
939
    }
940
    // ajax post start call
941
    var payload = {
942
        "start": {}
943
    };
944

    
945
    var serverID = serverIDs.pop();
946

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

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

    
985
    vd.writeln("[connection]");
986
    vd.writeln("host=" + host);
987
    vd.writeln("port=" + port);
988
    vd.writeln("password=" + password);
989

    
990
    vd.close();
991
}
992

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

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

    
1008
    // Restore os icon in list view
1009
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1010
    osIcon.attr('src',osIcon.attr('os'));
1011
    return false;
1012
}
1013

    
1014
// console action
1015
function open_console(serverIDs){
1016
    if (!serverIDs.length){
1017
        //ajax_success('DEFAULT');
1018
        return false;
1019
    }
1020
    // ajax post start call
1021
    var payload = {
1022
        "console": {"type": "vnc"}
1023
    };
1024

    
1025
    var serverID = serverIDs.pop();
1026

    
1027
    var machine = get_machine(serverID);
1028
    var serverName = machine.name;
1029
    var serverIP = machine.addresses.values[0].values[0].addr;
1030

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

    
1068
// rename server
1069
function rename(serverID, serverName){
1070
    if (!serverID.length){
1071
        //ajax_success('DEFAULT');
1072
        return false;
1073
    }
1074
    // ajax post rename call
1075
    var payload = {
1076
        "server": {"name": serverName}
1077
    };
1078

    
1079
    $.ajax({
1080
        url: API_URL + '/servers/' + serverID,
1081
        type: "PUT",
1082
        contentType: "application/json",
1083
        dataType: "json",
1084
        data: JSON.stringify(payload),
1085
        timeout: TIMEOUT,
1086
        error: function(jqXHR, textStatus, errorThrown) {
1087
                    try {
1088
                        display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1089
                    } catch(err) {
1090
                        display_failure(0, serverID, 'Rename', jqXHR.responseText);
1091
                    }
1092
                },
1093
        success: function(data, textStatus, jqXHR) {
1094
                    if ( jqXHR.status == '204' || jqXHR.status == '1223') {
1095
                        try {
1096
                            console.info('renamed ' + serverID);
1097
                        } catch(err) {}
1098
                        // indicate that the action succeeded
1099
                        display_success(serverID);
1100
                    } else {
1101
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1102
                    }
1103
                }
1104
    });
1105
    return false;
1106
}
1107

    
1108
// get server metadata
1109
function get_metadata(serverID, keys_only) {
1110
    $.ajax({
1111
        url: API_URL + '/servers/' + serverID + '/meta',
1112
        cache: false,
1113
        type: "GET",
1114
        //async: false,
1115
        dataType: "json",
1116
        timeout: TIMEOUT,
1117
        error: function(jqXHR, textStatus, errorThrown) {
1118
            try {
1119
                // close wizard and show error box
1120
                $("a#metadata-scrollable").data('overlay').close();
1121
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
1122
            } catch (err) {
1123
                ajax_error(err);
1124
            }
1125
        },
1126
        success: function(data, textStatus, jqXHR) {
1127
            // to list the new results in the edit dialog
1128
            if (keys_only) {
1129
                list_metadata_keys(serverID, data.metadata.values);
1130
            } else {
1131
                list_metadata(data);
1132
                list_metadata_keys(serverID, data.metadata.values);
1133
            }
1134
            //hide spinner
1135
            $('#metadata-wizard .large-spinner').hide();
1136
        }
1137
    });
1138
    return false;
1139
}
1140

    
1141
// delete metadata key-value pair
1142
function delete_metadata(serverID, meta_key) {
1143
    $.ajax({
1144
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1145
        type: "DELETE",
1146
        //async: false,
1147
        dataType: "json",
1148
        timeout: TIMEOUT,
1149
        error: function(jqXHR, textStatus, errorThrown) {
1150
            try {
1151
                // close wizard and show error box
1152
                $("a#metadata-scrollable").data('overlay').close();
1153
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1154
            } catch (err) {
1155
                ajax_error(err);
1156
            }
1157
        },
1158
        success: function(data, textStatus, jqXHR) {
1159
                    // success: Do nothing, the UI is already updated
1160
        }
1161
    });
1162
    return false;
1163
}
1164

    
1165
// add metadata key-value pair
1166
function update_metadata(serverID, meta_key, meta_value) {
1167
    var payload = {
1168
        "meta": {
1169
        }
1170
    };
1171
    payload["meta"][meta_key] = meta_value;
1172

    
1173
    $.ajax({
1174
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1175
        type: "PUT",
1176
        contentType: "application/json",
1177
        dataType: "json",
1178
        data: JSON.stringify(payload),
1179
        timeout: TIMEOUT,
1180
        error: function(jqXHR, textStatus, errorThrown) {
1181
            try {
1182
                // close wizard and show error box
1183
                $("a#metadata-scrollable").data('overlay').close();
1184
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1185
            } catch (err) {
1186
                ajax_error(err);
1187
            }
1188
        },
1189
        success: function(data, textStatus, jqXHR) {
1190
            // success: Update icons if meta key is OS
1191
            if (meta_key == "OS") {
1192
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1193
                $("#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');
1194
            }
1195
        }
1196
    });
1197
    return false;
1198
}
1199

    
1200
// create network
1201
function create_network(networkName){
1202
    // ajax post start call
1203
    var payload = {
1204
        "network": { "name": networkName }
1205
    };
1206

    
1207
    $.ajax({
1208
        url: API_URL + '/networks',
1209
        type: "POST",
1210
        contentType: "application/json",
1211
        dataType: "json",
1212
        data: JSON.stringify(payload),
1213
        timeout: TIMEOUT,
1214
        error: function(jqXHR, textStatus, errorThrown) {
1215
            try {
1216
                // close wizard and show error box
1217
                $("a#networkscreate").overlay().close();
1218
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1219
            } catch (err) {
1220
                ajax_error(err);
1221
            }
1222
        },
1223
        success: function(data, textStatus, jqXHR) {
1224
            if ( jqXHR.status == '202') {
1225
                try {
1226
                    console.info('created network ' + networkName);
1227
                } catch(err) {}
1228
                /*
1229
                On success of this call nothing happens.
1230
                When the UI gets the first update containing the created server,
1231
                the creation wizard is closed and the new network is inserted
1232
                to the DOM. This is done in update_networks_view()
1233
                */
1234
            } else {
1235
                // close wizard and show error box
1236
                $("a#networkscreate").overlay().close();
1237
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1238
            }
1239
        }
1240
    });
1241
    return false;
1242
}
1243

    
1244
// rename network
1245
function rename_network(networkID, networkName){
1246
    if (!networkID.length){
1247
        //ajax_success('DEFAULT');
1248
        return false;
1249
    }
1250
    // prepare payload
1251
    var payload = {
1252
        "network": {"name": networkName}
1253
    };
1254
    // ajax call
1255
    $.ajax({
1256
        url: API_URL + '/networks/' + networkID,
1257
        type: "PUT",
1258
        contentType: "application/json",
1259
        dataType: "json",
1260
        data: JSON.stringify(payload),
1261
        timeout: TIMEOUT,
1262
        error: function(jqXHR, textStatus, errorThrown) {
1263
            try {
1264
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1265
            } catch (err) {
1266
                ajax_error(0, undefined, 'Rename network', jqXHR.responseText);
1267
            }
1268
        },
1269
        success: function(data, textStatus, jqXHR) {
1270
            if ( jqXHR.status == '204') {
1271
                try {
1272
                    console.info('renamed network' + networkID);
1273
                } catch(err) {}
1274
            } else {
1275
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1276
            }
1277
        }
1278
    });
1279
    return false;
1280
}
1281

    
1282
function delete_network(networkIDs){
1283
    if (!networkIDs.length){
1284
        //ajax_success('DEFAULT');
1285
        return false;
1286
    }
1287
    // get a network
1288
    var networkID = networkIDs.pop();
1289
    // ajax post destroy call can have an empty request body
1290
    var payload = {};
1291
    // ajax call
1292
    $.ajax({
1293
        url: API_URL + '/networks/' + networkID,
1294
        type: "DELETE",
1295
        contentType: "application/json",
1296
        dataType: "json",
1297
        data: JSON.stringify(payload),
1298
        timeout: TIMEOUT,
1299
        error: function(jqXHR, textStatus, errorThrown) {
1300
            try {
1301
                display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1302
            } catch (err) {
1303
                display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1304
            }
1305
        },
1306
        success: function(data, textStatus, jqXHR) {
1307
            if ( jqXHR.status == '204') {
1308
                try {
1309
                    console.info('deleted network ' + networkID);
1310
                } catch(err) {}
1311
                // continue with the rest of the servers
1312
                delete_network(networkIDs);
1313
            } else {
1314
                try {
1315
                    display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1316
                } catch (err) {
1317
                    display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1318
                }
1319
            }
1320
        }
1321
    });
1322
    return false;
1323
}
1324

    
1325
function add_server_to_network(networkID, serverIDs, serverNames, serverStates) {
1326
    if (!serverIDs.length){
1327
        // close the overlay when all the calls are made
1328
        $("a#add-machines-overlay").overlay().close();
1329
        return false;
1330
    }
1331
    // get a server
1332
    var serverID = serverIDs.pop();
1333
    var serverName = serverNames.pop();
1334
    var serverState = serverStates.pop();
1335
    // prepare payload
1336
    var payload = {
1337
            "add": { "serverRef": serverID }
1338
        };
1339
    // prepare ajax call
1340
    $.ajax({
1341
        url: API_URL + '/networks/' + networkID + '/action',
1342
        type: "POST",
1343
        contentType: "application/json",
1344
        dataType: "json",
1345
        data: JSON.stringify(payload),
1346
        timeout: TIMEOUT,
1347
        error: function(jqXHR, textStatus, errorThrown) {
1348
            try {
1349
                // close wizard and show error box
1350
                $("a#add-machines-overlay").data('overlay').close();
1351
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1352
            } catch (err) {
1353
                ajax_error(0, undefined, 'Add server to network', jqXHR.responseText);
1354
            }
1355
        },
1356
        success: function(data, textStatus, jqXHR) {
1357
            if ( jqXHR.status == '202') {
1358
                try {
1359
                    console.info('added server ' + serverID + ' to network ' + networkID);
1360
                } catch(err) {}
1361
                // toggle the reboot dialog
1362
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1363
                // continue with the rest of the servers
1364
                add_server_to_network(networkID, serverIDs, serverNames, serverStates);
1365
            } else {
1366
                // close wizard and show error box
1367
                $("a#add-machines-overlay").data('overlay').close();
1368
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1369
            }
1370
        }
1371
    });
1372
    return false;
1373
}
1374

    
1375
function remove_server_from_network(networkIDs, serverIDs, serverNames, serverStates) {
1376
    if (!networkIDs.length){
1377
        //ajax_success('DEFAULT');
1378
        return false;
1379
    }
1380
    // get a network and a server
1381
    var networkID = networkIDs.pop();
1382
    var serverID = serverIDs.pop();
1383
    var serverName = serverNames.pop();
1384
    var serverState = serverStates.pop();
1385
    // prepare payload
1386
    var payload = {
1387
            "remove": { "serverRef": serverID }
1388
        };
1389
    // prepare ajax call
1390
    $.ajax({
1391
        url: API_URL + '/networks/' + networkID + '/action',
1392
        type: "POST",
1393
        contentType: "application/json",
1394
        dataType: "json",
1395
        data: JSON.stringify(payload),
1396
        timeout: TIMEOUT,
1397
        error: function(jqXHR, textStatus, errorThrown) {
1398
            try {
1399
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1400
            } catch (err) {
1401
                ajax_error(0, undefined, 'Remove server form network', jqXHR.responseText);
1402
            }
1403
        },
1404
        success: function(data, textStatus, jqXHR) {
1405
            if ( jqXHR.status == '202') {
1406
                try {
1407
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1408
                } catch(err) {}
1409
                // toggle the reboot dialog
1410
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1411
                // continue with the rest of the servers
1412
                remove_server_form_network(networkIDs, serverIDs, serverNames, serverStates);
1413
            } else {
1414
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1415
            }
1416
        }
1417
    });
1418
    return false;
1419
}
1420

    
1421
function set_firewall(networkID, serverID, profile) {
1422
    if (!networkID.length || !serverID.length || !profile.length){
1423
        return false;
1424
    }
1425
    // prepare payload
1426
    var payload = {
1427
            "firewallProfile": { "profile": profile }
1428
        };
1429
    // prepare ajax call
1430
    $.ajax({
1431
        url: API_URL + '/servers/' + serverID + '/action',
1432
        type: "POST",
1433
        contentType: "application/json",
1434
        dataType: "json",
1435
        data: JSON.stringify(payload),
1436
        timeout: TIMEOUT,
1437
        error: function(jqXHR, textStatus, errorThrown) {
1438
            try {
1439
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1440
            } catch (err) {
1441
                ajax_error(0, undefined, 'Set firewall profile', jqXHR.responseText);
1442
            }
1443
        },
1444
        success: function(data, textStatus, jqXHR) {
1445
            if ( jqXHR.status == '202') {
1446
                try {
1447
                    console.info('for server ' + serverID + ' set firewall profile to ' + profile);
1448
                } catch(err) {}
1449
                //remove progress gif and toggle the content
1450
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').html(VARIOUS["APPLY"]);
1451
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').attr("disabled", false);
1452
                $('div#net-' + networkID + '-server-' + serverID + ' div.firewall-header').click();
1453
                // change on/off
1454
                $('div#net-' + networkID + '-server-' + serverID + ' .firewall-label span').removeClass();
1455
                if ( profile == 'DISABLED' ) {
1456
                    $('div#net-' + networkID + '-server-' + serverID + ' .firewall-label span').addClass('firewall-off');
1457
                    $('div#net-' + networkID + '-server-' + serverID + ' .firewall-label span').html(VARIOUS["OFF"]);
1458
                }
1459
                else {
1460
                    $('div#net-' + networkID + '-server-' + serverID + ' .firewall-label span').addClass('firewall-on');
1461
                    $('div#net-' + networkID + '-server-' + serverID + ' .firewall-label span').html(VARIOUS["ON"]);
1462
                }
1463
                // toggle the reboot dialog
1464
                var serverName = $('div#net-' + networkID + '-server-' + serverID + ' div.machine-name-div span.name').text();
1465
                var serverState = $('div#net-' + networkID + '-server-' + serverID + ' img.logo').attr('src').split('-')[1];
1466
                serverState = serverState.split('.')[0];
1467
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1468
            } else {
1469
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1470
            }
1471
        }
1472
    });
1473
    return false;
1474
}
1475

    
1476
// show the welcome screen
1477
function showWelcome() {
1478
    $("#view-select").fadeOut("fast");
1479
    $("#emptymachineslist").fadeIn("fast");
1480
    $("#machinesview").hide();
1481
}
1482

    
1483
// hide the welcome screen
1484
function hideWelcome() {
1485
    $("#emptymachineslist").fadeOut("fast");
1486
    $("#view-select").fadeIn("fast");
1487
    $("div#view-select").show();
1488
    $("#machinesview").show();
1489
}
1490

    
1491
function log_server_status_change(server_entry, new_status) {
1492
    // firebug console logging
1493
    try {
1494
        if ($("#machinesview-single").length > 0) {
1495
            console.info(server_entry.find("div.machine-details div.name").text() +
1496
                        ' from ' + server_entry.find(".state-label").text() +
1497
                        ' to ' + STATUSES[new_status]);
1498
        } else {
1499
            console.info(server_entry.find("div.name span.name").text() +
1500
                        ' from ' + server_entry.find(".status").text() +
1501
                        ' to ' + STATUSES[new_status]);
1502
        }
1503
    } catch(err) {}
1504
}
1505

    
1506
function get_flavor_params(flavorRef) {
1507
    var cpus, ram, disk;
1508
    if ( flavors.length > 0 ) {
1509
        var current_flavor = '';
1510
        for (i=0; i<flavors.length; i++) {
1511
            if (flavors[i]['id'] == flavorRef) {
1512
                current_flavor = flavors[i];
1513
            }
1514
        }
1515
        cpus = current_flavor['cpu'];
1516
        ram = current_flavor['ram'];
1517
        disk = current_flavor['disk'];
1518
    } else {
1519
        cpus = 'undefined';
1520
        ram = 'undefined';
1521
        disk = 'undefined';
1522
    }
1523
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1524
}
1525

    
1526
function get_image_params(imageRef) {
1527
    var image_name, image_size;
1528
    if ( images.length > 0 ) {
1529
        var current_image = '';
1530
        for (i=0; i<images.length; i++) {
1531
            if (images[i]['id'] == imageRef) {
1532
                current_image = images[i];
1533
            }
1534
        }
1535
        try {
1536
            image_name = current_image['name'];
1537
        } catch(err) { image_name = 'undefined'; }
1538
        try{
1539
            image_size = current_image['metadata']['values']['size'];
1540
        } catch(err) { image_size = 'undefined'; }
1541
    } else {
1542
        image_name = 'undefined';
1543
        image_size = 'undefined';
1544
    }
1545
    return {'name': image_name,'size': image_size};
1546
}
1547

    
1548
function get_public_ips(server) {
1549
    var ip4, ip6;
1550
    try {
1551
        if (server.addresses.values) {
1552
            $.each (server.addresses.values, function(i, value) {
1553
                if (value.id == 'public') {
1554
                    try {
1555
                        $.each (value.values, function(i, ip) {
1556
                            if (ip.version == '4') {
1557
                                ip4 = ip.addr;
1558
                            } else if (ip.version == '6') {
1559
                                ip6 = ip.addr;
1560
                            } else {
1561
                                ip4 = 'undefined';
1562
                                ip6 = 'undefined';
1563
                            }
1564
                        });
1565
                    } catch (err){
1566
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1567
                        ip4 = 'undefined';
1568
                        ip6 = 'undefined';
1569
                    }
1570
                }
1571
            });
1572
        }
1573
    } catch (err) {
1574
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1575
        ip4 = 'undefined';
1576
        ip6 = 'undefined';
1577
    }
1578
    return {'ip4': ip4, 'ip6': ip6};
1579
}
1580

    
1581
function get_private_ips(server) {
1582

    
1583
}
1584

    
1585
function close_all_overlays() {
1586
        try {
1587
                $("a#networkscreate").overlay().close();
1588
        } catch(err) {}
1589
        try {
1590
                $('a#create').overlay().close();
1591
        } catch(err) {}
1592
        try {
1593
                $("a#add-machines-overlay").overlay().close();
1594
        } catch(err) {}
1595
        try {
1596
                $("a#metadata-scrollable").overlay().close();
1597
        } catch(err) {}
1598
}
1599

    
1600
// action indicators
1601
function init_action_indicator_handlers(machines_view)
1602
{
1603
    if (machines_view == "list")
1604
    {   
1605
        // totally different logic for list view
1606
        init_action_indicator_list_handlers();
1607
        return;
1608
    }
1609
    
1610
    var has_active_indicators = function(el)
1611
    {
1612
        return ($("img.spinner:visible", el.parent().parent()).length >= 1) || ($("img.wave:visible", el.parent().parent()).length >= 1) 
1613
    }
1614

    
1615
    // action indicators
1616
    $(".action-container").live('mouseover', function(evn){
1617
        var el = $(evn.currentTarget);
1618
        // we dont need the single-action class
1619
        var action_class = el.attr("class").replace("action-container","");
1620
        // pass the hovered element action related class to the indicator image
1621
        $("div.action-indicator", el.parent().parent()).attr("class", "action-indicator " + action_class);
1622

    
1623
        // spinner || wave indicators already visible. dont show action image to avoid clutter
1624
        if (has_active_indicators(el))
1625
        {
1626
            return;
1627
        }
1628
        $("div.action-indicator", el.parent().parent()).show();
1629
    });
1630

    
1631
    // hide action indicator image on mouse out, spinner appear, wave appear
1632
    $(".action-container").live("mouseout", function(evn){
1633
        var el = $(evn.currentTarget);
1634
        $("div.action-indicator").hide();
1635
        
1636
        var pending_for_confirm_action = $(".confirm_single:visible", el.parent().parent());
1637
        // if we mouse out and another action is in confirmation mode
1638
        if (!has_active_indicators(el))
1639
        {
1640
            // no actions pending
1641
            if (pending_for_confirm_action.length == 0)
1642
            {
1643
                return;
1644
            }
1645

    
1646
            // find action pending and show icon
1647
            var action_container = $($(pending_for_confirm_action[0]).parent());
1648
            var action_class = action_container.attr("class").replace("action-container","");
1649
            $("div.action-indicator", action_container.parent().parent()).attr("class", "action-indicator " + action_class);
1650
            $("div.action-indicator").show();
1651
        }
1652
        
1653
    });
1654

    
1655
    $("img.spinner, img.wave").live('show', function(){
1656
        $("div.action-indicator").hide();
1657
    });
1658
}
1659

    
1660
function init_action_indicator_list_handlers()
1661
{   
1662
    var skip_actions = { 'console':'','connect':'','details':'' };
1663

    
1664
    var has_pending_confirmation = function()
1665
    {
1666
        return $(".confirm_multiple:visible").length >= 1
1667
    }
1668
    
1669
    function update_action_indicator_icons(force_action, skip_pending)
1670
    {   
1671
        // pending action based on the element class
1672
        var pending_action = $(".selected", $(".actions"))[0];
1673
        var selected = get_list_view_selected_machine_rows();
1674

    
1675
        // reset previous state
1676
        list_view_hide_action_indicators();
1677
        
1678
        if (pending_action == undefined && !force_action)
1679
        {
1680
            // no action selected
1681
            return;
1682
        }
1683
        
1684
        if (force_action != undefined)
1685
        {
1686
            // user forced action choice
1687
            var action_class = force_action;
1688
        } else {
1689
            // retrieve action name (reboot, stop, etc..)
1690
            var action_class = $(pending_action).attr("id").replace("action-","");
1691
        }
1692

    
1693
        selected.each(function(index, el) {
1694
            if (has_pending_confirmation() && skip_pending)
1695
            {
1696
                return;
1697
            }
1698
            var el = $(el);
1699
            var logo = $("img.list-logo", el);
1700
            $(".action-indicator", el).remove();
1701
            var cls = "action-indicator " + action_class;
1702
            // add icon div
1703
            logo.after('<div class="' + cls + '"></div>');
1704
            // hide os logo
1705
            $("img.list-logo", el).hide();
1706
        });
1707
    }  
1708
    
1709
    // on mouseover we force the images to the hovered action
1710
    $(".actions a").live("mouseover", function(evn) {
1711
        var el = $(evn.currentTarget);
1712
        if (!el.hasClass("enabled"))
1713
        {   
1714
            return;
1715
        }
1716
        var action_class = el.attr("id").replace("action-","");
1717
        if (action_class in skip_actions)
1718
        {
1719
            return;
1720
        }
1721
        update_action_indicator_icons(action_class, false);
1722
    });
1723
    
1724
    $(".actions a").live("click", function(evn) {
1725
        var el = $(evn.currentTarget);
1726
        el.addClass("selected");
1727
        update_action_indicator_icons(undefined, false);
1728
    });
1729

    
1730
    $(".actions a").live("mouseout", function(evn) {
1731
        update_action_indicator_icons(undefined, false);
1732
    });
1733
    
1734
    $(".confirm_multiple button.no").click(function(){
1735
        list_view_hide_action_indicators();
1736
    });
1737

    
1738
    $(".confirm_multiple button.yes").click(function(){
1739
        list_view_hide_action_indicators();
1740
    });
1741
    
1742
    $("input[type=checkbox]").live('change', function(){
1743
        // pending_actions will become empty on every checkbox click/change
1744
        // line 154 machines_list.html
1745
        pending_actions = [];
1746
        if (pending_actions.length == 0)
1747
        {
1748
            $(".confirm_multiple").hide();
1749
            $("a.selected").each(function(index, el){$(el).removeClass("selected")});        
1750
        }
1751
        update_action_indicator_icons(undefined, false);
1752
    });
1753
    
1754
}
1755

    
1756
function list_view_hide_action_indicators()
1757
{
1758
    $("tr td .action-indicator").remove();
1759
    $("tr td img.list-logo").show();
1760
}
1761

    
1762
function get_list_view_selected_machine_rows()
1763
{   
1764
    var table = $("table.list-machines");
1765
    var rows = $("tr:has(input[type=checkbox]:checked)",table);
1766
    return rows;
1767
}
1768