diff --git a/SConstruct b/SConstruct index 5e4b36d856..4fdb6182ac 100644 --- a/SConstruct +++ b/SConstruct @@ -65,6 +65,7 @@ main_env.Append(LIBPATH=[ cwd+'/src/pool', cwd+'/src/template', cwd+'/src/vm', + cwd+'/src/vm_group', cwd+'/src/vm_template', cwd+'/src/vmm', cwd+'/src/lcm', @@ -221,6 +222,7 @@ build_scripts=[ 'src/nebula/SConstruct', 'src/pool/SConstruct', 'src/vm/SConstruct', + 'src/vm_group/SConstruct', 'src/vm_template/SConstruct', 'src/vmm/SConstruct', 'src/lcm/SConstruct', diff --git a/include/Nebula.h b/include/Nebula.h index e8a328d4ae..8a95086fa2 100644 --- a/include/Nebula.h +++ b/include/Nebula.h @@ -37,6 +37,7 @@ #include "VirtualRouterPool.h" #include "MarketPlacePool.h" #include "MarketPlaceAppPool.h" +#include "VMGroupPool.h" #include "VirtualMachineManager.h" #include "LifeCycleManager.h" @@ -156,6 +157,11 @@ public: return apppool; }; + VMGroupPool * get_vmgrouppool() + { + return vmgrouppool; + }; + // -------------------------------------------------------------- // Manager Accessors // -------------------------------------------------------------- @@ -580,10 +586,10 @@ private: "/DEFAULT_GROUP_QUOTAS/VM_QUOTA"), system_db(0), db(0), vmpool(0), hpool(0), vnpool(0), upool(0), ipool(0), gpool(0), tpool(0), - dspool(0), clpool(0), docpool(0), zonepool(0), - secgrouppool(0), vdcpool(0), vrouterpool(0), marketpool(0), apppool(0), - lcm(0), vmm(0), im(0), tm(0), dm(0), rm(0), hm(0), authm(0), - aclm(0), imagem(0), marketm(0), ipamm(0) + dspool(0), clpool(0), docpool(0), zonepool(0), secgrouppool(0), + vdcpool(0), vrouterpool(0), marketpool(0), apppool(0), vmgrouppool(0), + lcm(0), vmm(0), im(0), tm(0), dm(0), rm(0), hm(0), authm(0), aclm(0), + imagem(0), marketm(0), ipamm(0) { const char * nl = getenv("ONE_LOCATION"); @@ -634,6 +640,7 @@ private: delete vrouterpool; delete marketpool; delete apppool; + delete vmgrouppool; delete vmm; delete lcm; delete im; @@ -718,6 +725,7 @@ private: VirtualRouterPool * vrouterpool; MarketPlacePool * marketpool; MarketPlaceAppPool * apppool; + VMGroupPool * vmgrouppool; // --------------------------------------------------------------- // Nebula Managers diff --git a/include/NebulaUtil.h b/include/NebulaUtil.h index 029faab109..1289d6623c 100644 --- a/include/NebulaUtil.h +++ b/include/NebulaUtil.h @@ -96,22 +96,14 @@ namespace one_util * @param st string to split * @param delim delimiter character * @param result where the result will be saved - * @param clean_empty true to clean empty split parts. - * Example for st "a::b:c" - * clean_empty true will return ["a", "b", "c"] - * clean_empty fase will return ["a", "", "b", "c"] */ template - void split_unique( - const std::string& st, - char delim, - std::set& result, - bool clean_empty=true) + void split_unique(const std::string& st, char delim, std::set& result) { T elem; std::vector::const_iterator it; - std::vector strings = split(st, delim, clean_empty); + std::vector strings = split(st, delim, true); for (it = strings.begin(); it != strings.end(); it++) { @@ -127,6 +119,13 @@ namespace one_util } } + /** + * Explicit specialization for strings + */ + template <> + void split_unique(const std::string& st, char delim, + std::set& result); + /** * Joins the given element with the delimiter * diff --git a/include/PoolObjectSQL.h b/include/PoolObjectSQL.h index dd47343abf..6139fdaeaf 100644 --- a/include/PoolObjectSQL.h +++ b/include/PoolObjectSQL.h @@ -66,7 +66,8 @@ public: VDC = 0x0002000000000000LL, VROUTER = 0x0004000000000000LL, MARKETPLACE = 0x0008000000000000LL, - MARKETPLACEAPP = 0x0010000000000000LL + MARKETPLACEAPP = 0x0010000000000000LL, + VMGROUP = 0x0020000000000000LL }; static string type_to_str(ObjectType ob) @@ -90,6 +91,7 @@ public: case VROUTER: return "VROUTER" ; break; case MARKETPLACE: return "MARKETPLACE" ; break; case MARKETPLACEAPP: return "MARKETPLACEAPP" ; break; + case VMGROUP: return "VMGROUP" ; break; default: return ""; } }; diff --git a/include/RequestManagerAllocate.h b/include/RequestManagerAllocate.h index bf002e734c..9f099eeac7 100644 --- a/include/RequestManagerAllocate.h +++ b/include/RequestManagerAllocate.h @@ -679,4 +679,33 @@ private: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +class VMGroupAllocate : public RequestManagerAllocate +{ +public: + VMGroupAllocate(): + RequestManagerAllocate("VMGroupAllocate", + "Allocates a new vm group", + "A:ss", + true) + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupAllocate(){}; + + /* --------------------------------------------------------------------- */ + + Template * get_object_template() + { + return new Template; + }; + + Request::ErrorCode pool_allocate(xmlrpc_c::paramList const& paramList, + Template * tmpl, + int& id, + RequestAttributes& att); +}; + #endif diff --git a/include/RequestManagerChmod.h b/include/RequestManagerChmod.h index ed8881f3c3..4e83b39369 100644 --- a/include/RequestManagerChmod.h +++ b/include/RequestManagerChmod.h @@ -250,4 +250,19 @@ public: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +class VMGroupChmod: public RequestManagerChmod +{ +public: + VMGroupChmod(): + RequestManagerChmod("VMGroupChmod", + "Changes permission bits of a vm group") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupChmod(){}; +}; + #endif diff --git a/include/RequestManagerChown.h b/include/RequestManagerChown.h index d7840b1904..6656c27d02 100644 --- a/include/RequestManagerChown.h +++ b/include/RequestManagerChown.h @@ -341,4 +341,24 @@ public: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +class VMGroupChown: public RequestManagerChown +{ +public: + VMGroupChown(): + RequestManagerChown("VMGroupChown", + "Changes ownership of a vm group") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupChown(){}; + + PoolObjectSQL * get(const string& name, int uid, bool lock) + { + return static_cast(pool)->get(name, uid, lock); + }; +}; + #endif diff --git a/include/RequestManagerDelete.h b/include/RequestManagerDelete.h index b4382818d5..ea882363ad 100644 --- a/include/RequestManagerDelete.h +++ b/include/RequestManagerDelete.h @@ -449,7 +449,24 @@ protected: int drop(PoolObjectSQL * obj, bool resive, RequestAttributes& att); }; -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class VMGroupDelete : public RequestManagerDelete +{ +public: + VMGroupDelete(): + RequestManagerDelete("VMGroupDelete", + "Deletes a vm group") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupDelete(){}; +}; #endif + diff --git a/include/RequestManagerInfo.h b/include/RequestManagerInfo.h index 049e43bc24..4e2a96e17f 100644 --- a/include/RequestManagerInfo.h +++ b/include/RequestManagerInfo.h @@ -371,7 +371,23 @@ public: ~MarketPlaceAppInfo(){}; }; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +class VMGroupInfo : public RequestManagerInfo +{ +public: + VMGroupInfo(): + RequestManagerInfo("VMGroupInfo", + "Returns vm group information") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupInfo(){}; +}; + #endif diff --git a/include/RequestManagerPoolInfoFilter.h b/include/RequestManagerPoolInfoFilter.h index 1a3098f5ad..6f83e52389 100644 --- a/include/RequestManagerPoolInfoFilter.h +++ b/include/RequestManagerPoolInfoFilter.h @@ -553,4 +553,23 @@ public: ~MarketPlaceAppPoolInfo(){}; }; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class VMGroupPoolInfo : public RequestManagerPoolInfoFilter +{ +public: + VMGroupPoolInfo(): + RequestManagerPoolInfoFilter("VMGroupPoolInfo", + "Returns the vm group pool", + "A:siii") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupPoolInfo(){}; +}; + #endif diff --git a/include/RequestManagerRename.h b/include/RequestManagerRename.h index 8433d63f29..83b47d51cb 100644 --- a/include/RequestManagerRename.h +++ b/include/RequestManagerRename.h @@ -405,4 +405,23 @@ public: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +class VMGroupRename: public RequestManagerRename +{ +public: + VMGroupRename(): + RequestManagerRename("VMGroupRename", "Renames a vm group") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupRename(){}; + + PoolObjectSQL * get(const string& name, int uid, bool lock) + { + return static_cast(pool)->get(name, uid, lock); + }; +}; + #endif diff --git a/include/RequestManagerUpdateTemplate.h b/include/RequestManagerUpdateTemplate.h index 30988e99a9..021a30cbe4 100644 --- a/include/RequestManagerUpdateTemplate.h +++ b/include/RequestManagerUpdateTemplate.h @@ -343,4 +343,19 @@ public: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +class VMGroupUpdateTemplate : public RequestManagerUpdateTemplate +{ +public: + VMGroupUpdateTemplate(): + RequestManagerUpdateTemplate("VMGroupUpdateTemplate", + "Updates a vm group template") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_vmgrouppool(); + auth_object = PoolObjectSQL::VMGROUP; + }; + + ~VMGroupUpdateTemplate(){}; +}; + #endif diff --git a/include/Template.h b/include/Template.h index 70157ef32f..4c45094931 100644 --- a/include/Template.h +++ b/include/Template.h @@ -326,15 +326,15 @@ public: * @param name the attribute name. * @return true first attribute or 0 if not found or wrong type */ - inline const VectorAttribute * get(const string& name) const - { - return __get(name); - } + inline const VectorAttribute * get(const string& name) const + { + return __get(name); + } - inline VectorAttribute * get(const string& name) - { - return __get(name); - } + inline VectorAttribute * get(const string& name) + { + return __get(name); + } /** * Gets the value of a SingleAttribute with the given name and converts diff --git a/include/VMGroup.h b/include/VMGroup.h new file mode 100644 index 0000000000..786335e081 --- /dev/null +++ b/include/VMGroup.h @@ -0,0 +1,215 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------*/ + +#ifndef VMGROUP_H_ +#define VMGROUP_H_ + +#include "PoolObjectSQL.h" +#include "VMGroupRole.h" +#include "VMGroupRule.h" + +class VMGroupPool; + +enum class VMGroupPolicy; + +/** + * A VM group is a set of related VMs that may impose placement constraints. + * + * Data model: + * + * NAME = "Web server" + * DESCRIPTION = "A multi-tier web server: frontend, apps servers, db" + * + * ROLE = [ + * NAME = "frontend", + * ID = 0, + * VMS = "0,1" + * ] + * + * ROLE = [ + * NAME = "db", + * ID = 1, + * POLICY = ANTI_AFFINED, + * VMS = "2,3,4,5" + * ] + * + * ANTI_AFFINED = "db, front_end" + */ +class VMGroup : public PoolObjectSQL +{ +public: + /** + * Function to print the VMGroup object into a string in XML format + * @param xml the resulting XML string + * @return a reference to the generated string + */ + string& to_xml(string& xml) const; + + /** + * Rebuilds the object from an xml formatted string + * @param xml_str The xml-formatted string + * @return 0 on success, -1 otherwise + */ + int from_xml(const string &xml_str); + + /** + * Returns a copy of the Template + * @return A copy of the Template + */ + Template * clone_template() const + { + return new Template(*obj_template); + }; + + // ------------------------------------------------------------------------- + // Role Management + // ------------------------------------------------------------------------- + /** + * Adds a VM to a role + * @param role_name + * @param vmid + * + * @return 0 if VM was successfully added, -1 otherwise + */ + int add_vm(const std::string& role_name, int vmid) + { + return roles.add_vm(role_name, vmid); + } + + /** + * Deletes a VM from a role + * @param role_name + * @param vmid + * + * @return 0 if VM was successfully added, -1 otherwise + */ + int del_vm(const std::string& role_name, int vmid) + { + return roles.del_vm(role_name, vmid); + } + +private: + // ------------------------------------------------------------------------- + // Friends + // ------------------------------------------------------------------------- + friend class VMGroupPool; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + VMGroup(int _uid, int _gid, const string& _uname, const string& _gname, + int _umask, Template * group_template); + + ~VMGroup(); + + // ------------------------------------------------------------------------- + // Role Management + // ------------------------------------------------------------------------- + /** + * Check if all the roles in a AFFINED/ANTI_AFFINED rules are defined in + * the group + * @param policy attribute with a list (comma-separated) of role names + * @param error_str if any + * + * @return 0 if all roles are defined -1 otherwise + */ + int check_rule_names(VMGroupPolicy policy, std::string& error_str); + + /** + * Generate a rule_set from the AFFINED/ANTI_AFFINED rules + * @param p policy AFFINED or ANTIAFFINED + * @param rs rule_set with the rules + * @param error if some of the roles are not defined + * + * @return 0 if success -1 otherwise + */ + int get_rules(VMGroupPolicy p, VMGroupRule::rule_set& rs, std::string& err); + + int check_rule_consistency(std::string& error); + + // ------------------------------------------------------------------------- + // DataBase implementation + // ------------------------------------------------------------------------- + static const char * db_names; + + static const char * db_bootstrap; + + static const char * table; + + /** + * Execute an INSERT or REPLACE Sql query. + * @param db The SQL DB + * @param replace Execute an INSERT or a REPLACE + * @param error_str Returns the error reason, if any + * @return 0 one success + */ + int insert_replace(SqlDB *db, bool replace, string& error_str); + + /** + * Bootstraps the database table(s) associated to the VMGroup + * @return 0 on success + */ + static int bootstrap(SqlDB * db) + { + ostringstream oss(VMGroup::db_bootstrap); + + return db->exec(oss); + }; + + /** + * Writes the VMGroup in the database. + * @param db pointer to the db + * @return 0 on success + */ + int insert(SqlDB *db, string& error_str); + + /** + * Writes/updates the VMGroup's data fields in the database. + * @param db pointer to the db + * @return 0 on success + */ + int update(SqlDB *db) + { + string error_str; + return insert_replace(db, true, error_str); + } + + /** + * Checks the new roles and affined/anti-affined cross-references + * @param error string describing the error if any + * @return 0 on success + */ + int post_update_template(string& error); + + /** + * Factory method for VMGroup templates + */ + Template * get_new_template() const + { + return new Template; + } + + // ------------------------------------------------------------------------- + // VMGroup attributes + // ------------------------------------------------------------------------- + /** + * The role set + */ + VMGroupRoles roles; +}; + +#endif /*VMGROUP_H_*/ + diff --git a/include/VMGroupPool.h b/include/VMGroupPool.h new file mode 100644 index 0000000000..ae31a50880 --- /dev/null +++ b/include/VMGroupPool.h @@ -0,0 +1,158 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#ifndef VMGROUP_POOL_H_ +#define VMGROUP_POOL_H_ + +#include "PoolSQL.h" +#include "VMGroup.h" + +class AuthRequest; + +class VMGroupPool : public PoolSQL +{ +public: + VMGroupPool(SqlDB * db):PoolSQL(db, VMGroup::table, true, true){}; + + ~VMGroupPool(){}; + + /* ---------------------------------------------------------------------- */ + /* Methods for DB management */ + /* ---------------------------------------------------------------------- */ + /** + * Allocates a new VMGroup, writing it in the pool database. No memory is + * allocated for the object. + * + * @param uid user identifier + * @param gid the id of the group this object is assigned to + * @param uname user name + * @param gname group name + * @param umask permissions umask + * @param vmgroup_template a Template object + * @param oid the id assigned to the new VMGroup + * @param error_str Returns the error reason, if any + * + * @return the oid assigned to the object, -1 in case of failure + */ + int allocate(int uid, int gid, const std::string& uname, + const std::string& gname, int umask, Template * vmgrp_tmpl, int * oid, + std::string& error_str); + + /** + * Function to get a VMGroup from the pool, if the object is not in memory + * it is loaded from the DB + * @param oid VMGroup unique id + * @param lock locks the VMGroup mutex + * @return a pointer to the VMGroup, 0 if the VMGroup could not be loaded + */ + VMGroup * get(int oid, bool lock) + { + return static_cast(PoolSQL::get(oid, lock)); + }; + + /** + * Gets an object from the pool (if needed the object is loaded from the + * database). + * @param name of the object + * @param uid id of owner + * @param lock locks the object if true + * + * @return a pointer to the object, 0 in case of failure + */ + VMGroup * get(const std::string& name, int uid, bool lock) + { + return static_cast(PoolSQL::get(name, uid, lock)); + }; + + /** Update a VMGroup + * @param vmgroup pointer to VMGroup + * @return 0 on success + */ + int update(VMGroup * vmgroup) + { + return vmgroup->update(db); + }; + + /** + * Bootstraps the database table(s) associated to the VMGroup pool + * @return 0 on success + */ + static int bootstrap(SqlDB * _db) + { + return VMGroup::bootstrap(_db); + }; + + /** + * Dumps the VMGroup pool in XML format. A filter can be added to the query + * @param os the output stream to dump the pool contents + * @param where filter for the objects, defaults to all + * @param limit parameters used for pagination + * + * @return 0 on success + */ + int dump(std::ostringstream& os, const std::string& where, + const std::string& limit) + { + return PoolSQL::dump(os, "VM_GROUP_POOL", VMGroup::table, where, limit); + }; + + /** + * Parse the VMGROUP definition in a VM and fills the ROLE attributes. It + * also adds the VM to the role if found. + * @param va the vector attribute + * @param uid VM owner, used as default to look for the VMGroup + * @param vid of the VM + * @param err if any + * + * @return 0 on success + */ + int vmgroup_attribute(VectorAttribute * va, int uid, int vid, string& err); + + /** + * Removes VM from the VMGroup + * @param va with VMGROUP + * @param vid of the VM to be removed + */ + void del_vm(const VectorAttribute * va, int vid); + + /** + * Builds the AuthRequest for the VMGroup + * @param va with the VMGROUP + * @param uid VM owber, used as default to look for the VMGroup + * @param ar the authrequest + */ + void authorize(const VectorAttribute * va, int uid, AuthRequest* ar); + +private: + /** + * Factory method to produce objects + * @return a pointer to the new object + */ + PoolObjectSQL * create() + { + return new VMGroup(-1,-1,"","",0,0); + }; + + /** + * Gest a VMGroup from its vector attribute description + * @param va the VectorAttribute + * @param _uid default uid to look for the VMGroup + */ + VMGroup * get_from_attribute(const VectorAttribute *va, int _uid); +}; + +#endif /*VMGROUP_POOL_H_*/ + diff --git a/include/VMGroupRole.h b/include/VMGroupRole.h new file mode 100644 index 0000000000..aefd15d09b --- /dev/null +++ b/include/VMGroupRole.h @@ -0,0 +1,417 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------*/ + +#ifndef VMGROUP_ROLE_H_ +#define VMGROUP_ROLE_H_ + +#include "PoolObjectSQL.h" +#include "NebulaUtil.h" + +class VMGroupPool; + +enum class VMGroupPolicy; + +/** + * A VMGroupRole defines a VM type that typically implements a role in a + * multi-vm application. + * + * ROLE = [ + * NAME = "Web application servers", + * ID = 12, + * POLICY = AFFINED, + * VMS = "1,2,45,21" + * ] + * + */ +class VMGroupRole +{ +public: + VMGroupRole(VectorAttribute *_va); + + virtual ~VMGroupRole(){}; + + /** + * @return the role id + */ + int id() + { + int rid; + + va->vector_value("ID", rid); + + return rid; + } + + /** + * @return the role name + */ + std::string name() + { + return va->vector_value("NAME"); + } + + /** + * @return the set of VMs in a string in a comma separated list + */ + std::string vms_s() + { + return va->vector_value("VMS"); + } + + /** + * @return the policy of this role + */ + VMGroupPolicy policy(); + + std::string policy_s() + { + return va->vector_value("POLICY"); + }; + + /* ---------------------------------------------------------------------- */ + /* VMS set Interface */ + /* ---------------------------------------------------------------------- */ + const std::set& get_vms() + { + return vms; + }; + + int size_vms() + { + return vms.size(); + } + + void add_vm(int vm_id); + + void del_vm(int vm_id); + + /* ---------------------------------------------------------------------- */ + /* Placement constraints */ + /* ---------------------------------------------------------------------- */ + /** + * Generates a string with the boolean expression to conform the role + * internal policy + * @param vm_id of the VM to generate the requirements for + * @param requirements + */ + void vm_role_requirements(int vm_id, std::string& requirements); + + /** + * Generates a string with the boolean expression to conform an affinity + * constraint policy + * @param p policy to place VMs respect to this role VMs + * @param requirements + */ + void role_requirements(VMGroupPolicy p, std::string& requirements); + + /** + * Gets the placement requirements for the affined HOSTS + * @param reqs string with the requirements expression + */ + void affined_host_requirements(std::string& reqs); + + /** + * Gets the placement requirements for the antiaffined HOSTS + * @param reqs string with the requirements expression + */ + void antiaffined_host_requirements(std::string& reqs); + + /** + * Generate generic requirements for a set of hosts + * @param hosts the set + * @param op1 operator for each host requirement = or != + * @param op2 operator to join host requirements & or | + * @param oss stream where the requirement expression is output + */ + static void host_requirements(std::set& hosts, const std::string& op1, + const std::string& op2, std::ostringstream& oss); + +private: + /** + * The set of vms in the role + */ + std::set vms; + + /** + * The associated vector attribute + */ + VectorAttribute * va; + + /** + * Set the VMS attribute for the role (list of VM IDs) + */ + void set_vms(); +}; + +/** + * The VMGroupRoles class represents a set of ROLES stored in a Template + */ +class VMGroupRoles +{ +public: + VMGroupRoles():roles_template(false,'=',"ROLES"), next_role(0){}; + + ~VMGroupRoles() + { + by_id.delete_roles(); + }; + + /* ---------------------------------------------------------------------- */ + /* ---------------------------------------------------------------------- */ + /** + * Max number of roles in a VMGroup + */ + const static int MAX_ROLES = 32; + + /* ---------------------------------------------------------------------- */ + /* ---------------------------------------------------------------------- */ + /** + * Function to print the VMGroupRoles into a string in XML format + * @param xml the resulting XML string + * @return a reference to the generated string + */ + std::string& to_xml(std::string& xml_str) const + { + return roles_template.to_xml(xml_str); + } + + /** + * Builds the object from an xml node + * @param node for the ROLES template + * @return 0 on success, -1 otherwise + */ + int from_xml_node(const xmlNodePtr node); + + /** + * Adds a new role to the set + * @param vrole VectorAttribute of the role + * @param error string if any + * + * @return 0 on success + */ + int add_role(VectorAttribute * vrole, string& error); + + /** + * Generates the ids corresponding to a set of role names + * @param rnames string with a comma separated list of role names + * @param keyi the set of ids + * + * @return 0 if all the names were successfully translated + */ + int names_to_ids(const std::string& rnames, std::set& keyi); + + /** + * Adds a VM to a role + * @param role_name + * @param vmid + * + * @return 0 if VM was successfully added, -1 otherwise + */ + int add_vm(const std::string& role_name, int vmid); + + /** + * Deletes a VM from a role + * @param role_name + * @param vmid + * + * @return 0 if VM was successfully added, -1 otherwise + */ + int del_vm(const std::string& role_name, int vmid); + + /** + * @return the total number of VMs in the group + */ + int vm_size(); + + /** + * @return the a VMGroupRole by its name + * @param rname role name + */ + VMGroupRole * get(const std::string& rname) + { + return by_name.get(rname); + } + + /** + * @return the a VMGroupRole by its id + * @param rname role name + */ + VMGroupRole * get(int id) + { + return by_id.get(id); + } + + /* ---------------------------------------------------------------------- */ + /* ---------------------------------------------------------------------- */ + /** + * Iterator for the roles in the group + */ + class RoleIterator + { + public: + RoleIterator& operator=(const RoleIterator& rhs) + { + role_it = rhs.role_it; + return *this; + } + + RoleIterator& operator++() + { + ++role_it; + return *this; + } + + bool operator!=(const RoleIterator& rhs) + { + return role_it != rhs.role_it; + } + + VMGroupRole * operator*() const + { + return role_it->second; + } + + RoleIterator(){}; + RoleIterator(const RoleIterator& rit):role_it(rit.role_it){}; + RoleIterator(const std::map::iterator& _role_it) + :role_it(_role_it){}; + + virtual ~RoleIterator(){}; + + private: + std::map::iterator role_it; + }; + + RoleIterator begin() + { + RoleIterator it(by_id.begin()); + return it; + } + + RoleIterator end() + { + RoleIterator it(by_id.end()); + return it; + } + + typedef class RoleIterator role_iterator; + +private: + /** + * A role map indexed by different key types + */ + template + class RoleMap + { + public: + /** + * Inserts a new role in the map + * @param k the key + * @param r pointer to yhe VMGroupRole + * @return true if the role was successfully inserted + */ + bool insert(const T& k, VMGroupRole * r) + { + std::pair rpair(k, r); + std::pair rc; + + rc = roles.insert(rpair); + + return rc.second; + } + + /** + * Frees the memory associated to the map and clears it + */ + void delete_roles() + { + for (roles_it it = roles.begin() ; it != roles.end() ; ++it ) + { + delete it->second; + } + + clear(); + } + + VMGroupRole * get(T k) + { + typename std::map::iterator it; + + it = roles.find(k); + + if ( it == roles.end() ) + { + return 0; + } + + return it->second; + } + + /** + * Clears the contents of the map + */ + void clear() + { + roles.clear(); + } + + size_t erase(const T& k) + { + return roles.erase(k); + } + + /** + * Iterators for the map + */ + typedef typename std::map::iterator roles_it; + + roles_it begin() + { + return roles.begin(); + } + + roles_it end() + { + return roles.end(); + } + + private: + std::map roles; + }; + + /** + * The role template to store the VMGroupRole + */ + Template roles_template; + + /** + * The next role id + */ + int next_role; + + /** + * Map to access the roles by their name + */ + RoleMap by_name; + + /** + * Map to access the roles by their id + */ + RoleMap by_id; +}; + +#endif /*VMGROUP_ROLE_H*/ + diff --git a/include/VMGroupRule.h b/include/VMGroupRule.h new file mode 100644 index 0000000000..e083ce5f56 --- /dev/null +++ b/include/VMGroupRule.h @@ -0,0 +1,189 @@ + +/* ------------------------------------------------------------------------ */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------*/ + +#ifndef VMGROUP_RULE_H_ +#define VMGROUP_RULE_H_ + +#include +#include "VMGroupRole.h" + +struct VMGroupRule_compare; + +/** + * Placement policy rules for roles + * AFFINED: VMs of all roles are placed in the same hypervisor + * ANTI_AFFINED: VMs are placed in different hypervisors (role-wise) + * NONE: No additional placement constraints + */ +enum class VMGroupPolicy +{ + NONE = 0x00, + AFFINED = 0x01, + ANTI_AFFINED= 0x02 +}; + +std::ostream& operator<<(std::ostream& os, VMGroupPolicy policy); + +/** + * A rule represents a role placement policy + */ +class VMGroupRule +{ +public: + /** + * A specialized set for rules + */ + typedef std::set rule_set; + + typedef std::bitset role_bitset; + + /* ---------------------------------------------------------------------- */ + /* Rule Constructors */ + /* ---------------------------------------------------------------------- */ + VMGroupRule():policy(VMGroupPolicy::NONE),roles(){}; + + VMGroupRule(VMGroupPolicy p, std::set roles_id):policy(p) + { + std::set::iterator it; + + for ( it = roles_id.begin(); it != roles_id.end(); ++it ) + { + if ( *it < VMGroupRoles::MAX_ROLES ) + { + roles[*it] = 1; + } + } + }; + + VMGroupRule(VMGroupPolicy p, role_bitset _roles):policy(p), roles(_roles){}; + + VMGroupRule(const VMGroupRule& other) + { + policy = other.policy; + roles = other.roles; + } + + /* ---------------------------------------------------------------------- */ + /* Rule operators */ + /* ---------------------------------------------------------------------- */ + VMGroupRule& operator=(const VMGroupRule& other) + { + if ( this != &other ) + { + policy = other.policy; + roles = other.roles; + } + + return *this; + } + + VMGroupRule& operator&= (const VMGroupRule& other) + { + roles &= other.roles; + return *this; + } + + VMGroupRule operator& (const VMGroupRule& other) const + { + return VMGroupRule(policy, other.roles & roles ); + } + + VMGroupRule& operator|= (const VMGroupRule& other) + { + roles |= other.roles; + return *this; + } + + VMGroupRule operator| (const VMGroupRule& other) const + { + return VMGroupRule(policy, other.roles | roles ); + } + + bool none() + { + return roles.none(); + } + + /* ---------------------------------------------------------------------- */ + /* Rule interface */ + /* ---------------------------------------------------------------------- */ + /** + * Check if an affined and anti-affined rule set are compatible. Sets are + * compatible if there isn't a role in the affined and anti-affined sets + * at the same time + * @param affined rule set + * @param anti affined rule set + * @param err a rule with the roles in both sets + * + * @return true if sets are compatible + */ + static bool compatible(rule_set& affined, rule_set& anti, VMGroupRule& err); + + /** + * Reduce a set of affinity rules merging rules affecting the same roles + * Example: + * AFFINED = a, b + * AFFINED = b, c -------> AFFINED = a, b, c + * AFFINED = e, d AFFINED = e, d + * + * @param affined set of rules to be reduced + * @param reduced set + */ + static void reduce(rule_set affined, rule_set& reduced); + + /** + * @return the roles in the rule as a bitset (1 roles is in) + */ + const role_bitset& get_roles() const + { + return roles; + } + + VMGroupPolicy get_policy() const + { + return policy; + } + +private: + + friend class VMGroupRule_compare; + + /** + * Type of the rule + */ + VMGroupPolicy policy; + + /** + * Roles participating in the rule + */ + std::bitset roles; +}; + +/** + * Functor to compre two rules. Two rules are considered equivalent if the + * include the same roles. + */ +struct VMGroupRule_compare +{ + bool operator() (const VMGroupRule& lhs, const VMGroupRule& rhs) const + { + return lhs.roles.to_ullong() < rhs.roles.to_ullong(); + } +}; + +#endif /*VMGROUP_RULE_H_*/ + diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 3fa26008f7..aeb9f21b58 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -1085,6 +1085,14 @@ public: */ void remove_security_group(int sgid); + // ------------------------------------------------------------------------ + // Virtual Machine Groups + // ------------------------------------------------------------------------ + /** + * Remove this VM from its role and VM group if any + */ + void release_vmgroup(); + // ------------------------------------------------------------------------ // Imported VM interface // ------------------------------------------------------------------------ @@ -1999,7 +2007,7 @@ private: int parse_context_variables(VectorAttribute ** context, string& error_str); // ------------------------------------------------------------------------- - // NIC & DISK Management Helpers + // Management helpers: NIC, DISK and VMGROUP // ------------------------------------------------------------------------- /** * Get all network leases for this Virtual Machine @@ -2014,6 +2022,13 @@ private: */ int get_disk_images(string &error_str); + /** + * Adds the VM to the VM group if needed + * @param error_str Returns the error reason, if any + * @return 0 if success + */ + int get_vmgroup(string& error); + // ------------------------------------------------------------------------ // Public cloud templates related functions // ------------------------------------------------------------------------ diff --git a/install.sh b/install.sh index 04632755c2..e5ba5b9c7f 100755 --- a/install.sh +++ b/install.sh @@ -550,6 +550,7 @@ BIN_FILES="src/nebula/oned \ src/cli/oneflow \ src/cli/oneflow-template \ src/cli/onesecgroup \ + src/cli/onevmgroup \ src/cli/onevdc \ src/cli/onevrouter \ src/cli/onemarket \ @@ -1280,6 +1281,8 @@ RUBY_OPENNEBULA_LIB_FILES="src/oca/ruby/opennebula/acl_pool.rb \ src/oca/ruby/opennebula/pool.rb \ src/oca/ruby/opennebula/security_group_pool.rb \ src/oca/ruby/opennebula/security_group.rb \ + src/oca/ruby/opennebula/vm_group_pool.rb \ + src/oca/ruby/opennebula/vm_group.rb \ src/oca/ruby/opennebula/system.rb \ src/oca/ruby/opennebula/template_pool.rb \ src/oca/ruby/opennebula/template.rb \ @@ -1440,6 +1443,7 @@ ONE_CLI_LIB_FILES="src/cli/one_helper/onegroup_helper.rb \ src/cli/one_helper/onevdc_helper.rb \ src/cli/one_helper/oneacct_helper.rb \ src/cli/one_helper/onesecgroup_helper.rb \ + src/cli/one_helper/onevmgroup_helper.rb \ src/cli/one_helper/onevrouter_helper.rb \ src/cli/one_helper/onemarketapp_helper.rb \ src/cli/one_helper/onemarket_helper.rb" @@ -1459,6 +1463,7 @@ CLI_BIN_FILES="src/cli/onevm \ src/cli/oneflow-template \ src/cli/oneacct \ src/cli/onesecgroup \ + src/cli/onevmgroup \ src/cli/oneshowback \ src/cli/onevdc \ src/cli/onevrouter \ @@ -1478,6 +1483,7 @@ CLI_CONF_FILES="src/cli/etc/onegroup.yaml \ src/cli/etc/onezone.yaml \ src/cli/etc/oneacct.yaml \ src/cli/etc/onesecgroup.yaml \ + src/cli/etc/onevmgroup.yaml \ src/cli/etc/oneshowback.yaml \ src/cli/etc/onevdc.yaml \ src/cli/etc/onevrouter.yaml \ diff --git a/src/acl/AclManager.cc b/src/acl/AclManager.cc index c6edc990ea..b04c57724e 100644 --- a/src/acl/AclManager.cc +++ b/src/acl/AclManager.cc @@ -83,7 +83,7 @@ AclManager::AclManager( string error_str; // Users in group USERS can create standard resources - // @1 VM+IMAGE+TEMPLATE+DOCUMENT/* CREATE * + // @1 VM+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VMGROUP/* CREATE * add_rule(AclRule::GROUP_ID | 1, AclRule::ALL_ID | @@ -91,7 +91,8 @@ AclManager::AclManager( PoolObjectSQL::IMAGE | PoolObjectSQL::TEMPLATE | PoolObjectSQL::DOCUMENT | - PoolObjectSQL::SECGROUP, + PoolObjectSQL::SECGROUP | + PoolObjectSQL::VMGROUP, AuthRequest::CREATE, AclRule::ALL_ID, error_str); diff --git a/src/acl/AclRule.cc b/src/acl/AclRule.cc index 9a8dbb72b5..646261ab87 100644 --- a/src/acl/AclRule.cc +++ b/src/acl/AclRule.cc @@ -28,7 +28,7 @@ const long long AclRule::CLUSTER_ID = 0x0000000800000000LL; const long long AclRule::NONE_ID = 0x1000000000000000LL; -const int AclRule::num_pool_objects = 16; +const int AclRule::num_pool_objects = 17; const PoolObjectSQL::ObjectType AclRule::pool_objects[] = { PoolObjectSQL::VM, PoolObjectSQL::HOST, @@ -45,7 +45,8 @@ const PoolObjectSQL::ObjectType AclRule::pool_objects[] = { PoolObjectSQL::VDC, PoolObjectSQL::VROUTER, PoolObjectSQL::MARKETPLACE, - PoolObjectSQL::MARKETPLACEAPP + PoolObjectSQL::MARKETPLACEAPP, + PoolObjectSQL::VMGROUP }; const int AclRule::num_auth_operations = 4; @@ -61,7 +62,8 @@ const long long AclRule::INVALID_CLUSTER_OBJECTS = PoolObjectSQL::TEMPLATE | PoolObjectSQL::GROUP | PoolObjectSQL::ACL | PoolObjectSQL::CLUSTER | PoolObjectSQL::DOCUMENT | PoolObjectSQL::ZONE | PoolObjectSQL::SECGROUP | PoolObjectSQL::VDC | PoolObjectSQL::VROUTER | - PoolObjectSQL::MARKETPLACE | PoolObjectSQL::MARKETPLACEAPP; + PoolObjectSQL::MARKETPLACE | PoolObjectSQL::MARKETPLACEAPP | + PoolObjectSQL::VMGROUP; const long long AclRule::INVALID_GROUP_OBJECTS = PoolObjectSQL::HOST | PoolObjectSQL::GROUP | PoolObjectSQL::CLUSTER | @@ -237,7 +239,7 @@ bool AclRule::malformed(string& error_str) const oss << "[resource] type is missing"; } - if ( (resource & 0xFFE0000000000000LL) != 0 ) + if ( (resource & 0xFFC0000000000000LL) != 0 ) { if ( error ) { diff --git a/src/cli/etc/oneacl.yaml b/src/cli/etc/oneacl.yaml index f08817d68b..035116f78f 100644 --- a/src/cli/etc/oneacl.yaml +++ b/src/cli/etc/oneacl.yaml @@ -9,9 +9,9 @@ :size: 8 :right: true -:RES_VHNIUTGDCOZSvRMA: +:RES_VHNIUTGDCOZSvRMAP: :desc: Which resource the rule applies to - :size: 20 + :size: 21 :RID: :desc: Resource ID @@ -31,7 +31,7 @@ :default: - :ID - :USER -- :RES_VHNIUTGDCOZSvRMA +- :RES_VHNIUTGDCOZSvRMAP - :RID - :OPE_UMAC - :ZONE diff --git a/src/cli/etc/onevmgroup.yaml b/src/cli/etc/onevmgroup.yaml new file mode 100644 index 0000000000..a0f96ff19e --- /dev/null +++ b/src/cli/etc/onevmgroup.yaml @@ -0,0 +1,37 @@ +--- +:ID: + :desc: ONE identifier for the VM Group + :size: 4 + +:NAME: + :desc: Name of the VM Group + :size: 15 + :left: true + +:USER: + :desc: Username of the VM Group owner + :size: 8 + :left: true + +:GROUP: + :desc: Group of the VM Group + :size: 8 + :left: true + +:VMS: + :desc: Number of VMs in the VM Group + :size: 4 + :lef: true + +:ROLES: + :desc: Roles in the VM Group + :size: 36 + :left: true + +:default: +- :ID +- :USER +- :GROUP +- :NAME +- :VMS +- :ROLES diff --git a/src/cli/one_helper/oneacl_helper.rb b/src/cli/one_helper/oneacl_helper.rb index bad52a9e4f..8c1ed2dfb0 100644 --- a/src/cli/one_helper/oneacl_helper.rb +++ b/src/cli/one_helper/oneacl_helper.rb @@ -44,7 +44,7 @@ private def self.resource_mask(str) resource_type=str.split("/")[0] - mask = "----------------" + mask = "-----------------" resource_type.split("+").each{|type| case type @@ -80,6 +80,8 @@ private mask[14] = "M" when "MARKETPLACEAPP" mask[15] = "A" + when "VMGROUP" + mask[16] = "P" end } mask @@ -119,8 +121,8 @@ private d['STRING'].split(" ")[0] end - column :RES_VHNIUTGDCOZSvRMA, "Resource to which the rule applies", - :size => 20 do |d| + column :RES_VHNIUTGDCOZSvRMAP, "Resource to which the rule applies", + :size => 21 do |d| OneAclHelper::resource_mask d['STRING'].split(" ")[1] end @@ -137,7 +139,7 @@ private OneAclHelper::right_mask d['STRING'].split(" ")[2] end - default :ID, :USER, :RES_VHNIUTGDCOZSvRMA, :RID, :OPE_UMAC, :ZONE + default :ID, :USER, :RES_VHNIUTGDCOZSvRMAP, :RID, :OPE_UMAC, :ZONE end table diff --git a/src/cli/one_helper/onevmgroup_helper.rb b/src/cli/one_helper/onevmgroup_helper.rb new file mode 100644 index 0000000000..4fc82f64dc --- /dev/null +++ b/src/cli/one_helper/onevmgroup_helper.rb @@ -0,0 +1,170 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2016, OpenNebula Project, OpenNebula Systems # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'one_helper' + +class OneVMGroupHelper < OpenNebulaHelper::OneHelper + def self.rname + "VM_GROUP" + end + + def self.conf_file + "onevmgroup.yaml" + end + + def format_pool(options) + config_file = self.class.table_conf + + table = CLIHelper::ShowTable.new(config_file, self) do + column :ID, "ONE identifier for the VM Group", :size=>4 do |d| + d["ID"] + end + + column :NAME, "Name of the VM Group", :left, :size=>15 do |d| + d["NAME"] + end + + column :USER, "Username of the VM Group owner", :left, + :size=>8 do |d| + helper.user_name(d, options) + end + + column :GROUP, "Group of the VM Group", :left, :size=>8 do |d| + helper.group_name(d, options) + end + + column :VMS, "Number of VMs in the VM Group", :left, :size=>4 do |d| + roles = [d["ROLES"]["ROLE"]].flatten + vms = 0 + + if !roles.nil? + roles.each { |r| + vms += r["VMS"].split(',').size if !r["VMS"].nil? + } + end + + vms + end + + column :ROLES, "Roles in the VM Group", :left, :size=>36 do |d| + roles = [d["ROLES"]["ROLE"]].flatten + roles_names = "" + + if !roles.nil? + rnames = roles.collect { |i| i["NAME"] } + roles_names = rnames.join(", ") if !rnames.empty? + end + + roles_names + end + + default :ID, :USER, :GROUP, :NAME, :VMS, :ROLES + end + + table + end + + private + + def factory(id=nil) + if id + OpenNebula::VMGroup.new_with_id(id, @client) + else + xml=OpenNebula::VMGroup.build_xml + OpenNebula::VMGroup.new(xml, @client) + end + end + + def factory_pool(user_flag=-2) + OpenNebula::VMGroupPool.new(@client, user_flag) + end + + def format_resource(vmgroup, options = {}) + str="%-15s: %-20s" + str_h1="%-80s" + + CLIHelper.print_header( + str_h1 % "VM GROUP #{vmgroup['ID']} INFORMATION") + puts str % ["ID", vmgroup.id.to_s] + puts str % ["NAME", vmgroup.name] + puts str % ["USER", vmgroup['UNAME']] + puts str % ["GROUP", vmgroup['GNAME']] + + CLIHelper.print_header(str_h1 % "PERMISSIONS",false) + + puts + + ["OWNER", "GROUP", "OTHER"].each { |e| + mask = "---" + mask[0] = "u" if vmgroup["PERMISSIONS/#{e}_U"] == "1" + mask[1] = "m" if vmgroup["PERMISSIONS/#{e}_M"] == "1" + mask[2] = "a" if vmgroup["PERMISSIONS/#{e}_A"] == "1" + + puts str % [e, mask] + } + + puts + + CLIHelper.print_header(str_h1 % "ROLES", false) + + if !vmgroup.to_hash['VM_GROUP']['ROLES']['ROLE'].nil? + roles = [vmgroup.to_hash['VM_GROUP']['ROLES']['ROLE']].flatten + end + + CLIHelper::ShowTable.new(nil, self) do + column :ID, "", :left, :size=>4 do |d| + d["ID"] + end + + column :NAME, "", :left, :size=>8 do |d| + d["NAME"] + end + + column :POLICY, "", :left, :size=>12 do |d| + if d["POLICY"].nil? + '-' + else + d["POLICY"] + end + end + + column :AFFINED_HOSTS, "", :left, :size=>18 do |d| + if d["HOST_AFFINED"].nil? + '-' + else + d["HOST_AFFINED"] + end + end + + column :ANTI_AFFINED_HOST, "", :left, :size=>18 do |d| + if d["HOST_ANTI_AFFINED"].nil? + '-' + else + d["HOST_ANTI_AFFINED"] + end + end + + column :VIRTUAL_MACHINES, "", :left, :size=>20 do |d| + d["VMS"] + end + end.show(roles, {}) + + puts + + CLIHelper.print_header(str_h1 % "TEMPLATE CONTENTS",false) + puts vmgroup.template_str + end +end diff --git a/src/cli/onevmgroup b/src/cli/onevmgroup new file mode 100755 index 0000000000..982fa4d7ca --- /dev/null +++ b/src/cli/onevmgroup @@ -0,0 +1,185 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2016, OpenNebula Project, OpenNebula Systems # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cli" + +require 'command_parser' +require 'one_helper/onevmgroup_helper' + +cmd=CommandParser::CmdParser.new(ARGV) do + usage "`onevmgroup` [] []" + version OpenNebulaHelper::ONE_VERSION + + helper = OneVMGroupHelper.new + + before_proc do + helper.set_client(options) + end + + ######################################################################## + # Global Options + ######################################################################## + set :option, CommandParser::OPTIONS+OpenNebulaHelper::CLIENT_OPTIONS + + list_options = CLIHelper::OPTIONS + list_options << OpenNebulaHelper::XML + list_options << OpenNebulaHelper::NUMERIC + list_options << OpenNebulaHelper::DESCRIBE + + ######################################################################## + # Formatters for arguments + ######################################################################## + set :format, :groupid, OpenNebulaHelper.rname_to_id_desc("GROUP") do |arg| + OpenNebulaHelper.rname_to_id(arg, "GROUP") + end + + set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg| + OpenNebulaHelper.rname_to_id(arg, "USER") + end + + set :format, :vmgroupid, OneVMGroupHelper.to_id_desc do |arg| + helper.to_id(arg) + end + + set :format, :vmgroupid_list, OneVMGroupHelper.list_to_id_desc do |arg| + helper.list_to_id(arg) + end + + set :format, :filterflag, OneVMGroupHelper.filterflag_to_i_desc do |arg| + helper.filterflag_to_i(arg) + end + + ######################################################################## + # Commands + ######################################################################## + + create_desc = <<-EOT.unindent + Creates a new VM Group from the given description + EOT + + command :create, create_desc, :file do + helper.create_resource(options) do |obj| + begin + template = File.read(args[0]) + + obj.allocate(template) + rescue => e + STDERR.puts e.messsage + exit -1 + end + end + end + + delete_desc = <<-EOT.unindent + Deletes the VM Group + EOT + + command :delete, delete_desc, [:range, :vmgroupid_list] do + helper.perform_actions(args[0],options,"deleted") do |obj| + obj.delete + end + end + + list_desc = <<-EOT.unindent + Lists VM Group in the pool + EOT + + command :list, list_desc, [:filterflag, nil], :options=>list_options do + helper.list_pool(options, false, args[0]) + end + + show_desc = <<-EOT.unindent + Shows information for the given VM Group + EOT + + command :show, show_desc, :vmgroupid, :options=>OpenNebulaHelper::XML do + helper.show_resource(args[0],options) + end + + chgrp_desc = <<-EOT.unindent + Changes the VM Group's group + EOT + + command :chgrp, chgrp_desc,[:range, :vmgroupid_list], :groupid do + helper.perform_actions(args[0],options,"Group changed") do |obj| + obj.chown(-1, args[1].to_i) + end + end + + chown_desc = <<-EOT.unindent + Changes the VM Group's owner and group + EOT + + command :chown, chown_desc, [:range, :vmgroupid_list], :userid, + [:groupid,nil] do + gid = args[2].nil? ? -1 : args[2].to_i + helper.perform_actions(args[0],options,"Owner/Group changed") do |obj| + obj.chown(args[1].to_i, gid) + end + end + + chmod_desc = <<-EOT.unindent + Changes the VM Group permissions + EOT + + command :chmod, chmod_desc, [:range, :vmgroupid_list], :octet do + helper.perform_actions(args[0],options, "Permissions changed") do |t| + t.chmod_octet(args[1]) + end + end + + update_desc = <<-EOT.unindent + Update the template contents. If a path is not provided the editor will + be launched to modify the current content. + EOT + + command :update, update_desc, :vmgroupid, [:file, nil], + :options=>OpenNebulaHelper::APPEND do + helper.perform_action(args[0],options,"modified") do |obj| + if options[:append] + str = OpenNebulaHelper.append_template(args[0], obj, args[1]) + else + str = OpenNebulaHelper.update_template(args[0], obj, args[1]) + end + + helper.set_client(options) + obj = helper.retrieve_resource(obj.id) + + obj.update(str, options[:append]) + end + end + + rename_desc = <<-EOT.unindent + Renames the VM Group + EOT + + command :rename, rename_desc, :vmgroupid, :name do + helper.perform_action(args[0],options,"renamed") do |o| + o.rename(args[1]) + end + end +end diff --git a/src/common/NebulaUtil.cc b/src/common/NebulaUtil.cc index 8679c75cfa..247aafc0c3 100644 --- a/src/common/NebulaUtil.cc +++ b/src/common/NebulaUtil.cc @@ -339,6 +339,25 @@ std::string one_util::gsub(const std::string& st, const std::string& sfind, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +namespace one_util +{ +template<> +void split_unique(const std::string& st, char delim, std::set& res) +{ + std::vector::const_iterator it; + + std::vector strings = split(st, delim, true); + + for (it = strings.begin(); it != strings.end(); it++) + { + res.insert(*it); + } +} +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + /** * Buffer length for zlib inflate/deflate */ diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 05ff168b84..c5e4b588bf 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -223,6 +223,7 @@ void DispatchManager::free_vm_resources(VirtualMachine * vm) int vmid; vm->release_network_leases(); + vm->release_vmgroup(); vm->release_disk_images(ds_quotas); vm->set_exit_time(time(0)); diff --git a/src/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index 276b6fcf9b..cb48a9f1a9 100644 --- a/src/dm/DispatchManagerStates.cc +++ b/src/dm/DispatchManagerStates.cc @@ -230,6 +230,8 @@ void DispatchManager::done_action(int vid) { vm->release_network_leases(); + vm->release_vmgroup(); + vm->release_disk_images(ds_quotas); vm->set_state(VirtualMachine::DONE); diff --git a/src/group/Group.cc b/src/group/Group.cc index 3e6930c0cc..6882a8bd23 100644 --- a/src/group/Group.cc +++ b/src/group/Group.cc @@ -353,7 +353,7 @@ void Group::add_admin_rules(int user_id) NebulaLog::log("GROUP",Log::ERROR,error_msg); } - // # VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER/@ USE+MANAGE * + // # VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER+VMGROUP/@ USE+MANAGE * if ( aclm->add_rule( AclRule::INDIVIDUAL_ID | user_id, @@ -365,6 +365,7 @@ void Group::add_admin_rules(int user_id) PoolObjectSQL::DOCUMENT | PoolObjectSQL::SECGROUP | PoolObjectSQL::VROUTER | + PoolObjectSQL::VMGROUP | AclRule::GROUP_ID | oid, @@ -449,7 +450,7 @@ void Group::del_admin_rules(int user_id) NebulaLog::log("GROUP",Log::ERROR,error_msg); } - // # VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER/@ USE+MANAGE * + // # VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER+VMGROUP/@ USE+MANAGE * if ( aclm->del_rule( AclRule::INDIVIDUAL_ID | user_id, @@ -461,6 +462,7 @@ void Group::del_admin_rules(int user_id) PoolObjectSQL::DOCUMENT | PoolObjectSQL::SECGROUP | PoolObjectSQL::VROUTER | + PoolObjectSQL::VMGROUP | AclRule::GROUP_ID | oid, diff --git a/src/nebula/Nebula.cc b/src/nebula/Nebula.cc index f08daff7f2..08b3dc8493 100644 --- a/src/nebula/Nebula.cc +++ b/src/nebula/Nebula.cc @@ -323,6 +323,7 @@ void Nebula::start(bool bootstrap_only) rc += GroupQuotas::bootstrap(db); rc += SecurityGroupPool::bootstrap(db); rc += VirtualRouterPool::bootstrap(db); + rc += VMGroupPool::bootstrap(db); // Create the system tables only if bootstrap went well if (rc == 0) @@ -597,6 +598,8 @@ void Nebula::start(bool bootstrap_only) marketpool = new MarketPlacePool(db, is_federation_slave()); apppool = new MarketPlaceAppPool(db, is_federation_slave()); + vmgrouppool = new VMGroupPool(db); + default_user_quota.select(); default_group_quota.select(); } diff --git a/src/nebula/SConstruct b/src/nebula/SConstruct index 4c1e2a24c1..fe7c3216d0 100644 --- a/src/nebula/SConstruct +++ b/src/nebula/SConstruct @@ -67,6 +67,7 @@ env.Prepend(LIBS=[ 'nebula_vrouter', 'nebula_marketplace', 'nebula_ipamm', + 'nebula_vmgroup', 'crypto', 'xml2' ]) diff --git a/src/oca/ruby/opennebula.rb b/src/oca/ruby/opennebula.rb index f066cffe0e..8b995fe9dc 100644 --- a/src/oca/ruby/opennebula.rb +++ b/src/oca/ruby/opennebula.rb @@ -62,6 +62,8 @@ require 'opennebula/marketplace' require 'opennebula/marketplace_pool' require 'opennebula/marketplaceapp' require 'opennebula/marketplaceapp_pool' +require 'opennebula/vm_group' +require 'opennebula/vm_group_pool' module OpenNebula diff --git a/src/oca/ruby/opennebula/acl.rb b/src/oca/ruby/opennebula/acl.rb index 57e07fe1e0..2680689dbf 100644 --- a/src/oca/ruby/opennebula/acl.rb +++ b/src/oca/ruby/opennebula/acl.rb @@ -23,19 +23,23 @@ module OpenNebula # @ # ALL # RESOURCE -> + separated list and "/{#,@,%}|ALL" - # VM, + # VM # HOST # NET # IMAGE # USER # TEMPLATE # GROUP - # ACL + # DATASTORE + # CLUSTER + # DOCUMENT + # ZONE # SECGROUP # VDC # VROUTER # MARKETPLACE # MARKETPLACEAPP + # VMGROUP # RIGHTS -> + separated list # USE # MANAGE @@ -67,7 +71,8 @@ module OpenNebula "VDC" => 0x2000000000000, "VROUTER" => 0x4000000000000, "MARKETPLACE" => 0x8000000000000, - "MARKETPLACEAPP"=> 0x10000000000000 + "MARKETPLACEAPP"=> 0x10000000000000, + "VMGROUP" => 0x20000000000000 } RIGHTS = diff --git a/src/oca/ruby/opennebula/group.rb b/src/oca/ruby/opennebula/group.rb index f31809ca92..0232db71bb 100644 --- a/src/oca/ruby/opennebula/group.rb +++ b/src/oca/ruby/opennebula/group.rb @@ -37,7 +37,7 @@ module OpenNebula SELF = -1 # Default resource ACL's for group users (create) - GROUP_DEFAULT_ACLS = "VM+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER" + GROUP_DEFAULT_ACLS = "VM+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER+VMGROUP" # The default view for group and group admins, must be defined in # sunstone_views.yaml diff --git a/src/oca/ruby/opennebula/vm_group.rb b/src/oca/ruby/opennebula/vm_group.rb new file mode 100644 index 0000000000..725977bd16 --- /dev/null +++ b/src/oca/ruby/opennebula/vm_group.rb @@ -0,0 +1,151 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2016, OpenNebula Project, OpenNebula Systems # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'opennebula/pool_element' + +module OpenNebula + class VMGroup < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + VMGROUP_METHODS = { + :allocate => "vmgroup.allocate", + :info => "vmgroup.info", + :update => "vmgroup.update", + :delete => "vmgroup.delete", + :chown => "vmgroup.chown", + :chmod => "vmgroup.chmod", + :rename => "vmgroup.rename" + } + + # Creates a VMGroup description with just its identifier + # this method should be used to create plain VMGroup objects. + # @param pe_id [Integer] the id of the object + def VMGroup.build_xml(pe_id=nil) + if pe_id + obj_xml = "#{pe_id}" + else + obj_xml = "" + end + + XMLElement.build_xml(obj_xml,'VM_GROUP') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + + @client = client + end + + ####################################################################### + # XML-RPC Methods for the VMGroup Object + ####################################################################### + + # Retrieves the information of the VMGroup. + def info() + super(VMGROUP_METHODS[:info], 'VM_GROUP') + end + + alias_method :info!, :info + + # Allocates a new VMGroup in OpenNebula + # + # @param description [String] The contents of the VMGroup. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(description) + super(VMGROUP_METHODS[:allocate], description) + end + + # Deletes the SecurityGroup + def delete() + super(VMGROUP_METHODS[:delete]) + end + + # Replaces the vm group contents + # + # @param new_vmgroup [String] New vmgroup contents + # @param append [true, false] True to append new attributes instead of + # replace the whole securitygroup + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_vmgroup, append=false) + super(VMGROUP_METHODS[:update], new_vmgroup, append ? 1 : 0) + end + + # Changes the owner/group + # + # @param uid [Integer] the new owner id. Set to -1 to leave the current one + # @param gid [Integer] the new group id. Set to -1 to leave the current one + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chown(uid, gid) + super(VMGROUP_METHODS[:chown], uid, gid) + end + + # Changes the SecurityGroup permissions. + # + # @param octet [String] Permissions octed , e.g. 640 + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(octet) + super(VMGROUP_METHODS[:chmod], octet) + end + + # Changes the SecurityGroup permissions. + # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + super(VMGROUP_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, + group_m, group_a, other_u, other_m, other_a) + end + + # Renames this VMGroup + # + # @param name [String] New name for the VMGroup. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(VMGROUP_METHODS[:rename], @pe_id, name) + end + + ####################################################################### + # Helpers to get VMGroup information + ####################################################################### + + # Returns the group identifier + # [return] _Integer_ the element's group ID + def gid + self['GID'].to_i + end + + def owner_id + self['UID'].to_i + end + + # [return] _Array_ with the name of roles + def role_names + self.retrieve_elements('ROLES/ROLE/NAME') + end + end +end diff --git a/src/oca/ruby/opennebula/vm_group_pool.rb b/src/oca/ruby/opennebula/vm_group_pool.rb new file mode 100644 index 0000000000..b0dacfbd1b --- /dev/null +++ b/src/oca/ruby/opennebula/vm_group_pool.rb @@ -0,0 +1,78 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2016, OpenNebula Project, OpenNebula Systems # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'opennebula/pool' + +module OpenNebula + class VMGroupPool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + VMGROUP_POOL_METHODS = { + :info => "vmgrouppool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents an XML-RPC connection + # +user_id+ used to refer to a Pool with Templates from that user + def initialize(client, user_id=-1) + super('VM_GROUP_POOL','VM_GROUP',client) + + @user_id = user_id + end + + # Factory method to create Template objects + def factory(element_xml) + OpenNebula::VMGroup.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the Template Object + ####################################################################### + + # Retrieves all or part of the objects in the pool. + def info(*args) + case args.size + when 0 + info_filter(VMGROUP_POOL_METHODS[:info], @user_id, -1, -1) + when 3 + info_filter(VMGROUP_POOL_METHODS[:info], args[0], args[1], + args[2]) + end + end + + def info_all() + return super(VMGROUP_POOL_METHODS[:info]) + end + + def info_mine() + return super(VMGROUP_POOL_METHODS[:info]) + end + + def info_group() + return super(VMGROUP_POOL_METHODS[:info]) + end + + alias_method :info!, :info + alias_method :info_all!, :info_all + alias_method :info_mine!, :info_mine + alias_method :info_group!, :info_group + end +end + diff --git a/src/rm/Request.cc b/src/rm/Request.cc index 388fe02d05..88eff5c774 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -63,6 +63,8 @@ string Request::object_name(PoolObjectSQL::ObjectType ob) return "marketplace"; case PoolObjectSQL::MARKETPLACEAPP: return "marketplaceapp"; + case PoolObjectSQL::VMGROUP: + return "vm group"; default: return "-"; } diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 3f63410399..0f726b4386 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -366,6 +366,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr cluster_update(new ClusterUpdateTemplate()); xmlrpc_c::methodPtr secg_update(new SecurityGroupUpdateTemplate()); xmlrpc_c::methodPtr vrouter_update(new VirtualRouterUpdateTemplate()); + xmlrpc_c::methodPtr vmg_update(new VMGroupUpdateTemplate()); // Allocate Methods xmlrpc_c::methodPtr vm_allocate(new VirtualMachineAllocate()); @@ -378,6 +379,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr doc_allocate(new DocumentAllocate()); xmlrpc_c::methodPtr secg_allocate(new SecurityGroupAllocate()); xmlrpc_c::methodPtr vrouter_allocate(new VirtualRouterAllocate()); + xmlrpc_c::methodPtr vmg_allocate(new VMGroupAllocate()); // Clone Methods xmlrpc_c::methodPtr template_clone(new VMTemplateClone()); @@ -394,6 +396,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr doc_delete(new DocumentDelete()); xmlrpc_c::methodPtr secg_delete(new SecurityGroupDelete()); xmlrpc_c::methodPtr vrouter_delete(new VirtualRouterDelete()); + xmlrpc_c::methodPtr vmg_delete(new VMGroupDelete()); // Info Methods xmlrpc_c::methodPtr vm_info(new VirtualMachineInfo()); @@ -406,6 +409,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr doc_info(new DocumentInfo()); xmlrpc_c::methodPtr secg_info(new SecurityGroupInfo()); xmlrpc_c::methodPtr vrouter_info(new VirtualRouterInfo()); + xmlrpc_c::methodPtr vmg_info(new VMGroupInfo()); // Lock Methods xmlrpc_c::methodPtr doc_lock(new DocumentLock()); @@ -421,6 +425,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr clusterpool_info(new ClusterPoolInfo()); xmlrpc_c::methodPtr docpool_info(new DocumentPoolInfo()); xmlrpc_c::methodPtr secgpool_info(new SecurityGroupPoolInfo()); + xmlrpc_c::methodPtr vmgpool_info(new VMGroupPoolInfo()); xmlrpc_c::methodPtr vrouter_pool_info(new VirtualRouterPoolInfo()); // Host Methods @@ -449,6 +454,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr doc_chown(new DocumentChown()); xmlrpc_c::methodPtr secg_chown(new SecurityGroupChown()); xmlrpc_c::methodPtr vrouter_chown(new VirtualRouterChown()); + xmlrpc_c::methodPtr vmg_chown(new VMGroupChown()); // Chmod Methods xmlrpc_c::methodPtr vm_chmod(new VirtualMachineChmod()); @@ -459,6 +465,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr doc_chmod(new DocumentChmod()); xmlrpc_c::methodPtr secg_chmod(new SecurityGroupChmod()); xmlrpc_c::methodPtr vrouter_chmod(new VirtualRouterChmod()); + xmlrpc_c::methodPtr vmg_chmod(new VMGroupChmod()); // Cluster Methods xmlrpc_c::methodPtr cluster_addhost(new ClusterAddHost()); @@ -483,6 +490,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr host_rename(new HostRename()); xmlrpc_c::methodPtr secg_rename(new SecurityGroupRename()); xmlrpc_c::methodPtr vrouter_rename(new VirtualRouterRename()); + xmlrpc_c::methodPtr vmg_rename(new VMGroupRename()); // Virtual Router Methods xmlrpc_c::methodPtr vrouter_instantiate(new VirtualRouterInstantiate()); @@ -829,6 +837,18 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.secgrouppool.info",secgpool_info); + /* VM Group objects related methods*/ + + RequestManagerRegistry.addMethod("one.vmgroup.allocate", vmg_allocate); + RequestManagerRegistry.addMethod("one.vmgroup.delete", vmg_delete); + RequestManagerRegistry.addMethod("one.vmgroup.info", vmg_info); + RequestManagerRegistry.addMethod("one.vmgroup.chown", vmg_chown); + RequestManagerRegistry.addMethod("one.vmgroup.chmod", vmg_chmod); + RequestManagerRegistry.addMethod("one.vmgroup.rename", vmg_rename); + RequestManagerRegistry.addMethod("one.vmgroup.update", vmg_update); + + RequestManagerRegistry.addMethod("one.vmgrouppool.info", vmgpool_info); + /* Vdc related methods */ xmlrpc_c::method * vdc_allocate_pt; diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 3a2042c91d..c16f8d8fb5 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -1195,3 +1195,25 @@ Request::ErrorCode MarketPlaceAppAllocate::pool_allocate( return Request::SUCCESS; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +Request::ErrorCode VMGroupAllocate::pool_allocate( + xmlrpc_c::paramList const& paramList, + Template * tmpl, + int& id, + RequestAttributes& att) +{ + VMGroupPool * vmgpool = static_cast(pool); + + int rc = vmgpool->allocate(att.uid, att.gid, att.uname, att.gname, att.umask + ,tmpl, &id, att.resp_msg); + + if (rc < 0) + { + return Request::INTERNAL; + } + + return Request::SUCCESS; +} + diff --git a/src/scheduler/include/HostXML.h b/src/scheduler/include/HostXML.h index 08206bbcf4..965e268f4f 100644 --- a/src/scheduler/include/HostXML.h +++ b/src/scheduler/include/HostXML.h @@ -19,6 +19,7 @@ #define HOST_XML_H_ #include +#include #include "ObjectXML.h" #include "HostShare.h" #include "PoolObjectAuth.h" @@ -48,6 +49,11 @@ public: return cluster_id; }; + unsigned int dispatched() const + { + return dispatched_vms.size(); + } + /** * Tests whether a new VM can be hosted by the host or not * @param cpu needed by the VM (percentage) @@ -56,8 +62,8 @@ public: * @param error error message * @return true if the share can host the VM */ - bool test_capacity(long long cpu, long long mem, vector &pci, - string & error); + bool test_capacity(long long cpu, long long mem, + vector &pci, string & error); /** * Tests whether a new VM can be hosted by the host or not @@ -87,6 +93,8 @@ public: pci.add(p, vmid); + dispatched_vms.insert(vmid); + running_vms++; }; @@ -171,28 +179,51 @@ private: int cluster_id; // Host share values - long long mem_usage; /**< Memory allocated to VMs (in KB) */ - long long cpu_usage; /**< CPU allocated to VMs (in percentage) */ + long long mem_usage; /**< Memory allocated to VMs (in KB) */ + long long cpu_usage; /**< CPU allocated to VMs (in percentage)*/ - long long max_mem; /**< Total memory capacity (in KB) */ - long long max_cpu; /**< Total cpu capacity (in percentage) */ + long long max_mem; /**< Total memory capacity (in KB) */ + long long max_cpu; /**< Total cpu capacity (in percentage)*/ - long long free_disk; /**< Free disk capacity (in MB) */ + HostSharePCI pci; /**< PCI devices of the host */ + + long long free_disk; /**< Free disk capacity (in MB)*/ map ds_free_disk; /**< Free MB for local system DS */ - long long running_vms; /**< Number of running VMs in this Host */ + long long running_vms; /**< Number of running VMs in this Host */ + set dispatched_vms; /**< Dispatched VMs to this host in this cycle */ - bool public_cloud; - - HostSharePCI pci; + bool public_cloud; /**< This host is a public cloud */ // Configuration attributes static const char *host_paths[]; /**< paths for search function */ + static int host_num_paths; /**< number of paths */ - static int host_num_paths; /**< number of paths*/ + /* ---------------------------------------------------------------------- */ + /* Functions to search for values in the HostXML object */ + /* ---------------------------------------------------------------------- */ + bool is_dispatched(const std::string& vm_id) const + { + std::istringstream iss(vm_id); - void init_attributes(); + int vm_id_i; + + iss >> vm_id_i; + + return dispatched_vms.find(vm_id_i) != dispatched_vms.end(); + } + + bool is_dispatched(int vm_id) const + { + return dispatched_vms.find(vm_id) != dispatched_vms.end(); + } + + template + bool is_dispatched(const T& vm_id) const + { + return false; + } /** * Search the Object for a given attribute in a set of object specific @@ -221,6 +252,11 @@ private: } } + if ( is_dispatched(value) ) + { + return 0; + } + value = -1; //VMID not found in VMS value is -1 return 0; @@ -230,6 +266,11 @@ private: return ObjectXML::search(name, value); } }; + + /** + * Bootstrap the HostXML internal attributes + */ + void init_attributes(); }; #endif /* HOST_XML_H_ */ diff --git a/src/scheduler/include/PoolXML.h b/src/scheduler/include/PoolXML.h index 70d867097c..bd4fab99d4 100644 --- a/src/scheduler/include/PoolXML.h +++ b/src/scheduler/include/PoolXML.h @@ -46,15 +46,15 @@ public: { int rc; - // ------------------------------------------------------------------------- + // --------------------------------------------------------------------- // Clean the pool to get updated data from OpenNebula - // ------------------------------------------------------------------------- + // --------------------------------------------------------------------- flush(); - // ------------------------------------------------------------------------- + // --------------------------------------------------------------------- // Load the ids (to get an updated list of the pool) - // ------------------------------------------------------------------------- + // --------------------------------------------------------------------- xmlrpc_c::value result; @@ -124,8 +124,49 @@ public: } }; -protected: + /** + * Gets an object and removes it from the pool. The calling function + * needs to free the object memory + * @param oid of the object + * + * @return pointer of the object 0 if not found + */ + virtual ObjectXML * erase(int oid) + { + map::iterator it; + it = objects.find(oid); + + if ( it == objects.end() ) + { + return 0; + } + else + { + ObjectXML * obj = it->second; + + objects.erase(it); + + return obj; + } + } + + /** + * Inserts a new object in the pool + * @param obj pointer to the XML object to be inserted + * + * @return true if the object was successfully inserted + */ + virtual bool insert(int oid, ObjectXML * obj) + { + pair::iterator, bool> rc; + + rc = objects.insert(pair(oid, obj)); + + return rc.second; + } + +protected: // ------------------------------------------------------------------------ PoolXML(Client* client, unsigned int pool_limit = 0):ObjectXML() @@ -140,7 +181,6 @@ protected: }; // ------------------------------------------------------------------------ - /** * Inserts a new ObjectXML into the objects map */ @@ -156,10 +196,24 @@ protected: */ virtual int load_info(xmlrpc_c::value &result) = 0; + /** + * Deletes pool objects and frees resources. + */ + void flush() + { + map::iterator it; + + for (it=objects.begin();it!=objects.end();it++) + { + delete it->second; + } + + objects.clear(); + } + // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ - /** * XML-RPC client */ @@ -175,23 +229,6 @@ protected: * Hash map contains the suitable [id, object] pairs. */ map objects; - - -private: - /** - * Deletes pool objects and frees resources. - */ - void flush() - { - map::iterator it; - - for (it=objects.begin();it!=objects.end();it++) - { - delete it->second; - } - - objects.clear(); - } }; #endif /* POOL_XML_H_ */ diff --git a/src/scheduler/include/Scheduler.h b/src/scheduler/include/Scheduler.h index 0067f3f462..7baa7e7351 100644 --- a/src/scheduler/include/Scheduler.h +++ b/src/scheduler/include/Scheduler.h @@ -19,6 +19,7 @@ #include "Log.h" #include "HostPoolXML.h" +#include "VMGroupPoolXML.h" #include "UserPoolXML.h" #include "ClusterPoolXML.h" #include "DatastorePoolXML.h" @@ -49,14 +50,16 @@ public: protected: Scheduler(): + acls(0), + upool(0), hpool(0), clpool(0), - vmpool(0), - vmapool(0), dspool(0), img_dspool(0), - upool(0), - acls(0), + vmpool(0), + vm_roles_pool(0), + vmgpool(0), + vmapool(0), timer(0), one_xmlrpc(""), machines_limit(0), @@ -72,12 +75,14 @@ protected: delete clpool; delete vmpool; + delete vm_roles_pool; delete vmapool; delete dspool; delete img_dspool; delete upool; + delete vmgpool; delete acls; }; @@ -85,17 +90,21 @@ protected: // --------------------------------------------------------------- // Pools // --------------------------------------------------------------- + AclXML * acls; + UserPoolXML * upool; HostPoolXML * hpool; ClusterPoolXML * clpool; - VirtualMachinePoolXML * vmpool; - VirtualMachineActionsPoolXML* vmapool; SystemDatastorePoolXML * dspool; - ImageDatastorePoolXML * img_dspool; - UserPoolXML * upool; + ImageDatastorePoolXML * img_dspool; - AclXML * acls; + VirtualMachinePoolXML * vmpool; + VirtualMachineRolePoolXML * vm_roles_pool; + + VMGroupPoolXML * vmgpool; + + VirtualMachineActionsPoolXML* vmapool; // --------------------------------------------------------------- // Scheduler Policies @@ -135,6 +144,8 @@ protected: virtual int do_scheduled_actions(); + virtual void do_vm_groups(); + private: Scheduler(Scheduler const&){}; diff --git a/src/scheduler/include/VMGroupPoolXML.h b/src/scheduler/include/VMGroupPoolXML.h new file mode 100644 index 0000000000..742d5db939 --- /dev/null +++ b/src/scheduler/include/VMGroupPoolXML.h @@ -0,0 +1,51 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + + +#ifndef VMGROUP_POOL_XML_H_ +#define VMGROUP_POOL_XML_H_ + +#include + +#include "PoolXML.h" +#include "VMGroupXML.h" + +class VMGroupPoolXML : public PoolXML +{ +public: + + VMGroupPoolXML(Client* client):PoolXML(client){}; + + ~VMGroupPoolXML(){}; + + VMGroupXML * get(int oid) const + { + return static_cast(PoolXML::get(oid)); + }; + +protected: + + int get_suitable_nodes(std::vector& content) + { + return get_nodes("/VM_GROUP_POOL/VM_GROUP", content); + }; + + void add_object(xmlNodePtr node); + + int load_info(xmlrpc_c::value &result); +}; + +#endif /* VMGROUP_POOL_XML_H_ */ diff --git a/src/scheduler/include/VMGroupXML.h b/src/scheduler/include/VMGroupXML.h new file mode 100644 index 0000000000..39ad15c1b9 --- /dev/null +++ b/src/scheduler/include/VMGroupXML.h @@ -0,0 +1,103 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#ifndef VMGROUP_XML_H_ +#define VMGROUP_XML_H_ + +#include "ObjectXML.h" +#include "VMGroupRole.h" +#include "VMGroupRule.h" + +class VirtualMachinePoolXML; +class VirtualMachineRolePoolXML; + +class VMGroupXML : public ObjectXML +{ +public: + VMGroupXML(const std::string &xml_doc):ObjectXML(xml_doc) + { + init_attributes(); + }; + + VMGroupXML(const xmlNodePtr node):ObjectXML(node) + { + init_attributes(); + }; + + /* ---------------------------------------------------------------------- */ + /* ---------------------------------------------------------------------- */ + int get_oid() const + { + return oid; + }; + + const std::string& get_name() const + { + return name; + }; + + /** + * Dumps a Group, its roles and affinity rules to an output stream + */ + friend ostream& operator<<(ostream& os, VMGroupXML& vmg); + + /** + * Adds the internal role placement rules to each VM in the role + * @params vmpool VM set of pending VMs + * @params oss stream to output debug information + */ + void set_antiaffinity_requirements(VirtualMachinePoolXML * vmpool, + std::ostringstream& oss); + + /** + * Adds the internal role placement rules to each VM in the role + * @params vmpool VM set of pending VMs + * @params oss stream to output debug information + */ + void set_affinity_requirements(VirtualMachinePoolXML * vmpool, + VirtualMachineRolePoolXML * vm_roles_pool, std::ostringstream& oss); + + /** + * Adds host affinity rules to each VM in the roles + * @params vmp the VM set of pending VMs + * @params oss stream to output debug information + */ + void set_host_requirements(VirtualMachinePoolXML * vmp, + std::ostringstream& oss); + + +private: + // ------------------------------------------------------------------------ + // VMGroup Attributes + // ------------------------------------------------------------------------ + int oid; + + std::string name; + + VMGroupRoles roles; + + VMGroupRule::rule_set affined; + VMGroupRule::rule_set anti_affined; + + // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------ + /** + * Bootstrap VMGroup roles ans rules + */ + void init_attributes(); +}; + +#endif /* VMGROUP_XML_H_ */ diff --git a/src/scheduler/include/VirtualMachinePoolXML.h b/src/scheduler/include/VirtualMachinePoolXML.h index e73a806bdb..f2418636c9 100644 --- a/src/scheduler/include/VirtualMachinePoolXML.h +++ b/src/scheduler/include/VirtualMachinePoolXML.h @@ -84,6 +84,15 @@ public: return update(vm->get_oid(), vm->get_template(xml)); }; + /** + * + * + */ + void clear() + { + flush(); + } + protected: int get_suitable_nodes(vector& content) @@ -149,4 +158,36 @@ protected: } }; +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VirtualMachineRolePoolXML : public VirtualMachinePoolXML +{ +public: + + VirtualMachineRolePoolXML(Client * client, unsigned int machines_limit): + VirtualMachinePoolXML(client, machines_limit, false){}; + + virtual ~VirtualMachineRolePoolXML(){}; + + /** + * Retrieves the VMs part of a role + * + * @return 0 on success + * -1 on error + * -2 if no VMs in a role + */ + int set_up(); + +protected: + + int get_suitable_nodes(vector& content) + { + ostringstream oss; + + oss << "/VM_POOL/VM[TEMPLATE/VMGROUP/ROLE]"; + + return get_nodes(oss.str().c_str(), content); + } +}; #endif /* VM_POOL_XML_H_ */ diff --git a/src/scheduler/include/VirtualMachineXML.h b/src/scheduler/include/VirtualMachineXML.h index c8978b0ebb..d02aefb34f 100644 --- a/src/scheduler/include/VirtualMachineXML.h +++ b/src/scheduler/include/VirtualMachineXML.h @@ -60,7 +60,6 @@ public: //-------------------------------------------------------------------------- // Get Methods for VirtualMachineXML class //-------------------------------------------------------------------------- - int get_oid() const { return oid; @@ -116,9 +115,23 @@ public: return ds_requirements; } - void get_requirements (int& cpu, int& memory, long long& disk, + /** + * Return VM usage requirments + */ + void get_requirements(int& cpu, int& memory, long long& disk, vector &pci); + /** + * Return the requirements of this VM (as is) and reset them + * @param cpu in unit + * @param memory in kb + * @param disk in mb (system ds usage) + */ + void reset_requirements(float& cpu, int& memory, long long& disk); + + /** + * @return the usage requirements in image ds. + */ map get_storage_usage(); /** @@ -130,10 +143,45 @@ public: return public_cloud; }; + /** + * Adds usage requirements to this VM + * @param cpu in unit form + * @param m memory in kb + * @param d in mb (system ds usage) + */ + void add_requirements(float c, int m, long long d); + + /** + * Adds (logical AND) new placement requirements to the current ones + * @param reqs additional requirements + */ + void add_requirements(const string& reqs) + { + if ( reqs.empty() ) + { + return; + } + else if ( requirements.empty() ) + { + requirements = reqs; + } + else + { + requirements += " & (" + reqs + ")"; + } + } + + /** + * Check if the VM is ACTIVE state + */ + bool is_active() const + { + return state == 3; + } + //-------------------------------------------------------------------------- // Matched Resources Interface //-------------------------------------------------------------------------- - /** * Adds a matching host if it is not equal to the actual one * @param oid of the host @@ -214,6 +262,23 @@ public: */ bool is_only_public_cloud() const; + /** + * Add a VM to the set of affined VMs. This is used for the VM leader + * when scheduling a group. + * + * @param vmid of the affined vm + * + */ + void add_affined(int vmid) + { + affined_vms.insert(vmid); + } + + const set& get_affined_vms() const + { + return affined_vms; + } + //-------------------------------------------------------------------------- // Capacity Interface //-------------------------------------------------------------------------- @@ -313,12 +378,16 @@ protected: void init_storage_usage(); + /* ------------------- SCHEDULER INFORMATION --------------------------- */ + ResourceMatch match_hosts; ResourceMatch match_datastores; bool only_public_cloud; + set affined_vms; + /* ----------------------- VIRTUAL MACHINE ATTRIBUTES ------------------- */ int oid; @@ -331,6 +400,8 @@ protected: int resched; bool resume; + int state; + int memory; float cpu; long long system_ds_usage; @@ -347,6 +418,7 @@ protected: VirtualMachineTemplate * vm_template; /**< The VM template */ VirtualMachineTemplate * user_template; /**< The VM user template */ + }; #endif /* VM_XML_H_ */ diff --git a/src/scheduler/src/pool/SConstruct b/src/scheduler/src/pool/SConstruct index 80216447bc..b0380c9e46 100644 --- a/src/scheduler/src/pool/SConstruct +++ b/src/scheduler/src/pool/SConstruct @@ -28,6 +28,8 @@ source_files=[ 'VirtualMachineXML.cc', 'ClusterPoolXML.cc', 'UserPoolXML.cc', + 'VMGroupPoolXML.cc', + 'VMGroupXML.cc', 'DatastorePoolXML.cc', 'DatastoreXML.cc'] diff --git a/src/scheduler/src/pool/VMGroupPoolXML.cc b/src/scheduler/src/pool/VMGroupPoolXML.cc new file mode 100644 index 0000000000..a1c558f2e0 --- /dev/null +++ b/src/scheduler/src/pool/VMGroupPoolXML.cc @@ -0,0 +1,50 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "VMGroupPoolXML.h" + +void VMGroupPoolXML::add_object(xmlNodePtr node) +{ + if ( node == 0 || node->children == 0 ) + { + NebulaLog::log("VM_GROUP", Log::ERROR, "XML Node does not represent a " + "valid user"); + return; + } + + VMGroupXML * vmg = new VMGroupXML(node); + + objects.insert( pair(vmg->get_oid(), vmg) ); +} + +int VMGroupPoolXML::load_info(xmlrpc_c::value &result) +{ + try + { + client->call("one.vmgrouppool.info", "iii", &result, -2, -1, -1); + + return 0; + } + catch (exception const& e) + { + ostringstream oss; + oss << "Exception raised: " << e.what(); + + NebulaLog::log("VMGROUP", Log::ERROR, oss); + + return -1; + } +} diff --git a/src/scheduler/src/pool/VMGroupXML.cc b/src/scheduler/src/pool/VMGroupXML.cc new file mode 100644 index 0000000000..1a6ed2349c --- /dev/null +++ b/src/scheduler/src/pool/VMGroupXML.cc @@ -0,0 +1,524 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "VMGroupXML.h" +#include "VirtualMachinePoolXML.h" +#include + +static ostream& operator<<(ostream& os, VMGroupRule::rule_set rules); + +void VMGroupXML::init_attributes() +{ + vector content; + std::vector srules; + + std::vector::iterator it; + + xpath(oid, "/VM_GROUP/ID", -1); + xpath(name,"/VM_GROUP/NAME", "undefined"); + + // VMGroup roles + get_nodes("/VM_GROUP/ROLES", content); + + if (!content.empty()) + { + roles.from_xml_node(content[0]); + } + + free_nodes(content); + + content.clear(); + + xpaths(srules, "/VM_GROUP/TEMPLATE/AFFINED"); + + for ( it = srules.begin() ; it != srules.end(); ++it ) + { + std::set id_set; + + roles.names_to_ids(*it, id_set); + + VMGroupRule rule(VMGroupPolicy::AFFINED, id_set); + + affined.insert(rule); + } + + srules.clear(); + + xpaths(srules, "/VM_GROUP/TEMPLATE/ANTI_AFFINED"); + + for ( it = srules.begin() ; it != srules.end(); ++it ) + { + std::set id_set; + + roles.names_to_ids(*it, id_set); + + VMGroupRule rule(VMGroupPolicy::ANTI_AFFINED, id_set); + + anti_affined.insert(rule); + } +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VMGroupXML::set_antiaffinity_requirements(VirtualMachinePoolXML * vmpool, + std::ostringstream& oss) +{ + VMGroupRoles::role_iterator it; + + oss << "\n"; + oss << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + oss << "Intra-role Anti-affinity rules \n"; + oss << left << setw(8)<< "ROLE" << " " << left << setw(8) <<"VM" + << " " << left << "ANTI_AFFINITY REQUIRMENTS\n" + << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + + /* ---------------------------------------------------------------------- */ + /* Intra-role anti-affinity placement rule */ + /* ---------------------------------------------------------------------- */ + for ( it = roles.begin(); it != roles.end() ; ++it ) + { + VMGroupRole * r = *it; + + if ( r->policy() != VMGroupPolicy::ANTI_AFFINED || r->size_vms() <= 1 ) + { + continue; + } + + const std::set& vms = r->get_vms(); + std::set::const_iterator jt; + + for ( jt = vms.begin() ; jt != vms.end(); ++jt ) + { + std::string reqs; + + VirtualMachineXML * vm = vmpool->get(*jt); + + if ( vm == 0 ) + { + continue; + } + + r->vm_role_requirements(*jt, reqs); + + if ( !reqs.empty() ) + { + vm->add_requirements(reqs); + } + + oss << left << setw(8) << r->id() << left << setw(8) << *jt << reqs + << "\n"; + } + } + + + /* ---------------------------------------------------------------------- */ + /* Inter-role anti-affinity placement rule */ + /* ---------------------------------------------------------------------- */ + oss << "\n"; + oss << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + oss << "Inter-role Anti-affinity rules \n"; + oss << left << setw(8)<< "ROLE" << " " << left << setw(8) <<"VM" + << " " << left << "ANTI_AFFINITY REQUIRMENTS\n" + << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + VMGroupRule::rule_set::iterator rt; + + for ( rt = anti_affined.begin() ; rt != anti_affined.end() ; ++rt ) + { + VMGroupRule::role_bitset rroles = (*rt).get_roles(); + + for ( int i=0 ; i < VMGroupRoles::MAX_ROLES; ++i) + { + string role_reqs; + + if ( rroles[i] == 0 ) + { + continue; + } + + for ( int j = 0 ; j < VMGroupRoles::MAX_ROLES ; ++j ) + { + if ( j == i || rroles[j] == 0 ) + { + continue; + } + + VMGroupRole * r = roles.get(j); + + if ( r == 0 ) + { + continue; + } + + std::string reqs; + + r->role_requirements(VMGroupPolicy::ANTI_AFFINED, reqs); + + if ( reqs.empty() ) + { + continue; + } + + if ( role_reqs.empty() ) + { + role_reqs = reqs; + } + else + { + role_reqs += " & " + reqs; + } + } + + VMGroupRole * r = roles.get(i); + + const std::set& vms = r->get_vms(); + std::set::const_iterator vt; + + for ( vt=vms.begin() ; vt!=vms.end(); ++vt ) + { + VirtualMachineXML * vm = vmpool->get(*vt); + + if ( vm == 0 ) + { + continue; + } + + vm->add_requirements(role_reqs); + + oss << left << setw(8) << r->id() << left << setw(8) << *vt + << vm->get_requirements() << "\n"; + } + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VMGroupXML::set_host_requirements(VirtualMachinePoolXML * vmpool, + std::ostringstream& oss) +{ + VMGroupRoles::role_iterator it; + + oss << "\n"; + oss << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + oss << "Host affinity rules \n"; + oss << left << setw(8)<< "ROLE" << " " << left << setw(8) <<"VM" + << " " << left << "AFFINITY REQUIRMENTS\n" + << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + + for ( it = roles.begin(); it != roles.end() ; ++it ) + { + std::string areqs, aareqs; + + VMGroupRole * r = *it; + + r->affined_host_requirements(areqs); + + r->antiaffined_host_requirements(aareqs); + + if ( r->size_vms() == 0 || (areqs.empty() && aareqs.empty()) ) + { + continue; + } + + const std::set& vms = r->get_vms(); + + for (std::set::const_iterator jt=vms.begin(); jt!=vms.end(); ++jt) + { + VirtualMachineXML * vm = vmpool->get(*jt); + + if ( vm == 0 ) + { + continue; + } + + if ( !areqs.empty() ) + { + vm->add_requirements(areqs); + } + + if ( !aareqs.empty() ) + { + vm->add_requirements(aareqs); + } + + oss << left << setw(8) << r->id() << left << setw(8) << *jt + << vm->get_requirements() << "\n"; + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +static void schecule_affined_set(const std::set& vms, + VirtualMachinePoolXML * vmpool, VirtualMachineRolePoolXML * vm_roles_pool, + std::ostringstream& oss) +{ + std::set::iterator it; + std::set hosts; + + if ( vms.size() <= 1 ) + { + return; + } + + /* ---------------------------------------------------------------------- */ + /* Get hosts where the affined set is running */ + /* ---------------------------------------------------------------------- */ + for ( it = vms.begin() ; it != vms.end() ; ++it ) + { + VirtualMachineXML * vm = vm_roles_pool->get(*it); + + if ( vm == 0 ) + { + continue; + } + + int hid = vm->get_hid(); + + if ( vm->is_active() && hid != -1 ) + { + hosts.insert(hid); + } + } + + if ( hosts.size() == 0 ) + { + /* ------------------------------------------------------------------ */ + /* No VMs of the set are running: */ + /* 1. Select a set leader */ + /* 2. Allocate VMs in the same host as the leader */ + /* 3. Aggregate requirements in the leader for scheduling */ + /* ------------------------------------------------------------------ */ + VirtualMachineXML * vm; + + for ( it = vms.begin(); it != vms.end() ; ++it ) + { + vm = vmpool->get(*it); + + if ( vm != 0 ) + { + break; + } + } + + if ( vm == 0 ) + { + return; + } + + std::ostringstream areqs; + std::string areqs_s; + + areqs << "CURRENT_VMS = " << *it; + areqs_s = areqs.str(); + + for ( ++it ; it != vms.end() ; ++it ) + { + float cpu; + int memory; + + long long disk; + + VirtualMachineXML * tmp = vmpool->get(*it); + + if ( tmp == 0 ) + { + continue; + } + + tmp->reset_requirements(cpu, memory, disk); + + vm->add_requirements(cpu, memory, disk); + vm->add_requirements(tmp->get_requirements()); + vm->add_affined(*it); + + tmp->add_requirements(areqs_s); + + oss << left << setw(8) << tmp->get_oid() << " " + << tmp->get_requirements() << "\n"; + } + + oss << left << setw(8) << vm->get_oid() << " " + << vm->get_requirements() << "\n"; + } + else + { + /* ------------------------------------------------------------------ */ + /* VMs in the group already running */ + /* 1. Assign VMs to one of the hosts used by the affined set */ + /* ------------------------------------------------------------------ */ + std::ostringstream oss_reqs; + std::string reqs; + + VMGroupRole::host_requirements(hosts, "=", "|", oss_reqs); + + reqs = oss_reqs.str(); + + for ( it = vms.begin() ; it != vms.end() ; ++it ) + { + VirtualMachineXML * vm = vmpool->get(*it); + + if ( vm == 0 || reqs.empty()) + { + continue; + } + + vm->add_requirements(reqs); + + oss << left << setw(8) << vm->get_oid() << " " + << vm->get_requirements() << "\n"; + } + } +} + +/* -------------------------------------------------------------------------- */ + +void VMGroupXML::set_affinity_requirements(VirtualMachinePoolXML * vmpool, + VirtualMachineRolePoolXML * vm_roles_pool, std::ostringstream& oss) +{ + VMGroupRoles::role_iterator it; + + /* ---------------------------------------------------------------------- */ + /* Intra-role affinity placement rule */ + /* ---------------------------------------------------------------------- */ + oss << "\n"; + oss << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + oss << "Intra-role affinity requirements\n"; + oss << left << setw(8) << "VMID" << " " << left << "REQUIREMENTS\n"; + oss << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + + for ( it = roles.begin(); it != roles.end() ; ++it ) + { + VMGroupRole * r = *it; + + if ( r->policy() != VMGroupPolicy::AFFINED || r->size_vms() <= 1 ) + { + continue; + } + + const std::set& vms = r->get_vms(); + + schecule_affined_set(vms, vmpool, vm_roles_pool, oss); + } + + /* ---------------------------------------------------------------------- */ + /* Inter-role affinity placement rule */ + /* 1. Build the reduced set of affinity rules */ + /* 2. Build the set of VMs affected by each rule */ + /* 3. Schedule the resulting set */ + /* ---------------------------------------------------------------------- */ + VMGroupRule::rule_set reduced; + VMGroupRule::rule_set::iterator rit; + + oss << "\n"; + oss << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + oss << "Inter-role affinity requirements\n"; + oss << left << setw(8) << "VMID" << " " << left << "REQUIREMENTS\n"; + oss << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + + VMGroupRule::reduce(affined, reduced); + + for ( rit = reduced.begin() ; rit != reduced.end(); ++rit ) + { + const VMGroupRule::role_bitset rroles = (*rit).get_roles(); + std::set rule_vms; + + for (int i = 0 ; i & role_vms = r->get_vms(); + + rule_vms.insert(role_vms.begin(), role_vms.end()); + } + + schecule_affined_set(rule_vms, vmpool, vm_roles_pool, oss); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +static ostream& operator<<(ostream& os, VMGroupRule::rule_set rules) +{ + VMGroupRule::rule_set::iterator rit; + + for ( rit = rules.begin() ; rit != rules.end(); ++rit ) + { + const VMGroupRule::role_bitset rroles = (*rit).get_roles(); + + os << left << setw(14) << (*rit).get_policy() << " "; + + for (int i = 0 ; i id() << " " + << left << setw(8) << (*it)->name() << " " + << left << setw(12)<< (*it)->policy_s() << " " + << left << (*it)->vms_s() << "\n"; + } + + os << "\n"; + os << left << "RULES" << "\n" + << setfill('-') << setw(80) << '-' << setfill(' ') << "\n"; + + os << vmg.affined; + + os << vmg.anti_affined; + + return os; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + diff --git a/src/scheduler/src/pool/VirtualMachinePoolXML.cc b/src/scheduler/src/pool/VirtualMachinePoolXML.cc index 136ebcdbf7..8188237264 100644 --- a/src/scheduler/src/pool/VirtualMachinePoolXML.cc +++ b/src/scheduler/src/pool/VirtualMachinePoolXML.cc @@ -33,6 +33,8 @@ int VirtualMachinePoolXML::set_up() if (NebulaLog::log_level() >= Log::DDDEBUG) { + map::iterator it; + oss << "Pending/rescheduling VM and capacity requirements:" << endl; oss << right << setw(8) << "ACTION" << " " @@ -44,7 +46,7 @@ int VirtualMachinePoolXML::set_up() << " Image DS" << endl << setw(60) << setfill('-') << "-" << setfill(' ') << endl; - for (map::iterator it=objects.begin();it!=objects.end();it++) + for (it = objects.begin() ; it != objects.end() ; ++it) { int cpu, mem; long long disk; @@ -52,7 +54,9 @@ int VirtualMachinePoolXML::set_up() string action = "DEPLOY"; - VirtualMachineXML * vm = static_cast(it->second); + VirtualMachineXML * vm; + + vm = static_cast(it->second); vm->get_requirements(cpu, mem, disk, pci); @@ -73,11 +77,11 @@ int VirtualMachinePoolXML::set_up() << right << setw(11) << disk << " "; map ds_usage = vm->get_storage_usage(); + map::const_iterator ds_it; - for (map::const_iterator ds_it = ds_usage.begin(); - ds_it != ds_usage.end(); ds_it++) + for ( ds_it = ds_usage.begin(); ds_it != ds_usage.end(); ds_it++) { - oss << " DS " << ds_it->first << ": " << ds_it->second << " "; + oss << " DS " << ds_it->first << ": " << ds_it->second <<" "; } oss << endl; @@ -231,6 +235,7 @@ int VirtualMachinePoolXML::update(int vid, const string &st) const return 0; } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -263,6 +268,7 @@ int VirtualMachineActionsPoolXML::set_up() return rc; } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -304,3 +310,30 @@ int VirtualMachineActionsPoolXML::action( return 0; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineRolePoolXML::set_up() +{ + int rc = PoolXML::set_up(); + + if ( rc == 0 ) + { + ostringstream oss; + + oss << "VMs in VMGroups:" << endl; + + map::iterator it; + + for (it=objects.begin();it!=objects.end();it++) + { + oss << " " << it->first; + } + + NebulaLog::log("VM", Log::DEBUG, oss); + } + + return rc; +} + diff --git a/src/scheduler/src/pool/VirtualMachineXML.cc b/src/scheduler/src/pool/VirtualMachineXML.cc index d4f9638e03..33ec5ff23a 100644 --- a/src/scheduler/src/pool/VirtualMachineXML.cc +++ b/src/scheduler/src/pool/VirtualMachineXML.cc @@ -36,6 +36,8 @@ void VirtualMachineXML::init_attributes() xpath(uid, "/VM/UID", -1); xpath(gid, "/VM/GID", -1); + xpath(state, "/VM/STATE", -1); + xpath(memory, "/VM/TEMPLATE/MEMORY", 0); xpath(cpu, "/VM/TEMPLATE/CPU", 0); @@ -202,6 +204,30 @@ ostream& operator<<(ostream& os, VirtualMachineXML& vm) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void VirtualMachineXML::add_requirements(float c, int m, long long d) +{ + cpu += c; + memory += m; + system_ds_usage += d; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VirtualMachineXML::reset_requirements(float& c, int& m, long long& d) +{ + c = cpu; + m = memory; + d = system_ds_usage; + + cpu = 0; + memory = 0; + system_ds_usage = 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void VirtualMachineXML::get_requirements (int& cpu, int& memory, long long& disk, vector &pci) { @@ -406,14 +432,14 @@ int VirtualMachineXML::parse_action_name(string& action_st) bool VirtualMachineXML::test_image_datastore_capacity( ImageDatastorePoolXML * img_dspool, string & error_msg) const { - map::const_iterator ds_usage_it; + map::const_iterator ds_it; DatastoreXML* ds; - for (ds_usage_it = ds_usage.begin(); ds_usage_it != ds_usage.end(); ds_usage_it++) + for (ds_it = ds_usage.begin(); ds_it != ds_usage.end(); ++ds_it) { - ds = img_dspool->get(ds_usage_it->first); + ds = img_dspool->get(ds_it->first); - if (ds == 0 || !ds->test_capacity(ds_usage_it->second)) + if (ds == 0 || !ds->test_capacity(ds_it->second)) { ostringstream oss; @@ -434,20 +460,20 @@ bool VirtualMachineXML::test_image_datastore_capacity( void VirtualMachineXML::add_image_datastore_capacity( ImageDatastorePoolXML * img_dspool) { - map::const_iterator ds_usage_it; + map::const_iterator ds_it; DatastoreXML *ds; - for (ds_usage_it = ds_usage.begin(); ds_usage_it != ds_usage.end(); ds_usage_it++) + for (ds_it = ds_usage.begin(); ds_it != ds_usage.end(); ++ds_it) { - ds = img_dspool->get(ds_usage_it->first); + ds = img_dspool->get(ds_it->first); if (ds == 0) //Should never reach here { continue; } - ds->add_capacity(ds_usage_it->second); + ds->add_capacity(ds_it->second); } } diff --git a/src/scheduler/src/sched/SConstruct b/src/scheduler/src/sched/SConstruct index 6518152b2e..72dcda9357 100644 --- a/src/scheduler/src/sched/SConstruct +++ b/src/scheduler/src/sched/SConstruct @@ -30,6 +30,7 @@ sched_env.StaticLibrary(lib_name, source_files) sched_env.Prepend(LIBS=[ 'scheduler_sched', 'scheduler_pool', + 'nebula_vmgroup_roles', 'nebula_log', 'nebula_client', 'nebula_acl', diff --git a/src/scheduler/src/sched/Scheduler.cc b/src/scheduler/src/sched/Scheduler.cc index e60c0bc5e1..9fc157640f 100644 --- a/src/scheduler/src/sched/Scheduler.cc +++ b/src/scheduler/src/sched/Scheduler.cc @@ -26,6 +26,7 @@ #include #include +#include #include "Scheduler.h" #include "SchedulerTemplate.h" @@ -308,19 +309,23 @@ void Scheduler::start() // ------------------------------------------------------------------------- // Pools // ------------------------------------------------------------------------- + Client * client = Client::client(); - hpool = new HostPoolXML(Client::client()); - upool = new UserPoolXML(Client::client()); - clpool = new ClusterPoolXML(Client::client()); - vmpool = new VirtualMachinePoolXML(Client::client(), machines_limit, - live_rescheds==1); + acls = new AclXML(client, zone_id); + upool = new UserPoolXML(client); - vmapool = new VirtualMachineActionsPoolXML(Client::client(), machines_limit); + hpool = new HostPoolXML(client); + clpool = new ClusterPoolXML(client); - dspool = new SystemDatastorePoolXML(Client::client()); - img_dspool = new ImageDatastorePoolXML(Client::client()); + dspool = new SystemDatastorePoolXML(client); + img_dspool = new ImageDatastorePoolXML(client); - acls = new AclXML(Client::client(), zone_id); + vm_roles_pool = new VirtualMachineRolePoolXML(client, machines_limit); + vmpool = new VirtualMachinePoolXML(client, machines_limit, live_rescheds==1); + + vmgpool = new VMGroupPoolXML(client); + + vmapool = new VirtualMachineActionsPoolXML(client, machines_limit); // ----------------------------------------------------------- // Load scheduler policies @@ -407,7 +412,7 @@ int Scheduler::set_up_pools() map shares; //-------------------------------------------------------------------------- - //Cleans the cache and get the pending VMs + //Cleans the cache and get the pools //-------------------------------------------------------------------------- rc = vmpool->set_up(); @@ -417,10 +422,6 @@ int Scheduler::set_up_pools() return rc; } - //-------------------------------------------------------------------------- - //Cleans the cache and get the datastores - //-------------------------------------------------------------------------- - rc = dspool->set_up(); if ( rc != 0 ) @@ -435,10 +436,6 @@ int Scheduler::set_up_pools() return rc; } - //-------------------------------------------------------------------------- - //Cleans the cache and get the hosts ids - //-------------------------------------------------------------------------- - rc = upool->set_up(); if ( rc != 0 ) @@ -446,10 +443,6 @@ int Scheduler::set_up_pools() return rc; } - //-------------------------------------------------------------------------- - //Cleans the cache and get the hosts ids - //-------------------------------------------------------------------------- - rc = hpool->set_up(); if ( rc != 0 ) @@ -457,10 +450,6 @@ int Scheduler::set_up_pools() return rc; } - //-------------------------------------------------------------------------- - //Cleans the cache and get the cluster information - //-------------------------------------------------------------------------- - rc = clpool->set_up(); if ( rc != 0 ) @@ -468,16 +457,8 @@ int Scheduler::set_up_pools() return rc; } - //-------------------------------------------------------------------------- - //Add to each host the corresponding cluster template - //-------------------------------------------------------------------------- - hpool->merge_clusters(clpool); - //-------------------------------------------------------------------------- - //Cleans the cache and get the ACLs - //-------------------------------------------------------------------------- - rc = acls->set_up(); if ( rc != 0 ) @@ -485,6 +466,20 @@ int Scheduler::set_up_pools() return rc; } + rc = vmgpool->set_up(); + + if ( rc != 0 ) + { + return rc; + } + + rc = vm_roles_pool->set_up(); + + if ( rc != 0 ) + { + return rc; + } + return 0; }; @@ -603,7 +598,8 @@ static bool match_host(AclXML * acls, UserPoolXML * upool, VirtualMachineXML* vm if (matched == false) { - error = "It does not fulfill SCHED_REQUIREMENTS."; + error = "It does not fulfill SCHED_REQUIREMENTS: " + + vm->get_requirements(); return false; } } @@ -878,7 +874,8 @@ void Scheduler::match_schedule() vmpool->update(vm); - log_match(vm->get_oid(), "Cannot schedule VM, there is no suitable host."); + log_match(vm->get_oid(), + "Cannot schedule VM, there is no suitable host."); continue; } @@ -1022,13 +1019,20 @@ void Scheduler::match_schedule() ostringstream oss; oss << "Match Making statistics:\n" - << "\tNumber of VMs: " << pending_vms.size() << endl - << "\tTotal time: " << one_util::float_to_str(time(0) - stime) << "s" << endl - << "\tTotal Cluster Match time: " << one_util::float_to_str(total_cl_match_time) << "s" << endl - << "\tTotal Host Match time: " << one_util::float_to_str(total_host_match_time) << "s" << endl - << "\tTotal Host Ranking time: " << one_util::float_to_str(total_host_rank_time) << "s" << endl - << "\tTotal DS Match time: " << one_util::float_to_str(total_ds_match_time) << "s" << endl - << "\tTotal DS Ranking time: " << one_util::float_to_str(total_ds_rank_time) << "s" << endl; + << "\tNumber of VMs: " + << pending_vms.size() << endl + << "\tTotal time: " + << one_util::float_to_str(time(0) - stime) << "s" << endl + << "\tTotal Cluster Match time: " + << one_util::float_to_str(total_cl_match_time) << "s" << endl + << "\tTotal Host Match time: " + << one_util::float_to_str(total_host_match_time) << "s" << endl + << "\tTotal Host Ranking time: " + << one_util::float_to_str(total_host_rank_time) << "s" << endl + << "\tTotal DS Match time: " + << one_util::float_to_str(total_ds_match_time) << "s" << endl + << "\tTotal DS Ranking time: " + << one_util::float_to_str(total_ds_rank_time) << "s" << endl; NebulaLog::log("SCHED", Log::DDEBUG, oss); } @@ -1070,10 +1074,8 @@ void Scheduler::dispatch() int hid, dsid, cid; unsigned int dispatched_vms = 0; - bool dispatched; - - map host_vms; - pair::iterator, bool> rc; + bool dispatched, matched; + char * estr; map::const_iterator vm_it; @@ -1138,6 +1140,32 @@ void Scheduler::dispatch() cid = host->get_cid(); + //------------------------------------------------------------------ + // Check host still match requirements with CURRENT_VMS + //------------------------------------------------------------------ + matched = true; + + if ( one_util::regex_match("CURRENT_VMS", + vm->get_requirements().c_str()) == 0 ) + { + if (host->eval_bool(vm->get_requirements(), matched, &estr)!=0) + { + free(estr); + continue; + } + } + + if (matched == false) + { + std::ostringstream mss; + + mss << "Host " << hid << " no longer meets requirements for VM " + << vm->get_oid(); + + NebulaLog::log("SCHED", Log::DEBUG, mss); + continue; + } + //------------------------------------------------------------------ // Test host capacity //------------------------------------------------------------------ @@ -1155,11 +1183,9 @@ void Scheduler::dispatch() } //------------------------------------------------------------------ - // Test host dispatch limit (init counter if needed) + // Test host dispatch limit //------------------------------------------------------------------ - rc = host_vms.insert(make_pair(hid,0)); - - if (rc.first->second >= host_dispatch_limit) + if (host->dispatched() >= host_dispatch_limit) { continue; } @@ -1277,9 +1303,33 @@ void Scheduler::dispatch() } } - host->add_capacity(vm->get_oid(), cpu, mem, pci); + //------------------------------------------------------------------ + // VM leaders needs to add the select host to the affined VMs + //------------------------------------------------------------------ + const set& affined_vms = vm->get_affined_vms(); - host_vms[hid]++; + if ( affined_vms.size() > 0 ) + { + set::const_iterator it; + + for ( it = affined_vms.begin(); it != affined_vms.end(); ++it ) + { + VirtualMachineXML * avm = vmpool->get(*it); + + if ( avm == 0 ) + { + continue; + } + + avm->add_match_host(hid); + avm->add_match_datastore(dsid); + } + } + + //------------------------------------------------------------------ + // Update usage and statistics counters + //------------------------------------------------------------------ + host->add_capacity(vm->get_oid(), cpu, mem, pci); dispatched_vms++; @@ -1405,6 +1455,39 @@ int Scheduler::do_scheduled_actions() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void Scheduler::do_vm_groups() +{ + map::const_iterator it; + const map vmgrps = vmgpool->get_objects(); + + ostringstream oss; + + oss << "VM Group Scheduling information\n"; + + for (it = vmgrps.begin(); it != vmgrps.end() ; ++it) + { + VMGroupXML * grp = static_cast(it->second); + + oss << setfill('*') << setw(80) << '*' << setfill(' ') << "\n" + << "SCHEDULING RESULTS FOR VM GROUP " << grp->get_oid() << ", " + << grp->get_name() <<"\n" + << setfill('*') << setw(80) << '*' << setfill(' ') << "\n"; + + oss << *grp << "\n"; + + grp->set_affinity_requirements(vmpool, vm_roles_pool, oss); + + grp->set_antiaffinity_requirements(vmpool, oss); + + grp->set_host_requirements(vmpool, oss); + } + + NebulaLog::log("VMGRP", Log::DDDEBUG, oss); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void Scheduler::do_action(const string &name, void *args) { int rc; @@ -1431,6 +1514,10 @@ void Scheduler::do_action(const string &name, void *args) return; } + profile(true); + do_vm_groups(); + profile(false,"Setting VM groups placement constraints."); + match_schedule(); profile(true); diff --git a/src/secgroup/SecurityGroup.cc b/src/secgroup/SecurityGroup.cc index ee8a0a40b2..3488905dab 100644 --- a/src/secgroup/SecurityGroup.cc +++ b/src/secgroup/SecurityGroup.cc @@ -56,6 +56,8 @@ SecurityGroup::SecurityGroup( { obj_template = new Template; } + + set_umask(_umask); } /* -------------------------------------------------------------------------- */ diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 22caf2a6f3..7b9af2dd9f 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -741,12 +741,8 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) // ------------------------------------------------------------------------ // Set a name if the VM has not got one and VM_ID // ------------------------------------------------------------------------ - - oss << oid; - value = oss.str(); - user_obj_template->erase("VMID"); - obj_template->add("VMID", value); + obj_template->add("VMID", oid); user_obj_template->get("TEMPLATE_ID", value); user_obj_template->erase("TEMPLATE_ID"); @@ -1019,6 +1015,14 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) } } + // ------------------------------------------------------------------------ + // Associate to VM Group + // ------------------------------------------------------------------------ + if ( get_vmgroup(error_str) == -1 ) + { + goto error_rollback; + } + // ------------------------------------------------------------------------ parse_well_known_attributes(); @@ -1909,6 +1913,15 @@ void VirtualMachine::set_auth_request(int uid, { (*nic)->authorize(uid, &ar); } + + const VectorAttribute * vmgroup = tmpl->get("VMGROUP"); + + if ( vmgroup != 0 ) + { + VMGroupPool * vmgrouppool = Nebula::instance().get_vmgrouppool(); + + vmgrouppool->authorize(vmgroup, uid, &ar); + } } /* -------------------------------------------------------------------------- */ @@ -2883,6 +2896,74 @@ int VirtualMachine::set_detach_nic(int nic_id) return 0; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* VirtualMachine VMGroup interface */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int VirtualMachine::get_vmgroup(string& error) +{ + vector vmgroups; + vector::iterator it; + + bool found; + VectorAttribute * thegroup = 0; + + user_obj_template->remove("VMGROUP", vmgroups); + + for (it = vmgroups.begin(), found = false; it != vmgroups.end(); ) + { + if ( (*it)->type() != Attribute::VECTOR || found ) + { + delete *it; + it = vmgroups.erase(it); + } + else + { + thegroup = dynamic_cast(*it); + found = true; + + ++it; + } + } + + if ( thegroup == 0 ) + { + return 0; + } + + VMGroupPool * vmgrouppool = Nebula::instance().get_vmgrouppool(); + int rc; + + rc = vmgrouppool->vmgroup_attribute(thegroup, get_uid(), get_oid(), error); + + if ( rc != 0 ) + { + delete thegroup; + + return -1; + } + + obj_template->set(thegroup); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VirtualMachine::release_vmgroup() +{ + VectorAttribute * thegroup = obj_template->get("VMGROUP"); + + if ( thegroup == 0 ) + { + return; + } + + VMGroupPool * vmgrouppool = Nebula::instance().get_vmgrouppool(); + + vmgrouppool->del_vm(thegroup, get_oid()); +} + diff --git a/src/vm_group/SConstruct b/src/vm_group/SConstruct new file mode 100644 index 0000000000..dc6f285495 --- /dev/null +++ b/src/vm_group/SConstruct @@ -0,0 +1,42 @@ +# SConstruct for src/secgroup + +# -------------------------------------------------------------------------- # +# Copyright 2002-2016, OpenNebula Project, OpenNebula Systems # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +Import('env') + +lib_name='nebula_vmgroup' + +# Sources to generate the library +source_files=[ + 'VMGroupPool.cc', + 'VMGroup.cc', + 'VMGroupRole.cc', + 'VMGroupRule.cc' +] + +# Build library +env.StaticLibrary(lib_name, source_files) + +# Stripped library for scheduler with role logic +lib_name='nebula_vmgroup_roles' + +source_files=[ + 'VMGroupRole.cc', + 'VMGroupRule.cc' +] + +env.StaticLibrary(lib_name, source_files) diff --git a/src/vm_group/VMGroup.cc b/src/vm_group/VMGroup.cc new file mode 100644 index 0000000000..50d0a9d49f --- /dev/null +++ b/src/vm_group/VMGroup.cc @@ -0,0 +1,485 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------*/ + +#include "VMGroup.h" +#include "VMGroupRole.h" +#include "VMGroupRule.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* VMGroup */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const char * VMGroup::table = "vmgroup_pool"; + +const char * VMGroup::db_names = "oid, name, body, uid, gid, owner_u, group_u, " + "other_u"; + +const char * VMGroup::db_bootstrap = "CREATE TABLE IF NOT EXISTS vmgroup_pool " + "(oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, " + "uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, " + "other_u INTEGER, UNIQUE(name,uid))"; + +/* ------------------------------------------------------------------------ */ + +VMGroup::VMGroup(int _uid, int _gid, const string& _uname, const string& _gname, + int _umask, Template * group_template): + PoolObjectSQL(-1, VMGROUP, "", _uid, _gid, _uname, _gname, table) +{ + if (group_template != 0) + { + obj_template = group_template; + } + else + { + obj_template = new Template; + } + + set_umask(_umask); +} + +VMGroup::~VMGroup() +{ + delete obj_template; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +string& VMGroup::to_xml(string& xml) const +{ + ostringstream oss; + string template_xml; + string perms_xml; + string roles_xml; + + oss << + "" << + "" << oid << "" << + "" << uid << "" << + "" << gid << "" << + "" << uname << "" << + "" << gname << "" << + "" << name << "" << + perms_to_xml(perms_xml) << + roles.to_xml(roles_xml) << + obj_template->to_xml(template_xml) << + ""; + + xml = oss.str(); + + return xml; +} + +/* -------------------------------------------------------------------------- */ + +int VMGroup::from_xml(const string &xml_str) +{ + vector content; + int rc = 0; + + // Initialize the internal XML object + update_from_str(xml_str); + + // Get class base attributes + rc += xpath(oid, "/VM_GROUP/ID", -1); + rc += xpath(uid, "/VM_GROUP/UID", -1); + rc += xpath(gid, "/VM_GROUP/GID", -1); + rc += xpath(uname, "/VM_GROUP/UNAME","not_found"); + rc += xpath(gname, "/VM_GROUP/GNAME","not_found"); + rc += xpath(name, "/VM_GROUP/NAME", "not_found"); + + // Permissions + rc += perms_from_xml(); + + // Get associated template + ObjectXML::get_nodes("/VM_GROUP/TEMPLATE", content); + + if (content.empty()) + { + return -1; + } + + rc += obj_template->from_xml_node(content[0]); + + ObjectXML::free_nodes(content); + + content.clear(); + + // VMGroup roles + ObjectXML::get_nodes("/VM_GROUP/ROLES", content); + + if (!content.empty()) + { + rc += roles.from_xml_node(content[0]); + } + + ObjectXML::free_nodes(content); + + content.clear(); + + if (rc != 0) + { + return -1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroup::insert_replace(SqlDB *db, bool replace, string& error_str) +{ + ostringstream oss; + + int rc; + string xml_body; + + char * sql_name; + char * sql_xml; + + sql_name = db->escape_str(name.c_str()); + + if ( sql_name == 0 ) + { + goto error_name; + } + + sql_xml = db->escape_str(to_xml(xml_body).c_str()); + + if ( sql_xml == 0 ) + { + goto error_body; + } + + if ( validate_xml(sql_xml) != 0 ) + { + goto error_xml; + } + + if ( replace ) + { + oss << "REPLACE"; + } + else + { + oss << "INSERT"; + } + + // Construct the SQL statement to Insert or Replace + oss <<" INTO "<exec(oss); + + db->free_str(sql_name); + db->free_str(sql_xml); + + return rc; + +error_xml: + db->free_str(sql_name); + db->free_str(sql_xml); + + error_str = "Error transforming the VM group to XML."; + + goto error_common; + +error_body: + db->free_str(sql_name); + goto error_generic; + +error_name: + goto error_generic; + +error_generic: + error_str = "Error inserting VM group in DB."; +error_common: + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroup::check_rule_names(VMGroupPolicy policy, std::string& error) +{ + vector affined; + vector::const_iterator jt; + + std::ostringstream oss; + oss << policy; + + std::string aname = oss.str(); + + obj_template->get(aname, affined); + + for ( jt = affined.begin() ; jt != affined.end() ; ++jt ) + { + std::set id_set; + + if ( roles.names_to_ids((*jt)->value(), id_set) != 0 ) + { + std::ostringstream oss; + + oss << "Some roles used in " << aname << " attribute (" + << (*jt)->value() << ") are not defined"; + + error = oss.str(); + + return -1; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroup::get_rules(VMGroupPolicy policy, VMGroupRule::rule_set& rules, + std::string& error_str) +{ + vector affined; + vector::const_iterator jt; + + std::ostringstream oss; + oss << policy; + + std::string aname = oss.str(); + + obj_template->get(aname, affined); + + for ( jt = affined.begin() ; jt != affined.end() ; ++jt ) + { + std::set id_set; + + std::pair::iterator, bool> rc; + + roles.names_to_ids((*jt)->value(), id_set); + + VMGroupRule rule(policy, id_set); + + rc = rules.insert(rule); + + if ( rc.second == false ) + { + std::ostringstream oss; + + oss << "Duplicated " << aname << " rule (" << (*jt)->value() + << ") detected."; + + error_str = oss.str(); + + return -1; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroup::check_rule_consistency(std::string& error) +{ + VMGroupRule::rule_set affined, anti; + + VMGroupRule::rule_set::iterator it; + + VMGroupRule error_rule; + + if ( get_rules(VMGroupPolicy::AFFINED, affined, error) == -1 ) + { + return -1; + } + + for (it=affined.begin() ; it != affined.end(); ++it) + { + const VMGroupRule::role_bitset rs = (*it).get_roles(); + + for (int i = 0; i < VMGroupRoles::MAX_ROLES; ++i) + { + if ( rs[i] == 1 ) + { + VMGroupRole * role = roles.get(i); + + if ( role != 0 && role->policy() == VMGroupPolicy::ANTI_AFFINED ) + { + error = "Role " + role->name() + " is in an AFFINED rule " + "but the role policy is ANTI_AFFINED"; + + return -1; + } + } + } + } + + if ( get_rules(VMGroupPolicy::ANTI_AFFINED, anti, error) == -1 ) + { + return -1; + } + + if ( !VMGroupRule::compatible(affined, anti, error_rule) ) + { + ostringstream oss; + const VMGroupRule::role_bitset rs = error_rule.get_roles(); + + oss << "Roles defined in AFFINED and ANTI_AFFINED rules:"; + + for (int i = 0; i < VMGroupRoles::MAX_ROLES; ++i) + { + if ( rs[i] == 1 ) + { + VMGroupRole * role = roles.get(i); + + if ( role != 0 ) + { + oss << " " << role->name(); + } + } + } + + error = oss.str(); + return -1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroup::insert(SqlDB *db, string& error_str) +{ + vector va_roles; + vector::iterator it; + + erase_template_attribute("NAME", name); + + if (name.empty()) + { + error_str = "No NAME in template for VM group."; + return -1; + } + + int num_role = obj_template->remove("ROLE", va_roles); + + if ( num_role > VMGroupRoles::MAX_ROLES ) + { + for ( it = va_roles.begin(); it != va_roles.end(); ++it ) + { + delete *it; + } + + error_str = "Maximum number of roles in a VM Group reached"; + } + + bool error = false; + + for ( it = va_roles.begin(); it != va_roles.end(); ++it ) + { + VectorAttribute * vatt = dynamic_cast(*it); + + if (vatt == 0 || error) + { + delete *it; + continue; + } + + if ( roles.add_role(vatt, error_str) == -1 ) + { + delete *it; + error = true; + } + } + + if ( error ) + { + return -1; + } + + if ( check_rule_names(VMGroupPolicy::AFFINED, error_str) == -1 ) + { + return -1; + } + + if ( check_rule_names(VMGroupPolicy::ANTI_AFFINED, error_str) == -1 ) + { + return -1; + } + + if ( check_rule_consistency(error_str) == -1 ) + { + return -1; + } + + if ( insert_replace(db, false, error_str) != 0 ) + { + return -1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroup::post_update_template(string& error) +{ + int vms = roles.vm_size(); + + if ( vms > 0 ) + { + ostringstream oss; + + oss << "VM Group has " << vms << " VMs"; + error = oss.str(); + + return -1; + } + + obj_template->erase("ROLE"); + + if ( check_rule_names(VMGroupPolicy::AFFINED, error) == -1 ) + { + return -1; + } + + if ( check_rule_names(VMGroupPolicy::ANTI_AFFINED, error) == -1 ) + { + return -1; + } + + if ( check_rule_consistency(error) == -1 ) + { + return -1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/vm_group/VMGroupPool.cc b/src/vm_group/VMGroupPool.cc new file mode 100644 index 0000000000..14a2ea3cab --- /dev/null +++ b/src/vm_group/VMGroupPool.cc @@ -0,0 +1,185 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "VMGroupPool.h" +#include "AuthRequest.h" + +int VMGroupPool::allocate(int uid, int gid, const string& uname, + const string& gname, int umask, Template * vmgroup_template, int * oid, + string& error_str) +{ + VMGroup * vmgrp; + VMGroup * vmgrp_aux = 0; + + string name; + + ostringstream os; + + vmgrp = new VMGroup(uid, gid, uname, gname, umask, vmgroup_template); + + vmgrp->get_template_attribute("NAME", name); + + if ( !PoolObjectSQL::name_is_valid(name, error_str) ) + { + goto error_name; + } + + vmgrp_aux = get(name, uid, false); + + if( vmgrp_aux != 0 ) + { + goto error_duplicated; + } + + *oid = PoolSQL::allocate(vmgrp, error_str); + + return *oid; + +error_duplicated: + os << "NAME is already taken by VMGroup " << vmgrp_aux->get_oid() << "."; + error_str = os.str(); + +error_name: + delete vmgrp; + *oid = -1; + + return *oid; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +VMGroup * VMGroupPool::get_from_attribute(const VectorAttribute *va, int _uid) +{ + VMGroup * vmgroup = 0; + + string vmg_name = va->vector_value("VMGROUP_NAME"); + int vmg_id; + + if ( !vmg_name.empty() ) + { + int vmg_uid; + + if ( va->vector_value("VMGROUP_UID", vmg_uid) == -1 ) + { + vmg_uid = _uid; + } + + vmgroup = get(vmg_name, vmg_uid, true); + } + else if ( va->vector_value("VMGROUP_ID", vmg_id) == 0 ) + { + vmgroup = get(vmg_id, true); + } + + return vmgroup; +} + +/* -------------------------------------------------------------------------- */ + +int VMGroupPool::vmgroup_attribute(VectorAttribute * va, int uid, int vid, + string& error) +{ + string vmg_role = va->vector_value("ROLE"); + + if ( vmg_role.empty() ) + { + error = "Missing role name in VM Group definition"; + return -1; + } + + VMGroup * vmgroup = get_from_attribute(va, uid); + + if ( vmgroup == 0 ) + { + error = "Cannot find VM Group to associate the VM"; + return -1; + } + + va->replace("VMGROUP_ID", vmgroup->get_oid()); + + int rc = vmgroup->add_vm(vmg_role, vid); + + if ( rc != 0 ) + { + error = "Role does not exist in VM Group"; + } + else + { + update(vmgroup); + } + + vmgroup->unlock(); + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VMGroupPool::del_vm(const VectorAttribute * va, int vid) +{ + int vmg_id; + + if ( va->vector_value("VMGROUP_ID", vmg_id) == -1 ) + { + return; + } + + string vmg_role = va->vector_value("ROLE"); + + if ( vmg_role.empty() ) + { + return; + } + + VMGroup * vmgroup = get(vmg_id, true); + + if ( vmgroup == 0 ) + { + return; + } + + vmgroup->del_vm(vmg_role, vid); + + update(vmgroup); + + vmgroup->unlock(); +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VMGroupPool::authorize(const VectorAttribute * va, int uid,AuthRequest* ar) +{ + PoolObjectAuth perm; + + VMGroup * vmgroup = get_from_attribute(va, uid); + + if ( vmgroup == 0 ) + { + return; + } + + vmgroup->get_permissions(perm); + + vmgroup->unlock(); + + ar->add_auth(AuthRequest::USE, perm); +} + + diff --git a/src/vm_group/VMGroupRole.cc b/src/vm_group/VMGroupRole.cc new file mode 100644 index 0000000000..811d9e86bb --- /dev/null +++ b/src/vm_group/VMGroupRole.cc @@ -0,0 +1,405 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------*/ + +#include "VMGroupRole.h" +#include "VMGroupRule.h" + +#include + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* VMGroupRole */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +VMGroupRole::VMGroupRole(VectorAttribute *_va):va(_va) +{ + string vms_str = va->vector_value("VMS"); + + if ( !vms_str.empty() ) + { + one_util::split_unique(vms_str, ',', vms); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +VMGroupPolicy VMGroupRole::policy() +{ + string p = va->vector_value("POLICY"); + + one_util::toupper(p); + + if ( p == "AFFINED" ) + { + return VMGroupPolicy::AFFINED; + } + else if ( p == "ANTI_AFFINED" ) + { + return VMGroupPolicy::ANTI_AFFINED; + } + else + { + return VMGroupPolicy::NONE; + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VMGroupRole::add_vm(int vm_id) +{ + std::pair::iterator, bool> rc; + + rc = vms.insert(vm_id); + + if ( rc.second == false ) + { + return; + } + + set_vms(); +} + +void VMGroupRole::del_vm(int vm_id) +{ + size_t rc = vms.erase(vm_id); + + if ( rc == 0 ) + { + return; + } + + set_vms(); +} + +void VMGroupRole::set_vms() +{ + if ( vms.empty() ) + { + va->remove("VMS"); + return; + } + + std::string vms_str = one_util::join(vms.begin(), vms.end(), ','); + + va->replace("VMS", vms_str); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +static void affinity_requirements(int vm_id, std::string& requirements, + VMGroupPolicy policy, const std::set& vms) +{ + string op, op2; + + requirements = ""; + + switch(policy) + { + case VMGroupPolicy::AFFINED: + op = "="; + op2= " | "; + break; + case VMGroupPolicy::ANTI_AFFINED: + op = "!="; + op2= " & "; + break; + case VMGroupPolicy::NONE: + return; + } + + std::ostringstream oss; + std::set::const_iterator it; + + bool first = true; + + for ( it = vms.begin(); it != vms.end(); ++it ) + { + if ( vm_id == -1 || vm_id != *it ) + { + if ( !first ) + { + oss << op2; + } + + first = false; + + oss << "(CURRENT_VMS " << op << " " << *it << ")"; + } + } + + requirements = oss.str(); +} + +void VMGroupRole::vm_role_requirements(int vm_id, std::string& requirements) +{ + affinity_requirements(vm_id, requirements, policy(), vms); +} + +void VMGroupRole::role_requirements(VMGroupPolicy pol, std::string& reqs) +{ + affinity_requirements(-1, reqs, pol, vms); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VMGroupRole::host_requirements(std::set& hosts, const std::string& op1, + const std::string& op2, std::ostringstream& oss) +{ + std::set::const_iterator jt; + bool empty = true; + + for ( jt = hosts.begin() ; jt != hosts.end() ; ++jt ) + { + if ( empty == true ) + { + empty = false; + + oss << "(ID" << op1 << *jt << ")"; + } + else + { + oss << " " << op2 << " (ID" << op1 << *jt << ")"; + } + } +} + +/* -------------------------------------------------------------------------- */ + +void VMGroupRole::affined_host_requirements(std::string& reqs) +{ + std::ostringstream oss; + std::set hosts; + + string shosts = va->vector_value("HOST_AFFINED"); + + if ( !shosts.empty() ) + { + one_util::split_unique(shosts, ',', hosts); + } + + host_requirements(hosts, "=", "|", oss); + + reqs = oss.str(); +} + +/* -------------------------------------------------------------------------- */ + +void VMGroupRole::antiaffined_host_requirements(std::string& reqs) +{ + std::ostringstream oss; + std::set hosts; + + string shosts = va->vector_value("HOST_ANTI_AFFINED"); + + if ( !shosts.empty() ) + { + one_util::split_unique(shosts, ',', hosts); + } + + host_requirements(hosts, "!=", "&", oss); + + reqs = oss.str(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* VMGroupRoles */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroupRoles::from_xml_node(const xmlNodePtr node) +{ + std::vector roles; + std::vector::iterator it; + + if ( roles_template.from_xml_node(node) == -1 ) + { + return -1; + } + + roles_template.get("ROLE", roles); + + for (it = roles.begin(); it != roles.end(); ++it) + { + std::string rname = (*it)->vector_value("NAME"); + + int rid; + int rc = (*it)->vector_value("ID", rid); + + if ( rname.empty() || rc == -1 ) + { + return -1; + } + + if ( rid >= next_role ) + { + next_role = rid + 1; + } + + VMGroupRole * role = new VMGroupRole((*it)); + + if ( by_id.insert(rid, role) == false ) + { + delete role; + return -1; + } + + if ( by_name.insert(rname, role) == false ) + { + by_id.erase(rid); + + delete role; + return -1; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroupRoles::add_role(VectorAttribute * vrole, string& error) +{ + std::string rname = vrole->vector_value("NAME"); + + if ( rname.empty() ) + { + error = "Missing NAME in VM group role"; + return -1; + } + + // Remove internal attributes before inserting + vrole->replace("ID", next_role); + + vrole->remove("VMS"); + + VMGroupRole * role = new VMGroupRole(vrole); + + if ( by_id.insert(next_role, role) == false ) + { + delete role; + + error = "Role ID already exists"; + return -1; + } + + if ( by_name.insert(rname, role) == false ) + { + by_id.erase(next_role); + + delete role; + + error = "Role NAME already exists"; + return -1; + } + + next_role += 1; + + roles_template.set(vrole); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroupRoles::add_vm(const std::string& role_name, int vmid) +{ + VMGroupRole * role; + + role = by_name.get(role_name); + + if ( role == 0 ) + { + return -1; + } + + role->add_vm(vmid); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroupRoles::del_vm(const std::string& role_name, int vmid) +{ + VMGroupRole * role; + + role = by_name.get(role_name); + + if ( role == 0 ) + { + return -1; + } + + role->del_vm(vmid); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroupRoles::vm_size() +{ + int total = 0; + + for ( role_iterator it = begin(); it != end() ; ++it ) + { + total += (*it)->get_vms().size(); + } + + return total; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VMGroupRoles::names_to_ids(const std::string& rnames, std::set& keyi) +{ + std::set a_set, key_set; + std::set::iterator it; + + one_util::split_unique(rnames, ',', a_set); + + for ( it = a_set.begin(); it != a_set.end() ; ++it ) + { + key_set.insert(one_util::trim(*it)); + } + + for ( it = key_set.begin(); it != key_set.end(); ++it ) + { + VMGroupRole *r = by_name.get(*it); + + if ( r == 0 ) + { + keyi.clear(); + return -1; + } + + keyi.insert(r->id()); + } + + return 0; +} + diff --git a/src/vm_group/VMGroupRule.cc b/src/vm_group/VMGroupRule.cc new file mode 100644 index 0000000000..2d3e21ae42 --- /dev/null +++ b/src/vm_group/VMGroupRule.cc @@ -0,0 +1,114 @@ +/* ------------------------------------------------------------------------ */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------*/ + +#include "VMGroup.h" +#include "VMGroupRole.h" +#include "VMGroupRule.h" + +#include + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* VMGroupRule */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool VMGroupRule::compatible(rule_set& affined, rule_set& anti,VMGroupRule& err) +{ + VMGroupRule ta, taa; + + rule_set::iterator it; + + for (it=affined.begin() ; it != affined.end(); ++it) + { + ta |= *it; + } + + for (it=anti.begin() ; it != anti.end(); ++it) + { + taa |= *it; + } + + err = ta & taa; + + return err.none(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VMGroupRule::reduce(rule_set affined, rule_set& reduced_set) +{ + VMGroupRule::rule_set::iterator it, jt; + + VMGroupRule reduced_rule; + + for ( it = affined.begin(), reduced_rule = (*it) ; it != affined.end() ; ) + { + bool reduced = false; + + for ( jt = affined.begin() ; jt != affined.end() ; ) + { + VMGroupRule tmp = *it; + + tmp &= *jt; + + if ( it == jt || (reduced_rule & *jt).none() ) + { + ++jt; + } + else + { + reduced_rule |= *jt; + + jt = affined.erase(jt); + + reduced = true; + } + } + + if (!reduced) + { + reduced_set.insert(reduced_rule); + + reduced_rule = *(++it); + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +std::ostream& operator<<(std::ostream& os, VMGroupPolicy policy) +{ + switch(policy) + { + case VMGroupPolicy::AFFINED: + os << "AFFINED"; + break; + + case VMGroupPolicy::ANTI_AFFINED: + os << "ANTI_AFFINED"; + break; + + case VMGroupPolicy::NONE: + os << "NONE"; + break; + } + + return os; +} +