diff --git a/src/sunstone/models/OpenNebulaJSON/GroupJSON.rb b/src/sunstone/models/OpenNebulaJSON/GroupJSON.rb
index d502973d0c..35e01f1403 100644
--- a/src/sunstone/models/OpenNebulaJSON/GroupJSON.rb
+++ b/src/sunstone/models/OpenNebulaJSON/GroupJSON.rb
@@ -45,7 +45,8 @@ module OpenNebulaJSON
end
rc = case action_hash['perform']
- when "chown" then self.chown(action_hash['params'])
+ when "chown" then self.chown(action_hash['params'])
+ when "set_quota" then self.set_quota(action_hash['params'])
else
error_msg = "#{action_hash['perform']} action not " <<
" available for this resource"
@@ -56,5 +57,11 @@ module OpenNebulaJSON
def chown(params=Hash.new)
super(params['owner_id'].to_i)
end
+
+ def set_quota(params=Hash.new)
+ quota_json = params['quotas']
+ quota_template = template_to_str(quota_json)
+ super(quota_template)
+ end
end
end
diff --git a/src/sunstone/models/OpenNebulaJSON/UserJSON.rb b/src/sunstone/models/OpenNebulaJSON/UserJSON.rb
index c51c0bcfb2..ebf4338802 100644
--- a/src/sunstone/models/OpenNebulaJSON/UserJSON.rb
+++ b/src/sunstone/models/OpenNebulaJSON/UserJSON.rb
@@ -42,6 +42,7 @@ module OpenNebulaJSON
when "chgrp" then self.chgrp(action_hash['params'])
when "chauth" then self.chauth(action_hash['params'])
when "update" then self.update(action_hash['params'])
+ when "set_quota" then self.set_quota(action_hash['params'])
when "addgroup" then self.addgroup(action_hash['params'])
when "delgroup" then self.delgroup(action_hash['params'])
else
@@ -67,6 +68,12 @@ module OpenNebulaJSON
super(params['template_raw'])
end
+ def set_quota(params=Hash.new)
+ quota_json = params['quotas']
+ quota_template = template_to_str(quota_json)
+ super(quota_template)
+ end
+
def addgroup(params=Hash.new)
super(params['group_id'].to_i)
end
diff --git a/src/sunstone/public/css/application.css b/src/sunstone/public/css/application.css
index bed2567dca..36feb13fbd 100644
--- a/src/sunstone/public/css/application.css
+++ b/src/sunstone/public/css/application.css
@@ -663,4 +663,12 @@ ul.action_list li a:hover{
font-family: serif;
text-align:center;
vertical-align:middle;
+}
+
+.quota_edit_icon:hover, .quota_remove_icon:hover {
+ cursor: pointer;
+}
+
+div.current_quotas ul{
+ list-style: none;
}
\ No newline at end of file
diff --git a/src/sunstone/public/js/opennebula.js b/src/sunstone/public/js/opennebula.js
index fcc7fac15b..716a1ebdda 100644
--- a/src/sunstone/public/js/opennebula.js
+++ b/src/sunstone/public/js/opennebula.js
@@ -693,6 +693,13 @@ var OpenNebula = {
},
"list": function(params){
OpenNebula.Action.list(params,OpenNebula.Group.resource);
+ },
+ "set_quota" : function(params){
+ var action_obj = { quotas : params.data.extra_param };
+ OpenNebula.Action.simple_action(params,OpenNebula.Group.resource,"set_quota",action_obj);
+ },
+ "show" : function(params){
+ OpenNebula.Action.show(params,OpenNebula.Group.resource);
}
},
@@ -736,6 +743,11 @@ var OpenNebula = {
"fetch_template" : function(params){
OpenNebula.Action.show(params,OpenNebula.User.resource,"template");
},
+ "set_quota" : function(params){
+ var action_obj = { quotas : params.data.extra_param };
+ OpenNebula.Action.simple_action(params,OpenNebula.User.resource,"set_quota",action_obj);
+ },
+
// "addgroup" : function(params){
// var action_obj = {"group_id": params.data.extra_param };
// OpenNebula.Action.simple_action(params,OpenNebula.User.resource,
diff --git a/src/sunstone/public/js/plugins/groups-tab.js b/src/sunstone/public/js/plugins/groups-tab.js
index 03edb0f522..df5c7fe6ff 100644
--- a/src/sunstone/public/js/plugins/groups-tab.js
+++ b/src/sunstone/public/js/plugins/groups-tab.js
@@ -17,6 +17,7 @@
var groups_select="";
var dataTable_groups;
var $create_group_dialog;
+var $group_quotas_dialog;
var groups_tab_content = '\
'+tr("Groups")+'
\
@@ -59,6 +60,69 @@ var create_group_tmpl =
\
';
+var group_quotas_tmpl = '';
+
var group_actions = {
"Group.create" : {
@@ -119,6 +183,34 @@ var group_actions = {
// error : onError,
// notify:true
// },
+
+ "Group.fetch_quotas" : {
+ type: "single",
+ call: OpenNebula.Group.show,
+ callback: function (request,response) {
+ var parsed = parseQuotas(response.GROUP);
+ $('ul#quotas_ul_vm',$group_quotas_dialog).html(parsed.VM)
+ $('ul#quotas_ul_datastore',$group_quotas_dialog).html(parsed.DATASTORE)
+ $('ul#quotas_ul_image',$group_quotas_dialog).html(parsed.IMAGE)
+ $('ul#quotas_ul_network',$group_quotas_dialog).html(parsed.NETWORK)
+ },
+ error: onError
+ },
+
+ "Group.quotas_dialog" : {
+ type: "custom",
+ call: popUpGroupQuotasDialog
+ },
+
+ "Group.set_quota" : {
+ type: "multiple",
+ call: OpenNebula.Group.set_quota,
+ elements: groupElements,
+ callback: function() {
+ notifyMessage(tr("Quotas updated correctly"));
+ },
+ error: onError
+ },
"Group.help" : {
type: "custom",
call: function() {
@@ -145,7 +237,10 @@ var group_buttons = {
// tip: "Select the new group owner:",
// condition : True
// },
-
+ "Group.quotas_dialog" : {
+ type : "action",
+ text : tr("Update quotas")
+ },
"Group.delete" : {
type: "confirm",
text: tr("Delete")
@@ -276,6 +371,19 @@ function popUpCreateGroupDialog(){
return false;
}
+function setupGroupQuotasDialog(){
+ dialogs_context.append('');
+ $group_quotas_dialog = $('#group_quotas_dialog',dialogs_context);
+ var dialog = $group_quotas_dialog;
+ dialog.html(group_quotas_tmpl);
+
+ setupQuotasDialog(dialog);
+}
+
+function popUpGroupQuotasDialog(){
+ popUpQuotasDialog($group_quotas_dialog, 'Group', groupElements())
+}
+
//Prepares the autorefresh
function setGroupAutorefresh(){
setInterval(function(){
@@ -312,6 +420,7 @@ $(document).ready(function(){
Sunstone.runAction("Group.list");
setupCreateGroupDialog();
+ setupGroupQuotasDialog();
setGroupAutorefresh();
initCheckAllBoxes(dataTable_groups);
diff --git a/src/sunstone/public/js/plugins/users-tab.js b/src/sunstone/public/js/plugins/users-tab.js
index c3ed6343fd..543da2c588 100644
--- a/src/sunstone/public/js/plugins/users-tab.js
+++ b/src/sunstone/public/js/plugins/users-tab.js
@@ -18,6 +18,7 @@
var dataTable_users;
var users_select="";
var $create_user_dialog;
+var $user_quotas_dialog;
var $update_pw_dialog;
var users_tab_content = '\
@@ -100,6 +101,69 @@ var update_pw_tmpl = '';
+var user_quotas_tmpl = '';
+
var user_actions = {
"User.create" : {
@@ -254,6 +318,34 @@ var user_actions = {
error: onError
},
+ "User.fetch_quotas" : {
+ type: "single",
+ call: OpenNebula.User.show,
+ callback: function (request,response) {
+ var parsed = parseQuotas(response.USER);
+ $('ul#quotas_ul_vm',$user_quotas_dialog).html(parsed.VM)
+ $('ul#quotas_ul_datastore',$user_quotas_dialog).html(parsed.DATASTORE)
+ $('ul#quotas_ul_image',$user_quotas_dialog).html(parsed.IMAGE)
+ $('ul#quotas_ul_network',$user_quotas_dialog).html(parsed.NETWORK)
+ },
+ error: onError
+ },
+
+ "User.quotas_dialog" : {
+ type: "custom",
+ call: popUpUserQuotasDialog
+ },
+
+ "User.set_quota" : {
+ type: "multiple",
+ call: OpenNebula.User.set_quota,
+ elements: userElements,
+ callback: function() {
+ notifyMessage(tr("Quotas updated correctly"));
+ },
+ error: onError
+ },
+
"User.help" : {
type: "custom",
call: function() {
@@ -283,6 +375,10 @@ var user_buttons = {
type : "action",
text : tr("Change password"),
},
+ "User.quotas_dialog" : {
+ type : "action",
+ text : tr("Update quotas")
+ },
"User.chgrp" : {
type: "confirm_with_select",
text: tr("Change group"),
@@ -331,6 +427,10 @@ var user_info_panel = {
title: tr("User information"),
content:""
},
+ "user_quotas_tab" : {
+ title: tr("User quotas"),
+ content:""
+ },
};
var users_tab = {
@@ -473,7 +573,25 @@ function updateUserInfo(request,user){
''
};
+ var quotas_tab = {
+ title : tr("User quotas"),
+ content : '\
+ \
+ '+prettyPrintJSON(user_info.DATASTORE_QUOTA)+'\
+
\
+ \
+ '+prettyPrintJSON(user_info.VM_QUOTA)+'\
+
\
+ \
+ '+prettyPrintJSON(user_info.IMAGE_QUOTA)+'\
+
\
+ \
+ '+prettyPrintJSON(user_info.NETWORK_QUOTA)+'\
+
'
+ };
+
Sunstone.updateInfoPanelTab("user_info_panel","user_info_tab",info_tab);
+ Sunstone.updateInfoPanelTab("user_info_panel","user_quotas_tab",quotas_tab);
Sunstone.popUpInfoPanel("user_info_panel");
};
@@ -554,8 +672,22 @@ function setupUpdatePasswordDialog(){
});
};
+function setupUserQuotasDialog(){
+ dialogs_context.append('');
+ $user_quotas_dialog = $('#user_quotas_dialog',dialogs_context);
+ var dialog = $user_quotas_dialog;
+ dialog.html(user_quotas_tmpl);
+
+ setupQuotasDialog(dialog);
+}
+
+function popUpUserQuotasDialog(){
+ popUpQuotasDialog($user_quotas_dialog, 'User', userElements())
+}
+
function popUpCreateUserDialog(){
$create_user_dialog.dialog('open');
+
}
@@ -564,8 +696,6 @@ function popUpUpdatePasswordDialog(){
$update_pw_dialog.dialog('open');
}
-
-
// Prepare the autorefresh of the list
function setUserAutorefresh(){
setInterval(function(){
@@ -610,6 +740,7 @@ $(document).ready(function(){
setupCreateUserDialog();
setupUpdatePasswordDialog();
+ setupUserQuotasDialog();
setUserAutorefresh();
initCheckAllBoxes(dataTable_users);
diff --git a/src/sunstone/public/js/sunstone-util.js b/src/sunstone/public/js/sunstone-util.js
index f887985a43..dbdb401bf7 100644
--- a/src/sunstone/public/js/sunstone-util.js
+++ b/src/sunstone/public/js/sunstone-util.js
@@ -953,4 +953,208 @@ function buildOctet(permTable){
other+=1;
return ""+owner+group+other;
-};
\ No newline at end of file
+};
+
+function setupQuotasDialog(dialog){
+
+ var height = Math.floor($(window).height()*0.8); //set height to a percentage of the window
+
+ //Prepare jquery dialog
+ dialog.dialog({
+ autoOpen: false,
+ modal:true,
+ width: 740,
+ height: height
+ });
+
+ $('button',dialog).button();
+ $('#vm_quota,#datastore_quota,#image_quota,#network_quota',dialog).hide();
+
+ $('#quota_types input',dialog).click(function(){
+ $('#vm_quota,#datastore_quota,#image_quota,#network_quota',dialog).hide();
+ $('#'+$(this).val()+'_quota',dialog).show();
+ $('#add_quota_button',dialog).show();
+ })
+
+ $('#add_quota_button',dialog).hide();
+
+ $('#add_quota_button',dialog).click(function(){
+ var sel = $('#quota_types input:checked',dialog).val();
+ var fields = $('div#'+sel+'_quota input,div#'+sel+'_quota select',dialog);
+ var json = {};
+
+ for (var i = 0; i < fields.length; i++){
+ var field = $(fields[i]);
+ var name = field.attr('name');
+ var value = field.val();
+ if (name == 'ID' && !value.length){
+ notifyError(tr("Please select an element"));
+ return false;
+ };
+ if (!value) value = 0;
+ json[name] = value;
+ };
+
+ json['TYPE'] = sel.toUpperCase();
+
+ var li = quotaListItem(json)
+ $('ul#quotas_ul_'+sel,dialog).append($(li).hide().fadeIn());
+ return false;
+ });
+
+ $('form', dialog).submit(function(){
+ var obj = {};
+ $('ul li',this).each(function(){
+ var json = JSON.parse($(this).attr('quota'));
+ var type = json['TYPE'];
+ delete json['TYPE'];
+ obj[type.toUpperCase()] = json;
+ });
+
+ var action = $('div.form_buttons button',this).val();
+ var sel_elems = SunstoneCfg["actions"][action].elements();
+ Sunstone.runAction(action,sel_elems,obj);
+ dialog.dialog('close');
+ return false;
+ });
+}
+
+function popUpQuotasDialog(dialog, resource, sel_elems){
+ var im_sel = makeSelectOptions(dataTable_images,1,4,[],[]);
+ var vn_sel = makeSelectOptions(dataTable_vNetworks,1,4,[],[]);
+ $('#datastore_quota select',dialog).html(datastores_sel());
+ $('#image_quota select',dialog).html(im_sel);
+ $('#network_quota select',dialog).html(vn_sel);
+
+
+ //If only one user is selected we fecth the user's quotas, otherwise we do nothing.
+ if (sel_elems.length == 1){
+ var id = sel_elems[0];
+ Sunstone.runAction(resource + '.fetch_quotas',id);
+ } else {
+ $('ul',dialog).empty();
+ };
+
+ dialog.dialog('open');
+}
+
+function setupQuotaIcons(){
+ $('.quota_edit_icon').live('click',function(){
+ var dialog = $(this).parents('form');
+ var li = $(this).parents('li');
+ var quota = JSON.parse(li.attr('quota'));
+ switch (quota.TYPE){
+ case "VM":
+ $('div#vm_quota input[name="VMS"]',dialog).val(quota.VMS);
+ $('div#vm_quota input[name="MEMORY"]',dialog).val(quota.MEMORY);
+ $('div#vm_quota input[name="CPU"]',dialog).val(quota.CPU);
+ break;
+ case "DATASTORE":
+ $('div#datastore_quota select[name="ID"]',dialog).val(quota.ID);
+ $('div#datastore_quota input[name="SIZE"]',dialog).val(quota.SIZE);
+ $('div#datastore_quota input[name="IMAGES"]').val(quota.IMAGES);
+ break;
+ case "IMAGE":
+ $('div#image_quota select[name="ID"]',dialog).val(quota.ID);
+ $('div#image_quota input[name="RVMS"]',dialog).val(quota.RVMS);
+ break;
+ case "NETWORK":
+ $('div#network_quota select[name="ID"]',dialog).val(quota.ID);
+ $('div#network_quota input[name="LEASES"]',dialog).val(quota.LEASES);
+ break;
+ }
+ $('div#quota_types input[value="'+quota.TYPE.toLowerCase()+'"]',dialog).trigger('click');
+ $(this).parents('li').fadeOut(function(){$(this).remove()});
+ return false;
+ });
+
+ $('.quota_remove_icon').live('click',function(){
+ $(this).parents('li').fadeOut(function(){$(this).remove()});
+ return false;
+ });
+}
+
+function parseQuotas(elem){
+ var quotas = [];
+ var results = {
+ VM : "",
+ DATASTORE : "",
+ IMAGE : "",
+ NETWORK : ""
+ }
+ //max 1 vm quota
+ if (!$.isEmptyObject(elem.VM_QUOTA)){
+ elem.VM_QUOTA.VM.TYPE = 'VM'
+ quotas.push(elem.VM_QUOTA.VM)
+ }
+
+ var ds_arr = []
+ if ($.isArray(elem.DATASTORE_QUOTA.DATASTORE)){
+ ds_arr = elem.DATASTORE_QUOTA.DATASTORE
+ } else if (!$.isEmptyObject(elem.DATASTORE_QUOTA)){
+ ds_arr = [elem.DATASTORE_QUOTA.DATASTORE]
+ }
+
+ for (var i = 0; i < ds_arr.length; i++){
+ ds_arr[i].TYPE = 'DATASTORE';
+ quotas.push(ds_arr[i]);
+ }
+
+ var im_arr = []
+ if ($.isArray(elem.IMAGE_QUOTA.IMAGE)){
+ im_arr = elem.IMAGE_QUOTA.IMAGE
+ } else if (!$.isEmptyObject(elem.IMAGE_QUOTA)){
+ im_arr = [elem.IMAGE_QUOTA.IMAGE]
+ }
+
+ for (var i = 0; i < im_arr.length; i++){
+ im_arr[i].TYPE = 'IMAGE';
+ quotas.push(im_arr[i]);
+ }
+
+ var vn_arr = []
+ if ($.isArray(elem.NETWORK_QUOTA)){
+ vn_arr = elem.NETWORK_QUOTA.NETWORK
+ } else if (!$.isEmptyObject(elem.NETWORK_QUOTA)){
+ vn_arr = [elem.NETWORK_QUOTA.NETWORK]
+ }
+
+ for (var i = 0; i < vn_arr.length; i++){
+ vn_arr[i].TYPE = 'NETWORK';
+ quotas.push(vn_arr[i]);
+ }
+
+ for (var i = 0; i < quotas.length; i++){
+ var li = quotaListItem(quotas[i]);
+ results[quotas[i].TYPE] += li;
+ }
+ return results;
+}
+
+//Receives a quota json object. Returns a nice string out of it.
+function quotaListItem(quota_json){
+ var value = JSON.stringify(quota_json)
+ var str = '';
+ switch(quota_json.TYPE){
+ case "VM":
+ str += 'VMs: ' + quota_json.VMS + (quota_json.VMS_USED ? ' (' + quota_json.VMS_USED + '). ' : ". ") +
+ 'Memory: ' + quota_json.MEMORY + (quota_json.MEMORY_USED ? ' (' + quota_json.MEMORY_USED + '). ' : ". ") +
+ 'CPU: ' + quota_json.CPU + (quota_json.CPU_USED ? ' (' + quota_json.CPU_USED + '). ' : ". ");
+ break;
+ case "DATASTORE":
+ str += 'ID: ' + getDatastoreName(quota_json.ID) + '. ' +
+ 'Size: ' + quota_json.SIZE + (quota_json.SIZE_USED ? ' (' + quota_json.SIZE_USED + '). ' : ". ") +
+ 'Images: ' + quota_json.IMAGES + (quota_json.IMAGES_USED ? ' (' + quota_json.IMAGES_USED + '). ' : ".");
+ break;
+ case "IMAGE":
+ str += 'ID: ' + getImageName(quota_json.ID) + '. ' +
+ 'RVMs: ' + quota_json.RVMS + (quota_json.RVMS_USED ? ' (' + quota_json.RVMS_USED + '). ' : ". ");
+ break;
+ case "NETWORK":
+ str += 'ID: ' + getVNetName(quota_json.ID) + '. ' +
+ 'Leases: ' + quota_json.LEASES + (quota_json.LEASES_USED ? ' (' + quota_json.LEASES_USED + '). ': ". ");
+ break;
+ }
+ str += '
';
+ return str;
+}
\ No newline at end of file
diff --git a/src/sunstone/public/js/sunstone.js b/src/sunstone/public/js/sunstone.js
index 1cc349f8de..ae10d40f8d 100644
--- a/src/sunstone/public/js/sunstone.js
+++ b/src/sunstone/public/js/sunstone.js
@@ -309,6 +309,11 @@ $(document).ready(function(){
//This dialog is shared to update templates
setupTemplateUpdateDialog();
+ //Setup quota icons
+ //Live listeners not working when being added in specific
+ //context of users/groups dialog. Adding them globally then.
+ setupQuotaIcons();
+
//Listen for .action_buttons
//An action buttons runs a predefined action. If it has type
//"multiple" it runs that action on the elements of a datatable.
@@ -373,6 +378,8 @@ $(document).ready(function(){
return false;
});
+
+
//Start with the dashboard (supposing we have one).
showTab('dashboard_tab');