Revision 8d08f18a ui/static/synnefo.js

b/ui/static/synnefo.js
38 38
var networks = [], networks_changes_since = 0;
39 39
var error_timeout = 20000;
40 40
var last_request = {};
41
var CHANGES_SINCE_ERRORS = 0;
42

  
43
$.ajaxSetup({
44
    'cache': false,
45
    'beforeSend': function(xhr) {
46
          // save ajax settings, we might need them for error reporting
47
          last_request = this;
48
          xhr.setRequestHeader("X-Auth-Token", $.cookie("X-Auth-Token"));
49

  
50
          // append the date the request initialized to the ajax settings
51
          try {
52
            this.date_sent = new Date();
53
          } catch (err) {
54
          }
55
            
56
          if (CHANGES_SINCE_ERRORS > 0 && changes_since) {
57
            CHANGES_SINCE_ERRORS = 0;
58
          }
59
    },
60

  
61
    // catch uncaught error requests
62
    // stop interaction and show only for the 5xx errors
63
    // refresh the page after 20secs
64
    error: function(jqXHR, textStatus, errorThrown) {
65

  
66
        // check if xhr is in valid state (no status property)
67
        try {
68
            var status = jqXHR.status;
69
        } catch (err) {
70
            return false;
71
        }
72

  
73
        // stop interaction for important (aka 500) error codes only
74
        if (jqXHR.status >= 500 && jqXHR.status < 600)
75
        {
76
            handle_api_error(-11, undefined, 'Unknown', jqXHR, textStatus, errorThrown, this);
77
        }
78

  
79
        // refresh after 10 seconds
80
        window.setTimeout("window.location.reload()", window.error_timeout);
81
    }
82
});
83

  
84
// generic api error handler
85
//
86
// code: error code (uid or http status)
87
// context: something to identify the object 
88
//          that the error occured to (e.g. vm id) 
89
// xhr: xhr object
90
// jquery_error_status: error identified by the jquery ("timeout", "error" etc.)
91
// jquery_error: error identified by the jquery ("timeout", "error" etc.)
92
// ajax_settings: the settings 
93
// 
94
function handle_api_error(code, context, action, xhr, 
95
                          jquery_error_status, jquery_error, ajax_settings) {
96
    
97
    // handle timeouts (only for repeated requests)
98
    if (jquery_error_status == "timeout" && ajax_settings && ajax_settings.repeated) {
99
        // do not show error for the first timeout
100
        if (TIMEOUTS_OCCURED < SKIP_TIMEOUTS) {
101
            TIMEOUTS_OCCURED += 1;
102
            return;
103
        }
104
    }
105 41

  
106
    if (jquery_error_status == "timeout") {
107
        ajax_settings.disable_report = true;
108
        ajax_error("TIMEOUT", context, action, "", ajax_settings);
109
        return;
110
    }
111

  
112
    try {
113
        // malformed changes-since request, skip only first request
114
        // then ui will try to get requests with no changes-since parameter set
115
        // if for some reason server responds with 400/changes-since error
116
        // fallback to error message
117
        if (xhr.status === 400 && xhr.responseText.indexOf("changes-since") > -1 && CHANGES_SINCE_ERRORS == 0) {
118
            CHANGES_SINCE_ERRORS += 1;
119
            changes_since = 0;
120
            return;
121
        }
122

  
123
        // 413 no need to show report
124
        if (xhr.status === 413) {
125
            ajax_settings.disable_report = true;
126
            ajax_settings.no_details = false;
127
        }
128

  
129
        ajax_error(xhr.status, context, action, xhr.responseText, ajax_settings);
130
    } catch (err) {
131
        ajax_error(code, context, action, "NETWORK ERROR", ajax_settings);
132
    }
133
}
134 42

  
135 43
Object.prototype.toString = function(o){
136 44
    
......
172 80
    }
173 81
}
174 82

  
175
// jquery show/hide events
176
var _oldshow = $.fn.show;
177
$.fn.show = function(speed, callback) {
178
    $(this).trigger('show');
179
    return _oldshow.apply(this,arguments);
180
}
181
var _oldhide = $.fn.hide;
182
$.fn.hide = function(speed, callback) {
183
    $(this).trigger('hide');
184
    return _oldhide.apply(this,arguments);
185
}
186

  
187
function ISODateString(d){
188
    //return a date in an ISO 8601 format using UTC.
189
    //do not include time zone info (Z) at the end
190
    //taken from the Mozilla Developer Center
191
    function pad(n){ return n<10 ? '0'+n : n }
192
    return  d.getUTCFullYear()+ '-' +
193
            pad(d.getUTCMonth()+1) + '-' +
194
            pad(d.getUTCDate()) + 'T' +
195
            pad(d.getUTCHours()) + ':' +
196
            pad(d.getUTCMinutes()) + ':' +
197
            pad(d.getUTCSeconds()) +'Z'
198
}
199

  
200
function parse_error(responseText, errorCode){
201
    var errors = [];
202
    try {
203
        responseObj = JSON.parse(responseText);
204
    }
205
    catch(err) {
206
        errors[0] = {'code': errorCode};
207
        return errors;
208
    }
209
    for (var err in responseObj){
210
        errors[errors.length] = responseObj[err];
211
    }
212
    return errors;
213
}
214

  
215 83
// indexOf prototype for IE
216 84
if (!Array.prototype.indexOf) {
217 85
  Array.prototype.indexOf = function(elt /*, from*/) {
......
233 101
}
234 102

  
235 103
// trim prototype for IE
236
if(typeof String.prototype.trim !== 'function') {
237
    String.prototype.trim = function() {
238
        return this.replace(/^\s+|\s+$/g, '');
239
    }
240
}
241

  
242
// simple string format helper (http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format)
243
String.prototype.format = function() {
244
    var formatted = this;
245
    for (var i = 0; i < arguments.length; i++) {
246
        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
247
        formatted = formatted.replace(regexp, arguments[i]);
248
    }
249
    return formatted;
250
};
251

  
252

  
253
function update_confirmations() {
254
    // hide all confirm boxes to begin with
255
    $('#machines-pane div.confirm_single').hide();
256
    $('#machines-pane div.confirm_multiple').hide();
257
    var action_type = [];
258
    // standard view or single view
259
    if ($.cookie("view") == '0' || $.cookie("view") == '2') {
260
        for (var i=0; i<pending_actions.length; i++) {
261
            // show single confirms
262
            if (pending_actions[i][0] == reboot) {
263
                action_type = "reboot";
264
            } else if (pending_actions[i][0] == shutdown) {
265
                action_type = "shutdown";
266
            } else if (pending_actions[i][0] == start) {
267
                action_type = "start";
268
            } else if (pending_actions[i][0] == open_console) {
269
                action_type = "console";
270
            } else {
271
                action_type = "destroy";
272
            }
273
            $("#machines-pane #" + pending_actions[i][1] +
274
            " div.action-container." + action_type + " div.confirm_single").show();
275
        }
276
    }
277
    // if more than one pending action show multiple confirm box
278
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
279
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
280
        $('#machines-pane div.confirm_multiple').show();
281
    }
282
}
283

  
284
function update_network_confirmations(){
285
    // hide all confirm boxes to begin with
286
    $('#networks-pane div.confirm_multiple').hide();
287

  
288
    for (var i=0;i<pending_actions.length;i++){
289
        // show single confirms depending on the action
290
        if (pending_actions[i][0] == delete_network) {
291
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
292
        } else if (pending_actions[i][0] == remove_server_from_network) {
293
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
294
        } // else {}
295
    }
296

  
297
    // if more than one pending action show multiple confirm box
298
    if (pending_actions.length > 1){
299
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
300
        $('#networks-pane div.confirm_multiple').show();
301
    }
302

  
303
    try {
304
        update_network_confirmations_position();
305
    } catch (err) { console.error(err) };
306
}
307

  
308
function list_view() {
309
    changes_since = 0; // to reload full list
310
    pending_actions = []; // clear pending actions
311
    update_confirmations();
312
    clearTimeout(deferred);    // clear old deferred calls
313
    try {
314
        update_request.abort(); // cancel pending ajax updates
315
        load_request.abort();
316
    }catch(err){}
317
    $.cookie("view", '1'); // set list cookie
318
    uri = $("a#list").attr("href");
319
    load_request = $.ajax({
320
        url: uri,
321
        type: "GET",
322
        timeout: TIMEOUT,
323
        dataType: "html",
324
        error: function(jqXHR, textStatus, errorThrown) {
325
            return false;
326
        },
327
        success: function(data, textStatus, jqXHR) {
328
            $("a#list")[0].className += ' activelink';
329
            $("a#standard")[0].className = '';
330
            $("a#single")[0].className = '';
331
            $("div#machinesview").html(data);
332
        }
333
    });
334
    return false;
335
}
336

  
337
function single_view() {
338
    changes_since = 0; // to reload full list
339
    pending_actions = []; // clear pending actions
340
    update_confirmations();
341
    clearTimeout(deferred);    // clear old deferred calls
342
    try {
343
        update_request.abort(); // cancel pending ajax updates
344
        load_request.abort();
345
    }catch(err){}
346
    $.cookie("view", '2'); // set list cookie
347
    uri = $("a#single").attr("href");
348
    load_request = $.ajax({
349
        url: uri,
350
        type: "GET",
351
        timeout: TIMEOUT,
352
        dataType: "html",
353
        error: function(jqXHR, textStatus, errorThrown) {
354
            return false;
355
        },
356
        success: function(data, textStatus, jqXHR) {
357
            $("a#single")[0].className += ' activelink';
358
            $("a#standard")[0].className = '';
359
            $("a#list")[0].className = '';
360
            $("div#machinesview").html(data);
361
        }
362
    });
363
    return false;
364
}
365

  
366
function standard_view() {
367
    changes_since = 0; // to reload full list
368
    pending_actions = []; // clear pending actions
369
    update_confirmations();
370
    clearTimeout(deferred);    // clear old deferred calls
371
    try {
372
        update_request.abort() // cancel pending ajax updates
373
        load_request.abort();
374
    }catch(err){}
375
    $.cookie("view", '0');
376
    uri = $("a#standard").attr("href");
377
    load_request = $.ajax({
378
        url: uri,
379
        type: "GET",
380
        timeout: TIMEOUT,
381
        dataType: "html",
382
        error: function(jqXHR, textStatus, errorThrown) {
383
            return false;
384
        },
385
        success: function(data, textStatus, jqXHR) {
386
            $("a#standard")[0].className += ' activelink';
387
            $("a#list")[0].className = '';
388
            $("a#single")[0].className = '';
389
            $("div#machinesview").html(data);
390
        }
391
    });
392
    return false;
393
}
394

  
395
function choose_view() {
396
    if ($.cookie("view")=='1') {
397
        list_view();
398
    } else if ($.cookie("view")=='2'){
399
        single_view();
400
    } else {
401
        standard_view();
402
    }
403
}
404

  
405
// return value from metadata key "OS", if it exists
406
function os_icon(metadata) {
407
    if (!metadata) {
408
        return 'okeanos';
409
    }
410
    if (metadata.values.OS == undefined || metadata.values.OS == '') {
411
        return 'okeanos';
412
    } else {
413
        if (os_icons.indexOf(metadata.values.OS) == -1) {
414
            return 'okeanos';
415
        } else {
416
            return metadata.values.OS;
417
        }
418
    }
419
}
420

  
421
function os_icon_from_value(metadata) {
422
    if (!metadata) {
423
        return 'okeanos';
424
    }
425
if (metadata == undefined || metadata == '') {
426
        return 'okeanos';
427
    } else {
428
        if (os_icons.indexOf(metadata) == -1) {
429
            return 'okeanos';
430
        } else {
431
            return metadata;
432
        }
433
    }
434
}
435

  
436
// get and show a list of running and terminated machines
437
function update_vms(interval) {
438
    try{ console.info('updating machines'); } catch(err){}
439
    var uri= API_URL + '/servers/detail';
440

  
441
    if (changes_since != 0)
442
        uri+='?changes-since='+changes_since
443
    
444
    update_request = $.ajax({
445
        repeated: true,
446
        cache: false,
447
        url: uri,
448
        type: "GET",
449
        timeout: TIMEOUT,
450
        dataType: "json",
451
        error: function(jqXHR, textStatus, errorThrown) {
452
            // don't forget to try again later
453
            if (interval) {
454
                clearTimeout(deferred);    // clear old deferred calls
455
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
456
            }
457
            // as for now, just show an error message
458
            try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
459

  
460
            handle_api_error(-12, undefined, 'Update VMs', jqXHR, textStatus, errorThrown, this);
461
            return false;
462
        },
463
        success: function(data, textStatus, jqXHR) {
464
            // create changes_since string if necessary
465
            if (jqXHR.getResponseHeader('Date') != null){
466
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
467
                changes_since = ISODateString(changes_since_date);
468
            }
469
            
470
            if (interval) {
471
                clearTimeout(deferred);    // clear old deferred calls
472
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
473
            }
474

  
475
            if (jqXHR.status == 200 || jqXHR.status == 203) {
476
                try {
477
                    //servers = data.servers.values;
478
                    update_servers_data(data.servers.values, data);
479
                    update_machines_view(data);
480
                } catch (err) { ajax_error(-503, "UI Error", 'Update VMs', err, this);}
481
            } else if (jqXHR.status != 304){
482
                try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
483
                /*
484
                FIXME:  Here it should return the error, however Opera does not support 304.
485
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
486
                        304, which should be corrected (Bug #317).
487
                */
488
                // ajax_error(jqXHR.status, "Ajax error", 'Update VMs', jqXHR.responseText);
489
            }
490
            return false;
491
        }
492
    });
493
    return false;
494
}
495

  
496
function update_servers_data(servers_update, data) {
497
    $(window).trigger("vm:update", servers_update, data);
498

  
499
    // first call
500
    if (!window.servers || window.servers.length == 0) {
501
        window.servers = servers_update;
502
        return;
503
    }
504
    
505
    // server exists helper
506
    server_exists = function(server) {
507
        var id = server.id;
508
        var found = false;
509
        var index = 0;
510
        $.each(servers, function(i, s) {
511
            if (s.id == id) { found = true, index = i };
512
        });
513
        if (found)
514
            return [found, index];
515

  
516
        return false;
517
    }
518

  
519
    // merge object properties
520
    merge = function() {
521
        var initial = arguments[0];
522
        var status_changed = undefined;
523
        $.each(arguments, function(index, el) {
524
            $.each(el, function(key,v) {
525
                // new attribute added
526
                var previous_value = initial[key];
527
                var v = v;
528
                if (initial[key] == undefined) {
529
                    $(window).trigger("vm:attr:add", initial, key, v);
530
                } else {
531
                    // value changed
532
                    if (initial[key] != v) {
533
                        if (key == "status") {
534
                            // dont change if in destroy state
535
                            if (initial.status == "DESTROY") {
536
                                v = "DESTROY";
537
                            }
538
                            status_changed = {'old': previous_value, 'new': v}; 
539
                        }
540

  
541
                        $(window).trigger("vm:attr:change", {'initial': initial, 'attr': key, 'newvalue': v});
542
                    }
543
                }
544
                initial[key] = v;
545
            });
546
        });
547
        if (status_changed !== undefined) {
548
            $(window).trigger('vm:status:change', {'vm': initial, 'old': status_changed['old'], 'new': status_changed['new']});
549
        }
550
        return initial;
551
    }
552
    
553
    // server removed
554
    var remove = [];
555
    $.each(servers_update, function(index, server) {
556
        if (server.status == "DELETED") {
557
            remove.push(server.id);
558
        }
559
    });
560
    
561
    // check if server is in transition, apply appropriate logic
562
    function update_server_transition(vm) {
563
        if (vm.state_transition == "DESTROY" && vm.status != "DELETE") {
564
            return;
565
        }
566

  
567
        if (vm.state_transition == "SHUTDOWN" && vm.state_transition == "ACTIVE") {
568
            return;
569
        } else {
570
            // clear transition
571
            vm.state_transition = false;
572
            return;
573
        }
574

  
575
        if (vm.state_transition == "START" && vm.state_transition == "STOPPED") {
576
            return;
577
        } else {
578
            // clear transition
579
            vm.state_transition = false;
580
            return;
581
        }
582
    }
583

  
584
    // check server, if exists merge it with new values else add it
585
    $.each(servers_update, function(index, server) {
586
        var exists = server_exists(server);
587
        var old_server = servers[exists[1]];
588

  
589
        // reset network transition
590
        try {
591
            if (old_server.network_transition) {
592
                if (old_server.network_transition == "NETWORK_CHANGE") {
593
                    // network profile changed, servers data updated, so act if the change was made
594
                    // this flag will trigger ui to remove any transiiton indicators
595
                    // and hopefully apply the new value to the profile options
596
                    old_server.network_transition = "CHANGED"
597
                } else {
598
                    // nothing happened
599
                    old_server.network_transition = undefined;
600
                };
601
            }
602
        } catch (err) {
603
            // no old server
604
        }
605

  
606
        if (exists !== false) {
607
            try {
608
                servers[exists[1]] = merge(servers[exists[1]], server);
609
                update_server_transition(servers[exists[1]]);
610
            } catch (err) {
611
            }
612
        } else {
613
            servers.push(server);
614
            $(window).trigger("vm:add", server);
615
        }
616
        if (remove.indexOf(server.id) > -1) {
617
            var remove_exists = server_exists(server);
618
            servers.splice(remove_exists[1], 1);
619
            $(window).trigger("vm:remove", server);
620
        }
621
    });
622
}
623

  
624
// get a list of running and terminated machines, used in network view
625
function update_networks(interval) {
626
    try{ console.info('updating networks'); } catch(err){}
627
    var uri= API_URL + '/servers/detail';
628

  
629
    if (changes_since != 0)
630
        uri+='?changes-since='+changes_since
631

  
632
    update_request = $.ajax({
633
        repeated: true,
634
        cache: false,
635
        url: uri,
636
        type: "GET",
637
        timeout: TIMEOUT,
638
        dataType: "json",
639
        error: function(jqXHR, textStatus, errorThrown) {
640
            // don't forget to try again later
641
            if (interval) {
642
                clearTimeout(deferred);    // clear old deferred calls
643
                deferred = setTimeout(function() {update_networks(interval);},interval);
644
            }
645
            // as for now, just show an error message
646
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
647

  
648
            handle_api_error(-13, undefined, 'Update networks', jqXHR, textStatus, errorThrown, this);
649
            return false;
650
            },
651
        success: function(data, textStatus, jqXHR) {
652
            // create changes_since string if necessary
653
            if (jqXHR.getResponseHeader('Date') != null){
654
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
655
                changes_since = ISODateString(changes_since_date);
656
            }
657

  
658
            if (interval) {
659
                clearTimeout(deferred);    // clear old deferred calls
660
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
661
            }
662

  
663
            if (jqXHR.status == 200 || jqXHR.status == 203) {
664
                try {
665
                    //servers = data.servers.values;
666
                    update_servers_data(data.servers.values, data);
667
                    update_network_names(data);
668
                } catch(err) { ajax_error(-505, "UI Error", 'Update networks', err, this);}
669
            } else if (jqXHR.status == 304) {
670
                update_network_names();
671
            }
672
            else {
673
                try { console.info('update_networks callback:' + jqXHR.status ) } catch(err) {}
674
                /*
675
                FIXME:  Here it should return the error, however Opera does not support 304.
676
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
677
                        304, which should be corrected (Bug #317).
678
                */
679
                //ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
680
                update_network_names();
681
            }
682
            return false;
683
        }
684
    });
685
    return false;
686
}
687

  
688
// get and show a list of public and private networks
689
function update_network_names(servers_data) {
690
    try{ console.info('updating network names'); } catch(err){}
691
    var uri= API_URL + '/networks/detail';
692

  
693
    if (networks_changes_since != 0)
694
        //FIXME: Comment out the following, until metadata do not 304 when changed
695
        uri+='?changes-since=' + networks_changes_since
696

  
697
    update_request = $.ajax({
698
        cache: false,
699
        url: uri,
700
        type: "GET",
701
        timeout: TIMEOUT,
702
        dataType: "json",
703
        error: function(jqXHR, textStatus, errorThrown) {
704
            // as for now, just show an error message
705
            try {
706
                console.info('update_network names errback:' + jqXHR.status )
707
            } catch(err) {}
708

  
709
            handle_api_error(-14, undefined, 'Update network names', jqXHR, textStatus, errorThrown, this);
710
            return false;
711
        },
712
        success: function(data, textStatus, jqXHR) {
713
            // create changes_since string if necessary
714
            if (jqXHR.getResponseHeader('Date') != null){
715
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
716
                networks_changes_since = ISODateString(changes_since_date);
717
            }
718

  
719
            if (jqXHR.status == 200 || jqXHR.status == 203) {
720
                try {
721
                    networks = data.networks.values;
722
                    update_networks_view(servers_data, data);
723
                } catch(err) {
724
                    ajax_error(-507, "UI Error", 'Update network names', err, this);
725
                }
726
            } else if (jqXHR.status == 304) {
727
                    update_networks_view(servers_data);
728
            } else if (jqXHR.status != 304){
729
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
730
                /*
731
                FIXME:  Here it should return the error, however Opera does not support 304.
732
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
733
                        304, which should be corrected (Bug #317).
734
                */
735
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
736
                update_networks_view(servers_data);
737
            }
738
            return false;
739
        }
740
    });
741
    return false;
742
}
743

  
744
// get and show a list of available standard and custom images
745
function update_images() {
746
    $.ajax({
747
        url: API_URL + '/images/detail',
748
        type: "GET",
749
        //async: false,
750
        dataType: "json",
751
        timeout: TIMEOUT,
752
        error: function(jqXHR, textStatus, errorThrown) {
753
            handle_api_error(-15, undefined, 'Update images', jqXHR, textStatus, errorThrown, this);
754
        },
755
        success: function(data, textStatus, jqXHR) {
756
            try {
757
                images = update_images_data(data.images.values);
758
                update_wizard_images();
759
                update_images_info_data(images);
760

  
761
                // update images options
762
                update_image_flavor_options();
763
                handle_image_choice_changed();
764
            } catch(err){
765
                ajax_error("NO_IMAGES");
766
            }
767
        }
768
    });
769
    return false;
770
}
771

  
772
function update_images_data(data) {
773
    var images = data;
774

  
775
    function get_order(img) {
776
        var def = -1;
777
        try {
778
            return parseInt(img.metadata.values.sortorder) || def;
779
        } catch (err) {
780
            return def;
781
        }
782
    }
783

  
784
    images.sort(function(a,b) {
785
        return get_order(a) < get_order(b);
786
    })
787

  
788
    return images
789
}
790

  
791
// update images panel
792
function update_wizard_images() {
793
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
794
        $.each(images, function(i,image){
795
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
796
            img.find("label").attr('for',"img-radio-" + image.id);
797
            img.find(".image-title").text(image.name);
798
            if (image.metadata) {
799
                if (image.metadata.values.description != undefined) {
800
                    img.find(".description").text(image.metadata.values.description);
801
                }
802
                if (image.metadata.values.size != undefined) {
803
                    img.find("#size").text(image.metadata.values.size);
804
                }
805
            }
806
            img.find("input.radio").attr('id',"img-radio-" + image.id);
807
            if (i==0) img.find("input.radio").attr("checked","checked");
808
            var image_logo = os_icon(image.metadata);
809
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
810
            if (image.metadata) {
811
                if (image.metadata.values.serverId != undefined) {
812
                    img.appendTo("ul#custom-images");
813
                } else {
814
                    img.appendTo("ul#standard-images");
815
                }
816
            } else {
817
                img.appendTo("ul#standard-images");
818
            }
819
        });
820
        
821
        $(".image-container input[type=radio]").change(function(){
822
            handle_image_choice_changed();
823
        });
824
    }
825
}
826

  
827

  
828
// get closest value from specified percentage
829
function get_closest_option(perc, map) {
830
    min = 1;
831
    max = Math.max.apply(Math, map);
832
    
833
    // create a map with percentages
834
    perc_map = [];
835
    $.each(map, function(i,v) {
836
        perc_map[i] = parseInt(range_value_to_percentage(v, map));
837

  
838
    })
839
    
840
    perc_map = perc_map.sort(function(a,b){return a>b});
841
    // find closest percentage
842
    var close = perc_map[0];
843
    found = close;
844
    found_pos = 0;
845
    diff = Math.abs(close - perc);
846
    
847
    // get closest based on map values
848
    for (var i=1; i< perc_map.length; i++) {
849
        if (Math.abs(perc_map[i] - perc) > diff) {
850
            break;
851
        } else {
852
            found = perc_map[i];
853
            found_pos = i;
854
            diff = Math.abs(perc_map[i] - perc);
855
        }
856
    }
857
    
858
    var val = range_percentage_to_value(perc_map[found_pos], map);
859
    return val;
860
}
861

  
862
// get closest percentage from specified value
863
function get_closest_from_value(value, map) {
864
    var perc = range_value_to_percentage(value, map, false);
865
    var close_val = get_closest_option(perc, map);
866
    var value = range_value_to_percentage(close_val, map);
867
    return value;
868
}
869

  
870
// convert a range value (e.g. ram 1024) to percentage
871
function range_value_to_percentage(value, map, valid) {
872
    if (valid == undefined) { valid = true }
873
    var pos = map.indexOf(value)
874
    
875
    // do we need to validate that value is in the map
876
    if (pos == -1 && valid ) { return 0 }
877
    if (value == 1) { return 0; }
878
    if (pos == map.length -1) { return 100; }
879

  
880
    perc = value * (100 / Math.max.apply(Math, map));
881

  
882
    // fix for small fragmentations
883
    min = 1; max = Math.max.apply(Math, map);
884
    if (max - min <= 4) {
885
        perc = perc - 12
886
    }
887

  
888
    return perc;
889
} 
890

  
891
// get range value to display based on the percentage value
892
// of the range control
893
function range_percentage_to_value(value, map) {
894
    min = 0; max = Math.max.apply(Math, map);
895
    ret = Math.round(value * max / 100);
896
    
897
    // fix for small fragmentations
898
    if (max - min <= 4) { ret = ret; }
899
    if (ret < min) { ret = min; }
900
    if (ret >= max) { ret = max; }
901
    ret = ret;
902
    // 0 is not an option
903
    ret = ret == 0 ? 1: ret;
904
    return ret;
905
}
906

  
907
// get flavor unique index key
908
function get_flavor_index_key(flv) {
909
    return "cpu:" + flv.cpu + ":ram:" + flv.ram + ":disk:" + flv.disk
910
}
911

  
912
// update last creation step information
913
function update_creating_vm_details() {
914
    var flavor = get_flavor_sliders_values();
915
    var image = IMAGES_DATA[get_selected_image_id()].image;
916

  
917
    var cont = $("#page3-container");
918
    var image_name = cont.find("#machine_image-label");
919
    var cpu = cont.find("#machine_cpu-label");
920
    var ram = cont.find("#machine_ram-label");
921
    var disk = cont.find("#machine_storage-label");
922

  
923
    image_name.text(image.name);
924
    cpu.text(flavor.cpu);
925
    ram.text(flavor.ram);
926
    disk.text(flavor.disk);
927

  
928
    var name = "My " + image.name + " server";
929
    
930
    // check if server with predefined name already exists
931
    j = 2;
932
    if (servers) {
933
        var predefined_name = name;
934
        $.each(servers, function(index, el) {
935
            if (el.name == name) {
936
                name = predefined_name + " " + j;
937
                j++;
938
            }
939
        })
940
    }
941
    cont.find("input[type=text][name=machine_name]").val(name);
942
}
943

  
944
// image information registry
945
window.images_info = {};
946
function update_images_info_data(images) {
947
    $.each(images, function(i,img) {
948
        images_info[img.id] = img;
949
    });
950
}
951

  
952
// create a map with available flavors for each image
953
function update_image_flavor_options() {
954
    // invalid state, do not update, 
955
    // wait for both images and flavors to get filled/updated
956
    if (!window.images || !window.flavors) {
957
        return
958
    }
959
    
960
    // create images flavors map
961
    var images_options = {};
962
    $.each(images, function(i, img) {
963
        images_options[img.id] = {flavors:{}, options:{cpu:[], ram:[], disk:[]}};
964
        // check disk usage
965
        var disk_limit = img.metadata.values.size;
966
        var image_flavors = {};
967
        var image_options = {cpu:[], ram:[], disk:[]};
968
        var flavor_combinations = [];
969
        var default_flavor = undefined;
970
        $.each(flavors, function(j, flv) {
971
            var disk = flv.disk * 1000;
972
            // flavor size can contain image size
973
            if (disk > disk_limit) {
974
                image_flavors[flv.id] = flv;
975
                image_options.cpu.push(flv.cpu)
976
                image_options.ram.push(flv.ram)
977
                image_options.disk.push(flv.disk)
978
                
979
                // create combinations indexes
980
                flavor_combinations.push(get_flavor_index_key(flv));
981
                default_flavor = default_flavor || flv;
982
            } else {
983
            }
984
        });
985
        
986
        // update image suggested flavors
987
        var suggested = [];
988
        $.each(SUGGESTED_FLAVORS, function(i, el) {
989
            // image contains suggested flavor ???
990
            if (flavor_combinations.indexOf(get_flavor_index_key(el)) > -1){
991
                suggested.push(i);
992
            }
993
        });
994

  
995
        // unique data
996
        image_options.cpu = image_options.cpu.unique();
997
        image_options.ram = image_options.ram.unique();
998
        image_options.disk = image_options.disk.unique();
999
        flavor_combinations = flavor_combinations.unique();
1000
        
1001
        // sort data
1002
        var numeric_sort = function(a,b){return a>b};
1003
        image_options.cpu = image_options.cpu.sort(numeric_sort);
1004
        image_options.ram = image_options.ram.sort(numeric_sort);
1005
        image_options.disk = image_options.disk.sort(numeric_sort);
1006

  
1007
        // append data
1008
        images_options[img.id].flavors = image_flavors;
1009
        images_options[img.id].options = image_options;
1010
        images_options[img.id].image = img;
1011
        images_options[img.id].flavors_index = flavor_combinations;
1012
        images_options[img.id].default_flavor = default_flavor;
1013
        images_options[img.id].suggested = suggested;
1014
    })
1015
    
1016
    // export it to global namespace
1017
    window.IMAGES_DATA = images_options;
1018
}
1019

  
1020
// is flavor available for the specified image ???
1021
function image_flavor_available(image_ref, flavor_object) {
1022
    return IMAGES_DATA[image_ref].flavors_index.indexOf(get_flavor_index_key(flavor_object)) > -1;
1023
}
1024

  
1025
// update sliders and flavor choices on ui
1026
function handle_image_choice_changed() {
1027
    try {
1028
        validate_selected_flavor_options();
1029
        repaint_flavor_choices();
1030
        update_suggested_flavors();
1031
    } catch (err) {
1032
        //console.error(err);
1033
    }
1034
}
1035

  
1036
// disable/enable suggested flavor options (small/medium/large)
1037
function update_suggested_flavors() {
1038
    var img_id = get_selected_image_id();
1039
    var img = IMAGES_DATA[img_id];
1040
    
1041
    // disable all
1042
    $("#machinetype input[type=radio]").attr("disabled", "disabled").parent().addClass("disabled");
1043
    
1044
    $.each(SUGGESTED_FLAVORS, function(i, el) {
1045
        if (img.suggested.indexOf(i) != -1) {
1046
            $("#machinetype label input[value="+i+"]").attr("disabled", "").parent().removeClass("disabled");
1047
        }
1048
    })
1049
    $("#machinetype label input[value=custom]").attr("disabled", "").parent().removeClass("disabled");
1050

  
1051
    // select first enabled
1052
    $($("#machinetype input[type=radio]").not(":disabled")[0]).click();
1053
}
1054

  
1055
// clear points
1056
function clean_flavor_choice_points() {
1057
    $(".slider-container .slider .slider-point").remove();
1058
}
1059

  
1060
function repaint_flavor_choices() {
1061
    clean_flavor_choice_points();
1062
    var img = IMAGES_DATA[get_selected_image_id()];
1063
    function paint_slider_points(slider, points) {
1064
        $.each(points, function(i, point) {
1065
             // proper width
1066
             var width = slider.width() - slider.find(".handle").width();
1067
             // progress number
1068
             var progress = slider.find(".progress").width();
1069
             // percentage based on value
1070
             var perc = range_value_to_percentage(point, points);
1071
             // position
1072
             var pos = perc*width/100;
1073
            
1074
             // handlers
1075
             var last = false;
1076
             var first = false;
1077
             if (pos == 0) { first - true; pos = 2}
1078
             if (pos == width) { last = true; }
1079
            
1080
             // create pointer container and text
1081
             var text = $('<span class="slider-point-text">' + point + '</span>');
1082
             var span = $('<span class="slider-point"></span>').css("left", pos + "px").addClass(pos <= progress ? "slider-point-light": "");
1083
             span.append(text);
1084
             
1085
             // wait for element to get dimensions
1086
             setTimeout(function() {
1087
                 // choose text pointer position
1088
                 move = "-" + ($(text).width()/2 + 1) + "px";
1089
                 if (last) { move = "-" + ($(text).width() - 2) +  "px"; }
1090
                 if (first) { move = "0px"; }
1091
                 $(text).css("margin-left", move);
1092
             }, 100);
1093
             // append to slider
1094
             slider.append(span);
1095
        });
1096
    }
1097
    
1098
    // paint points for each slider
1099
    paint_slider_points($("#cpu-indicator").parent().find(".slider"), img.options.cpu);
1100
    paint_slider_points($("#storage-indicator").parent().find(".slider"), img.options.disk);
1101
    paint_slider_points($("#ram-indicator").parent().find(".slider"), img.options.ram);
1102
}
1103

  
1104
function validate_selected_flavor_options(selected) {
1105
    var img = IMAGES_DATA[get_selected_image_id()];
1106
    if (!check_selected_flavor_values()) {
1107
        var flv = img.default_flavor;
1108
        set_flavor_sliders_values(flv.cpu, flv.disk, flv.ram);
1109
    }
1110

  
1111
    update_creating_vm_details();
1112
}
1113

  
1114
// check if selected values are available
1115
// as a flavor for the image
1116
function check_selected_flavor_values() {
1117
    var img = IMAGES_DATA[get_selected_image_id()];
1118
    var values = get_flavor_sliders_values();
1119
    var found = false;
1120
    
1121
    // index lookup
1122
    if (img.flavors_index.indexOf(get_flavor_index_key(values)) > -1) {
1123
        // return flavor id
1124
        return identify_flavor(values.cpu, values.disk, values.ram);
1125
    }
1126
    
1127
    return false;
1128
}
1129

  
1130
// find which image is selected
1131
// return the options requested available for this image
1132
function get_selected_image_options(opt_name) {
1133
    var img_id = get_selected_image_id();
1134
    var img = IMAGES_DATA[img_id];
1135
    return img.options[opt_name];
1136
}
1137

  
1138
// identify selected image
1139
function get_selected_image_id() {
1140
    return $(".image-container input:checked").attr("id").replace("img-radio-", "");
1141
}
1142

  
1143
function update_wizard_flavors(){
1144
    
1145
    // find max range values
1146
    cpus_max = Math.max.apply(Math, cpus); 
1147
    cpus_min = 1;
1148

  
1149
    disks_max = Math.max.apply(Math, disks);
1150
    disks_min = 1;
1151

  
1152
    ram_max = Math.max.apply(Math, ram);
1153
    ram_min = 1;
1154
    
1155
    // sliders for selecting VM flavor
1156
    $("#cpu:range").rangeinput({min:1,
1157
                               value:0,
1158
                               step:1,
1159
                               progress: true,
1160
                               max:100});
1161

  
1162
    $("#storage:range").rangeinput({min:1,
1163
                               value:0,
1164
                               step:1,
1165
                               progress: true,
1166
                               max:100});
1167

  
1168
    $("#ram:range").rangeinput({min:1,
1169
                               value:0,
1170
                               step:1,
1171
                               progress: true,
1172
                               max:100});
1173

  
1174
    // update the indicators when sliding
1175
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
1176
        var cpus = get_selected_image_options("cpu");
1177
        $("#cpu-indicator")[0].value = range_percentage_to_value(value, cpus);
1178
        $("#cpu-indicator").addClass('selectedrange');
1179
    });
1180
    $("#cpu:range").data().rangeinput.change(function(event,value){
1181
        var cpus = get_selected_image_options("cpu");
1182
        $("#cpu-indicator")[0].value = range_percentage_to_value(value, cpus);
1183
        normal_value = range_value_to_percentage(get_closest_option(value, cpus), cpus);
1184
        if (this.getValue() != normal_value) {
1185
            this.setValue(normal_value);
1186
        }
1187
        $("#custom").click();
1188
        $("#cpu-indicator").removeClass('selectedrange');
1189
        validate_selected_flavor_options("cpu");
1190
    });
1191
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
1192
        var ram = get_selected_image_options("ram");
1193
        $("#ram-indicator")[0].value = range_percentage_to_value(value, ram);
1194
        $("#ram-indicator").addClass('selectedrange');
1195
    });
1196
    $("#ram:range").data().rangeinput.change(function(event,value){
1197
        var ram = get_selected_image_options("ram");
1198
        $("#ram-indicator")[0].value = range_percentage_to_value(value, ram);
1199
        normal_value = range_value_to_percentage(get_closest_option(value, ram), ram);
1200
        if (this.getValue() != normal_value) {
1201
            this.setValue(normal_value);
1202
        }
1203
        $("#custom").click();
1204
        $("#ram-indicator").removeClass('selectedrange');
1205
        validate_selected_flavor_options("ram");
1206
    });
1207
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
1208
        var disks = get_selected_image_options("disk")
1209
        $("#storage-indicator")[0].value = range_percentage_to_value(value, disks);
1210
        $("#storage-indicator").addClass('selectedrange');
1211
    });
1212
    $("#storage:range").data().rangeinput.change(function(event,value){
1213
        var disks = get_selected_image_options("disk")
1214
        $("#storage-indicator")[0].value = range_percentage_to_value(value, disks);
1215
        normal_value = range_value_to_percentage(get_closest_option(value, disks), disks);
1216
        if (this.getValue() != normal_value) {
1217
            this.setValue(normal_value);
1218
        }
1219
        $("#custom").click();
1220
        $("#storage-indicator").removeClass('selectedrange');
1221
        validate_selected_flavor_options("disk");
1222
    });
1223
}
1224

  
1225
function get_flavor_slider(name) {
1226
    return $("#" + name + ":range").data().rangeinput;
1227
}
1228

  
1229
// utility function to grab the value of the slider
1230
function get_flavor_slider_value(name) {
1231
    var maps = {
1232
        'cpu': cpus,
1233
        'ram': ram,
1234
        'storage': disks
1235
    }
1236
    return range_percentage_to_value(get_flavor_slider(name).getValue(), maps[name]);
1237
}
1238

  
1239
function set_flavor_sliders_values(cpu, disk, ram) {
1240
    get_flavor_slider("cpu").setValue(range_value_to_percentage(cpu, get_selected_image_options("cpu")));
1241
    get_flavor_slider("storage").setValue(range_value_to_percentage(disk, get_selected_image_options("disk")));
1242
    get_flavor_slider("ram").setValue(range_value_to_percentage(ram, get_selected_image_options("ram")));
1243
}
1244

  
1245
function get_flavor_sliders_values() {
1246
    return {
1247
        'cpu': get_flavor_slider_value("cpu"),
1248
        'ram': get_flavor_slider_value("ram"),
1249
        'disk': get_flavor_slider_value("storage")
1250
    }
1251
}
1252

  
1253
Array.prototype.unique = function () {
1254
    var r = new Array();
1255
    o:for(var i = 0, n = this.length; i < n; i++)
1256
    {
1257
        for(var x = 0, y = r.length; x < y; x++)
1258
        {
1259
            if(r[x]==this[i])
1260
            {
1261
                continue o;
1262
            }
1263
        }
1264
        r[r.length] = this[i];
1265
    }
1266
    return r;
1267
}
1268

  
1269
// get and configure flavor selection
1270
function update_flavors() {
1271
    $.ajax({
1272
        url: API_URL + '/flavors/detail',
1273
        type: "GET",
1274
        //async: false,
1275
        dataType: "json",
1276
        timeout: TIMEOUT,
1277
        error: function(jqXHR, textStatus, errorThrown) {
1278
            handle_api_error(-16, undefined, 'Update flavors', jqXHR, textStatus, errorThrown, this);
1279
        },
1280
        success: function(data, textStatus, jqXHR) {
1281

  
1282
            try {
1283
                flavors = data.flavors.values;
1284
                jQuery.parseJSON(data);
1285
                $.each(flavors, function(i, flavor) {
1286
                    cpus[i] = flavor['cpu'];
1287
                    disks[i] = flavor['disk'];
1288
                    ram[i] = flavor['ram'];
1289
                });
1290

  
1291
                // we only need unique and sorted arrays
1292
                cpus = cpus.unique();
1293
                disks = disks.unique();
1294
                ram = ram.unique();
1295
                
1296
                // sort arrays
1297
                var numeric_sort = function(a,b) { return a>b};
1298
                disks.sort(numeric_sort);
1299
                cpus.sort(numeric_sort);
1300
                ram.sort(numeric_sort);
1301
            
1302
                // ui update handlers
1303
                update_wizard_flavors();
1304
                update_image_flavor_options();
1305
            } catch(err){
1306
                ajax_error("NO_FLAVORS");
1307
            }
1308
            // start updating vm list
1309
            update_vms(UPDATE_INTERVAL);
1310
        }
1311
    });
1312
    return false;
1313
}
1314

  
1315
// return flavorRef from cpu, disk, ram values
1316
function identify_flavor(cpu, disk, ram){
1317
    for (i=0;i<flavors.length;i++){
1318
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
1319
            return flavors[i]['id']
1320
        }
1321
    }
1322
    return 0;
1323
}
1324

  
1325
// return image entry from imageRef
1326
function get_image(imageRef) {
1327
    for (i=0;i<images.length;i++){
1328
        if (images[i]['id'] == imageRef) {
1329
            return images[i];
1330
        }
1331
    }
1332
    return 0;
1333
}
1334

  
1335
// return machine entry from serverID
1336
function get_machine(serverID) {
1337
    for (i=0;i<servers.length;i++){
1338
        if (servers[i]['id'] == serverID) {
1339
            return servers[i];
1340
        }
1341
    }
1342
    return 0;
1343
}
1344

  
1345
// helper function, returns the name of the current view
1346
function get_current_view() {
1347
    
1348
    if ($.cookie('pane') == 1) {
1349
        return "network"
1350
    }
1351

  
1352
    if ($.cookie('pane') == 2) {
1353
        return "disks"
1354
    }
1355
    
1356
    switch ($.cookie('view')) {
1357
        case "0":
1358
            return "icon";
1359
            break;
1360
        case "1":
1361
            return "list";
1362
            break;
1363
        case "2":
1364
            return "single";
1365
            break;
1366
    }
1367

  
1368
    return false;
1369
}
1370

  
1371
// update vms actions based on current view
1372
function update_machine_actions(serverID, server_status) {
1373
    var view = get_current_view();
1374
    
1375
    // call the proper update actions method
1376
    if (['icon', 'single'].indexOf(view) > -1) {
1377
        update_iconview_actions(serverID, server_status);
1378
    } else if (['list'].indexOf(view) > -1) {
1379
        update_listview_actions();
1380
    }
1381
}
1382

  
1383
// update the actions in icon view, per server
1384
function update_iconview_actions(serverID, server_status) {
1385

  
1386
    // vm in destroy status ???
1387
    var vm = get_machine(serverID)
1388
    if (vm.state_transition == "DESTROY") {
1389
        server_status = "DESTROY";
1390
    }
1391

  
1392
    if ($.cookie("view")=='2') {
1393
        // remove .disable from all actions to begin with
1394
        $('#machinesview-single #' + serverID + ' div.single-action').show();
1395
        // decide which actions should be disabled
1396
        for (current_action in actions) {
1397
            if (actions[current_action].indexOf(server_status) == -1 ) {
1398
                $('#machinesview-single #' + serverID + ' div.action-' + current_action).hide();
1399
            }
1400
        }
1401
    } else {
1402
        // remove .disable from all actions to begin with
1403
        $('#machinesview-icon.standard #' + serverID + ' div.actions').find('a').removeClass('disabled');
1404
        // decide which actions should be disabled
1405
        for (current_action in actions) {
1406
            if (actions[current_action].indexOf(server_status) == -1 ) {
1407
                $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
1408
            }
1409
        }
1410
    }
1411
}
1412

  
1413
// update the actions in list view
1414
function update_listview_actions() {
1415
    var states = [];
1416
    var on = [];
1417
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
1418
    // disable all actions to begin with
1419
    $('#machinesview .list div.actions').children().removeClass('enabled');
1420

  
1421
    // are there multiple machines selected?
1422
    if (checked.length>1)
1423
        states[0] = 'multiple';
1424

  
1425
    // check the states of selected machines
1426
    checked.each(function(i,checkbox) {
1427

  
1428
        // force destroy mode
1429
        var vm = get_machine(checkbox.id);
1430
        if (vm.state_transition == "DESTROY") {
1431
            states[states.length] = "DESTROY";
1432
        } else {
1433
            states[states.length] = checkbox.className;
1434
        }
1435

  
1436
        if (vm_has_public_ip(vm) && vm.status == "ACTIVE") { 
1437
            states = ['network'];
1438
            if (checked.length>1)
1439
                states[states.length] = 'multiple';
1440
        }
1441
    });
1442

  
1443
    // decide which actions should be enabled
1444
    for (a in actions) {
1445
        var enabled = false;
1446
        for (var s =0; s<states.length; s++) {
1447
            if (actions[a].indexOf(states[s]) != -1 ) {
1448
                enabled = true;
1449
            } else {
1450
                enabled = false;
1451
                break;
1452
            }
1453
        }
1454
        if (enabled)
1455
            on[on.length]=a;
1456
    }
1457
    // enable those actions
1458
    for (action in on) {
1459
        $("#action-" + on[action]).addClass('enabled');
1460
    }
1461
}
1462

  
1463
// return a metadata dict containing the metadata
1464
// that should be cloned from image to vm
1465
function get_image_metadata_to_copy(imageRef) {
1466
    var image = IMAGES_DATA[imageRef].image;
1467
    var metadata = image.metadata;
1468

  
1469
    // if no metadata return empty object
1470
    if (!metadata || !metadata.values) {
1471
        return {};
1472
    }
1473
    
1474
    var vm_meta = {};
1475
    // find existing keys, copy their values to the server
1476
    // metadata object
1477
    $.each(VM_IMAGE_COMMON_METADATA, function(index, key) {
1478
        if (metadata.values[key] !== undefined) {
1479
            vm_meta[key] = metadata.values[key];
1480
        }
1481
    })
1482

  
1483
    return vm_meta;
1484
}
1485
//create server action
1486
function create_vm(machineName, imageRef, flavorRef) {
1487
    var image_logo = os_icon(get_image(imageRef).metadata);
1488
    var uri = API_URL + '/servers';
1489

  
1490
    var vm_meta = get_image_metadata_to_copy(imageRef);
1491

  
1492
    var payload = {
1493
        "server": {
1494
            "name": machineName,
1495
            "imageRef": imageRef,
1496
            "flavorRef" : flavorRef,
1497
            "metadata" : vm_meta
1498
        }
1499
    };
1500

  
1501
    $.ajax({
1502
    url: uri,
1503
    type: "POST",
1504
    contentType: "application/json",
1505
    dataType: "json",
1506
    data: JSON.stringify(payload),
1507
    timeout: TIMEOUT,
1508
    error: function(jqXHR, textStatus, errorThrown) {
1509
                // close wizard and show error box
1510
                $('#machines-pane a#create').data('overlay').close();
1511
                handle_api_error(-17, undefined, 'Create VM', jqXHR, textStatus, errorThrown, this);
1512
           },
1513
    success: function(data, textStatus, jqXHR) {
1514
                if ( jqXHR.status == '202') {
1515
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
1516
                } else {
1517
                    // close wizard and show error box
1518
                    $('#machines-pane a#create').data('overlay').close();
1519
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText, this);
1520
                }
1521
            }
1522
    });
1523
}
1524

  
1525
// reboot action
1526
function reboot(serverIDs){
1527
    if (!serverIDs.length){
1528
        //ajax_success('DEFAULT');
1529
        return false;
1530
    }
1531
    // ajax post reboot call
1532
    var payload = {
1533
        "reboot": {"type" : "HARD"}
1534
    };
1535

  
1536
    var serverID = serverIDs.pop();
1537

  
1538
    $.ajax({
1539
        url: API_URL + '/servers/' + serverID + '/action',
1540
        type: "POST",
1541
        contentType: "application/json",
1542
        dataType: "json",
1543
        data: JSON.stringify(payload),
1544
        timeout: TIMEOUT,
1545
        error: function(jqXHR, textStatus, errorThrown) {
1546
                    // in machine views
1547
                    if ( $.cookie("pane") == 0) {
1548
                        try {
1549
                            display_failure(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
1550
                        } catch (err) {
1551
                            display_failure(0, serverID, 'Reboot', jqXHR.responseText);
1552
                        }
1553
                    }
1554
                    // in network view
1555
                    else {
1556
                        try {
1557
                            display_reboot_failure(jqXHR.status, serverID, jqXHR.responseText);
1558
                        } catch (err) {
1559
                            display_reboot_failure(0, serverID, jqXHR.responseText);
1560
                        }
1561
                    }
1562
                },
1563
        success: function(data, textStatus, jqXHR) {
1564
                    if ( jqXHR.status == '202') {
1565
                        try {
1566
                            console.info('rebooted ' + serverID);
1567
                        } catch(err) {}
1568
                        // indicate that the action succeeded
1569
                        // in machine views
1570
                        if ( $.cookie("pane") == 0) {
1571
                            display_success(serverID);
1572
                        }
1573
                        // in network view
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff