From 442041fd0772e41da96f1a73c171f34ba7f8da4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Czern=C3=BD?= Date: Mon, 8 Jan 2024 13:56:40 +0100 Subject: [PATCH] F #6341: Generic Quotas - Generic quotas are defined and read from oned.conf (QUOTA_VM_ATTRIBUTE) - Generic quotas react to the running mode (i.e. account only when the VM is running). For each quota oned also adds a RUNNIN_* metric - one.system.config API call has been updated so every user can read quota configuration: . Sensitive information is hidden (***) . Oneadmin group has no longer access to sensitive information through the API - CLI has been updated to render generic quotas - onedb fsck fixes generic quotas reading the configuration from oned.conf Other changes in this PR: - Refactor Quota metrics to use std::vector instead of C array Squashed commit of the following: - New methods to Template and Attribute classes to render hidden attributes as "***" - Update Quota calls to not include unneeded quotas co-authored-by:Ruben S. Montero --- include/Attribute.h | 29 +++++ include/ExtendedAttribute.h | 6 ++ include/NebulaService.h | 14 ++- include/NebulaTemplate.h | 12 ++- include/Quota.h | 11 +- include/QuotaDatastore.h | 5 +- include/QuotaImage.h | 7 +- include/QuotaNetwork.h | 11 +- include/QuotaVirtualMachine.h | 30 ++++-- include/RequestManagerUpdateTemplate.h | 35 ++---- include/Template.h | 12 ++- include/VirtualMachine.h | 25 ++++- share/etc/oned.conf | 9 ++ src/cli/one_helper/onegroup_helper.rb | 2 +- src/cli/one_helper/onequota_helper.rb | 75 +++++++++++++ src/cli/one_helper/oneuser_helper.rb | 2 +- src/common/Attribute.cc | 32 ++++++ src/dm/DispatchManagerActions.cc | 26 ++--- src/dm/DispatchManagerStates.cc | 10 +- src/lcm/LifeCycleActions.cc | 14 +-- src/lcm/LifeCycleStates.cc | 6 +- src/monitor/include/MonitorDriver.h | 4 +- src/nebula/Nebula.cc | 18 ++++ src/onedb/fsck.rb | 45 ++++++++ src/onedb/fsck/quotas.rb | 66 ++++++------ src/onedb/onedb.rb | 2 + src/rm/RequestManagerAllocate.cc | 4 + src/rm/RequestManagerChown.cc | 20 +--- src/rm/RequestManagerSystem.cc | 12 +-- src/rm/RequestManagerUpdateTemplate.cc | 144 +++++++++++++++++++++++-- src/rm/RequestManagerVMTemplate.cc | 2 + src/rm/RequestManagerVirtualMachine.cc | 17 +-- src/template/NebulaTemplate.cc | 9 ++ src/template/Template.cc | 18 ++++ src/um/Quota.cc | 68 +++++------- src/um/QuotaDatastore.cc | 4 +- src/um/QuotaImage.cc | 4 +- src/um/QuotaNetwork.cc | 4 +- src/um/QuotaVirtualMachine.cc | 92 +++++++++++++++- src/vm/VirtualMachine.cc | 82 +++++++++----- src/vm/VirtualMachineDisk.cc | 1 - 41 files changed, 722 insertions(+), 267 deletions(-) diff --git a/include/Attribute.h b/include/Attribute.h index 86e74cb93f..dc44ecc93f 100644 --- a/include/Attribute.h +++ b/include/Attribute.h @@ -81,6 +81,9 @@ public: virtual void to_token(std::ostringstream& s) const = 0; + virtual void to_xml(std::ostringstream& s, + const std::map> &hidden) const = 0; + /** * Builds a new attribute from a string. */ @@ -167,6 +170,9 @@ public: * attribute_value * * @paran s the stream to write the attribute. + * + * NOTE: For Simple attributes hidden are in the form { "PORT". {} } + * A hidden attribute is rendered as *** */ void to_xml(std::ostringstream& s) const override { @@ -175,6 +181,23 @@ public: } + void to_xml(std::ostringstream& s, + const std::map> &hidden) const override + { + s << "<" << attribute_name << ">"; + + if (hidden.find(attribute_name) != hidden.end() ) + { + s << "***"; + } + else + { + s << one_util::escape_xml(attribute_value); + } + + s << ""; + } + void to_json(std::ostringstream& s) const override { one_util::escape_json(attribute_value, s); @@ -422,9 +445,15 @@ public: * ... * val_value_n * + * + * NOTE: For Vector attributes hidden are in the form { "DB", { "USER", "PASSWD"} } + * A hidden attribute is rendered as *** */ void to_xml(std::ostringstream& s) const override; + void to_xml(std::ostringstream& s, + const std::map> &hidden) const override; + void to_json(std::ostringstream& s) const override; void to_token(std::ostringstream& s) const override; diff --git a/include/ExtendedAttribute.h b/include/ExtendedAttribute.h index 529a61592a..1303e3a39f 100644 --- a/include/ExtendedAttribute.h +++ b/include/ExtendedAttribute.h @@ -98,6 +98,12 @@ public: return va->to_token(s); }; + void to_xml(std::ostringstream& s, + const std::map> &hidden) const override + { + return va->to_xml(s, hidden); + } + protected: /** * Creates the attribute with a reference to a VectorAttribute. The object diff --git a/include/NebulaService.h b/include/NebulaService.h index ec0a4a091a..85d6160540 100644 --- a/include/NebulaService.h +++ b/include/NebulaService.h @@ -175,10 +175,20 @@ public: * Gets an XML document with all of the configuration attributes * @return the XML */ - std::string get_configuration_xml() const + std::string get_configuration_xml(bool admin) const { std::string xml; - return config->to_xml(xml); + + if (admin) + { + config->to_xml(xml); + } + else + { + config->to_xml_hidden(xml); + } + + return xml; }; protected: diff --git a/include/NebulaTemplate.h b/include/NebulaTemplate.h index 6f77dc3026..457fec0a1a 100644 --- a/include/NebulaTemplate.h +++ b/include/NebulaTemplate.h @@ -30,7 +30,9 @@ public: NebulaTemplate(const std::string& etc_location, const char * _conf_name, const char * root_name) : Template(false, '=', root_name) - , hidden_attributes{ { "DB", { "PASSWD" } } } + , hidden_attributes{ + {"DB", {"BACKEND", "SERVER", "PORT", "USER", "PASSWD", "DB_NAME"}} + } { if (_conf_name[0] == '/') { @@ -51,11 +53,13 @@ public: std::string& to_str(std::string& str) const override; + std::string& to_xml_hidden(std::string& str) const; + protected: /** * Full path to the configuration file */ - std::string conf_file; + std::string conf_file; /** * Defaults for the configuration file @@ -64,8 +68,8 @@ protected: /** * Hidden attributes, which shouldn't be displayed to non-admin users - * For Simple attributes use { "PORT". {} } - * For Vector attributes use { "DB", { "USER", "PASSWD"} } + * For Simple attributes use {"PORT", {}} + * For Vector attributes use {"DB", {"USER", "PASSWD"}} */ std::map> hidden_attributes; diff --git a/include/Quota.h b/include/Quota.h index 9198be7cd0..c6ad537729 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -194,13 +194,11 @@ protected: Quota(const char * quota_name, const char * _template_name, - const char ** _metrics, - int _num_metrics, + const std::vector& _metrics, bool _is_default) : Template(false, '=', quota_name), template_name(_template_name), metrics(_metrics), - num_metrics(_num_metrics), is_default(_is_default){}; virtual ~Quota(){}; @@ -225,12 +223,7 @@ protected: /** * The name of the quota metrics */ - const char ** metrics; - - /** - * Length - */ - int num_metrics; + const std::vector& metrics; /** * Whether or not this is a default quota. Default quotas do not have usage, diff --git a/include/QuotaDatastore.h b/include/QuotaDatastore.h index a5da521fb1..71c5e03d6e 100644 --- a/include/QuotaDatastore.h +++ b/include/QuotaDatastore.h @@ -40,7 +40,6 @@ public: Quota("DATASTORE_QUOTA", "DATASTORE", DS_METRICS, - NUM_DS_METRICS, is_default) {}; @@ -77,9 +76,7 @@ protected: Quotas& default_quotas, VectorAttribute **va) override; - static const char * DS_METRICS[]; - - static const int NUM_DS_METRICS; + static const std::vector DS_METRICS; }; #endif /*QUOTA_DATASTORE_H_*/ diff --git a/include/QuotaImage.h b/include/QuotaImage.h index 68a6cd23b7..54dad099c2 100644 --- a/include/QuotaImage.h +++ b/include/QuotaImage.h @@ -30,7 +30,7 @@ * 0 = unlimited, default if missing */ -class QuotaImage : public Quota +class QuotaImage : public Quota { public: @@ -38,7 +38,6 @@ public: Quota("IMAGE_QUOTA", "IMAGE", IMAGE_METRICS, - NUM_IMAGE_METRICS, is_default) {}; @@ -75,9 +74,7 @@ protected: Quotas& default_quotas, VectorAttribute **va) override; - static const char * IMAGE_METRICS[]; - - static const int NUM_IMAGE_METRICS; + static const std::vector IMAGE_METRICS; }; #endif /*QUOTA_IMAGE_H_*/ diff --git a/include/QuotaNetwork.h b/include/QuotaNetwork.h index cefcc5a2fc..6890c0ed96 100644 --- a/include/QuotaNetwork.h +++ b/include/QuotaNetwork.h @@ -30,11 +30,12 @@ * * 0 = unlimited, default if missing */ -class QuotaNetwork : public Quota +class QuotaNetwork : public Quota { public: - QuotaNetwork(bool is_default): Quota("NETWORK_QUOTA", "NETWORK", NET_METRICS, - NUM_NET_METRICS, is_default) {}; + QuotaNetwork(bool is_default) + : Quota("NETWORK_QUOTA", "NETWORK", NET_METRICS, is_default) + {} virtual ~QuotaNetwork(){}; @@ -75,9 +76,7 @@ protected: Quotas& default_quotas, VectorAttribute **va) override; - static const char * NET_METRICS[]; - - static const int NUM_NET_METRICS; + static const std::vector NET_METRICS; private: /** diff --git a/include/QuotaVirtualMachine.h b/include/QuotaVirtualMachine.h index 3ac9c588da..579ddcc1fe 100644 --- a/include/QuotaVirtualMachine.h +++ b/include/QuotaVirtualMachine.h @@ -41,7 +41,7 @@ * 0 = unlimited, default if missing */ -class QuotaVirtualMachine : public Quota +class QuotaVirtualMachine : public Quota { public: @@ -49,7 +49,6 @@ public: Quota("VM_QUOTA", "VM", VM_METRICS, - NUM_VM_METRICS, is_default) {}; @@ -96,6 +95,27 @@ public: */ int get_quota(const std::string& id, VectorAttribute **va) override; + /** + * Add generic quota to metrics. It adds also RUNNING_ quota attribute + * @param metric Name of the quota metri + * + * @return 0 success, -1 if metric already exists + */ + static int add_metric_generic(const std::string& metric); + + /** + * Return vector of generic quota metrics + */ + static const std::vector& generic_metrics() + { + return VM_GENERIC; + } + + /* + * Add RUNNING_ quotas for generic metrics present in the template + */ + static void add_running_quota_generic(Template& tmpl); + protected: /** @@ -130,10 +150,8 @@ protected: Quotas& default_quotas, VectorAttribute **va) override; - static const char * VM_METRICS[]; - - static const int NUM_VM_METRICS; - + static std::vector VM_METRICS; + static std::vector VM_GENERIC; }; #endif /*QUOTA_VIRTUALMACHINE_H_*/ diff --git a/include/RequestManagerUpdateTemplate.h b/include/RequestManagerUpdateTemplate.h index 19ee988d50..bad8423867 100644 --- a/include/RequestManagerUpdateTemplate.h +++ b/include/RequestManagerUpdateTemplate.h @@ -60,6 +60,11 @@ protected: void request_execute(xmlrpc_c::paramList const& _paramList, RequestAttributes& att) override; + virtual void request_execute(int oid, + const std::string& templ, + int update_type, + RequestAttributes& att); + virtual int replace_template(PoolObjectSQL * object, const std::string & tmpl, const RequestAttributes &att, @@ -80,17 +85,6 @@ protected: { return 0; } - - /** - * Method for extra checks on specific objects - * @param obj to check conditions form update - * @param error return reason of error - * @return 0 on success - */ - virtual int extra_preconditions_check(PoolObjectSQL * obj, std::string& error) - { - return 0; - } }; /* ------------------------------------------------------------------------- */ @@ -164,21 +158,10 @@ protected: return vmpool->update_search(vm); } - int extra_preconditions_check(PoolObjectSQL * obj, std::string& error) override - { - auto vm = static_cast(obj); - - // Check if the action is supported for imported VMs - if (vm->is_imported() && - !vm->is_imported_action_supported(VMActions::UPDATE_ACTION)) - { - error = "Action \"update\" is not supported for imported VMs"; - - return -1; - } - - return 0; - } + void request_execute(int oid, + const std::string& templ, + int update_type, + RequestAttributes& att) override; }; /* ------------------------------------------------------------------------- */ diff --git a/include/Template.h b/include/Template.h index 79f91e5454..39b00f4ccb 100644 --- a/include/Template.h +++ b/include/Template.h @@ -182,13 +182,21 @@ public: * ... * * The name of the root element is set when the Template object is created - * @param xml string that hold the xml template representation - * @return a reference to the generated string + * + * Hidden attributes are defined as a map, where + * - Simple attributes use an empty set { "PORT", {} } + * - Vector attributes use a set of hidden subattributes { "DB", { "USER", "PASSWD"} } + * + * @param xml string that hold the xml template representation + * + * @return a reference to the generated string */ std::string& to_xml(std::string& xml) const; std::string& to_xml(std::string& xml, const std::string& extra) const; + std::string& to_xml(std::string& xml, const std::map>& hidden) const; + std::string& to_json(std::string& xml) const; std::string& to_token(std::string& xml) const; diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 7709d5f18a..f6e1de0e18 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -940,6 +940,15 @@ public: return std::make_unique(*obj_template); } + /** + * Returns a copy of the VirtualMachine User Template + * @return A copy of the VirtualMachine User Template + */ + std::unique_ptr clone_user_template() const + { + return std::make_unique(*user_obj_template); + } + /** * This function replaces the *user template*. * @param tmpl_str new contents @@ -967,10 +976,10 @@ public: * @param name of the attribute * @param value of the attribute */ - void get_user_template_attribute(const std::string& name, - std::string& value) const + template + bool get_user_template_attribute(const std::string& name, T& value) const { - user_obj_template->get(name, value); + return user_obj_template->get(name, value); } /** @@ -1052,12 +1061,18 @@ public: */ bool is_pinned() const; + /** + * @return true if Virtual Machine is in state, when running quota applies + */ + bool is_running_quota() const; + /** * Fill a template only with the necessary attributes to update the quotas * @param qtmpl template that will be filled - * @param only_running true to not add CPU, MEMORY and VMS counters + * @param basic_quota true to add basic quota attributes (from Template and User template) + * @param running_quota true to add RUNNING_ quota attributes (for Template and User Template) */ - void get_quota_template(VirtualMachineTemplate& qtmpl, bool only_running); + void get_quota_template(VirtualMachineTemplate& quota_tmpl, bool basic_quota, bool running_quota); // ------------------------------------------------------------------------ // Virtual Machine Disks diff --git a/share/etc/oned.conf b/share/etc/oned.conf index 524048ad6e..bcac626d50 100644 --- a/share/etc/oned.conf +++ b/share/etc/oned.conf @@ -873,6 +873,15 @@ DEFAULT_VDC_CLUSTER_HOST_ACL = "MANAGE" DEFAULT_VDC_CLUSTER_NET_ACL = "USE" DEFAULT_VDC_CLUSTER_DATASTORE_ACL = "USE" +#******************************************************************************* +# Generic VM Quotas Configuration +#******************************************************************************* +# The following attributes may be limited by quotas. Use any numeric attribute +# from Template or User Template +#******************************************************************************* + +#QUOTA_VM_ATTRIBUTE = "VCPU" + #******************************************************************************* # Restricted Attributes Configuration #******************************************************************************* diff --git a/src/cli/one_helper/onegroup_helper.rb b/src/cli/one_helper/onegroup_helper.rb index c76b64714b..500a802302 100644 --- a/src/cli/one_helper/onegroup_helper.rb +++ b/src/cli/one_helper/onegroup_helper.rb @@ -328,7 +328,7 @@ class OneGroupHelper < OpenNebulaHelper::OneHelper default_quotas = elem } - helper = OneQuotaHelper.new + helper = OneQuotaHelper.new(@client) helper.format_quota(group_hash['GROUP'], default_quotas, group.id) end end diff --git a/src/cli/one_helper/onequota_helper.rb b/src/cli/one_helper/onequota_helper.rb index 9140155c4e..a718e84d41 100644 --- a/src/cli/one_helper/onequota_helper.rb +++ b/src/cli/one_helper/onequota_helper.rb @@ -75,6 +75,10 @@ class OneQuotaHelper #----------------------------------------------------------------------- EOT + def initialize(client = nil) + @client=client + end + # Edits the quota template of a resource # @param [XMLElement] resource to get the current info from # @param [String] path to the new contents. If nil a editor will be @@ -207,6 +211,8 @@ class OneQuotaHelper vm_quotas = [qh['VM_QUOTA']['VM']].flatten + generic_quotas = get_generic_quotas + # This initializes the VM quotas for users/groups that don't have any # resource usage yet. It not applied to oneamdin if vm_quotas[0].nil? && resource_id.to_i != 0 @@ -228,6 +234,13 @@ class OneQuotaHelper "SYSTEM_DISK_SIZE" => limit, "SYSTEM_DISK_SIZE_USED" => "0" }] + + generic_quotas.each do |q| + vm_quotas[0][q] = limit + vm_quotas[0]["#{q}_USED"] = "0" + vm_quotas[0]["RUNNING_#{q}"] = limit + vm_quotas[0]["RUNNING_#{q}_USED"] = "0" + end end if !vm_quotas[0].nil? @@ -378,6 +391,55 @@ class OneQuotaHelper puts end + if !generic_quotas.empty? && !vm_quotas[0].nil? + CLIHelper.print_header(str_h1 % "VMS GENERIC QUOTAS",false) + size = [80 / generic_quotas.length - 1, 18].min + + CLIHelper::ShowTable.new(nil, self) do + generic_quotas.each do |elem| + column elem.to_sym, "", :right, :size=>size do |d| + if !d.nil? + limit = d[elem] + limit = helper.get_default_limit( + limit, "VM_QUOTA/VM/#{elem}") + + if limit == LIMIT_UNLIMITED + "%6s / -" % [d["#{elem}_USED"]] + else + "%6s / %6s" % [d["#{elem}_USED"], limit] + end + end + end + end + end.show(vm_quotas, {}) + + puts + + CLIHelper.print_header(str_h1 % "VMS GENERIC RUNNING QUOTAS",false) + size = [80 / generic_quotas.length - 1, 18].min + + CLIHelper::ShowTable.new(nil, self) do + generic_quotas.each do |q| + elem = "RUNNING_#{q}" + column elem.to_sym, "", :right, :size=>size do |d| + if !d.nil? + limit = d[elem] + limit = helper.get_default_limit( + limit, "VM_QUOTA/VM/#{elem}") + + if limit == LIMIT_UNLIMITED + "%6s / -" % [d["#{elem}_USED"]] + else + "%6s / %6s" % [d["#{elem}_USED"], limit] + end + end + end + end + end.show(vm_quotas, {}) + + puts + end + CLIHelper.print_header(str_h1 % "DATASTORE USAGE & QUOTAS",false) puts @@ -503,4 +565,17 @@ class OneQuotaHelper return limit end + + private + + def get_generic_quotas + conf = OpenNebula::System.new(@client).get_configuration + + return [] if OpenNebula.is_error?(conf) + + conf.retrieve_elements('/OPENNEBULA_CONFIGURATION/QUOTA_VM_ATTRIBUTE') + rescue StandardError + [] + end + end diff --git a/src/cli/one_helper/oneuser_helper.rb b/src/cli/one_helper/oneuser_helper.rb index 874497d30c..fd4b4a2a6d 100644 --- a/src/cli/one_helper/oneuser_helper.rb +++ b/src/cli/one_helper/oneuser_helper.rb @@ -613,7 +613,7 @@ class OneUserHelper < OpenNebulaHelper::OneHelper default_quotas = elem } - helper = OneQuotaHelper.new + helper = OneQuotaHelper.new(@client) helper.format_quota(user_hash['USER'], default_quotas, user.id) end diff --git a/src/common/Attribute.cc b/src/common/Attribute.cc index 1f732121ae..9966ded65b 100644 --- a/src/common/Attribute.cc +++ b/src/common/Attribute.cc @@ -80,6 +80,38 @@ void VectorAttribute::to_xml(ostringstream &oss) const oss << ""; } +void VectorAttribute::to_xml(ostringstream &oss, + const std::map> &hidden) const +{ + oss << "<" << name() << ">"; + + auto hidden_it = hidden.find(name()); + + for (auto it=attribute_value.begin(); it!=attribute_value.end(); it++) + { + if ( it->first.empty()) + { + continue; + } + + oss << "<" << it->first << ">"; + + if (hidden_it != hidden.end() && + hidden_it->second.find(it->first) != hidden_it->second.end()) + { + oss << "***"; + } + else + { + oss << one_util::escape_xml(it->second); + } + + oss << "first << ">"; + } + + oss << ""; +} + void VectorAttribute::to_json(std::ostringstream& s) const { if ( attribute_value.empty() ) diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index beda9ad684..7cc817fd2f 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -76,7 +76,7 @@ int DispatchManager::deploy(unique_ptr vm, uid = vm->get_uid(); gid = vm->get_gid(); - vm->get_quota_template(quota_tmpl, true); + vm->get_quota_template(quota_tmpl, false, true); } lcm->trigger_deploy(vid); @@ -152,7 +152,7 @@ int DispatchManager::import(unique_ptr vm, const RequestAttribut uid = vm->get_uid(); gid = vm->get_gid(); - vm->get_quota_template(quota_tmpl, true); + vm->get_quota_template(quota_tmpl, false, true); do_quotas = true; } @@ -290,23 +290,9 @@ void DispatchManager::free_vm_resources(unique_ptr vm, int vrid = -1; unsigned int port; - auto quota_tmpl = vm->clone_template(); + VirtualMachineTemplate quota_tmpl; - if ( (vm->get_state() == VirtualMachine::ACTIVE) || - (vm->get_state() == VirtualMachine::PENDING) || - (vm->get_state() == VirtualMachine::HOLD) ) - { - std::string memory, cpu; - - quota_tmpl->get("MEMORY", memory); - quota_tmpl->get("CPU", cpu); - - quota_tmpl->add("RUNNING_MEMORY", memory); - quota_tmpl->add("RUNNING_CPU", cpu); - quota_tmpl->add("RUNNING_VMS", 1); - } - - quota_tmpl->add("VMS", 1); + vm->get_quota_template(quota_tmpl, true, vm->is_running_quota()); vm->release_network_leases(); @@ -370,7 +356,7 @@ void DispatchManager::free_vm_resources(unique_ptr vm, vm.reset(); //force unlock of vm mutex - Quotas::vm_del(uid, gid, quota_tmpl.get()); + Quotas::vm_del(uid, gid, "a_tmpl); if ( !ds_quotas.empty() ) { @@ -1240,7 +1226,7 @@ int DispatchManager::delete_recreate(unique_ptr vm, if ( do_quotas ) { - vm->get_quota_template(quota_tmpl, true); + vm->get_quota_template(quota_tmpl, false, true); } break; diff --git a/src/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index 2d127ef965..3ba45f9d31 100644 --- a/src/dm/DispatchManagerStates.cc +++ b/src/dm/DispatchManagerStates.cc @@ -43,7 +43,7 @@ void DispatchManager::trigger_suspend_success(int vid) { VirtualMachineTemplate quota_tmpl; - vm->get_quota_template(quota_tmpl, true); + vm->get_quota_template(quota_tmpl, false, true); vm->set_state(VirtualMachine::SUSPENDED); @@ -96,7 +96,7 @@ void DispatchManager::trigger_stop_success(int vid) { VirtualMachineTemplate quota_tmpl; - vm->get_quota_template(quota_tmpl, true); + vm->get_quota_template(quota_tmpl, false, true); vm->set_state(VirtualMachine::STOPPED); @@ -151,7 +151,9 @@ void DispatchManager::trigger_undeploy_success(int vid) { VirtualMachineTemplate quota_tmpl; - vm->get_quota_template(quota_tmpl, true); + // Bug: From the LCM state we don't know if we undeploy from RUNNING or POWEROFF + // state. In first case we should remove RUNNING_* quotas, in the second not + vm->get_quota_template(quota_tmpl, false, vm->is_running_quota()); vm->set_state(VirtualMachine::UNDEPLOYED); @@ -214,7 +216,7 @@ void DispatchManager::trigger_poweroff_success(int vid) { VirtualMachineTemplate quota_tmpl; - vm->get_quota_template(quota_tmpl, true); + vm->get_quota_template(quota_tmpl, false, vm->is_running_quota()); vm->set_state(VirtualMachine::POWEROFF); diff --git a/src/lcm/LifeCycleActions.cc b/src/lcm/LifeCycleActions.cc index 48f2c9c41b..39079545f5 100644 --- a/src/lcm/LifeCycleActions.cc +++ b/src/lcm/LifeCycleActions.cc @@ -202,9 +202,7 @@ void LifeCycleManager::trigger_stop(int vid, const RequestAttributes& ra) vm->get_template_attribute("MEMORY", memory); vm->get_template_attribute("CPU", cpu); - quota_tmpl.add("RUNNING_MEMORY", memory); - quota_tmpl.add("RUNNING_CPU", cpu); - quota_tmpl.add("RUNNING_VMS", 1); + vm->get_quota_template(quota_tmpl, false, true); vm->set_state(VirtualMachine::ACTIVE); vm->set_state(VirtualMachine::EPILOG_STOP); @@ -220,7 +218,7 @@ void LifeCycleManager::trigger_stop(int vid, const RequestAttributes& ra) //---------------------------------------------------- tm->trigger_epilog_stop(vm.get()); - + // Add running quota, it will be removed in DM::stop_success vm.reset(); Quotas::vm_add(quota_uid, quota_gid, "a_tmpl); @@ -465,9 +463,7 @@ void LifeCycleManager::trigger_shutdown(int vid, bool hard, (vm->get_state() == VirtualMachine::STOPPED) || (vm->get_state() == VirtualMachine::UNDEPLOYED)) { - quota_tmpl.add("RUNNING_MEMORY", memory); - quota_tmpl.add("RUNNING_CPU", cpu); - quota_tmpl.add("RUNNING_VMS", 1); + vm->get_quota_template(quota_tmpl, false, true); } auto lcm_state = vm->get_lcm_state(); @@ -639,9 +635,7 @@ void LifeCycleManager::trigger_undeploy(int vid, bool hard, vm->get_template_attribute("MEMORY", memory); vm->get_template_attribute("CPU", cpu); - quota_tmpl.add("RUNNING_MEMORY", memory); - quota_tmpl.add("RUNNING_CPU", cpu); - quota_tmpl.add("RUNNING_VMS", 1); + vm->get_quota_template(quota_tmpl, false, true); vm->set_state(VirtualMachine::ACTIVE); vm->set_state(VirtualMachine::EPILOG_UNDEPLOY); diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 5ab3db02d6..2250038dae 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -1226,7 +1226,7 @@ void LifeCycleManager::trigger_monitor_poweron(int vid) vmpool->update(vm.get()); - vm->get_quota_template(quota_tmpl, true); + vm->get_quota_template(quota_tmpl, false, true); vm.reset(); @@ -1512,7 +1512,6 @@ void LifeCycleManager::trigger_snapshot_create_failure(int vid) Template quota_tmpl; quota_tmpl.set(snap); - quota_tmpl.replace("VMS", 0); Quotas::quota_del(Quotas::VM, vm_uid, vm_gid, "a_tmpl); } @@ -2622,7 +2621,7 @@ void LifeCycleManager::trigger_resize_failure(int vid) deltas.add("MEMORY", nmem - omem); deltas.add("CPU", ncpu - ocpu); - deltas.add("VMS", 0); + deltas.add("VCPU", nvcpu - ovcpu); auto state = vm->get_state(); @@ -2632,6 +2631,7 @@ void LifeCycleManager::trigger_resize_failure(int vid) { deltas.add("RUNNING_MEMORY", nmem - omem); deltas.add("RUNNING_CPU", ncpu - ocpu); + deltas.add("RUNNING_VCPU", nvcpu - ovcpu); } vm->resize(ocpu, omem, ovcpu, error); diff --git a/src/monitor/include/MonitorDriver.h b/src/monitor/include/MonitorDriver.h index e127b6b134..8e8f26e7e5 100644 --- a/src/monitor/include/MonitorDriver.h +++ b/src/monitor/include/MonitorDriver.h @@ -62,7 +62,7 @@ public: msg.type(MonitorDriverMessages::START_MONITOR); msg.oid(oid); - msg.payload(format_message(host_xml + ns.get_configuration_xml())); + msg.payload(format_message(host_xml + ns.get_configuration_xml(true))); write(msg); } @@ -74,7 +74,7 @@ public: msg.type(MonitorDriverMessages::STOP_MONITOR); msg.oid(oid); - msg.payload(format_message(host_xml + ns.get_configuration_xml())); + msg.payload(format_message(host_xml + ns.get_configuration_xml(true))); write(msg); } diff --git a/src/nebula/Nebula.cc b/src/nebula/Nebula.cc index b579a71e7d..98b0254eff 100644 --- a/src/nebula/Nebula.cc +++ b/src/nebula/Nebula.cc @@ -614,6 +614,24 @@ void Nebula::start(bool bootstrap_only) ssl_util::SSLMutex::initialize(); + // ----------------------------------------------------------- + // Generic Quotas + // ----------------------------------------------------------- + { + vector qouta_vm_attrs; + + nebula_configuration->get("QUOTA_VM_ATTRIBUTE", qouta_vm_attrs); + + for (const auto* quota : qouta_vm_attrs) + { + if (QuotaVirtualMachine::add_metric_generic(quota->value()) != 0) + { + NebulaLog::warn("ONE", "Unable to add QUOTA_VM_ATTRIBUTE " + quota->value() + + ", it already exists"); + } + } + } + // ----------------------------------------------------------- //Managers // ----------------------------------------------------------- diff --git a/src/onedb/fsck.rb b/src/onedb/fsck.rb index 49a2f8cac6..b15f7ced33 100644 --- a/src/onedb/fsck.rb +++ b/src/onedb/fsck.rb @@ -171,6 +171,51 @@ EOT end end + def read_config + begin + # Suppress augeas require warning message + $VERBOSE = nil + + gem 'augeas', '~> 0.6' + require 'augeas' + rescue Gem::LoadError + STDERR.puts( + 'Augeas gem is not installed, run `gem install ' \ + 'augeas -v \'0.6\'` to install it' + ) + exit(-1) + end + + work_file_dir = File.dirname(ONED_CONF) + work_file_name = File.basename(ONED_CONF) + + aug = Augeas.create(:no_modl_autoload => true, + :no_load => true, + :root => work_file_dir, + :loadpath => ONED_CONF) + + aug.clear_transforms + aug.transform(:lens => 'Oned.lns', :incl => work_file_name) + aug.context = "/files/#{work_file_name}" + aug.load + + @generic_quotas = [] + + i = 0 + loop do + i += 1 + + quota = aug.get("QUOTA_VM_ATTRIBUTE[#{i}]") + + break if quota.nil? + + @generic_quotas << quota.chomp('"').reverse.chomp('"').reverse + end + rescue StandardError => e + STDERR.puts "Unable to parse oned.conf: #{e}" + exit(-1) + end + ######################################################################## # Acl ######################################################################## diff --git a/src/onedb/fsck/quotas.rb b/src/onedb/fsck/quotas.rb index 3c6e474c87..b2bf1d4222 100644 --- a/src/onedb/fsck/quotas.rb +++ b/src/onedb/fsck/quotas.rb @@ -56,7 +56,9 @@ module OneDBFsck # VM quotas query = "SELECT body FROM vm_pool WHERE #{filter} AND state<>6" - resources = { :cpu => 'CPU', :mem => 'MEMORY', :vms => 'VMS' } + resources = { :CPU => 'CPU', :MEMORY => 'MEMORY', :VMS => 'VMS' } + + @generic_quotas.each {|q| resources[q] = q } vm_elem = calculate_vm_quotas(doc, query, resource, resources) @@ -95,9 +97,11 @@ module OneDBFsck query = "SELECT body FROM vm_pool WHERE #{filter} AND #{running_states}" - resources = { :cpu => 'RUNNING_CPU', - :mem => 'RUNNING_MEMORY', - :vms => 'RUNNING_VMS' } + resources = { :CPU => 'RUNNING_CPU', + :MEMORY => 'RUNNING_MEMORY', + :VMS => 'RUNNING_VMS' } + + @generic_quotas.each {|q| resources[q] = "RUNNING_#{q}" } calculate_vm_quotas(doc, query, resource, resources) end @@ -344,12 +348,12 @@ module OneDBFsck oid = doc.root.at_xpath('ID').text.to_i cpu_used = 0 - mem_used = 0 - vms_used = 0 - cpu = resources[:cpu] - mem = resources[:mem] - vms = resources[:vms] + cpu = resources[:CPU] + vms = resources[:VMS] + + quotas = {} + resources.each {|_, q| quotas[q] = 0 } @db.fetch(query) do |vm_row| vmdoc = nokogiri_doc(vm_row[:body], 'vm_pool') @@ -359,11 +363,17 @@ module OneDBFsck cpu_used += (e.text.to_f * 100).to_i end - vmdoc.root.xpath('TEMPLATE/MEMORY').each do |e| - mem_used += e.text.to_i + resources.each do |att_name, quota_name| + next if [:CPU, :VMS].include?(att_name) + + value = vmdoc.root.at_xpath("TEMPLATE/#{att_name}") || + vmdoc.root.at_xpath("USER_TEMPLATE/#{att_name}") + value = value.text unless value.nil? + + quotas[quota_name] += value.to_i end - vms_used += 1 + quotas[vms] += 1 end vm_elem = nil @@ -375,14 +385,10 @@ module OneDBFsck vm_quota = doc.root.add_child(doc.create_element('VM_QUOTA')) vm_elem = vm_quota.add_child(doc.create_element('VM')) - vm_elem.add_child(doc.create_element(cpu)).content = '-1' - vm_elem.add_child(doc.create_element("#{cpu}_USED")).content = '0' - - vm_elem.add_child(doc.create_element(mem)).content = '-1' - vm_elem.add_child(doc.create_element("#{mem}_USED")).content = '0' - - vm_elem.add_child(doc.create_element(vms)).content = '-1' - vm_elem.add_child(doc.create_element("#{vms}_USED")).content = '0' + resources.each do |_, quota_name| + vm_elem.add_child(doc.create_element(quota_name)).content = '-1' + vm_elem.add_child(doc.create_element("#{quota_name}_USED")).content = '0' + end system_disk_e = doc.create_element('SYSTEM_DISK_SIZE') system_disk_used_e = doc.create_element('SYSTEM_DISK_SIZE_USED') @@ -408,20 +414,16 @@ module OneDBFsck e.content = cpu_used_str end - vm_elem.xpath("#{mem}_USED").each do |e| - next if e.text == mem_used.to_s + resources.each do |att_name, quota_name| + next if att_name == :CPU - log_error("#{resource} #{oid} quotas: #{mem}_USED has " \ - "#{e.text} \tis\t#{mem_used}") - e.content = mem_used.to_s - end + vm_elem.xpath("#{quota_name}_USED").each do |e| + next if e.text.to_i == quotas[quota_name].to_i - vm_elem.xpath("#{vms}_USED").each do |e| - next if e.text == vms_used.to_s - - log_error("#{resource} #{oid} quotas: #{vms}_USED has " \ - "#{e.text} \tis\t#{vms_used}") - e.content = vms_used.to_s + log_error("#{resource} #{oid} quotas: #{quota_name}_USED has " \ + "#{e.text} \tis\t#{quotas[quota_name]}") + e.content = quotas[quota_name].to_s + end end vm_elem diff --git a/src/onedb/onedb.rb b/src/onedb/onedb.rb index 42dfc3d861..dc02228689 100644 --- a/src/onedb/onedb.rb +++ b/src/onedb/onedb.rb @@ -483,6 +483,8 @@ class OneDB time0 = Time.now + @backend.read_config + result = @backend.fsck if !result diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 93f79103af..8364dea7fa 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -129,6 +129,8 @@ bool VirtualMachineAllocate::allocate_authorization( aux_tmpl.add("RUNNING_VMS", 1); aux_tmpl.add("VMS", 1); + QuotaVirtualMachine::add_running_quota_generic(aux_tmpl); + if ( quota_authorization(&aux_tmpl, Quotas::VIRTUALMACHINE, att) == false ) { return false; @@ -372,6 +374,8 @@ error_drop_vm: tmpl_back.add("RUNNING_VMS", 1); tmpl_back.add("VMS", 1); + QuotaVirtualMachine::add_running_quota_generic(tmpl_back); + quota_rollback(&tmpl_back, Quotas::VIRTUALMACHINE, att); VirtualMachineDisks::extended_info(att.uid, &tmpl_back); diff --git a/src/rm/RequestManagerChown.cc b/src/rm/RequestManagerChown.cc index cafb5eead1..6052dfdaee 100644 --- a/src/rm/RequestManagerChown.cc +++ b/src/rm/RequestManagerChown.cc @@ -39,8 +39,6 @@ unique_ptr RequestManagerChown::get_and_quota( int old_uid; int old_gid; - std::string memory, cpu; - auto object = pool->get_ro(oid); if ( object == nullptr ) @@ -63,23 +61,9 @@ unique_ptr RequestManagerChown::get_and_quota( return 0; } - auto tmpl = vm->clone_template(); + auto tmpl = std::make_unique(); - if ( (vm->get_state() == VirtualMachine::ACTIVE) || - (vm->get_state() == VirtualMachine::PENDING) || - (vm->get_state() == VirtualMachine::CLONING) || - (vm->get_state() == VirtualMachine::CLONING_FAILURE) || - (vm->get_state() == VirtualMachine::HOLD) ) - { - vm->get_template_attribute("MEMORY", memory); - vm->get_template_attribute("CPU", cpu); - - tmpl->add("RUNNING_MEMORY", memory); - tmpl->add("RUNNING_CPU", cpu); - tmpl->add("RUNNING_VMS", 1); - } - - tmpl->add("VMS", 1); + vm->get_quota_template(*tmpl, true, vm->is_running_quota()); VirtualMachineDisks::image_ds_quotas(tmpl.get(), ds_quotas); diff --git a/src/rm/RequestManagerSystem.cc b/src/rm/RequestManagerSystem.cc index 0e44cd22f4..8cbfb70edf 100644 --- a/src/rm/RequestManagerSystem.cc +++ b/src/rm/RequestManagerSystem.cc @@ -38,15 +38,9 @@ void SystemVersion::request_execute(xmlrpc_c::paramList const& paramList, void SystemConfig::request_execute(xmlrpc_c::paramList const& paramList, RequestAttributes& att) { - if ( att.gid != GroupPool::ONEADMIN_ID ) - { - att.resp_msg = "The oned configuration can only be retrieved by users " - "in the oneadmin group"; - failure_response(AUTHORIZATION, att); - return; - } - - success_response(Nebula::instance().get_configuration_xml(), att); + //bool is_admin = att.gid == GroupPool::ONEADMIN_ID; + //Do not send sensitive configuration data over the wire + success_response(Nebula::instance().get_configuration_xml(false), att); return; } diff --git a/src/rm/RequestManagerUpdateTemplate.cc b/src/rm/RequestManagerUpdateTemplate.cc index 4eff7245e3..558f91ed8f 100644 --- a/src/rm/RequestManagerUpdateTemplate.cc +++ b/src/rm/RequestManagerUpdateTemplate.cc @@ -63,8 +63,6 @@ void RequestManagerUpdateTemplate::request_execute( xmlrpc_c::paramList const& paramList, RequestAttributes& att) { - int rc; - int oid = xmlrpc_c::value_int(paramList.getInt(1)); string tmpl = xmlrpc_c::value_string(paramList.getString(2)); @@ -87,6 +85,18 @@ void RequestManagerUpdateTemplate::request_execute( return; } + request_execute(oid, tmpl, update_type, att); +} + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void RequestManagerUpdateTemplate::request_execute(int oid, + const std::string& tmpl, + int update_type, + RequestAttributes& att) +{ + int rc; auto object = pool->get(oid); @@ -97,13 +107,6 @@ void RequestManagerUpdateTemplate::request_execute( return; } - if (extra_preconditions_check(object.get(), att.resp_msg)) - { - failure_response(ACTION, att); - - return; - } - if (update_type == 0) { rc = replace_template(object.get(), tmpl, att, att.resp_msg); @@ -126,8 +129,129 @@ void RequestManagerUpdateTemplate::request_execute( extra_updates(object.get()); success_response(oid, att); +} - return; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void VirtualMachineUpdateTemplate::request_execute(int oid, + const std::string& tmpl, + int update_type, + RequestAttributes& att) +{ + int rc; + + auto vm = pool->get(oid); + + if ( vm == nullptr ) + { + att.resp_id = oid; + failure_response(NO_EXISTS, att); + + return; + } + + // Check if the action is supported for imported VMs + if (vm->is_imported() && !vm->is_imported_action_supported(VMActions::UPDATE_ACTION)) + { + att.resp_msg = "Action \"update\" is not supported for imported VMs"; + failure_response(ACTION, att); + + return; + } + + // Apply generic quota deltas + auto new_tmpl = make_unique(false,'=',"USER_TEMPLATE"); + + if ( new_tmpl->parse_str_or_xml(tmpl, att.resp_msg) != 0 ) + { + failure_response(ACTION, att); + + return; + } + + if ( update_type == 1 ) //append mode + { + auto user_tmpl = vm->clone_user_template(); + + user_tmpl->merge(new_tmpl.get()); + + new_tmpl.swap(user_tmpl); + } + + // Compute quota deltas (only generic quota may appear in User template) + bool do_quotas = false; + + for ( const string& metric : QuotaVirtualMachine::generic_metrics()) + { + float value_new, value_old; + + bool exists_old = vm->get_user_template_attribute(metric, value_old); + bool exists_new = new_tmpl->get(metric, value_new); + + if ( exists_old || exists_new ) + { + float delta = value_new - value_old; + + new_tmpl->replace(metric, delta); + + do_quotas |= delta != 0; + } + } + + if (vm->is_running_quota()) + { + QuotaVirtualMachine::add_running_quota_generic(*new_tmpl); + } + + RequestAttributes att_quota(att); + + att_quota.uid = vm->get_uid(); + att_quota.gid = vm->get_gid(); + + vm.reset(); + + if ( do_quotas ) + { + if (!quota_authorization(new_tmpl.get(), Quotas::VIRTUALMACHINE, att_quota, att.resp_msg)) + { + failure_response(ACTION, att); + + return; + } + } + + vm = pool->get(oid); + + if (update_type == 0) + { + rc = replace_template(vm.get(), tmpl, att, att.resp_msg); + } + else //if (update_type == 1) + { + rc = append_template(vm.get(), tmpl, att, att.resp_msg); + } + + if ( rc != 0 ) + { + vm.reset(); + + if (do_quotas) + { + quota_rollback(new_tmpl.get(), Quotas::VIRTUALMACHINE, att_quota); + } + + att.resp_msg = "Cannot update template. " + att.resp_msg; + failure_response(INTERNAL, att); + + return; + } + + pool->update(vm.get()); + + extra_updates(vm.get()); + + success_response(oid, att); } /* ------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerVMTemplate.cc b/src/rm/RequestManagerVMTemplate.cc index a994323162..1c91861df2 100644 --- a/src/rm/RequestManagerVMTemplate.cc +++ b/src/rm/RequestManagerVMTemplate.cc @@ -238,6 +238,8 @@ Request::ErrorCode VMTemplateInstantiate::request_execute(int id, const string& extended_tmpl.add("RUNNING_VMS", 1); extended_tmpl.add("VMS", 1); + QuotaVirtualMachine::add_running_quota_generic(extended_tmpl); + if (quota_authorization(&extended_tmpl, Quotas::VIRTUALMACHINE, att, att.resp_msg) == false) { diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index e20c2a9c0f..b6ff363227 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -517,18 +517,11 @@ Request::ErrorCode VirtualMachineAction::request_execute(RequestAttributes& att, rc = dm->suspend(vid, att, error); break; case VMActions::RESUME_ACTION: - // Generate quota information for resume action - vm->get_template_attribute("MEMORY", memory); - vm->get_template_attribute("CPU", cpu); - - quota_tmpl.add("RUNNING_MEMORY", memory); - quota_tmpl.add("RUNNING_CPU", cpu); - quota_tmpl.add("RUNNING_VMS", 1); + vm->get_quota_template(quota_tmpl, false, true); att_aux.uid = vm->get_uid(); att_aux.gid = vm->get_gid(); - if (!quota_authorization("a_tmpl, Quotas::VIRTUALMACHINE, att_aux, att.resp_msg)) { return ACTION; @@ -1876,8 +1869,6 @@ Request::ErrorCode VirtualMachineAttach::request_execute(int id, VirtualMachineTemplate deltas(tmpl); VirtualMachineDisks::extended_info(att.uid, &deltas); - deltas.add("VMS", 0); - if (quota_resize_authorization(&deltas, att_quota, vm_perms) == false) { att.resp_msg = std::move(att_quota.resp_msg); @@ -1980,7 +1971,7 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList, float ncpu, ocpu, dcpu; long nmemory, omemory, dmemory; - int nvcpu, ovcpu; + int nvcpu, ovcpu, dvcpu; bool update_running_quota; Template deltas; @@ -2109,15 +2100,18 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList, } dcpu = ncpu - ocpu; + dvcpu = nvcpu - ovcpu; dmemory = nmemory - omemory; deltas.add("MEMORY", dmemory); deltas.add("CPU", dcpu); + deltas.add("VCPU", dvcpu); if (update_running_quota) { deltas.add("RUNNING_MEMORY", dmemory); deltas.add("RUNNING_CPU", dcpu); + deltas.add("RUNNING_VCPU", dvcpu); } if (quota_resize_authorization(&deltas, att, vm_perms) == false) @@ -2203,7 +2197,6 @@ Request::ErrorCode VirtualMachineSnapshotCreate::request_execute(RequestAttribut Template quota_tmpl; quota_tmpl.set(snap); - quota_tmpl.add("VMS", 0); RequestAttributes att_quota(vm_perms.uid, vm_perms.gid, att); diff --git a/src/template/NebulaTemplate.cc b/src/template/NebulaTemplate.cc index 50097544af..501ca93b80 100644 --- a/src/template/NebulaTemplate.cc +++ b/src/template/NebulaTemplate.cc @@ -87,6 +87,14 @@ void NebulaTemplate::set_conf_single(const std::string& attr, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +std::string& NebulaTemplate::to_xml_hidden(std::string& str) const +{ + return Template::to_xml(str, hidden_attributes); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + std::string& NebulaTemplate::to_str(std::string& str) const { ostringstream os; @@ -96,6 +104,7 @@ std::string& NebulaTemplate::to_str(std::string& str) const string s; auto hidden_it = hidden_attributes.find(it->first); + if (hidden_it != hidden_attributes.end()) { if (it->second->type() == Attribute::SIMPLE) diff --git a/src/template/Template.cc b/src/template/Template.cc index bec5f0a375..b21d9c234c 100644 --- a/src/template/Template.cc +++ b/src/template/Template.cc @@ -371,6 +371,24 @@ string& Template::to_xml(string& xml) const return xml; } +string& Template::to_xml(string& xml, const std::map> &hidden) const +{ + ostringstream oss; + + oss << "<" << xml_root << ">"; + + for ( auto it = attributes.begin(); it!=attributes.end(); it++) + { + it->second->to_xml(oss, hidden); + } + + oss << ""; + + xml = oss.str(); + + return xml; +} + string& Template::to_xml(string& xml, const string& extra) const { ostringstream oss; diff --git a/src/um/Quota.cc b/src/um/Quota.cc index f0aabfc2a5..0dd80356e8 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -178,13 +178,11 @@ bool Quota::check_quota(const string& qid, { map values; - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - string metrics_used = metrics[i]; + string metrics_used = metric + "_USED"; - metrics_used += "_USED"; - - values.insert(make_pair(metrics[i], DEFAULT_STR)); + values.insert(make_pair(metric, DEFAULT_STR)); values.insert(make_pair(metrics_used, "0")); } @@ -201,27 +199,25 @@ bool Quota::check_quota(const string& qid, // ------------------------------------------------------------------------- // Check the quotas for each usage request // ------------------------------------------------------------------------- - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - string metrics_used = metrics[i]; + string metrics_used = metric + "_USED"; - metrics_used += "_USED"; - - auto it = usage_req.find(metrics[i]); + auto it = usage_req.find(metric); if (it == usage_req.end()) { continue; } - q->vector_value(metrics[i], limit); + q->vector_value(metric, limit); q->vector_value(metrics_used, usage); if ( limit == DEFAULT ) { if ( default_q != 0 ) { - default_q->vector_value(metrics[i], limit); + default_q->vector_value(metric, limit); } else { @@ -235,7 +231,7 @@ bool Quota::check_quota(const string& qid, { ostringstream oss; - oss << "limit of " << limit << " reached for " << metrics[i] + oss << "limit of " << limit << " reached for " << metric << " quota in " << template_name; if ( !qid.empty() ) @@ -252,13 +248,11 @@ bool Quota::check_quota(const string& qid, // ------------------------------------------------------------------------- // Add resource usage to quotas // ------------------------------------------------------------------------- - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - string metrics_used = metrics[i]; + string metrics_used = metric + "_USED"; - metrics_used += "_USED"; - - auto it = usage_req.find(metrics[i]); + auto it = usage_req.find(metric); if (it == usage_req.end()) { @@ -288,13 +282,13 @@ void Quota::add_quota(const string& qid, map& usage_req) return; } - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - string metrics_used = metrics[i]; + string metrics_used = metric; metrics_used += "_USED"; - auto it = usage_req.find(metrics[i]); + auto it = usage_req.find(metric); if (it == usage_req.end()) { @@ -322,13 +316,11 @@ void Quota::del_quota(const string& qid, map& usage_req) return; } - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - string metrics_used = metrics[i]; + string metrics_used = metric + "_USED"; - metrics_used += "_USED"; - - auto it = usage_req.find(metrics[i]); + auto it = usage_req.find(metric); if (it == usage_req.end()) { @@ -370,13 +362,11 @@ void Quota::cleanup_quota(const string& qid) implicit_limit = DEFAULT; } - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - string metrics_used = metrics[i]; + string metrics_used = metric + "_USED"; - metrics_used += "_USED"; - - q->vector_value(metrics[i], limit); + q->vector_value(metric, limit); q->vector_value(metrics_used, usage); if ( usage != 0 || limit != implicit_limit ) @@ -399,9 +389,9 @@ int Quota::update_limits( { float limit_f; - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - const string& limit = va->vector_value_str(metrics[i], limit_f); + const string& limit = va->vector_value_str(metric, limit_f); if (limit.empty()) { @@ -425,7 +415,7 @@ int Quota::update_limits( return -1; } - quota->replace(metrics[i], one_util::float_to_str(limit_f)); + quota->replace(metric, one_util::float_to_str(limit_f)); } return 0; @@ -440,13 +430,11 @@ VectorAttribute * Quota::new_quota(const VectorAttribute * va) float limit_f; - for (int i=0; i < num_metrics; i++) + for (const string& metric : metrics) { - string metrics_used = metrics[i]; + string metrics_used = metric + "_USED"; - metrics_used += "_USED"; - - const string& limit = va->vector_value_str(metrics[i], limit_f); + const string& limit = va->vector_value_str(metric, limit_f); if (limit.empty()) { @@ -469,7 +457,7 @@ VectorAttribute * Quota::new_quota(const VectorAttribute * va) return 0; } - limits.insert(make_pair(metrics[i], one_util::float_to_str(limit_f))); + limits.insert(make_pair(metric, one_util::float_to_str(limit_f))); limits.insert(make_pair(metrics_used, "0")); } diff --git a/src/um/QuotaDatastore.cc b/src/um/QuotaDatastore.cc index 2a25aadc75..c9ce75cfe4 100644 --- a/src/um/QuotaDatastore.cc +++ b/src/um/QuotaDatastore.cc @@ -22,9 +22,7 @@ using namespace std; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -const char * QuotaDatastore::DS_METRICS[] = {"SIZE", "IMAGES"}; - -const int QuotaDatastore::NUM_DS_METRICS = 2; +const std::vector QuotaDatastore::DS_METRICS = {"SIZE", "IMAGES"}; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/um/QuotaImage.cc b/src/um/QuotaImage.cc index 0a7a90c2aa..0c5f10d67a 100644 --- a/src/um/QuotaImage.cc +++ b/src/um/QuotaImage.cc @@ -22,9 +22,7 @@ using namespace std; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -const char * QuotaImage::IMAGE_METRICS[] = {"RVMS"}; - -const int QuotaImage::NUM_IMAGE_METRICS = 1; +const std::vector QuotaImage::IMAGE_METRICS = {"RVMS"}; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/um/QuotaNetwork.cc b/src/um/QuotaNetwork.cc index a663eceb4c..c632a720d8 100644 --- a/src/um/QuotaNetwork.cc +++ b/src/um/QuotaNetwork.cc @@ -23,9 +23,7 @@ using namespace std; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -const char * QuotaNetwork::NET_METRICS[] = {"LEASES"}; - -const int QuotaNetwork::NUM_NET_METRICS = 1; +const std::vector QuotaNetwork::NET_METRICS = {"LEASES"}; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/um/QuotaVirtualMachine.cc b/src/um/QuotaVirtualMachine.cc index ac25340773..b1d4b6bb05 100644 --- a/src/um/QuotaVirtualMachine.cc +++ b/src/um/QuotaVirtualMachine.cc @@ -24,10 +24,10 @@ using namespace std; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -const char * QuotaVirtualMachine::VM_METRICS[] = {"VMS", "RUNNING_VMS", "CPU", +std::vector QuotaVirtualMachine::VM_METRICS = {"VMS", "RUNNING_VMS", "CPU", "RUNNING_CPU", "MEMORY", "RUNNING_MEMORY", "SYSTEM_DISK_SIZE"}; -const int QuotaVirtualMachine::NUM_VM_METRICS = 7; +std::vector QuotaVirtualMachine::VM_GENERIC; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -101,6 +101,20 @@ bool QuotaVirtualMachine::check(Template * tmpl, vm_request.insert(make_pair("RUNNING_VMS", running_vms)); } + for (const auto& metric : VM_GENERIC) + { + float generic_quota; + if ( tmpl->get(metric, generic_quota) ) + { + vm_request.insert(make_pair(metric, generic_quota)); + } + + if ( tmpl->get("RUNNING_" + metric, generic_quota) ) + { + vm_request.insert(make_pair("RUNNING_" + metric, generic_quota)); + } + } + return check_quota("", vm_request, default_quotas, error); } @@ -149,6 +163,20 @@ void QuotaVirtualMachine::add(Template * tmpl) vm_request.insert(make_pair("SYSTEM_DISK_SIZE", size)); + for (const auto& metric : VM_GENERIC) + { + float generic_quota; + if ( tmpl->get(metric, generic_quota) ) + { + vm_request.insert(make_pair(metric, generic_quota)); + } + + if ( tmpl->get("RUNNING_" + metric, generic_quota) ) + { + vm_request.insert(make_pair("RUNNING_" + metric, generic_quota)); + } + } + add_quota("", vm_request); } @@ -199,12 +227,58 @@ void QuotaVirtualMachine::del(Template * tmpl) vm_request.insert(make_pair("SYSTEM_DISK_SIZE", size)); + for (const auto& metric : VM_GENERIC) + { + float generic_quota; + if ( tmpl->get(metric, generic_quota) ) + { + vm_request.insert(make_pair(metric, generic_quota)); + } + + if ( tmpl->get("RUNNING_" + metric, generic_quota) ) + { + vm_request.insert(make_pair("RUNNING_" + metric, generic_quota)); + } + } + del_quota("", vm_request); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int QuotaVirtualMachine::add_metric_generic(const std::string& metric) +{ + if (std::find(VM_METRICS.begin(), VM_METRICS.end(), metric) != VM_METRICS.end()) + { + return -1; + } + + VM_METRICS.push_back(metric); + VM_METRICS.push_back("RUNNING_" + metric); + VM_GENERIC.push_back(metric); + + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +void QuotaVirtualMachine::add_running_quota_generic(Template& tmpl) +{ + for (const string& metric : VM_GENERIC) + { + string value; + if (tmpl.get(metric, value)) + { + tmpl.add("RUNNING_" + metric, value); + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + int QuotaVirtualMachine::get_default_quota( const string& id, Quotas& default_quotas, @@ -261,5 +335,19 @@ bool QuotaVirtualMachine::update(Template * tmpl, vm_request.insert(make_pair("SYSTEM_DISK_SIZE", delta_size)); } + for (const auto& metric : VM_GENERIC) + { + float generic_quota; + if ( tmpl->get(metric, generic_quota) ) + { + vm_request.insert(make_pair(metric, generic_quota)); + } + + if ( tmpl->get("RUNNING_" + metric, generic_quota) ) + { + vm_request.insert(make_pair("RUNNING_" + metric, generic_quota)); + } + } + return check_quota("", vm_request, default_quotas, error); } diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index acf375b146..601e8003c9 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -3950,28 +3950,56 @@ void VirtualMachine::decrypt() /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ -void VirtualMachine::get_quota_template(VirtualMachineTemplate& quota_tmpl, - bool only_running) +bool VirtualMachine::is_running_quota() const { - if ((state == VirtualMachine::PENDING) || - (state == VirtualMachine::CLONING) || - (state == VirtualMachine::CLONING_FAILURE) || - (state == VirtualMachine::HOLD) || - ((state == VirtualMachine::ACTIVE && - (lcm_state != VirtualMachine::HOTPLUG_SAVEAS_POWEROFF && - lcm_state != VirtualMachine::HOTPLUG_SAVEAS_SUSPENDED && - lcm_state != VirtualMachine::DISK_SNAPSHOT_POWEROFF && - lcm_state != VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF && - lcm_state != VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF && - lcm_state != VirtualMachine::DISK_SNAPSHOT_SUSPENDED && - lcm_state != VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED && - lcm_state != VirtualMachine::DISK_RESIZE_POWEROFF && - lcm_state != VirtualMachine::DISK_RESIZE_UNDEPLOYED && - lcm_state != VirtualMachine::HOTPLUG_NIC_POWEROFF && - lcm_state != VirtualMachine::HOTPLUG_SAVEAS_UNDEPLOYED && - lcm_state != VirtualMachine::HOTPLUG_SAVEAS_STOPPED )))) + return (state == VirtualMachine::PENDING) || + (state == VirtualMachine::CLONING) || + (state == VirtualMachine::CLONING_FAILURE) || + (state == VirtualMachine::HOLD) || + ((state == VirtualMachine::ACTIVE && + (lcm_state != VirtualMachine::HOTPLUG_SAVEAS_POWEROFF && + lcm_state != VirtualMachine::HOTPLUG_SAVEAS_SUSPENDED && + lcm_state != VirtualMachine::DISK_SNAPSHOT_POWEROFF && + lcm_state != VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF && + lcm_state != VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF && + lcm_state != VirtualMachine::DISK_SNAPSHOT_SUSPENDED && + lcm_state != VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED && + lcm_state != VirtualMachine::DISK_RESIZE_POWEROFF && + lcm_state != VirtualMachine::DISK_RESIZE_UNDEPLOYED && + lcm_state != VirtualMachine::HOTPLUG_NIC_POWEROFF && + lcm_state != VirtualMachine::HOTPLUG_SAVEAS_UNDEPLOYED && + lcm_state != VirtualMachine::HOTPLUG_SAVEAS_STOPPED && + lcm_state != VirtualMachine::HOTPLUG_PROLOG_POWEROFF && + lcm_state != VirtualMachine::HOTPLUG_EPILOG_POWEROFF ))); +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +void VirtualMachine::get_quota_template(VirtualMachineTemplate& quota_tmpl, + bool basic_quota, bool running_quota) +{ + if (basic_quota) { - std::string memory, cpu; + quota_tmpl.replace("VMS", 1); + + for (const string& metric : QuotaVirtualMachine::generic_metrics()) + { + string value; + + // Use value from user template, if it's not already added from template + if (user_obj_template->get(metric, value)) + { + quota_tmpl.replace(metric, value); + } + } + + quota_tmpl.merge(obj_template.get()); + } + + if (running_quota) + { + string memory, cpu; get_template_attribute("MEMORY", memory); get_template_attribute("CPU", cpu); @@ -3980,11 +4008,17 @@ void VirtualMachine::get_quota_template(VirtualMachineTemplate& quota_tmpl, quota_tmpl.add("RUNNING_CPU", cpu); quota_tmpl.add("RUNNING_VMS", 1); - if (!only_running) + for (const string& metric : QuotaVirtualMachine::generic_metrics()) { - quota_tmpl.add("MEMORY", memory); - quota_tmpl.add("CPU", cpu); - quota_tmpl.add("VMS", 1); + string value; + if (obj_template->get(metric, value)) + { + quota_tmpl.add("RUNNING_" + metric, value); + } + else if (user_obj_template->get(metric, value)) + { + quota_tmpl.add("RUNNING_" + metric, value); + } } } } diff --git a/src/vm/VirtualMachineDisk.cc b/src/vm/VirtualMachineDisk.cc index 5d68e62345..438121271d 100644 --- a/src/vm/VirtualMachineDisk.cc +++ b/src/vm/VirtualMachineDisk.cc @@ -476,7 +476,6 @@ void VirtualMachineDisk::resize_quotas(long long new_size, Template& ds_deltas, delta_disk->replace("TYPE", "FS"); delta_disk->replace("SIZE", delta_size); - vm_deltas.add("VMS", 0); vm_deltas.set(delta_disk); } }