/* -------------------------------------------------------------------------- */ /* Copyright 2002-2026, 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 SCHEDULER_MANAGER_MATCH_H_ #define SCHEDULER_MANAGER_MATCH_H_ #include "VirtualMachinePool.h" #include "DatastorePool.h" #include "HostPool.h" #include "VirtualNetworkPool.h" #include "UserPool.h" #include "ClusterPool.h" #include #include #include #include /** * This class represents a generic OpenNebula pool for structuring scheduling * data */ template class SchedPool { public: SchedPool(P * p, const std::string& n):pool(p), name(n) {}; ~SchedPool() = default; //List of relevant ids in the pool std::vector ids; //Simpe cache to store object references std::map> objects; //Reference to the OpenNebula pool P * pool; //Pool name std::string name; /** * Gets and object from the pool. It loads the object from DB and stores * the reference in the cache if not found. */ O * get(int id) { O * object; auto it = objects.find(id); if ( it == objects.end() ) { if (std::unique_ptr pobj = pool->get_ro(id)) { object = pobj.get(); if constexpr (std::is_same_v || std::is_same_v) { pobj->load_monitoring(); } objects.insert({id, std::move(pobj)}); } else { return nullptr; } } else { object = it->second.get(); } return object; } /** * executes callable on each object ID */ void each_id(const std::function& func) { for (auto i: ids) { func(i); } } /** * Sets an object in the pool */ void set(int id, std::unique_ptr object) { objects[id] = std::move(object); } /** * Deletes an object from the Pool if condition is true */ void delete_if(const std::function& test) { for(auto it = ids.begin(); it != ids.end(); ) { if (test(*it)) { objects.erase(*it); it = ids.erase(it); } else { ++it; } } } /** * Dumps all objects included in the dump_ids set into the given stream. */ void to_xml(std::ostringstream& oss) { to_xml(oss, ids); } template void to_xml(std::ostringstream& oss, const C& dump_ids) { std::string tmp; oss << "<" << name << ">"; for (int id: dump_ids) { O * obj = get(id); if ( obj != nullptr ) { oss << obj->to_xml(tmp); } } oss << ""; } }; /** * This class represents a the set of resource matches for scheduling VMs. * A match consists of: * - Hosts * - Datastores * - Vnets per NIC using NETWORK_MODE auto * * The class stores also a union of all sets to conform SCHEDULER_DRIVER_MESSAGES * with relevant objects. */ struct SchedMatch { // The struct represents the ID of the objects that matches the requirements // of a VM struct match { std::set host_ids; std::set ds_ids; std::map> vnet_ids; }; // Match pool, contains matches for all VMs std::map requirements; // All VMs needed to do the match std::set vms; // ID set of matched resources for any VM std::set match_host; std::set match_ds; std::set match_net; std::set match_vmgroups; /** * Add a host ID to the list of matching hosts for this VM */ void add_host(int vmid, int hostid) { auto it = requirements.find(vmid); if ( it == requirements.end() ) { requirements[vmid] = { {hostid}, {}, {} }; } else { it->second.host_ids.insert(hostid); } match_host.insert(hostid); } /** * Add a datastore ID to the list of matching system datastore for this VM */ void add_ds(int vmid, int dsid) { auto it = requirements.find(vmid); if ( it == requirements.end() ) { //Discard DS match without a previous Host match return; } it->second.ds_ids.insert(dsid); match_ds.insert(dsid); } /** * Initialize the VM vnet match map */ void init_net(int vm_id, std::set& nic_ids) { auto it = requirements.find(vm_id); if ( it == requirements.end() ) { return; } for (int nic_id : nic_ids) { it->second.vnet_ids[nic_id] = {}; } } /** * Add a vnet ID to the list of matching vnets for this VM NIC */ void add_net(int vm_id, int nic_id, int net_id) { auto it = requirements.find(vm_id); if ( it == requirements.end() ) { return; } auto jt = it->second.vnet_ids.find(nic_id); if ( jt == it->second.vnet_ids.end() ) { it->second.vnet_ids[nic_id] = { net_id }; } else { jt->second.insert(net_id); } match_net.insert(net_id); } // ------------------------------------------------------------------------- // Functions to check VM matches // ------------------------------------------------------------------------- bool is_host_matched(int id) { auto it = requirements.find(id); return it != requirements.end() && !it->second.host_ids.empty(); } bool is_ds_matched(int id) { auto it = requirements.find(id); return it != requirements.end() && !it->second.ds_ids.empty(); } bool is_net_matched(int id) { auto it = requirements.find(id); if (it == requirements.end()) { return false; } if ( it->second.vnet_ids.empty() ) { //empty map means VM has not auto nics return true; } for (const auto& kv : it->second.vnet_ids ) { if (kv.second.empty()) { return false; } } return true; } /** * Render VM requirements as an XML document */ void to_xml(std::ostringstream& oss) { oss << ""; for (const auto& pair : requirements) { oss << ""; oss << "" << pair.first << ""; oss << ""; for (int hid : pair.second.host_ids) { oss << "" << hid << ""; } oss << ""; oss << ""; for (int did : pair.second.ds_ids) { oss << "" << did << ""; } oss << ""; for (const auto& vpair: pair.second.vnet_ids) { oss << ""; oss << "" << vpair.first << ""; oss << ""; for (int vid : vpair.second) { oss << "" << vid << ""; } oss << ""; oss << ""; } oss << ""; } oss << ""; } }; /** * This class is used to build a Scheduler driver request message. It includes * the relevant pool objects and the matchmaking results for each VM. */ struct SchedRequest { SchedRequest(VirtualMachinePool * vmp, HostPool * hp, DatastorePool *dp, VirtualNetworkPool *vn, UserPool * up, ClusterPool *cp): vmpool(vmp, "VM_POOL"), hpool(hp, "HOST_POOL"), dspool(dp, "DATASTORE_POOL"), vnpool(vn, "VNET_POOL"), upool(up, "USER_POOL"), clpool(cp, "CLUSTER_POOL") {}; SchedPool vmpool; SchedPool hpool; SchedPool dspool; SchedPool vnpool; SchedPool upool; SchedPool clpool; SchedMatch match; void merge_cluster_to_host() { std::map cluster_templates; std::map> cluster_template_nodes; for (auto hid : hpool.ids) { auto host = hpool.get(hid); if (!host) { continue; } // Get the host monitoring information auto monitoring = host->get_monitoring(); ObjectXML mon(monitoring.to_xml()); std::vector mon_nodes; mon.get_nodes("/MONITORING", mon_nodes); if (!mon_nodes.empty()) { host->add_node("/HOST", mon_nodes[0], "MONITORING"); ObjectXML::free_nodes(mon_nodes); } // Merge Cluster template to Host int cid = host->get_cluster_id(); auto cluster = clpool.get(cid); if (!cluster) { continue; } auto& nodes = cluster_template_nodes[cid]; if (nodes.empty()) { cluster->get_nodes("/CLUSTER/TEMPLATE", nodes); } if (!nodes.empty()) { host->add_node("/HOST", nodes[0], "CLUSTER_TEMPLATE"); } std::string& extra = cluster_templates[cid]; if (extra.empty()) { cluster->template_xml(extra); } host->cluster_template(extra); } for (auto& [cid, nodes] : cluster_template_nodes) { ObjectXML::free_nodes(nodes); } } }; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #endif /*SCHEDULER_MANAGER_MATCH_H_*/