Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ dc3e66b9

History | View | Annotate | Download (46.9 kB)

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

    
40

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

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

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

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

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

    
101
function update_confirmations() {
102
    // hide all confirm boxes to begin with
103
    $('#machines-pane div.confirm_single').hide();
104
    $('#machines-pane div.confirm_multiple').hide();
105
    var action_type = [];
106
    // standard view only
107
    if ($.cookie("view") == '0') {
108
        for (var i=0;i<pending_actions.length;i++){
109
            // show single confirms
110
            if (pending_actions[i][0] == reboot) {
111
                action_type = 'reboot';
112
            } else if (pending_actions[i][0] == shutdown) {
113
                action_type = 'shutdown';
114
            } else if (pending_actions[i][0] == start) {
115
                action_type = 'start';
116
            } else if (pending_actions[i][0] == console) {
117
                action_type = 'console';
118
            } else {
119
                action_type = 'destroy';
120
            }
121
            $("#machines-pane div.machine-container#" + pending_actions[i][1] +
122
            " div.actions div." + action_type + " div.confirm_single").show();
123
        }
124
    } else if ($.cookie("view") == '2') {
125
        for (var i=0;i<pending_actions.length;i++){
126
            // show single confirms
127
            $("#machines-pane div.single-container#"+pending_actions[i][1]+' .confirm_single').show();
128
        }
129
    }
130

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

    
138
function update_network_confirmations(){
139
    // hide all confirm boxes to begin with
140
    $('#networks-pane div.confirm_multiple').hide();
141

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

    
151
    // if more than one pending action show multiple confirm box
152
    if (pending_actions.length > 1){
153
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
154
        $('#networks-pane div.confirm_multiple').show();
155
    }
156
}
157

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

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

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

    
245
function choose_view() {
246
    if ($.cookie("view")=='1') {
247
        list_view();
248
    } else if ($.cookie("view")=='2'){
249
        single_view();
250
    } else {
251
        standard_view();
252
    }
253
}
254

    
255
// get and show a list of running and terminated machines
256
function update_vms(interval) {
257
    try{ console.info('updating machines'); } catch(err){}
258
    var uri= API_URL + '/servers/detail';
259

    
260
    if (changes_since != 0)
261
        uri+='?changes-since='+changes_since
262

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

    
287
            if (interval) {
288
                clearTimeout(deferred);    // clear old deferred calls
289
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
290
            }
291

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

    
312
// get a list of running and terminated machines, used in network view
313
function update_networks(interval) {
314
    try{ console.info('updating networks'); } catch(err){}
315
    var uri= API_URL + '/servers/detail';
316

    
317
    if (changes_since != 0)
318
        uri+='?changes-since='+changes_since
319

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

    
344
            if (interval) {
345
                clearTimeout(deferred);    // clear old deferred calls
346
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
347
            }
348

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

    
373
// get and show a list of public and private networks
374
function update_network_names(servers_data) {
375
    try{ console.info('updating network names'); } catch(err){}
376
    var uri= API_URL + '/networks/detail';
377

    
378
    if (networks_changes_since != 0)
379
        //FIXME: Comment out the following, until metadata do not 304 when changed
380
        uri+='?changes-since=' + networks_changes_since
381

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

    
403
            if (jqXHR.status == 200 || jqXHR.status == 203) {
404
                try {
405
                    networks = data.networks.values;
406
                } catch(err) {
407
                    ajax_error('400', undefined, 'Update network names', jqXHR.responseText);
408
                }
409
                update_networks_view(servers_data, data);
410
            } else if (jqXHR.status == 304) {
411
                update_networks_view(servers_data);
412
            } else if (jqXHR.status != 304){
413
                try { console.info('update_network_names 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 network names', jqXHR.responseText);
420
                update_networks_view(servers_data);
421
            }
422
            return false;
423
        }
424
    });
425
    return false;
426
}
427

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

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

    
482
function update_wizard_flavors(){
483
    // sliders for selecting VM flavor
484
    $("#cpu:range").rangeinput({min:0,
485
                               value:0,
486
                               step:1,
487
                               progress: true,
488
                               max:cpus.length-1});
489

    
490
    $("#storage:range").rangeinput({min:0,
491
                               value:0,
492
                               step:1,
493
                               progress: true,
494
                               max:disks.length-1});
495

    
496
    $("#ram:range").rangeinput({min:0,
497
                               value:0,
498
                               step:1,
499
                               progress: true,
500
                               max:ram.length-1});
501
    $("#small").click();
502

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

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

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

    
584
// return flavorRef from cpu, disk, ram values
585
function identify_flavor(cpu, disk, ram){
586
    for (i=0;i<flavors.length;i++){
587
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
588
            return flavors[i]['id']
589
        }
590
    }
591
    return 0;
592
}
593

    
594
// return image entry from imageRef
595
function get_image(imageRef) {
596
    for (i=0;i<images.length;i++){
597
        if (images[i]['id'] == imageRef) {
598
            return images[i];
599
        }
600
    }
601
    return 0;
602
}
603

    
604
// return machine entry from serverID
605
function get_machine(serverID) {
606
    for (i=0;i<servers.length;i++){
607
        if (servers[i]['id'] == serverID) {
608
            return servers[i];
609
        }
610
    }
611
    return 0;
612
}
613

    
614
// update the actions in icon view, per server
615
function update_iconview_actions(serverID, server_status) {
616
    if ($.cookie("view")=='2') {
617
        // remove .disable from all actions to begin with
618
        $('#machinesview-single.single #' + serverID + ' div.single-actions').children().show();
619
        // decide which actions should be disabled
620
        for (current_action in actions) {
621
            if (actions[current_action].indexOf(server_status) == -1 ) {
622
                $('#machinesview-single.single #' + serverID + ' div.action-' + current_action).hide();
623
            }
624
        }
625
    } else {
626
        // remove .disable from all actions to begin with
627
        $('#machinesview-icon.standard #' + serverID + ' div.actions').children().removeClass('disabled');
628
        // decide which actions should be disabled
629
        for (current_action in actions) {
630
            if (actions[current_action].indexOf(server_status) == -1 ) {
631
                $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
632
            }
633
        }
634
    }
635
}
636

    
637
// update the actions in list view
638
function update_listview_actions() {
639
    var states = [];
640
    var on = [];
641
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
642
    // disable all actions to begin with
643
    $('#machinesview .list div.actions').children().removeClass('enabled');
644

    
645
    // are there multiple machines selected?
646
    if (checked.length>1)
647
        states[0] = 'multiple';
648

    
649
    // check the states of selected machines
650
    checked.each(function(i,checkbox) {
651
        states[states.length] = checkbox.className;
652
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
653
        if (ip.replace('undefined','').length)
654
            states[states.length] = 'network';
655
    });
656

    
657
    // decide which actions should be enabled
658
    for (a in actions) {
659
        var enabled = false;
660
        for (var s =0; s<states.length; s++) {
661
            if (actions[a].indexOf(states[s]) != -1 ) {
662
                enabled = true;
663
            } else {
664
                enabled = false;
665
                break;
666
            }
667
        }
668
        if (enabled)
669
            on[on.length]=a;
670
    }
671
    // enable those actions
672
    for (action in on) {
673
        $("#action-" + on[action]).addClass('enabled');
674
    }
675
}
676

    
677
//create server action
678
function create_vm(machineName, imageRef, flavorRef){
679
    var image_logo = os_icon(get_image(imageRef).metadata);
680
    var uri = API_URL + '/servers';
681
    var payload = {
682
        "server": {
683
            "name": machineName,
684
            "imageRef": imageRef,
685
            "flavorRef" : flavorRef,
686
            "metadata" : {
687
                "OS" : image_logo
688
            }
689
        }
690
    };
691

    
692
    $.ajax({
693
    url: uri,
694
    type: "POST",
695
    contentType: "application/json",
696
    dataType: "json",
697
    data: JSON.stringify(payload),
698
    timeout: TIMEOUT,
699
    error: function(jqXHR, textStatus, errorThrown) {
700
                ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
701
           },
702
    success: function(data, textStatus, jqXHR) {
703
                if ( jqXHR.status == '202') {
704
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
705
                } else {
706
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
707
                }
708
            }
709
    });
710
}
711

    
712
// reboot action
713
function reboot(serverIDs){
714
    if (!serverIDs.length){
715
        //ajax_success('DEFAULT');
716
        return false;
717
    }
718
    // ajax post reboot call
719
    var payload = {
720
        "reboot": {"type" : "HARD"}
721
    };
722

    
723
    var serverID = serverIDs.pop();
724

    
725
    $.ajax({
726
        url: API_URL + '/servers/' + serverID + '/action',
727
        type: "POST",
728
        contentType: "application/json",
729
        dataType: "json",
730
        data: JSON.stringify(payload),
731
        timeout: TIMEOUT,
732
        error: function(jqXHR, textStatus, errorThrown) {
733
                    display_failure(jqXHR.status, serverID, 'Reboot', jqXHR.responseText)
734
                },
735
        success: function(data, textStatus, jqXHR) {
736
                    if ( jqXHR.status == '202') {
737
                        try {
738
                            console.info('rebooted ' + serverID);
739
                        } catch(err) {}
740
                        // indicate that the action succeeded
741
                        display_success(serverID);
742
                        // continue with the rest of the servers
743
                        reboot(serverIDs);
744
                    } else {
745
                        ajax_error(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
746
                    }
747
                }
748
    });
749
    return false;
750
}
751

    
752
// shutdown action
753
function shutdown(serverIDs) {
754
    if (!serverIDs.length){
755
        //ajax_success('DEFAULT');
756
        return false;
757
    }
758
    // ajax post shutdown call
759
    var payload = {
760
        "shutdown": {}
761
    };
762

    
763
    var serverID = serverIDs.pop();
764

    
765
    $.ajax({
766
        url: API_URL + '/servers/' + serverID + '/action',
767
        type: "POST",
768
        contentType: "application/json",
769
        dataType: "json",
770
        data: JSON.stringify(payload),
771
        timeout: TIMEOUT,
772
        error: function(jqXHR, textStatus, errorThrown) {
773
                    display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText)
774
                    },
775
        success: function(data, textStatus, jqXHR) {
776
                    if ( jqXHR.status == '202') {
777
                        try {
778
                            console.info('suspended ' + serverID);
779
                        } catch(err) {}
780
                        // indicate that the action succeeded
781
                        display_success(serverID);
782
                        // continue with the rest of the servers
783
                        shutdown(serverIDs);
784
                    } else {
785
                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
786
                    }
787
                }
788
    });
789
    return false;
790
}
791

    
792
// destroy action
793
function destroy(serverIDs) {
794
    if (!serverIDs.length){
795
        //ajax_success('DEFAULT');
796
        return false;
797
    }
798
    // ajax post destroy call can have an empty request body
799
    var payload = {};
800

    
801
    var serverID = serverIDs.pop();
802

    
803
    $.ajax({
804
        url: API_URL + '/servers/' + serverID,
805
        type: "DELETE",
806
        contentType: "application/json",
807
        dataType: "json",
808
        data: JSON.stringify(payload),
809
        timeout: TIMEOUT,
810
        error: function(jqXHR, textStatus, errorThrown) {
811
                    display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText)
812
                    },
813
        success: function(data, textStatus, jqXHR) {
814
                    if ( jqXHR.status == '204') {
815
                        try {
816
                            console.info('destroyed ' + serverID);
817
                        } catch (err) {}
818
                        // indicate that the action succeeded
819
                        display_success(serverID);
820
                        // continue with the rest of the servers
821
                        destroy(serverIDs);
822
                    } else {
823
                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
824
                    }
825
                }
826
    });
827
    return false;
828
}
829

    
830
// start action
831
function start(serverIDs){
832
    if (!serverIDs.length){
833
        //ajax_success('DEFAULT');
834
        return false;
835
    }
836
    // ajax post start call
837
    var payload = {
838
        "start": {}
839
    };
840

    
841
    var serverID = serverIDs.pop();
842

    
843
    $.ajax({
844
        url: API_URL + '/servers/' + serverID + '/action',
845
        type: "POST",
846
        contentType: "application/json",
847
        dataType: "json",
848
        data: JSON.stringify(payload),
849
        timeout: TIMEOUT,
850
        error: function(jqXHR, textStatus, errorThrown) {
851
                    display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText)
852
                    },
853
        success: function(data, textStatus, jqXHR) {
854
                    if ( jqXHR.status == '202') {
855
                        try {
856
                            console.info('started ' + serverID);
857
                        } catch(err) {}
858
                        // indicate that the action succeeded
859
                        display_success(serverID);
860
                        // continue with the rest of the servers
861
                        start(serverIDs);
862
                    } else {
863
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
864
                    }
865
                }
866
    });
867
    return false;
868
}
869

    
870
// Show VNC console
871
function vnc_attachment(host, port, password) {
872
    // FIXME: Must be made into parameters, in settings.py
873
    //vnc = open("", "displayWindow",
874
    //    "status=yes,toolbar=yes,menubar=yes");
875
    vd = document.open("application/x-vnc");
876

    
877
    vd.writeln("[connection]");
878
    vd.writeln("host=" + host);
879
    vd.writeln("port=" + port);
880
    vd.writeln("password=" + password);
881

    
882
    vd.close();
883
}
884

    
885
// Show VNC console
886
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
887
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
888
    var params_window = 'scrollbars=no,' +
889
                        'menubar=no,' +
890
                        'toolbar=no,' +
891
                        'status=no,' +
892
                        'top=0,' +
893
                        'left=0,' +
894
                        'height=' + screen.height + ',' +
895
                        'width=' + screen.width + ',' +
896
                        'fullscreen=yes';
897

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

    
900
    // Restore os icon in list view
901
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
902
    osIcon.attr('src',osIcon.attr('os'));
903
    return false;
904
}
905

    
906
// console action
907
function open_console(serverIDs){
908
    if (!serverIDs.length){
909
        //ajax_success('DEFAULT');
910
        return false;
911
    }
912
    // ajax post start call
913
    var payload = {
914
        "console": {"type": "vnc"}
915
    };
916

    
917
    var serverID = serverIDs.pop();
918

    
919
    var machine = get_machine(serverID);
920
    var serverName = machine.name;
921
    var serverIP = machine.addresses.values[0].values[0].addr;
922

    
923
    $.ajax({
924
        url: API_URL + '/servers/' + serverID + '/action',
925
        type: "POST",
926
        contentType: "application/json",
927
        dataType: "json",
928
        data: JSON.stringify(payload),
929
        timeout: TIMEOUT,
930
        error: function(jqXHR, textStatus, errorThrown) {
931
                    display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText)
932
                    },
933
        success: function(data, textStatus, jqXHR) {
934
                    if ( jqXHR.status == '200') {
935
                        try {
936
                            console.info('got_console ' + serverID);
937
                        } catch(err) {}
938
                        // indicate that the action succeeded
939
                        //show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
940
show_vnc_console(serverID, serverName, serverIP, data.console.host,data.console.port,data.console.password);
941
                        display_success(serverID);
942
                        // hide spinner
943
                        $('#' + serverID + ' .spinner').hide();
944
                        // continue with the rest of the servers
945
                        open_console(serverIDs);
946
                    } else {
947
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
948
                    }
949
                }
950
    });
951
    return false;
952
}
953

    
954
// rename server
955
function rename(serverID, serverName){
956
    if (!serverID.length){
957
        //ajax_success('DEFAULT');
958
        return false;
959
    }
960
    // ajax post rename call
961
    var payload = {
962
        "server": {"name": serverName}
963
    };
964

    
965
    $.ajax({
966
        url: API_URL + '/servers/' + serverID,
967
        type: "PUT",
968
        contentType: "application/json",
969
        dataType: "json",
970
        data: JSON.stringify(payload),
971
        timeout: TIMEOUT,
972
        error: function(jqXHR, textStatus, errorThrown) {
973
                    display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText)
974
                    },
975
        success: function(data, textStatus, jqXHR) {
976
                    if ( jqXHR.status == '204') {
977
                        try {
978
                            console.info('renamed ' + serverID);
979
                        } catch(err) {}
980
                        // indicate that the action succeeded
981
                        display_success(serverID);
982
                    } else {
983
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
984
                    }
985
                }
986
    });
987
    return false;
988
}
989

    
990
// get server metadata
991
function get_metadata(serverID, keys_only) {
992
    $.ajax({
993
        url: API_URL + '/servers/' + serverID + '/meta',
994
        type: "GET",
995
        //async: false,
996
        dataType: "json",
997
        timeout: TIMEOUT,
998
        error: function(jqXHR, textStatus, errorThrown) {
999
            try {
1000
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
1001
            } catch (err) {
1002
                ajax_error(err);
1003
            }
1004
        },
1005
        success: function(data, textStatus, jqXHR) {
1006
            // to list the new results in the edit dialog
1007
            if (keys_only) {
1008
                list_metadata_keys(serverID, data.metadata.values);
1009
            } else {
1010
                list_metadata(data);
1011
                list_metadata_keys(serverID, data.metadata.values);
1012
            }
1013
            //hide spinner
1014
            $('#metadata-wizard .large-spinner').hide();
1015
        }
1016
    });
1017
    return false;
1018
}
1019

    
1020
// delete metadata key-value pair
1021
function delete_metadata(serverID, meta_key) {
1022
    $.ajax({
1023
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1024
        type: "DELETE",
1025
        //async: false,
1026
        dataType: "json",
1027
        timeout: TIMEOUT,
1028
        error: function(jqXHR, textStatus, errorThrown) {
1029
            try {
1030
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1031
            } catch (err) {
1032
                ajax_error(err);
1033
            }
1034
        },
1035
        success: function(data, textStatus, jqXHR) {
1036
            // success: Do nothing, the UI is already updated
1037
        }
1038
    });
1039
    return false;
1040
}
1041

    
1042
// add metadata key-value pair
1043
function update_metadata(serverID, meta_key, meta_value) {
1044
    var payload = {
1045
        "meta": {
1046
        }
1047
    };
1048
    payload["meta"][meta_key] = meta_value;
1049

    
1050
    $.ajax({
1051
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1052
        type: "PUT",
1053
        contentType: "application/json",
1054
        dataType: "json",
1055
        data: JSON.stringify(payload),
1056
        timeout: TIMEOUT,
1057
        error: function(jqXHR, textStatus, errorThrown) {
1058
            try {
1059
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1060
            } catch (err) {
1061
                ajax_error(err);
1062
            }
1063
        },
1064
        success: function(data, textStatus, jqXHR) {
1065
            // success: Update icons if meta key is OS
1066
            if (meta_key == "OS") {
1067
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1068
                $("#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');
1069
            }
1070
        }
1071
    });
1072
    return false;
1073
}
1074

    
1075
// create network
1076
function create_network(networkName){
1077
    // ajax post start call
1078
    var payload = {
1079
        "network": { "name": networkName }
1080
    };
1081

    
1082
    $.ajax({
1083
        url: API_URL + '/networks',
1084
        type: "POST",
1085
        contentType: "application/json",
1086
        dataType: "json",
1087
        data: JSON.stringify(payload),
1088
        timeout: TIMEOUT,
1089
        error: function(jqXHR, textStatus, errorThrown) {
1090
            try {
1091
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1092
            } catch (err) {
1093
                ajax_error(err);
1094
            }
1095
        },
1096
        success: function(data, textStatus, jqXHR) {
1097
            if ( jqXHR.status == '202') {
1098
                try {
1099
                    console.info('created network ' + networkName);
1100
                } catch(err) {}
1101
                update_networks(UPDATE_INTERVAL);
1102
                $("a#networkscreate").overlay().close();
1103
            } else {
1104
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1105
            }
1106
        }
1107
    });
1108
    return false;
1109
}
1110

    
1111
// rename network
1112
function rename_network(networkID, networkName){
1113
    if (!networkID.length){
1114
        //ajax_success('DEFAULT');
1115
        return false;
1116
    }
1117
    // prepare payload
1118
    var payload = {
1119
        "network": {"name": networkName}
1120
    };
1121
    // ajax call
1122
    $.ajax({
1123
        url: API_URL + '/networks/' + networkID,
1124
        type: "PUT",
1125
        contentType: "application/json",
1126
        dataType: "json",
1127
        data: JSON.stringify(payload),
1128
        timeout: TIMEOUT,
1129
        error: function(jqXHR, textStatus, errorThrown) {
1130
            try {
1131
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1132
            } catch (err) {
1133
                ajax_error(err);
1134
            }
1135
        },
1136
        success: function(data, textStatus, jqXHR) {
1137
            if ( jqXHR.status == '204') {
1138
                try {
1139
                    console.info('renamed network' + networkID);
1140
                } catch(err) {}
1141
            } else {
1142
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1143
            }
1144
        }
1145
    });
1146
    return false;
1147
}
1148

    
1149
function delete_network(networkIDs){
1150
    if (!networkIDs.length){
1151
        //ajax_success('DEFAULT');
1152
        return false;
1153
    }
1154
    // get a network
1155
    var networkID = networkIDs.pop();
1156
    // ajax post destroy call can have an empty request body
1157
    var payload = {};
1158
    // ajax call
1159
    $.ajax({
1160
        url: API_URL + '/networks/' + networkID,
1161
        type: "DELETE",
1162
        contentType: "application/json",
1163
        dataType: "json",
1164
        data: JSON.stringify(payload),
1165
        timeout: TIMEOUT,
1166
        error: function(jqXHR, textStatus, errorThrown) {
1167
            try {
1168
                ajax_error(jqXHR.status, undefined, 'Delete network', jqXHR.responseText);
1169
            } catch (err) {
1170
                ajax_error(err);
1171
            }
1172
        },
1173
        success: function(data, textStatus, jqXHR) {
1174
            if ( jqXHR.status == '204') {
1175
                try {
1176
                    console.info('deleted network ' + networkID);
1177
                } catch(err) {}
1178
                // continue with the rest of the servers
1179
                delete_network(networkIDs);
1180
            } else {
1181
                ajax_error(jqXHR.status, undefined, 'Delete network', jqXHR.responseText);
1182
            }
1183
        }
1184
    });
1185
    return false;
1186
}
1187

    
1188
function add_server_to_network(networkID, serverIDs) {
1189
    if (!serverIDs.length){
1190
        //ajax_success('DEFAULT');
1191
        update_networks(UPDATE_INTERVAL);
1192
        $("a#add-machines-overlay").overlay().close();
1193
        return false;
1194
    }
1195
    // get a server
1196
    var serverID = serverIDs.pop();
1197
    // prepare payload
1198
    var payload = {
1199
            "add": { "serverRef": serverID }
1200
        };
1201
    // prepare ajax call
1202
    $.ajax({
1203
        url: API_URL + '/networks/' + networkID + '/action',
1204
        type: "POST",
1205
        contentType: "application/json",
1206
        dataType: "json",
1207
        data: JSON.stringify(payload),
1208
        timeout: TIMEOUT,
1209
        error: function(jqXHR, textStatus, errorThrown) {
1210
            try {
1211
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1212
            } catch (err) {
1213
                ajax_error(err);
1214
            }
1215
        },
1216
        success: function(data, textStatus, jqXHR) {
1217
            if ( jqXHR.status == '202') {
1218
                try {
1219
                    console.info('added server ' + serverID + ' to network ' + networkID);
1220
                } catch(err) {}
1221
                // continue with the rest of the servers
1222
                add_server_to_network(networkID, serverIDs);
1223
            } else {
1224
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1225
            }
1226
        }
1227
    });
1228
    return false;
1229
}
1230

    
1231
function remove_server_from_network(networkIDs, serverIDs) {
1232
    if (!networkIDs.length){
1233
        //ajax_success('DEFAULT');
1234
        return false;
1235
    }
1236
    // get a network and a server
1237
    var networkID = networkIDs.pop();
1238
    var serverID = serverIDs.pop();
1239
    // prepare payload
1240
    var payload = {
1241
            "remove": { "serverRef": serverID }
1242
        };
1243
    // prepare ajax call
1244
    $.ajax({
1245
        url: API_URL + '/networks/' + networkID + '/action',
1246
        type: "POST",
1247
        contentType: "application/json",
1248
        dataType: "json",
1249
        data: JSON.stringify(payload),
1250
        timeout: TIMEOUT,
1251
        error: function(jqXHR, textStatus, errorThrown) {
1252
            try {
1253
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1254
            } catch (err) {
1255
                ajax_error(err);
1256
            }
1257
        },
1258
        success: function(data, textStatus, jqXHR) {
1259
            if ( jqXHR.status == '202') {
1260
                try {
1261
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1262
                } catch(err) {}
1263
                // remove it from DOM
1264
                $('#net-' + networkID + '-server-' + serverID).fadeOut('slow').remove();
1265
                // continue with the rest of the servers
1266
                remove_server_form_network(networkIDs, serverIDs);
1267
            } else {
1268
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1269
            }
1270
        }
1271
    });
1272
    return false;
1273
}
1274

    
1275
// show the welcome screen
1276
function showWelcome() {
1277
    $("#view-select").fadeOut("fast");
1278
    $("#emptymachineslist").fadeIn("fast");
1279
    $("#machinesview").hide();
1280
}
1281

    
1282
// hide the welcome screen
1283
function hideWelcome() {
1284
    $("#emptymachineslist").fadeOut("fast");
1285
    $("#view-select").fadeIn("fast");
1286
    $("div#view-select").show();
1287
    $("#machinesview").show();
1288
}
1289

    
1290
function log_server_status_change(server_entry, new_status) {
1291
    // firebug console logging
1292
    try {
1293
        if ($("#machinesview-single").length > 0) {
1294
            console.info(server_entry.find("div.machine-details div.name").text() +
1295
                        ' from ' + server_entry.find(".state-label").text() +
1296
                        ' to ' + STATUSES[new_status]);
1297
        } else {
1298
            console.info(server_entry.find("div.name span.name").text() +
1299
                        ' from ' + server_entry.find(".status").text() +
1300
                        ' to ' + STATUSES[new_status]);
1301
        }
1302
    } catch(err) {}
1303
}
1304

    
1305
function get_flavor_params(flavorRef) {
1306
    var cpus, ram, disk;
1307
    if ( flavors.length > 0 ) {
1308
        var current_flavor = '';
1309
        for (i=0; i<flavors.length; i++) {
1310
            if (flavors[i]['id'] == flavorRef) {
1311
                current_flavor = flavors[i];
1312
            }
1313
        }
1314
        cpus = current_flavor['cpu'];
1315
        ram = current_flavor['ram'];
1316
        disk = current_flavor['disk'];
1317
    } else {
1318
        cpus = 'undefined';
1319
        ram = 'undefined';
1320
        disk = 'undefined';
1321
    }
1322
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1323
}
1324

    
1325
function get_image_params(imageRef) {
1326
    var image_name, image_size;
1327
    if ( images.length > 0 ) {
1328
        var current_image = '';
1329
        for (i=0; i<images.length; i++) {
1330
            if (images[i]['id'] == imageRef) {
1331
                current_image = images[i];
1332
            }
1333
        }
1334
        image_name = current_image['name'];
1335
        image_size = current_image['metadata']['values']['size'];
1336
    } else {
1337
        image_name = 'undefined';
1338
        image_size = 'undefined';
1339
    }
1340
    return {'name': image_name,'size': image_size};
1341
}
1342

    
1343
function get_public_ips(server) {
1344
    var ip4, ip6;
1345
    try {
1346
        if (server.addresses.values) {
1347
            $.each (server.addresses.values, function(i, value) {
1348
                if (value.id == 'public') {
1349
                    try {
1350
                        $.each (value.values, function(i, ip) {
1351
                            if (ip.version == '4') {
1352
                                ip4 = ip.addr;
1353
                            } else if (ip.version == '6') {
1354
                                ip6 = ip.addr;
1355
                            } else {
1356
                                ip4 = 'undefined';
1357
                                ip6 = 'undefined';
1358
                            }
1359
                        });
1360
                    } catch (err){
1361
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1362
                        ip4 = 'undefined';
1363
                        ip6 = 'undefined';
1364
                    }
1365
                }
1366
            });
1367
        }
1368
    } catch (err) {
1369
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1370
        ip4 = 'undefined';
1371
        ip6 = 'undefined';
1372
    }
1373
    return {'ip4': ip4, 'ip6': ip6};
1374
}
1375

    
1376
function get_private_ips(server) {
1377

    
1378
}