Revision ab3df8df
b/snf-cyclades-app/synnefo/ui/static/snf/css/main.css | ||
---|---|---|
7094 | 7094 |
.suspended .action-indicator { |
7095 | 7095 |
display: none !important; |
7096 | 7096 |
} |
7097 |
|
|
7098 |
|
|
7099 |
.trigger-resize { |
|
7100 |
background-image: url("../images/pencil-disabled.png"); |
|
7101 |
background-position: right 2px; |
|
7102 |
background-repeat: no-repeat; |
|
7103 |
padding-right: 15px !important; |
|
7104 |
cursor: pointer; |
|
7105 |
} |
|
7106 |
|
|
7107 |
.can-resize .trigger-resize:hover { |
|
7108 |
text-decoration: underline; |
|
7109 |
} |
|
7110 |
|
|
7111 |
.can-resize .trigger-resize { |
|
7112 |
font-weight: bold; |
|
7113 |
background-image: url("../images/pencil.png"); |
|
7114 |
} |
|
7115 |
|
|
7116 |
.vm-resize .flavor-options-cont { |
|
7117 |
float: none; |
|
7118 |
width: 100%; |
|
7119 |
margin: 0; |
|
7120 |
height: auto; |
|
7121 |
} |
|
7122 |
|
|
7123 |
.form-action.disabled { |
|
7124 |
background-color: #ddd; |
|
7125 |
color: #aaa; |
|
7126 |
border-color: #999; |
|
7127 |
} |
b/snf-cyclades-app/synnefo/ui/static/snf/js/models.js | ||
---|---|---|
1044 | 1044 |
models.VM.__super__.unbind.apply(this, arguments); |
1045 | 1045 |
}, |
1046 | 1046 |
|
1047 |
can_resize: function() { |
|
1048 |
return this.get('status') == 'STOPPED'; |
|
1049 |
}, |
|
1050 |
|
|
1047 | 1051 |
handle_stats_error: function() { |
1048 | 1052 |
stats = {}; |
1049 | 1053 |
_.each(['cpuBar', 'cpuTimeSeries', 'netBar', 'netTimeSeries'], function(k) { |
... | ... | |
1249 | 1253 |
return flv; |
1250 | 1254 |
}, |
1251 | 1255 |
|
1256 |
get_resize_flavors: function() { |
|
1257 |
var vm_flavor = this.get_flavor(); |
|
1258 |
var flavors = synnefo.storage.flavors.filter(function(f){ |
|
1259 |
return f.get('disk_template') == |
|
1260 |
vm_flavor.get('disk_template') && f.get('disk') == |
|
1261 |
vm_flavor.get('disk'); |
|
1262 |
}); |
|
1263 |
return flavors; |
|
1264 |
}, |
|
1265 |
|
|
1266 |
get_flavor_quotas: function() { |
|
1267 |
var flavor = this.get_flavor(); |
|
1268 |
return { |
|
1269 |
cpu: flavor.get('cpu') + 1, |
|
1270 |
ram: flavor.get_ram_size() + 1, |
|
1271 |
disk:flavor.get_disk_size() + 1 |
|
1272 |
} |
|
1273 |
}, |
|
1274 |
|
|
1252 | 1275 |
get_meta: function(key, deflt) { |
1253 | 1276 |
if (this.get('metadata') && this.get('metadata')) { |
1254 | 1277 |
if (!this.get('metadata')[key]) { return deflt } |
... | ... | |
1449 | 1472 |
}, |
1450 | 1473 |
error, 'destroy', params); |
1451 | 1474 |
break; |
1475 |
case 'resize': |
|
1476 |
this.__make_api_call(this.get_action_url(), // vm actions url |
|
1477 |
"create", // create so that sync later uses POST to make the call |
|
1478 |
{resize: {flavorRef:params.flavor}}, // payload |
|
1479 |
function() { |
|
1480 |
success.apply(this, arguments) |
|
1481 |
snf.api.trigger("call"); |
|
1482 |
}, |
|
1483 |
error, 'resize', params); |
|
1484 |
break; |
|
1485 |
case 'destroy': |
|
1486 |
this.__make_api_call(this.url(), // vm actions url |
|
1487 |
"delete", // create so that sync later uses POST to make the call |
|
1488 |
undefined, // payload |
|
1489 |
function() { |
|
1490 |
// set state after successful call |
|
1491 |
self.state('DESTROY'); |
|
1492 |
success.apply(this, arguments); |
|
1493 |
synnefo.storage.quotas.get('cyclades.vm').decrease(); |
|
1494 |
|
|
1495 |
}, |
|
1496 |
error, 'destroy', params); |
|
1497 |
break; |
|
1452 | 1498 |
default: |
1453 | 1499 |
throw "Invalid VM action ("+action_name+")"; |
1454 | 1500 |
} |
... | ... | |
1902 | 1948 |
comparator: function(flv) { |
1903 | 1949 |
return flv.get("disk") * flv.get("cpu") * flv.get("ram"); |
1904 | 1950 |
}, |
1905 |
|
|
1906 |
unavailable_values_for_quotas: function(quotas, flavors) { |
|
1951 |
|
|
1952 |
unavailable_values_for_quotas: function(quotas, flavors, extra) {
|
|
1907 | 1953 |
var flavors = flavors || this.active(); |
1908 | 1954 |
var index = {cpu:[], disk:[], ram:[]}; |
1955 |
var extra = extra == undefined ? {cpu:0, disk:0, ram:0} : extra; |
|
1909 | 1956 |
|
1910 | 1957 |
_.each(flavors, function(el) { |
1911 | 1958 |
|
1912 |
var disk_available = quotas['disk']; |
|
1959 |
var disk_available = quotas['disk'] + extra.disk;
|
|
1913 | 1960 |
var disk_size = el.get_disk_size(); |
1914 | 1961 |
if (index.disk.indexOf(disk_size) == -1) { |
1915 | 1962 |
var disk = el.disk_to_bytes(); |
... | ... | |
1917 | 1964 |
index.disk.push(disk_size); |
1918 | 1965 |
} |
1919 | 1966 |
} |
1920 |
|
|
1921 |
var ram_available = quotas['ram']; |
|
1967 |
|
|
1968 |
var ram_available = quotas['ram'] + extra.ram * 1024 * 1024;
|
|
1922 | 1969 |
var ram_size = el.get_ram_size(); |
1923 | 1970 |
if (index.ram.indexOf(ram_size) == -1) { |
1924 | 1971 |
var ram = el.ram_to_bytes(); |
... | ... | |
1928 | 1975 |
} |
1929 | 1976 |
|
1930 | 1977 |
var cpu = el.get('cpu'); |
1931 |
var cpu_available = quotas['cpu']; |
|
1932 |
if (index.ram.indexOf(cpu) == -1) {
|
|
1978 |
var cpu_available = quotas['cpu'] + extra.cpu;
|
|
1979 |
if (index.cpu.indexOf(cpu) == -1) {
|
|
1933 | 1980 |
if (cpu > cpu_available) { |
1934 | 1981 |
index.cpu.push(el.get('cpu')) |
1935 | 1982 |
} |
... | ... | |
1970 | 2017 |
}, |
1971 | 2018 |
|
1972 | 2019 |
get_data: function(lst) { |
1973 |
var data = {'cpu': [], 'mem':[], 'disk':[]}; |
|
2020 |
var data = {'cpu': [], 'mem':[], 'disk':[], 'disk_template':[]};
|
|
1974 | 2021 |
|
1975 | 2022 |
_.each(lst, function(flv) { |
1976 | 2023 |
if (data.cpu.indexOf(flv.get("cpu")) == -1) { |
... | ... | |
1982 | 2029 |
if (data.disk.indexOf(flv.get("disk")) == -1) { |
1983 | 2030 |
data.disk.push(flv.get("disk")); |
1984 | 2031 |
} |
2032 |
if (data.disk_template.indexOf(flv.get("disk_template")) == -1) { |
|
2033 |
data.disk_template.push(flv.get("disk_template")); |
|
2034 |
} |
|
1985 | 2035 |
}) |
1986 | 2036 |
|
1987 | 2037 |
return data; |
... | ... | |
2444 | 2494 |
return this.get('resource').get('unit') == 'bytes'; |
2445 | 2495 |
}, |
2446 | 2496 |
|
2447 |
get_available: function() { |
|
2448 |
var value = this.get('limit') - this.get('usage'); |
|
2497 |
get_available: function(active) { |
|
2498 |
suffix = ''; |
|
2499 |
if (active) { suffix = '_active'} |
|
2500 |
var value = this.get('limit'+suffix) - this.get('usage'+suffix); |
|
2449 | 2501 |
if (value < 0) { return value } |
2450 | 2502 |
return value |
2451 | 2503 |
}, |
2452 | 2504 |
|
2453 |
get_readable: function(key) { |
|
2505 |
get_readable: function(key, active) {
|
|
2454 | 2506 |
var value; |
2455 | 2507 |
if (key == 'available') { |
2456 |
value = this.get_available(); |
|
2508 |
value = this.get_available(active);
|
|
2457 | 2509 |
} else { |
2458 | 2510 |
value = this.get(key) |
2459 | 2511 |
} |
... | ... | |
2469 | 2521 |
api_type: 'accounts', |
2470 | 2522 |
path: 'quotas', |
2471 | 2523 |
parse: function(resp) { |
2472 |
return _.map(resp.system, function(value, key) {
|
|
2524 |
filtered = _.map(resp.system, function(value, key) {
|
|
2473 | 2525 |
var available = (value.limit - value.usage) || 0; |
2526 |
var available_active = available; |
|
2527 |
var keysplit = key.split("."); |
|
2528 |
var limit_active = value.limit; |
|
2529 |
var usage_active = value.usage; |
|
2530 |
keysplit[keysplit.length-1] = "active_" + keysplit[keysplit.length-1]; |
|
2531 |
var activekey = keysplit.join("."); |
|
2532 |
var exists = resp.system[activekey]; |
|
2533 |
if (exists) { |
|
2534 |
available_active = exists.limit - exists.usage; |
|
2535 |
limit_active = exists.limit; |
|
2536 |
usage_active = exists.usage; |
|
2537 |
} |
|
2474 | 2538 |
return _.extend(value, {'name': key, 'id': key, |
2475 | 2539 |
'available': available, |
2540 |
'available_active': available_active, |
|
2541 |
'limit_active': limit_active, |
|
2542 |
'usage_active': usage_active, |
|
2476 | 2543 |
'resource': snf.storage.resources.get(key)}); |
2477 |
}) |
|
2544 |
}); |
|
2545 |
return filtered; |
|
2546 |
}, |
|
2547 |
|
|
2548 |
get_by_id: function(k) { |
|
2549 |
return this.filter(function(q) { return q.get('name') == k})[0] |
|
2550 |
}, |
|
2551 |
|
|
2552 |
get_available_for_vm: function(active) { |
|
2553 |
var quotas = synnefo.storage.quotas; |
|
2554 |
var key = 'available'; |
|
2555 |
if (active) { key = 'available_active'; } |
|
2556 |
var quota = { |
|
2557 |
'ram': quotas.get('cyclades.ram').get(key), |
|
2558 |
'cpu': quotas.get('cyclades.cpu').get(key), |
|
2559 |
'disk': quotas.get('cyclades.disk').get(key) |
|
2560 |
} |
|
2561 |
return quota; |
|
2478 | 2562 |
} |
2479 | 2563 |
}) |
2480 | 2564 |
|
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_create_view.js | ||
---|---|---|
677 | 677 |
image_excluded = storage.flavors.unavailable_values_for_image(this.current_image); |
678 | 678 |
} |
679 | 679 |
|
680 |
quotas = this.get_vm_params_quotas();
|
|
681 |
user_excluded = storage.flavors.unavailable_values_for_quotas(quotas); |
|
680 |
var quotas = synnefo.storage.quotas.get_available_for_vm({active: true});
|
|
681 |
var user_excluded = storage.flavors.unavailable_values_for_quotas(quotas);
|
|
682 | 682 |
|
683 | 683 |
unavailable.disk = user_excluded.disk.concat(image_excluded.disk); |
684 | 684 |
unavailable.ram = user_excluded.ram.concat(image_excluded.ram); |
... | ... | |
687 | 687 |
this.unavailable_values = unavailable; |
688 | 688 |
}, |
689 | 689 |
|
690 |
get_vm_params_quotas: function() { |
|
691 |
var quotas = synnefo.storage.quotas; |
|
692 |
var quota = { |
|
693 |
'ram': quotas.get('cyclades.ram').get('available'), |
|
694 |
'cpu': quotas.get('cyclades.cpu').get('available'), |
|
695 |
'disk': quotas.get('cyclades.disk').get('available') |
|
696 |
} |
|
697 |
return quota; |
|
698 |
}, |
|
699 |
|
|
700 | 690 |
flavor_is_valid: function(flv) { |
701 | 691 |
if (!flv) { return false }; |
702 | 692 |
|
... | ... | |
942 | 932 |
|
943 | 933 |
var quotas = synnefo.storage.quotas; |
944 | 934 |
_.each(["disk", "ram", "cpu"], function(type) { |
945 |
var available_dsp = quotas.get('cyclades.'+type).get_readable('available'); |
|
946 |
var available = quotas.get('cyclades.'+type).get('available'); |
|
935 |
var active = true; |
|
936 |
var key = 'available'; |
|
937 |
var available_dsp = quotas.get('cyclades.'+type).get_readable(key, active); |
|
938 |
var available = quotas.get('cyclades.'+type).get(key); |
|
947 | 939 |
var content = "({0} left)".format(available_dsp); |
948 | 940 |
if (available <= 0) { content = "(None left)" } |
949 | 941 |
|
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_icon_view.js | ||
---|---|---|
622 | 622 |
this.view_id = "vm_{0}_details".format(vm.id); |
623 | 623 |
|
624 | 624 |
views.VMDetailsView.__super__.initialize.call(this); |
625 |
|
|
625 |
|
|
626 |
this.resize_actions = this.$(".trigger-resize"); |
|
627 |
this.init_handlers(); |
|
626 | 628 |
this.update_layout(); |
627 | 629 |
}, |
630 |
|
|
631 |
init_handlers: function() { |
|
632 |
this.resize_actions.bind('click', _.bind(function(e){ |
|
633 |
ui.main.vm_resize_view.show(this.vm); |
|
634 |
}, this)); |
|
635 |
}, |
|
628 | 636 |
|
629 | 637 |
update_layout: function() { |
630 | 638 |
if (!this.visible() && this.parent.details_hidden) { return }; |
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_main_view.js | ||
---|---|---|
536 | 536 |
storage.vms.bind("change:status", _.bind(this.check_empty, this)); |
537 | 537 |
storage.vms.bind("reset", _.bind(this.check_empty, this)); |
538 | 538 |
storage.quotas.bind("change", _.bind(this.update_create_buttons_status, this)); |
539 |
storage.quotas.bind("add", _.bind(this.update_create_buttons_status, this)); |
|
539 | 540 |
|
540 | 541 |
}, |
541 | 542 |
|
... | ... | |
714 | 715 |
synnefo.glance.register(); |
715 | 716 |
} |
716 | 717 |
this.error_view = new views.ErrorView(); |
718 |
this.vm_resize_view = new views.VmResizeView(); |
|
717 | 719 |
// api request error handling |
718 | 720 |
synnefo.api.bind("error", _.bind(this.handle_api_error, this)); |
719 | 721 |
synnefo.api.bind("change:error_state", _.bind(this.handle_api_error_state, this)); |
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_vm_resize_view.js | ||
---|---|---|
1 |
// Copyright 2013 GRNET S.A. All rights reserved. |
|
2 |
// |
|
3 |
// Redistribution and use in source and binary forms, with or |
|
4 |
// without modification, are permitted provided that the following |
|
5 |
// conditions are met: |
|
6 |
// |
|
7 |
// 1. Redistributions of source code must retain the above |
|
8 |
// copyright notice, this list of conditions and the following |
|
9 |
// disclaimer. |
|
10 |
// |
|
11 |
// 2. Redistributions in binary form must reproduce the above |
|
12 |
// copyright notice, this list of conditions and the following |
|
13 |
// disclaimer in the documentation and/or other materials |
|
14 |
// provided with the distribution. |
|
15 |
// |
|
16 |
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
// POSSIBILITY OF SUCH DAMAGE. |
|
28 |
// |
|
29 |
// The views and conclusions contained in the software and |
|
30 |
// documentation are those of the authors and should not be |
|
31 |
// interpreted as representing official policies, either expressed |
|
32 |
// or implied, of GRNET S.A. |
|
33 |
// |
|
34 |
|
|
35 |
;(function(root){ |
|
36 |
|
|
37 |
// root |
|
38 |
var root = root; |
|
39 |
|
|
40 |
// setup namepsaces |
|
41 |
var snf = root.synnefo = root.synnefo || {}; |
|
42 |
var models = snf.models = snf.models || {} |
|
43 |
var storage = snf.storage = snf.storage || {}; |
|
44 |
var ui = snf.ui = snf.ui || {}; |
|
45 |
var util = snf.util = snf.util || {}; |
|
46 |
|
|
47 |
var views = snf.views = snf.views || {} |
|
48 |
|
|
49 |
// shortcuts |
|
50 |
var bb = root.Backbone; |
|
51 |
|
|
52 |
|
|
53 |
views.FlavorOptionsView = views.View.extend({ |
|
54 |
|
|
55 |
choices_meta: { |
|
56 |
'cpu': {title: 'CPUs', description: 'Choose number of CPU cores'}, |
|
57 |
'ram': {title: 'Memory size', description: 'Choose memory size'}, |
|
58 |
'disk': {title: 'Disk size', description: 'Choose disk size'}, |
|
59 |
'disk_template': {title: 'Storage', description: 'Select storage type'} |
|
60 |
}, |
|
61 |
|
|
62 |
initialize: function(options) { |
|
63 |
views.FlavorOptionsView.__super__.initialize.apply(this); |
|
64 |
_.bindAll(this); |
|
65 |
this.$el = $(this.el); |
|
66 |
this.flavors = options.flavors || synnefo.storage.flavors.active(); |
|
67 |
this.collection = options.collection || synnefo.storage.flavors; |
|
68 |
this.choices = options.choices || ['cpu', 'ram', 'disk', |
|
69 |
'disk_template']; |
|
70 |
this.hidden_choices = options.hidden_choices || []; |
|
71 |
this.quotas = options.quotas || synnefo.storage.quotas; |
|
72 |
this.selected_flavor = options.selected_flavor || undefined; |
|
73 |
this.extra_quotas = options.extra_quotas || undefined; |
|
74 |
this.render(); |
|
75 |
if (this.selected_flavor) { this.set_flavor(this.selected_flavor)} |
|
76 |
}, |
|
77 |
|
|
78 |
render: function() { |
|
79 |
this.$el.empty(); |
|
80 |
this.each_choice(function(choice) { |
|
81 |
var el = this[choice + '_el'] = $("<div>"); |
|
82 |
el.addClass("flavor-options clearfix"); |
|
83 |
el.addClass(choice); |
|
84 |
var title = $("<h4 class='clearfix'><span class='title'>" + |
|
85 |
"</span><span class='available'></span>" + |
|
86 |
"<span class='desc'></span></h4>"); |
|
87 |
el.append(title); |
|
88 |
el.find("span.title").text(this.choices_meta[choice].title); |
|
89 |
el.find("span.desc").text(this.choices_meta[choice].description); |
|
90 |
var ul = $("<ul/>"); |
|
91 |
ul.addClass("flavors-"+choice+"-list flavor-opts-list clearfix"); |
|
92 |
el.append(ul); |
|
93 |
this.$el.append(el); |
|
94 |
if (this.hidden_choices.indexOf(choice) > -1) { |
|
95 |
el.hide(); |
|
96 |
} |
|
97 |
}); |
|
98 |
this.update_quota_left(); |
|
99 |
this.fill(); |
|
100 |
this.set_unavailable(); |
|
101 |
this.init_handlers(); |
|
102 |
}, |
|
103 |
|
|
104 |
init_handlers: function() { |
|
105 |
this.$el.on('click', 'li.choice', _.bind(function(e) { |
|
106 |
var el = $(e.target).closest('li'); |
|
107 |
if (el.hasClass('disabled')) { return } |
|
108 |
var choice = el.data('type'); |
|
109 |
var value = el.data('value'); |
|
110 |
var to_select = this.selected_flavor; |
|
111 |
if (to_select) { |
|
112 |
var attrs = _.clone(to_select.attributes); |
|
113 |
attrs[choice] = value; |
|
114 |
to_select = this.collection.get_flavor(attrs.cpu, attrs.ram, |
|
115 |
attrs.disk, |
|
116 |
attrs.disk_template); |
|
117 |
} |
|
118 |
|
|
119 |
if (!to_select) { |
|
120 |
var found = false; |
|
121 |
_.each(this.flavors, _.bind(function(f){ |
|
122 |
if (found) { return } |
|
123 |
if (f.get(choice) == value) { |
|
124 |
found = true; |
|
125 |
to_select = f; |
|
126 |
} |
|
127 |
}, this)); |
|
128 |
} |
|
129 |
this.set_flavor(to_select); |
|
130 |
}, this)); |
|
131 |
}, |
|
132 |
|
|
133 |
update_quota_left: function() { |
|
134 |
this.each_choice(function(choice){ |
|
135 |
var el = this[choice + '_el'].find(".available"); |
|
136 |
el.removeClass("error"); |
|
137 |
var quota = this.quotas.get('cyclades.'+choice); |
|
138 |
if (!quota) { return } |
|
139 |
var type = choice; |
|
140 |
var active = true; |
|
141 |
var key = 'available'; |
|
142 |
var quotas = synnefo.storage.quotas; |
|
143 |
var available_dsp = quotas.get('cyclades.'+type).get_readable(key, active); |
|
144 |
var available = quotas.get('cyclades.'+type).get(key); |
|
145 |
var content = "({0} left)".format(available_dsp); |
|
146 |
if (available <= 0) { content = "(None left)"; el.addClass("error") } |
|
147 |
el.text(content); |
|
148 |
}); |
|
149 |
}, |
|
150 |
|
|
151 |
metric_for_choice: function(choice) { |
|
152 |
var map = {ram:'GB', cpu:'X', disk:'GB', disk_template:''}; |
|
153 |
return map[choice] || ''; |
|
154 |
}, |
|
155 |
|
|
156 |
set_unavailable: function() { |
|
157 |
this.$el.find("li.choice").removeClass("disabled"); |
|
158 |
var quotas = this.quotas.get_available_for_vm({'active': true}); |
|
159 |
var extra_quotas = this.extra_quotas; |
|
160 |
var user_excluded = storage.flavors.unavailable_values_for_quotas( |
|
161 |
quotas, |
|
162 |
storage.flavors.active()); |
|
163 |
_.each(user_excluded, _.bind(function(values, key) { |
|
164 |
_.each(values, _.bind(function(value) { |
|
165 |
var choice_el = this.select_choice(key, value); |
|
166 |
choice_el.addClass("disabled"); |
|
167 |
}, this)); |
|
168 |
}, this)); |
|
169 |
}, |
|
170 |
|
|
171 |
select_choice: function(key, value) { |
|
172 |
return this.$el.find(".choice[data-type="+key+"][data-value="+value+"]"); |
|
173 |
}, |
|
174 |
|
|
175 |
fill: function(flavors) { |
|
176 |
var flavors = flavors || this.flavors; |
|
177 |
var data = this.collection.get_data(flavors); |
|
178 |
this.each_choice(function(choice) { |
|
179 |
var el = this[choice + '_el'].find("ul"); |
|
180 |
el.empty(); |
|
181 |
var key = choice; |
|
182 |
if (key == 'ram') { key = 'mem'} |
|
183 |
var values = data[key]; |
|
184 |
if (!values) { return } |
|
185 |
_.each(values, _.bind(function(value) { |
|
186 |
var entry = $("<li class='choice choice-" + choice + "' " + |
|
187 |
"data-value=" + value + |
|
188 |
" data-type=" + choice + ">" + |
|
189 |
"<span class='value'></span>" + |
|
190 |
"<span class='metric'></span></li>"); |
|
191 |
entry.find(".value").text(value); |
|
192 |
entry.find(".metric").text(this.metric_for_choice(choice)); |
|
193 |
el.append(entry); |
|
194 |
el.attr('value', value); |
|
195 |
}, this)); |
|
196 |
}); |
|
197 |
}, |
|
198 |
|
|
199 |
set_flavor: function(flavor) { |
|
200 |
this.$el.find("li").removeClass("selected"); |
|
201 |
if (!flavor) {this.selected_flavor = undefined; return} |
|
202 |
this.each_choice(function(choice){ |
|
203 |
var el = this[choice + '_el']; |
|
204 |
var choice = el.find('.choice-'+choice+'[data-value='+flavor.get(choice)+']'); |
|
205 |
choice.addClass("selected"); |
|
206 |
}); |
|
207 |
this.selected_flavor = flavor; |
|
208 |
this.trigger("flavor:select", this.selected_flavor); |
|
209 |
}, |
|
210 |
|
|
211 |
each_choice: function(f) { |
|
212 |
return _.each(this.choices, _.bind(f, this)); |
|
213 |
} |
|
214 |
}); |
|
215 |
|
|
216 |
views.VmResizeView = views.Overlay.extend({ |
|
217 |
|
|
218 |
view_id: "vm_resize_view", |
|
219 |
content_selector: "#vm-resize-overlay-content", |
|
220 |
css_class: 'overlay-vm-resize overlay-info', |
|
221 |
overlay_id: "vm-resize-overlay", |
|
222 |
|
|
223 |
subtitle: "", |
|
224 |
title: "Resize Machine", |
|
225 |
|
|
226 |
initialize: function(options) { |
|
227 |
this.flavors_view = undefined; |
|
228 |
views.VmResizeView.__super__.initialize.apply(this); |
|
229 |
_.bindAll(this); |
|
230 |
this.submit = this.$(".form-action"); |
|
231 |
this.pre_init_handlers(); |
|
232 |
}, |
|
233 |
|
|
234 |
pre_init_handlers: function() { |
|
235 |
this.submit.click(_.bind(function(){ |
|
236 |
if (this.submit.hasClass("disabled")) { |
|
237 |
return; |
|
238 |
}; |
|
239 |
this.submit_resize(this.flavors_view.selected_flavor); |
|
240 |
}, this)); |
|
241 |
}, |
|
242 |
|
|
243 |
submit_resize: function(flv) { |
|
244 |
this.submit.addClass("in-progress"); |
|
245 |
var complete = _.bind(function() { |
|
246 |
this.vm.set({'flavor': flv}); |
|
247 |
this.vm.set({'flavorRef': flv.id}); |
|
248 |
this.hide() |
|
249 |
}, this); |
|
250 |
this.vm.call("resize", complete, complete, {flavor:flv.id}); |
|
251 |
}, |
|
252 |
|
|
253 |
show: function(vm) { |
|
254 |
this.submit.removeClass("in-progress"); |
|
255 |
this.vm = vm; |
|
256 |
this.vm.bind("change", this.handle_vm_change); |
|
257 |
if (this.flavors_view) { |
|
258 |
this.flavors_view.remove(); |
|
259 |
} |
|
260 |
|
|
261 |
if (!this.vm.can_resize()) { |
|
262 |
this.$(".warning").show(); |
|
263 |
this.submit.hide(); |
|
264 |
} else { |
|
265 |
this.$(".warning").hide(); |
|
266 |
this.submit.show(); |
|
267 |
this.$(".flavor-options-inner-cont").append("<div>"); |
|
268 |
this.flavors_view = new snf.views.FlavorOptionsView({ |
|
269 |
flavors:this.vm.get_resize_flavors(), |
|
270 |
el: this.$(".flavor-options-inner-cont div"), |
|
271 |
hidden_choices:['disk', 'disk_template'], |
|
272 |
selected_flavor: this.vm.get_flavor(), |
|
273 |
extra_quotas: this.vm.get_flavor_quotas() |
|
274 |
}); |
|
275 |
this.flavors_view.bind("flavor:select", this.handle_flavor_select) |
|
276 |
this.submit.addClass("disabled"); |
|
277 |
} |
|
278 |
views.VmResizeView.__super__.show.apply(this); |
|
279 |
}, |
|
280 |
|
|
281 |
handle_flavor_select: function(flv) { |
|
282 |
if (flv.id == this.vm.get_flavor().id) { |
|
283 |
this.submit.addClass("disabled"); |
|
284 |
} else { |
|
285 |
this.submit.removeClass("disabled"); |
|
286 |
} |
|
287 |
}, |
|
288 |
|
|
289 |
beforeOpen: function() { |
|
290 |
this.update_layout(); |
|
291 |
this.init_handlers(); |
|
292 |
}, |
|
293 |
|
|
294 |
update_layout: function() { |
|
295 |
this.update_vm_details(); |
|
296 |
this.render_choices(); |
|
297 |
}, |
|
298 |
|
|
299 |
render_choices: function() { |
|
300 |
}, |
|
301 |
|
|
302 |
update_vm_details: function() { |
|
303 |
this.set_subtitle(this.vm.escape("name") + |
|
304 |
snf.ui.helpers.vm_icon_tag(this.vm, |
|
305 |
"small")); |
|
306 |
}, |
|
307 |
|
|
308 |
handle_vm_change: function() { |
|
309 |
this.update_layout(); |
|
310 |
}, |
|
311 |
|
|
312 |
init_handlers: function() { |
|
313 |
}, |
|
314 |
|
|
315 |
onClose: function() { |
|
316 |
this.editing = false; |
|
317 |
this.vm.unbind("change", this.handle_vm_change); |
|
318 |
this.vm = undefined; |
|
319 |
} |
|
320 |
}); |
|
321 |
|
|
322 |
})(this); |
|
323 |
|
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_vms_base_view.js | ||
---|---|---|
326 | 326 |
} |
327 | 327 |
|
328 | 328 |
var el = this.vm(vm); |
329 |
if (vm.can_resize()) { |
|
330 |
el.addClass("can-resize"); |
|
331 |
} else { |
|
332 |
el.removeClass("can-resize"); |
|
333 |
} |
|
334 |
|
|
329 | 335 |
if (vm.get('suspended')) { |
330 | 336 |
el.addClass("suspended"); |
331 | 337 |
} else { |
b/snf-cyclades-app/synnefo/ui/templates/home.html | ||
---|---|---|
73 | 73 |
<script src="{{ SYNNEFO_JS_WEB_URL }}ui_feedback_view.js"></script> |
74 | 74 |
<script src="{{ SYNNEFO_JS_WEB_URL }}ui_create_view.js"></script> |
75 | 75 |
<script src="{{ SYNNEFO_JS_WEB_URL }}ui_connect_view.js"></script> |
76 |
<script src="{{ SYNNEFO_JS_WEB_URL }}ui_vm_resize_view.js"></script> |
|
76 | 77 |
<script src="{{ SYNNEFO_JS_WEB_URL }}ui_public_keys_view.js"></script> |
77 | 78 |
<script src="{{ SYNNEFO_JS_WEB_URL }}ui_custom_images.js"></script> |
78 | 79 |
|
... | ... | |
568 | 569 |
<div id="user_custom_images" class="overlay-content overlay-content hidden"> |
569 | 570 |
{% include "partials/custom_images.html" %} |
570 | 571 |
</div> |
572 |
{% include "partials/vm_resize.html" %} |
|
571 | 573 |
{% include "footer.html" %} |
572 | 574 |
|
573 | 575 |
<script> |
... | ... | |
678 | 680 |
synnefo.ui.init(); |
679 | 681 |
synnefo.ui.main.bind("ready", function(){ |
680 | 682 |
}); |
683 |
window.setTimeout(function(){ |
|
684 |
$($(".machine-detail.ram.trigger-resize")[0]).click(); |
|
685 |
}, 800); |
|
681 | 686 |
|
682 | 687 |
}) |
683 | 688 |
</script> |
b/snf-cyclades-app/synnefo/ui/templates/partials/machines_icon.html | ||
---|---|---|
54 | 54 |
<div class="metadata-container"> |
55 | 55 |
<div class="vm-details metadata-column"> |
56 | 56 |
<div class="flavor-details"> |
57 |
<span class="vm-detail"><span class="key">{% trans "CPUs" %}:</span> <span class="value"><span class="cpu-data">1</span></span></span> |
|
58 |
<span class="vm-detail"><span class="key">{% trans "RAM" %}:</span><span class="value"><span class="ram-data">2048</span>MB</span></span> |
|
59 |
<span class="vm-detail"><span class="key">{% trans "System Disk" %}:</span> <span class="value"><span class="disk-data">20</span>GB</span></span> |
|
60 |
</div> |
|
57 |
<span class="vm-detail"><span class="key"> |
|
58 |
{% trans "CPUs" %}:</span> <span |
|
59 |
class="value"><span |
|
60 |
class="cpu-data trigger-resize">1</span></span></span> |
|
61 |
<span class="vm-detail"><span class="key"> |
|
62 |
{% trans "RAM" %}:</span><span |
|
63 |
class="value trigger-resize"><span |
|
64 |
class="ram-data">2048</span>MB</span></span> |
|
65 |
<span class="vm-detail"><span class="key"> |
|
66 |
{% trans "System Disk" %}:</span> <span |
|
67 |
class="value"><span |
|
68 |
class="disk-data">20</span>GB</span></span> |
|
69 |
</div> |
|
61 | 70 |
<div class="image-details"> |
62 | 71 |
<span class="vm-detail"><span class="key">{% trans "Image" %}:</span> <span class="value"><span class="image-data">Debian</span></span></span> |
63 | 72 |
<span class="vm-detail"><span class="key">{% trans "Image Size" %}:</span> <span class="value"><span class="image-size-data">2.3</span></span></span> |
b/snf-cyclades-app/synnefo/ui/templates/partials/machines_single.html | ||
---|---|---|
39 | 39 |
<div class="machine-label ipv6">{% trans "Public IPv6" %}:</div> |
40 | 40 |
</div> |
41 | 41 |
<div class="machine-details"> |
42 |
<div class="machine-detail cpus">4</div> |
|
43 |
<div class="machine-detail ram">2048</div> |
|
42 |
<div class="machine-detail cpus trigger-resize">4</div>
|
|
43 |
<div class="machine-detail ram trigger-resize">2048</div>
|
|
44 | 44 |
<div class="machine-detail disk">100</div> |
45 | 45 |
<div class="machine-detail image-name">windos_XP_blah_blah</div> |
46 | 46 |
<div class="machine-detail image-size">2.3</div> |
b/snf-cyclades-app/synnefo/ui/templates/partials/vm_resize.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
|
|
3 |
<div id="vm-resize-overlay-content" class="hidden vm-resize content create-vm"> |
|
4 |
<div class="warning hidden"> |
|
5 |
{% blocktrans %} |
|
6 |
Machine resize is only available for machines in shutdown state. |
|
7 |
{% endblocktrans %} |
|
8 |
</div> |
|
9 |
<div class="flavor-options-cont content-cont clearfix"> |
|
10 |
<div class="flavor-options-inner-cont clearfix" |
|
11 |
id="create-vm-flavor-options"> |
|
12 |
</div> |
|
13 |
<div class="form-actions plain clearfix"> |
|
14 |
<span class="form-action create">{% trans "resize machine" %}</span> |
|
15 |
</div> |
|
16 |
</div> |
|
17 |
</div> |
|
18 |
|
Also available in: Unified diff