1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-28 14:50:08 +03:00

F #3256: Support for NUMA topology, Hugepages and CPU pinning.

This commit is contained in:
Ruben S. Montero 2019-07-01 17:52:47 +02:00
parent b75c5fc02a
commit 3b9ace0577
No known key found for this signature in database
GPG Key ID: A0CEA6FA880A1D87
32 changed files with 4138 additions and 1474 deletions

View File

@ -280,97 +280,55 @@ public:
}
/**
* Adds a new VM to the given share by incrementing the cpu, mem and disk
* counters
* @param vm_id id of the vm to add to the host
* @param cpu needed by the VM (percentage)
* @param mem needed by the VM (in KB)
* @param disk needed by the VM
* @param pci devices needed by th VM
* Adds a new VM to the host share by incrementing usage counters
* @param sr the capacity request of the VM
* @return 0 on success
*/
void add_capacity(int vm_id, long long cpu, long long mem, long long disk,
vector<VectorAttribute *> pci)
void add_capacity(HostShareCapacity &sr)
{
if ( vm_collection.add(vm_id) == 0 )
if ( vm_collection.add(sr.vmid) == 0 )
{
host_share.add(vm_id, cpu, mem, disk, pci);
host_share.add(sr);
}
else
{
ostringstream oss;
oss << "Trying to add VM " << vm_id
<< ", that it is already associated to host " << oid << ".";
oss << "VM " << sr.vmid << " is already in host " << oid << ".";
NebulaLog::log("ONE", Log::ERROR, oss);
}
};
/**
* Deletes a new VM from the given share by decrementing the cpu,mem and
* disk counters
* @param vm_id id of the vm to delete from the host
* @param cpu used by the VM (percentage)
* @param mem used by the VM (in KB)
* @param disk used by the VM
* @param pci devices needed by th VM
* Deletes a new VM to the host share by incrementing usage counters
* @param sr the capacity request of the VM
* @return 0 on success
*/
void del_capacity(int vm_id, long long cpu, long long mem, long long disk,
const vector<VectorAttribute *>& pci)
void del_capacity(HostShareCapacity& sr)
{
if ( vm_collection.del(vm_id) == 0 )
if ( vm_collection.del(sr.vmid) == 0 )
{
host_share.del(cpu, mem, disk, pci);
host_share.del(sr);
}
else
{
ostringstream oss;
oss << "Trying to remove VM " << vm_id
<< ", that it is not associated to host " << oid << ".";
oss << "VM " << sr.vmid << " is not in host " << oid << ".";
NebulaLog::log("ONE", Log::ERROR, oss);
}
};
/**
* Updates the capacity used in a host when a VM is resized
* counters
* @param cpu increment of cpu requested by the VM
* @param mem increment of memory requested by the VM
* @param disk not used
* @return 0 on success
*/
void update_capacity(int cpu, long int mem, int disk)
{
host_share.update(cpu,mem,disk);
};
/**
* Tests whether a new VM can be hosted by the host or not
* @param cpu needed by the VM (percentage)
* @param mem needed by the VM (in Kb)
* @param disk needed by the VM
* @param pci devices needed by the VM
* @param error Returns the error reason, if any
* Tests whether a VM device capacity can be allocated in the host
* @param sr capacity requested by the VM
* @param error returns the error reason, if any
*
* @return true if the share can host the VM
*/
bool test_capacity(long long cpu, long long mem, long long disk,
vector<VectorAttribute *> &pci, string& error) const
bool test_capacity(HostShareCapacity &sr, string& error)
{
return host_share.test(cpu, mem, disk, pci, error);
}
/**
* Tests whether a new VM can be hosted by the host or not, checking the
* PCI devices only.
* @param pci devices needed by the VM
* @param error Returns the error reason, if any
* @return true if the share can host the VM
*/
bool test_capacity(vector<VectorAttribute *> &pci, string& error) const
{
return host_share.test(pci, error);
return host_share.test(sr, error);
}
/**

View File

@ -211,15 +211,14 @@ public:
*
* @return 0 on success -1 in case of failure
*/
int add_capacity(int oid, int vm_id, int cpu, int mem, int disk,
vector<VectorAttribute *> pci)
int add_capacity(int oid, HostShareCapacity &sr)
{
int rc = 0;
Host * host = get(oid);
if ( host != 0 )
{
host->add_capacity(vm_id, cpu, mem, disk, pci);
host->add_capacity(sr);
update(host);
@ -242,14 +241,13 @@ public:
* @param disk amount of disk
* @param pci devices requested by the VM
*/
void del_capacity(int oid, int vm_id, int cpu, int mem, int disk,
vector<VectorAttribute *> pci)
void del_capacity(int oid, HostShareCapacity &sr)
{
Host * host = get(oid);
if ( host != 0 )
{
host->del_capacity(vm_id, cpu, mem, disk, pci);
host->del_capacity(sr);
update(host);

View File

@ -21,10 +21,51 @@
#include "Template.h"
#include <time.h>
#include <set>
#include <map>
//Forward declarations
class Host;
class HostShareNUMA;
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
/**
* This class represents a HostShare capacity allocation from a VM. The following
* attributes are updated with the final allocation in the Host:
* - topology, number of sockets, cores and threads
* - pci, with device address
* - nodes with the numa nodes configured for the VM
*
* NUMA node requests are described by an attribute:
*
* NUMA_NODE = [ TOTAL_CPUS=, MEMORY="...", CPUS="...", NODE_ID="...",
* MEMORY_NODE_ID="..." ]
*
* CPUS: list of CPU IDs to pin the vCPUs in this host
* NODE_ID: the ID of the numa node in the host to pin this virtual node
* MEMORY_NODE_ID: the ID of the node to allocate memory for this virtual node
*/
struct HostShareCapacity
{
int vmid;
unsigned int vcpu;
long long cpu;
long long mem;
long long disk;
vector<VectorAttribute *> pci;
VectorAttribute * topology;
vector<VectorAttribute *> nodes;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
class HostShareDatastore : public Template
{
public:
@ -33,6 +74,9 @@ public:
virtual ~HostShareDatastore(){};
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/**
* This class represents a PCI DEVICE list for the host. The list is in the
* form:
@ -161,7 +205,391 @@ private:
map <string, PCIDevice *> pci_devices;
};
class Host;
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/**
* This class represents the NUMA nodes in a hypervisor for the following attr:
* NODE_ID = 0
* HUGEPAGE = [ SIZE = "2048", PAGES = "0", FREE = "0"]
* HUGEPAGE = [ SIZE = "1048576", PAGES = "0", FREE = "0"]
* CORE = [ ID = "3", CPUS = "3:-1,7:-1", FREE = 2]
* CORE = [ ID = "1", CPUS = "1:23,5:-1", FREE = 0 ]
* CORE = [ ID = "2", CPUS = "2:47,6:-1", FREE = 1]
* CORE = [ ID = "0", CPUS = "0:23,4:-1", FREE = 0]
* MEMORY = [ TOTAL = "66806708", FREE = "390568", USED = "66416140",
* DISTANCE = "0 1", USAGE = "8388608" ]
*
* - NODE_ID
* - HUGEPAGE is the total PAGES and FREE hugepages of a given SIZE in the node
* - CORE is a CPU core with its ID and sibling CPUs for HT architectures
*/
class HostShareNode : public Template
{
public:
HostShareNode() : Template(false, '=', "NODE"){};
HostShareNode(unsigned int i) : Template(false, '=', "NODE"), node_id(i)
{
replace("NODE_ID", i);
};
virtual ~HostShareNode(){};
/**
* Builds the node from its XML representation. This function is used when
* loading the host from the DB.
* @param node xmlNode for the template
* @return 0 on success
*/
int from_xml_node(const xmlNodePtr &node);
/**
* Get free capacity of the node
* @param fcpus number of free virtual cores
* @param memory free in the node
* @param threads_core per virtual core
*/
void free_capacity(unsigned int &fcpus, long long &memory, unsigned int tc);
void free_dedicated_capacity(unsigned int &fcpus, long long &memory);
/**
* Allocate tcpus with a dedicated policy
* @param id of the VM allocating the CPUs
* @param tcpus total number of cpus
* @param c_s the resulting allocation string CPUS="0,4,2,6"
*
* @return 0 on success
*/
int allocate_dedicated_cpus(int id, unsigned int tcpus, std::string &c_s);
/**
* Allocate tcpus with a HT policy
* @param id of the VM allocating the CPUs
* @param tcpus total number of cpus
* @param tc allocate cpus in tc (threads/core) chunks
* @param c_s the resulting allocation string CPUS="0,4,2,6"
*
* @return 0 on success
*/
int allocate_ht_cpus(int id, unsigned int tcpus, unsigned int tc,
std::string &c_s);
/**
* Remove allocation for the given CPUs
* @param cpu_ids list of cpu ids to free, comma separated
*/
void del_cpu(const std::string &cpu_ids);
/**
* Remove memory allocation
* @param memory to free
*/
void del_memory(long long memory)
{
mem_usage -= memory;
}
/**
* Prints the NUMA node to an output stream.
*/
friend ostream& operator<<(ostream& o, const HostShareNode& n);
private:
friend class HostShareNUMA;
//This stuct represents a core and its allocation status
struct Core
{
/**
* Initializes the structure from the CORE attributes:
* @param _i ID of core
* @param _c CPUS list <cpu_id>:<vm_id>
* @param _f FREE cpus in core. If -1 it will be derived from CPUS
*/
Core(unsigned int _i, const std::string& _c, int _f);
/**
* ID of this CPU CORE
*/
unsigned int id;
/**
* Number of free cpus in the core. A VM can use one thread but
* reserve all the cpus in the core when using the dedicated policy.
*/
unsigned int free_cpus;
/**
* cpu_id - vm_id map. represents assigment status of the
* cpu thread, (-1) means the VM is free.
*/
std::map<unsigned int, int> cpus;
/**
* @return a VectorAttribute representing this core in the form:
* CORE = [ ID = "3", CPUS = "3:-1,7:-1", FREE = 2]
*/
VectorAttribute * to_attribute();
};
//This stuct represents the hugepages available in the node
struct HugePage
{
unsigned long size_kb;
unsigned int nr;
unsigned int free;
unsigned long usage;
unsigned long allocated;
/**
* @return a VectorAttribute representing this core in the form:
* HUGEPAGE = [ SIZE = "1048576", PAGES = "200", FREE = "100",
* USAGE = "100"]
*/
VectorAttribute * to_attribute();
};
/**
* ID of this node as reported by the Host
*/
unsigned int node_id;
/**
* Threads per core in this node
*/
unsigned int threads_core = 1;
/**
* CPU Cores in this node
*/
std::map<unsigned int, struct Core> cores;
/**
* Huge pages configured in this node
*/
std::map<unsigned long, struct HugePage> pages;
/**
* Memory information for this node:
* - total, free and used memory as reported by IM (meminfo file)
* - mem_used memory allocated to VMs by oned in this node
* - distance sorted list of nodes, first is the closest (this one)
*/
long long total_mem = 0;
long long free_mem = 0;
long long used_mem = 0;
long long mem_usage = 0;
std::vector<unsigned int> distance;
/**
* Temporal allocation on the node. This is used by the scheduling
*/
unsigned int allocated_cpus = 0;
long long allocated_memory = 0;
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/**
* Creates a new Core element and associates it to this node. If the
* core already exists this function does nothing
* @param id of core
* @param cpus string representing the cpu_id and allocation
* @param free cpus in core -1 to derive them from cpus string
* @param update if true also adds the core to the object Template
*/
void set_core(unsigned int id, std::string& cpus, int free, bool update);
/**
* Regenerate the template representation of the CORES for this node.
*/
void update_cores();
/**
* Regenerate the template representation of the HUGEPAGES for this node.
*/
void update_hugepages();
/**
* Creates a new HugePage element and associates it to this node. If a
* hugepage of the same size already exists this function does nothing
* @param size in kb of the page
* @param nr number of pages
* @param free pages
* @param update if true also adds the page to the object Template
*/
void set_hugepage(unsigned long size, unsigned int nr, unsigned int fr,
unsigned long usage, bool update);
void update_hugepage(unsigned long size);
/**
* Adds a new memory attribute based on the moniroting attributes and
* current mem usage.
*/
void set_memory();
/**
* Updates the memory usage for the node in the template representation
*/
void update_memory();
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/**
* This class includes a list of all NUMA nodes in the host. And structure as
* follows:
*
* <NUMA_NODES>
* <NODE>
* <ID>0</ID>
* <HUGEPAGE>
* <SIZE>2048</SIZE>
* <PAGES>0</PAGES>
* <FREE>0</FREE>
* </HUGEPAGE>
* ...
* <CORE>
* <ID>3</ID>
* <CPUS>3,7</CPUS>
* </CORE>
* ...
* </NODE>
* <NODE>
* <ID>1</ID>
* ...
* </NODE>
* </NUMA_NODES>
*/
class HostShareNUMA
{
public:
HostShareNUMA():threads_core(1){};
virtual ~HostShareNUMA()
{
for (auto it = nodes.begin(); it != nodes.end(); ++it)
{
delete it->second;
}
};
/**
* Builds the NUMA nodes from its XML representation. This function is used
* when loading the host from the DB.
* @param node xmlNode for the template
* @return 0 on success
*/
int from_xml_node(const vector<xmlNodePtr> &ns);
/**
* Updates the NUMA node information with monitor data
* @param ht template with the information returned by monitor probes.
*/
void set_monitorization(Template &ht);
/**
* @param idx of the node
* @return the NUMA node for the the fiven index. If the node does not
* exit it is created
*/
HostShareNode& get_node(unsigned int idx);
/**
* Function to print the HostShare 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;
/**
* Test if the virtual nodes and topology request fits in the host.
* @param sr the share request with the node/topology
* @return true if the nodes fit in the host, false otherwise
*/
bool test(HostShareCapacity &sr)
{
return make_topology(sr, -1, false) == 0;
}
/**
* Assign the requested nodes to the host.
* @param sr the share request with the node/topology
* @param vmid of the VM
*/
void add(HostShareCapacity &sr)
{
make_topology(sr, sr.vmid, true);
}
/**
* Remove the VM assignment from the NUMA nodes
*/
void del(HostShareCapacity &sr);
/**
* Prints the NUMA nodes to an output stream.
*/
friend ostream& operator<<(ostream& o, const HostShareNUMA& n);
private:
/**
* Number of threads per core of the host
*/
unsigned int threads_core;
std::map<unsigned int, HostShareNode *> nodes;
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/**
* Computes the virtual topology for this VM in this host based on:
* - user preferences TOPOLOGY/[SOCKETS, CORES, THREADS].
* - Architecture of the Host core_threads
* - allocation policy
*
* @param sr the resource allocation request
* @param vm_id of the VM making the request
* @param do_alloc actually allocate the nodes (true) or just test (false)
* @return 0 success (vm was allocated) -1 otherwise
*/
int make_topology(HostShareCapacity &sr, int vm_id, bool do_alloc);
/**
* This is an internal structure to represent a virtual node allocation
* request and the resulting schedule
*/
struct NUMANodeRequest
{
VectorAttribute * attr;
unsigned int total_cpus;
long long memory;
//NUMA node to allocate CPU cores from
int node_id;
std::string cpu_ids;
//NUMA node to allocate memory from
int mem_node_id;
};
bool schedule_nodes(NUMANodeRequest &nr, unsigned int thr, bool dedicated,
unsigned long hpsz_kb, std::set<unsigned int> &pci, bool do_alloc);
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/**
* The HostShare class. It represents a logical partition of a host...
@ -170,13 +598,23 @@ class HostShare : public ObjectXML
{
public:
HostShare(
long long _max_disk=0,
long long _max_mem=0,
long long _max_cpu=0);
HostShare();
~HostShare(){};
/**
* Pin policy for the host
*/
enum PinPolicy
{
PP_NONE = 0, /**< No pin. Default. */
PP_CORE = 1, /**< vCPUs are assigned to host cores exclusively */
PP_THREAD = 2, /**< vCPUS are assigned to host threads */
PP_SHARED = 3 /**< vCPUs are assigned to a set of host threads */
};
static PinPolicy str_to_pin_policy(std::string& pp_s);
/**
* Rebuilds the object from an xml node
* @param node The xml node pointer
@ -186,53 +624,35 @@ public:
int from_xml_node(const xmlNodePtr node);
/**
* Add a new VM to this share
* @param vmid of the VM
* @param cpu requested by the VM, in percentage
* @param mem requested by the VM, in KB
* @param disk requested by the VM
* @param pci_devs requested by the VM
* Add a VM capacity to this share
* @param sr requested capacity by the VM
*/
void add(int vmid, long long cpu, long long mem, long long disk,
vector<VectorAttribute *> pci_devs)
void add(HostShareCapacity &sr)
{
cpu_usage += cpu;
mem_usage += mem;
disk_usage += disk;
cpu_usage += sr.cpu;
mem_usage += sr.mem;
disk_usage += sr.disk;
pci.add(pci_devs, vmid);
pci.add(sr.pci, sr.vmid);
numa.add(sr);
running_vms++;
}
/**
* Updates the capacity of VM in this share
* @param cpu increment
* @param mem increment
* @param disk increment
* Delete VM capacity from this share
* @param sr requested capacity by the VM
*/
void update(int cpu, int mem, int disk)
void del(HostShareCapacity &sr)
{
cpu_usage += cpu;
mem_usage += mem;
disk_usage += disk;
}
cpu_usage -= sr.cpu;
mem_usage -= sr.mem;
disk_usage -= sr.disk;
/**
* Delete a VM from this share
* @param cpu requested by the VM
* @param mem requested by the VM
* @param disk requested by the VM
* @param pci_devs requested by the VM
*/
void del(long long cpu, long long mem, long long disk,
const vector<VectorAttribute *>& pci_devs)
{
cpu_usage -= cpu;
mem_usage -= mem;
disk_usage -= disk;
pci.del(sr.pci);
pci.del(pci_devs);
numa.del(sr);
running_vms--;
}
@ -248,49 +668,24 @@ public:
* @return true if the share can host the VM or it is the only one
* configured
*/
bool test(long long cpu, long long mem, long long disk,
vector<VectorAttribute *>& pci_devs, string& error) const
bool test(HostShareCapacity& sr, string& error)
{
bool pci_fits = pci.test(pci_devs);
bool fits = (((max_cpu - cpu_usage ) >= cpu) &&
((max_mem - mem_usage ) >= mem) &&
((max_disk - disk_usage) >= disk)&&
pci_fits);
if (!fits)
if ( !test_compute(sr.cpu, sr.mem, error) )
{
if ( pci_fits )
{
error = "Not enough capacity.";
}
else
{
error = "Unavailable PCI device.";
}
return false;
}
return fits;
}
/**
* Check if this share can host a VM, testing only the PCI devices.
* @param pci_devs requested by the VM
* @param error Returns the error reason, if any
*
* @return true if the share can host the VM or it is the only one
* configured
*/
bool test(vector<VectorAttribute *>& pci_devs, string& error) const
{
bool fits = pci.test(pci_devs);
if (!fits)
if ( !test_pci(sr.pci, error) )
{
error = "Unavailable PCI device.";
return false;
}
return fits;
if ( !test_numa(sr, error) )
{
return false;
}
return true;
}
/**
@ -306,6 +701,10 @@ public:
*/
string& to_xml(string& xml) const;
/**
* Set host information based on the monitorinzation attributes
* sent by the probes.
*/
void set_ds_monitorization(const vector<VectorAttribute*> &ds_att);
void set_pci_monitorization(vector<VectorAttribute*> &pci_att)
@ -313,6 +712,11 @@ public:
pci.set_monitorization(pci_att);
}
void set_numa_monitorization(Template &ht)
{
numa.set_monitorization(ht);
}
/**
* Resets capaity values of the share
*/
@ -385,6 +789,61 @@ private:
HostShareDatastore ds;
HostSharePCI pci;
HostShareNUMA numa;
/**
* Check if this share can host a VM, testing only the PCI devices.
* @param pci_devs requested by the VM
* @param error Returns the error reason, if any
*
* @return true if the share can host the VM or it is the only one
* configured
*/
bool test_compute(int cpu, long long mem, std::string &error) const
{
bool cpu_fit = (max_cpu - cpu_usage ) >= cpu;
bool mem_fit = (max_mem - mem_usage ) >= mem;
bool fits = cpu_fit && mem_fit;
if ( fits )
{
return true;
}
ostringstream oss;
if (!cpu_fit)
{
oss << "Not enough CPU: " << cpu << "/" << max_cpu - cpu_usage;
}
else if (!mem_fit)
{
oss << "Not enough memory: " << mem << "/" << max_mem - mem_usage;
}
error = oss.str();
return false;
}
bool test_pci(vector<VectorAttribute *>& pci_devs, string& error) const
{
bool fits = pci.test(pci_devs);
error = "Unavailable PCI device.";
return fits;
}
bool test_numa(HostShareCapacity &sr, string& error)
{
bool fits = numa.test(sr);
error = "Cannot allocate NUMA topology";
return fits;
}
};
#endif /*HOST_SHARE_H_*/

View File

@ -99,9 +99,35 @@ namespace one_util
*
* @return a vector containing the resulting substrings
*/
std::vector<std::string> split(
const std::string& st,
char delim,
template <class T>
void split(const std::string &st, char delim, std::vector<T> &parts)
{
std::string part;
std::stringstream ss(st);
while (getline(ss, part, delim))
{
if (part.empty())
{
continue;
}
std::istringstream iss(part);
T part_t;
iss >> part_t;
if ( iss.fail() )
{
continue;
}
parts.push_back(part_t);
}
}
std::vector<std::string> split(const std::string& st, char delim,
bool clean_empty=true);
/**

View File

@ -240,15 +240,15 @@ public:
oss << value;
set(new SingleAttribute(name, oss.str()));
}
}
void add(const string& name, const string& value)
{
set(new SingleAttribute(name, value));
}
void add(const string& name, const string& value)
{
set(new SingleAttribute(name, value));
}
void add(const string& name, bool value)
{
void add(const string& name, bool value)
{
if ( value )
{
set(new SingleAttribute(name, "YES"));
@ -257,7 +257,7 @@ public:
{
set(new SingleAttribute(name, "NO"));
}
}
}
/**
* Removes an attribute from the template. The attributes are returned. The

View File

@ -37,6 +37,7 @@ using namespace std;
class AuthRequest;
class Snapshots;
class HostShareCapacity;
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
@ -1011,14 +1012,10 @@ public:
};
/**
* Get the VM physical requirements for the host.
* @param cpu
* @param memory
* @param disk
* @param pci_dev
* Get the VM physical capacity requirements for the host.
* @param sr the HostShareCapacity to store the capacity request.
*/
void get_requirements(int& cpu, int& memory, int& disk,
vector<VectorAttribute *>& pci_dev);
void get_capacity(HostShareCapacity &sr);
/**
* Adds automatic placement requirements: Datastore and Cluster
@ -1033,27 +1030,27 @@ public:
return automatic_requirements(cluster_ids, datastore_ids, error_str);
}
/**
* Checks if the resize parameters are valid
* @param cpu New CPU. 0 means unchanged.
* @param memory New MEMORY. 0 means unchanged.
* @param vcpu New VCPU. 0 means unchanged.
* @param error_str Error reason, if any
*
* @return 0 on success
*/
int check_resize(float cpu, long int memory, int vcpu, string& error_str);
/**
* Resize the VM capacity
* @param cpu
* @param memory
* @param vcpu
* @param error_str Error reason, if any
*
* @return 0 on success
*/
int resize(float cpu, long int memory, int vcpu, string& error_str);
int resize(float cpu, long int memory, unsigned int vcpu, string& error);
/**
* Parse TOPOLOGY and NUMA_NODE
* @param tmpl template of the virtual machine
* @param error if any
*
* @return 0 on sucess
*/
static int parse_topology(Template * tmpl, std::string &error);
/**
* @return true if the VM is being deployed with a pinned policy
*/
bool is_pinned();
// ------------------------------------------------------------------------
// Virtual Machine Disks

View File

@ -1026,6 +1026,7 @@ IM_PROBES_KVM_PROBES_FILES="src/im_mad/remotes/kvm-probes.d/kvm.rb \
src/im_mad/remotes/kvm-probes.d/machines-models.rb \
src/im_mad/remotes/kvm-probes.d/name.sh \
src/im_mad/remotes/kvm-probes.d/pci.rb \
src/im_mad/remotes/kvm-probes.d/numa.rb \
src/im_mad/remotes/common.d/monitor_ds.sh \
src/im_mad/remotes/common.d/version.sh \
src/im_mad/remotes/common.d/collectd-client-shepherd.sh"
@ -1038,6 +1039,7 @@ IM_PROBES_LXD_PROBES_FILES="src/im_mad/remotes/lxd-probes.d/lxd.rb \
src/im_mad/remotes/lxd-probes.d/poll.sh \
src/im_mad/remotes/lxd-probes.d/name.sh \
src/im_mad/remotes/lxd-probes.d/pci.rb \
src/im_mad/remotes/lxd-probes.d/numa.rb \
src/im_mad/remotes/lxd-probes.d/monitor_ds.sh \
src/im_mad/remotes/lxd-probes.d/version.sh \
src/im_mad/remotes/lxd-probes.d/profiles.sh \

View File

@ -636,6 +636,16 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
print_pcis(pcis)
end
begin
numa_nodes = host.to_hash['HOST']['HOST_SHARE']['NUMA_NODES']['NODE']
rescue StandardError
numa_nodes = nil
end
if numa_nodes && !numa_nodes.empty?
print_numa_nodes(numa_nodes, str, str_h1)
end
puts
CLIHelper.print_header('WILD VIRTUAL MACHINES', false)
puts
@ -718,4 +728,180 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
table.show(pcis)
end
def print_numa_nodes(numa_nodes, str, str_h1)
numa_nodes = get_numa_data(numa_nodes)
print_numa_cores(numa_nodes)
print_numa_memory(numa_nodes)
print_numa_hugepages(numa_nodes)
end
def get_numa_data(numa_nodes)
numa_nodes = [numa_nodes] if numa_nodes.class == Hash
numa_nodes.map! do |core|
cores = core['CORE']
free, used, cores_str = get_numa_cores(cores)
core['CORE'] = {}
core['CORE']['CORES'] = cores_str
core['CORE']['FREE'] = free
core['CORE']['USED'] = used
core
end
numa_nodes
end
def get_numa_cores(cores)
ret = ''
free = 0
used = 0
cores.each do |info|
core = info['CPUS'].split(',')
core.each do |c|
c = c.split(':')
if c[1] == '-1'
ret += '-'
free += 1
elsif c[1] != '-1' && info['FREE'] == '0'
ret += 'X'
used += 1
else
ret += 'x'
used += 1
end
end
ret += ' '
end
[free, used, ret]
end
def print_numa_cores(numa_nodes)
puts
CLIHelper.print_header('NUMA NODES', false)
puts
table = CLIHelper::ShowTable.new(nil, self) do
column :ID, 'Node ID', :size => 4, :left => false do |d|
d['NODE_ID']
end
column :CORES, 'Cores usage', :size => 50, :left => true do |d|
d['CORE']['CORES']
end
column :USED, 'Used CPUs', :size => 4, :left => true do |d|
d['CORE']['USED']
end
column :FREE, 'Free CPUs', :size => 4, :left => true do |d|
d['CORE']['FREE']
end
default :ID, :CORES, :USED, :FREE
end
table.show(numa_nodes)
end
def print_numa_memory(numa_nodes)
nodes = numa_nodes.clone
nodes.reject! do |node|
node['MEMORY'].nil? || node['MEMORY'].empty?
end
return if nodes.empty?
puts
CLIHelper.print_header('NUMA MEMORY', false)
puts
table = CLIHelper::ShowTable.new(nil, self) do
column :NODE_ID, 'Node ID', :size => 8, :left => false do |d|
d['NODE_ID']
end
column :TOTAL, 'Total memory', :size => 8, :left => true do |d|
OpenNebulaHelper.unit_to_str(d['MEMORY']['TOTAL'].to_i, {})
end
column :USED_REAL, 'Used memory', :size => 20, :left => true do |d|
OpenNebulaHelper.unit_to_str(d['MEMORY']['USED'].to_i, {})
end
column :USED_ALLOCATED, 'U memory', :size => 20, :left => true do |d|
OpenNebulaHelper.unit_to_str(d['MEMORY']['USAGE'].to_i, {})
end
column :FREE, 'Free memory', :size => 8, :left => true do |d|
OpenNebulaHelper.unit_to_str(d['MEMORY']['FREE'].to_i, {})
end
default :NODE_ID, :TOTAL, :USED_REAL, :USED_ALLOCATED, :FREE
end
table.show(nodes)
end
def print_numa_hugepages(numa_nodes)
nodes = numa_nodes.clone
nodes.reject! do |node|
node['HUGEPAGE'].nil? || node['HUGEPAGE'].empty?
end
return if nodes.empty?
puts
CLIHelper.print_header('NUMA HUGEPAGES', false)
puts
table = CLIHelper::ShowTable.new(nil, self) do
column :NODE_ID, 'Node ID', :size => 8, :left => false do |d|
d['NODE_ID']
end
column :TOTAL, 'Total pages', :size => 8, :left => true do |d|
d['HUGEPAGE']['PAGES']
end
column :SIZE, 'Pages size', :size => 8, :left => true do |d|
OpenNebulaHelper.unit_to_str(d['HUGEPAGE']['SIZE'].to_i/1024, {}, "M")
end
column :FREE, 'Free pages', :size => 8, :left => true do |d|
d['HUGEPAGE']['FREE']
end
column :USED, 'allocated pages', :size => 8, :left => true do |d|
d['HUGEPAGE']['USAGE']
end
default :NODE_ID, :SIZE, :TOTAL, :FREE, :USED
end
hugepages = []
nodes.each do |node|
node['HUGEPAGE'].each do |hugepage|
h = {}
h['NODE_ID'] = node['NODE_ID']
h['HUGEPAGE'] = hugepage
hugepages << h
end
end
table.show(hugepages)
end
end

View File

@ -1,3 +1,4 @@
require 'pry'
# -------------------------------------------------------------------------- #
# Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
# #
@ -1170,11 +1171,79 @@ in the frontend machine.
puts vm.template_like_str('USER_TEMPLATE')
end
if vm.has_elements?('/VM/TEMPLATE/NUMA_NODE')
print_numa_nodes(vm.to_hash['VM']['TEMPLATE']['NUMA_NODE'])
end
if vm.has_elements?('/VM/TEMPLATE/TOPOLOGY')
print_topology([vm.to_hash['VM']['TEMPLATE']['TOPOLOGY']])
end
while vm.has_elements?('/VM/TEMPLATE/NUMA_NODE')
vm.delete_element('/VM/TEMPLATE/NUMA_NODE')
end if !options[:all]
while vm.has_elements?('/VM/TEMPLATE/TOPOLOGY')
vm.delete_element('/VM/TEMPLATE/TOPOLOGY')
end if !options[:all]
puts
CLIHelper.print_header(str_h1 % "VIRTUAL MACHINE TEMPLATE",false)
puts vm.template_str
end
def print_numa_nodes(numa_nodes)
puts
CLIHelper.print_header('NUMA NODES', false)
puts
table = CLIHelper::ShowTable.new(nil, self) do
column :ID, 'Node ID', :size => 4, :left => false do |d|
d['NODE_ID']
end
column :CPUS, 'Cpus used', :size => 6, :left => false do |d|
d['CPUS']
end
column :MEMORY, 'Memory used', :size => 10, :left => false do |d|
OpenNebulaHelper.unit_to_str(d['MEMORY'].to_i, {})
end
column :TOTAL_CPUS, 'Total CPUs', :size => 10, :left => false do |d|
d['TOTAL_CPUS']
end
default :ID, :CPUS, :MEMORY, :TOTAL_CPUS
end
table.show(numa_nodes)
end
def print_topology(topology)
puts
CLIHelper.print_header('TOPOLOGY', false)
puts
table = CLIHelper::ShowTable.new(nil, self) do
column :CORES, 'Cores', :size => 6, :left => false do |d|
d['CORES']
end
column :SOCKETS, 'Sockets', :size => 8, :left => false do |d|
d['SOCKETS']
end
column :THREADS, 'Threads', :size => 8, :left => false do |d|
d['THREADS']
end
default :CORES, :SOCKETS, :THREADS
end
table.show(topology)
end
def format_history(vm)
table=CLIHelper::ShowTable.new(nil, self) do
column :SEQ, "Sequence number", :size=>3 do |d|

View File

@ -124,12 +124,11 @@ int DispatchManager::import(VirtualMachine * vm, const RequestAttributes& ra)
}
time_t the_time = time(0);
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
hpool->add_capacity(vm->get_hid(), sr);
import_state = vm->get_import_state();
@ -1078,8 +1077,7 @@ int DispatchManager::delete_vm(VirtualMachine * vm, const RequestAttributes& ra,
{
ostringstream oss;
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
bool is_public_host = false;
int host_id = -1;
@ -1117,9 +1115,9 @@ int DispatchManager::delete_vm(VirtualMachine * vm, const RequestAttributes& ra,
{
case VirtualMachine::SUSPENDED:
case VirtualMachine::POWEROFF:
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->del_capacity(vm->get_hid(), vid, cpu, mem, disk, pci);
hpool->del_capacity(vm->get_hid(), sr);
if (is_public_host)
{
@ -1277,14 +1275,11 @@ int DispatchManager::delete_recreate(VirtualMachine * vm,
int DispatchManager::delete_vm_db(VirtualMachine * vm,
const RequestAttributes& ra, string& error_str)
{
HostShareCapacity sr;
ostringstream oss;
int cpu, mem, disk;
vector<VectorAttribute *> pci;
int vid = vm->get_oid();
oss << "Deleting VM from DB " << vm->get_oid();
NebulaLog::log("DiM",Log::DEBUG,oss);
switch (vm->get_state())
@ -1292,9 +1287,9 @@ int DispatchManager::delete_vm_db(VirtualMachine * vm,
case VirtualMachine::SUSPENDED:
case VirtualMachine::POWEROFF:
case VirtualMachine::ACTIVE:
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->del_capacity(vm->get_hid(), vid, cpu, mem, disk, pci);
hpool->del_capacity(vm->get_hid(), sr);
case VirtualMachine::STOPPED:
case VirtualMachine::UNDEPLOYED:

View File

@ -460,6 +460,8 @@ int Host::update_info(Template &tmpl,
host_share.set_pci_monitorization(pci_att);
host_share.set_numa_monitorization(*obj_template);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -82,7 +82,8 @@ class DummyInformationManager < OpenNebulaDriver
SLOT = \"00\",\n
TYPE = \"10de:0863:0300\",\n
VENDOR = \"10de\",\n
VENDOR_NAME = \"NVIDIA Corporation\"\n
VENDOR_NAME = \"NVIDIA Corporation\",\n
NUMA_NODE=\"1\"
]\n
PCI = [
ADDRESS = \"0000:00:06:0\",\n
@ -97,7 +98,8 @@ class DummyInformationManager < OpenNebulaDriver
SLOT = \"06\",\n
TYPE = \"10de:0aa7:0c03\",\n
VENDOR = \"10de\",\n
VENDOR_NAME = \"NVIDIA Corporation\"\n
VENDOR_NAME = \"NVIDIA Corporation\",\n
NUMA_NODE=\"1\"
]\n
PCI = [
ADDRESS = \"0000:00:06:1\",\n
@ -112,9 +114,12 @@ class DummyInformationManager < OpenNebulaDriver
SLOT = \"06\",\n
TYPE = \"10de:0aa9:0c03\",\n
VENDOR = \"10de\",\n
VENDOR_NAME = \"NVIDIA Corporation\"\n
VENDOR_NAME = \"NVIDIA Corporation\"\n,
NUMA_NODE=\"0\"
]\n"
make_topology(results, 2, 8, [2048, 1048576], 4, 16777216)
results = Base64::encode64(results).strip.delete("\n")
send_message("MONITOR", RESULT[:success], number, results)
@ -123,6 +128,45 @@ class DummyInformationManager < OpenNebulaDriver
def stop_monitor(number, host)
send_message("STOPMONITOR", RESULT[:success], number, nil)
end
def make_topology(result, nodes, cores, pages, threads, mem)
nodes.times do |i|
cores.times do |j|
core_id = j + ( i * cores)
core_str = "CORE = [ NODE_ID=\"#{i}\", ID=\"#{core_id}\", "\
"CPUS=\""
threads.times do |k|
cpu_id = core_id + k * ( cores * nodes )
core_str << "," if k != 0
core_str << "#{cpu_id}"
end
core_str << "\"]\n"
result << core_str
end
pages.each do |p|
result << "HUGEPAGE = [ SIZE = \"#{p}\", FREE = \"1024\", "\
"PAGES = \"1024\", NODE_ID = \"#{i}\" ] "
end
memn = mem.to_i/nodes
result << "MEMORY_NODE = [ NODE_ID = \"#{i}\", TOTAL = \"#{memn}\"" \
", FREE = \"#{rand(memn)}\", USED = \"#{rand(memn)}\", " \
"DISTANCE = \"#{i} "
nodes.times do |l|
result << "#{l} " if l != i
end
result << "\" ]\n"
end
end
end

View File

@ -0,0 +1 @@
../node-probes.d/numa.rb

View File

@ -0,0 +1 @@
../node-probes.d/numa.rb

View File

@ -0,0 +1,225 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2019, 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. #
#--------------------------------------------------------------------------- #
#-------------------------------------------------------------------------------
# This probe uses the sysfs interface to get information about the NUMA topology
# of the host. References:
# - https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-devices-node
# - https://www.kernel.org/doc/Documentation/cputopology.txt
#
#-------------------------------------------------------------------------------
NODE_PATH = '/sys/bus/node/devices/'
# Print node information in OpenNebula Template format. Example:
#
# HUGEPAGE = [NODE_ID = "0", SIZE = "1048576", PAGES = "0", FREE = "0"]
# HUGEPAGE = [NODE_ID = "0", SIZE = "2048", PAGES = "0", FREE = "0"]
# CORE = [ NODE_ID = "0", ID = "0", CPUS = "0,2"]
# CORE = [ NODE_ID = "0", ID = "1", CPUS = "1,3"]
#
# Corresponding Hash is:
# {"hugepages"=>
# [{"size"=>"2048", "free"=>"0", "nr"=>"0", "surplus"=>"0"},
# {"size"=>"1048576", "free"=>"0", "nr"=>"0", "surplus"=>"0"}],
# "cores"=>
# [{"id"=>"3", "cpus"=>["3", "7"]},
# {"id"=>"1", "cpus"=>["1", "5"]},
# {"id"=>"2", "cpus"=>["2", "6"]},
# {"id"=>"0", "cpus"=>["0", "4"]}],
# "memory"=>
# {"total"=>"7992880", "free"=>"2041004", "used"=>"5951876", "distance"=>"0"}}
def node_to_template(node, nid)
node_s = ''
node.each do |k, v|
case k
when 'hugepages'
v.each do |h|
node_s << "HUGEPAGE = [ NODE_ID = \"#{nid}\","
node_s << " SIZE = \"#{h['size']}\","
node_s << " PAGES = \"#{h['nr']}\","
node_s << " FREE = \"#{h['free']}\" ]\n"
end
when 'cores'
v.each do |c|
node_s << "CORE = [ NODE_ID = \"#{nid}\","
node_s << " ID = \"#{c['id']}\","
node_s << " CPUS = \"#{c['cpus'].join(',')}\" ]\n"
end
when 'memory'
node_s << "MEMORY_NODE = [ NODE_ID = \"#{nid}\","
node_s << " TOTAL = \"#{v['total']}\","
node_s << " FREE = \"#{v['free']}\","
node_s << " USED = \"#{v['used']}\","
node_s << " DISTANCE = \"#{v['distance']}\" ]\n"
end
end
node_s
end
# ------------------------------------------------------------------------------
# hugepages information
# ------------------------------------------------------------------------------
# This function parses the HUGE_PAGES information for the node
# @param [Hash] nodes the attributes of the NUMA nodes
# @param [String] node name of the node
# @param [String] node_id of the node
#
def huge_pages(nodes, node_id)
nodes[node_id]['hugepages'] = []
hp_path = "#{NODE_PATH}/node#{node_id}/hugepages"
return unless Dir.exist?(hp_path)
Dir.foreach(hp_path) do |hp|
/hugepages-(?<hp_size>\d+)kB/ =~ hp
next unless hp_size
hpsz_path = "#{hp_path}/#{hp}"
hp_info = { 'size' => hp_size }
begin
%w[free nr surplus].each do |var|
var_path = "#{hpsz_path}/#{var}_hugepages"
hp_info[var] = File.read(var_path).chomp
end
rescue StandardError
next
end
nodes[node_id]['hugepages'] << hp_info
end
end
# ------------------------------------------------------------------------------
# CPU topology
# ------------------------------------------------------------------------------
# This function parses the CPU topology information for the node
# @param [Hash] nodes the attributes of the NUMA nodes
# @param [String] node name of the node
# @param [String] node_id of the node
#
def cpu_topology(nodes, node_id)
nodes[node_id]['cores'] = []
cpu_visited = []
cpu_path = "#{NODE_PATH}/node#{node_id}/"
return unless Dir.exist?(cpu_path)
Dir.foreach(cpu_path) do |cp|
/cpu(?<cpu_id>\d+)/ =~ cp
next unless cpu_id
next if cpu_visited.include? cpu_id
begin
core_path = "#{cpu_path}/#{cp}/topology"
siblings = File.read("#{core_path}/thread_siblings_list").chomp
siblings = siblings.split(',')
cpu_visited.concat(siblings)
core_id = File.read("#{core_path}/core_id").chomp
nodes[node_id]['cores'] << { 'id' => core_id, 'cpus' => siblings }
rescue StandardError
next
end
end
end
# ------------------------------------------------------------------------------
# Memory
# ------------------------------------------------------------------------------
# This function parses the CPU topology information for the node
# @param [Hash] nodes the attributes of the NUMA nodes
# @param [String] node name of the node
# @param [String] node_id of the node
#
def memory(nodes, node_id)
meminfo_path = "#{NODE_PATH}/node#{node_id}/meminfo"
return unless File.exist?(meminfo_path)
bind = binding
mem_vars = %w[MemTotal MemFree MemUsed]
mem_vars.each {|var| bind.eval("#{var.downcase.to_sym} = 0") }
File.readlines(meminfo_path).each do |line|
mem_vars.each do |metric|
md = /Node #{node_id} #{metric}:\s+(?<value>\d+) k/.match(line)
bind.eval("#{metric.downcase.to_sym} = #{md[:value]}") if md
break if md
end
end
nodes[node_id]['memory'] = {
'total' => bind.eval("#{:memtotal}"),
'free' => bind.eval("#{:memfree}"),
'used' => bind.eval("#{:memused}")
}
# Node distance to priotitize memory allocation
distance_path = "#{NODE_PATH}/node#{node_id}/distance"
return unless File.exist?(distance_path)
distance = File.read(distance_path)
distance_a = distance.split(' ')
distance_h = {}
distance_a.each_with_index {|d, i| distance_h[d.to_i] = i }
distance_h = Hash[distance_h.sort_by {|k| k }]
closer = ''
distance_h.each {|_, v| closer << v.to_s << ' ' }
nodes[node_id]['memory']['distance'] = closer.chop
end
# ------------------------------------------------------------------------------
# Get information for each NUMA node.
# ------------------------------------------------------------------------------
nodes = {}
Dir.foreach(NODE_PATH) do |node|
/node(?<node_id>\d+)/ =~ node
next unless node_id
nodes[node_id] = {}
huge_pages(nodes, node_id)
cpu_topology(nodes, node_id)
memory(nodes, node_id)
end
nodes_s = ''
nodes.each {|i, v| nodes_s << node_to_template(v, i) }
puts nodes_s

View File

@ -56,6 +56,16 @@ def parse_pci(pci)
"pci_0000_#{card[:short_address].gsub(/[:.]/, '_')}"
card[:address] = "0000:#{card[:short_address].gsub(/[:.]/, ':')}"
begin
numa_node = File.read("/sys/bus/pci/devices/0000:#{pci[0]}/numa_node").chomp
rescue
numa_node = '-'
end
numa_node = '-' if numa_node.to_i < 0
card[:numa_node] = numa_node
card[:class_name], card[:class] = get_name_and_id(pci[1])
card[:vendor_name], card[:vendor] = get_name_and_id(pci[2])
card[:device_name], card[:device] = get_name_and_id(pci[3])
@ -112,7 +122,8 @@ devices.each do |dev|
pval('DOMAIN', '0000'),
pval('BUS', dev[:bus]),
pval('SLOT', dev[:slot]),
pval('FUNCTION', dev[:function])
pval('FUNCTION', dev[:function]),
pval('NUMA_NODE', dev[:numa_node])
]
puts values.join(",\n")

View File

@ -36,9 +36,10 @@ void LifeCycleManager::deploy_action(const LCMAction& la)
if ( vm->get_state() == VirtualMachine::ACTIVE )
{
HostShareCapacity sr;
time_t thetime = time(0);
int cpu, mem, disk, rc;
vector<VectorAttribute *> pci;
int rc;
VirtualMachine::LcmState vm_state;
TMAction::Actions tm_action;
@ -47,7 +48,7 @@ void LifeCycleManager::deploy_action(const LCMAction& la)
// PROLOG STATE
//----------------------------------------------------
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
vm_state = VirtualMachine::PROLOG;
tm_action = TMAction::PROLOG;
@ -69,7 +70,7 @@ void LifeCycleManager::deploy_action(const LCMAction& la)
vm->set_state(vm_state);
rc = hpool->add_capacity(vm->get_hid(),vm->get_oid(),cpu,mem,disk,pci);
rc = hpool->add_capacity(vm->get_hid(), sr);
vm->set_stime(thetime);
@ -214,8 +215,7 @@ void LifeCycleManager::stop_action(const LCMAction& la)
void LifeCycleManager::migrate_action(const LCMAction& la)
{
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
time_t the_time = time(0);
@ -254,9 +254,9 @@ void LifeCycleManager::migrate_action(const LCMAction& la)
vm->set_resched(false);
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
hpool->add_capacity(vm->get_hid(), sr);
vm->set_stime(the_time);
@ -334,14 +334,13 @@ void LifeCycleManager::migrate_action(const LCMAction& la)
vm->reset_info();
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
hpool->add_capacity(vm->get_hid(), sr);
if ( vm->get_hid() != vm->get_previous_hid() )
{
hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem,
disk, pci);
hpool->del_capacity(vm->get_previous_hid(), sr);
}
vm->set_stime(the_time);
@ -385,8 +384,7 @@ void LifeCycleManager::live_migrate_action(const LCMAction& la)
if (vm->get_state() == VirtualMachine::ACTIVE &&
vm->get_lcm_state() == VirtualMachine::RUNNING)
{
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
//----------------------------------------------------
// MIGRATE STATE
@ -396,9 +394,9 @@ void LifeCycleManager::live_migrate_action(const LCMAction& la)
vm->set_resched(false);
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
hpool->add_capacity(vm->get_hid(), sr);
vm->set_stime(time(0));
@ -967,10 +965,10 @@ void LifeCycleManager::delete_recreate_action(const LCMAction& la)
void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose,
int& image_id, const LCMAction& la)
{
int cpu, mem, disk;
HostShareCapacity sr;
unsigned int port;
vector<VectorAttribute *> pci;
time_t the_time = time(0);
VirtualMachine::LcmState state = vm->get_lcm_state();
@ -1000,9 +998,9 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose,
vm->set_etime(the_time);
vm->set_vm_info();
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
hpool->del_capacity(vm->get_hid(), sr);
const VectorAttribute * graphics = vm->get_template_attribute("GRAPHICS");
@ -1139,8 +1137,7 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose,
vm->set_previous_vm_info();
vm->set_previous_running_etime(the_time);
hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu,
mem, disk, pci);
hpool->del_capacity(vm->get_previous_hid(), sr);
vmpool->update_previous_history(vm);
@ -1163,8 +1160,7 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose,
vm->set_previous_vm_info();
vm->set_previous_running_etime(the_time);
hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu,
mem, disk, pci);
hpool->del_capacity(vm->get_previous_hid(), sr);
vmpool->update_previous_history(vm);

View File

@ -22,8 +22,7 @@
void LifeCycleManager::start_prolog_migrate(VirtualMachine* vm)
{
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
time_t the_time = time(0);
@ -52,12 +51,13 @@ void LifeCycleManager::start_prolog_migrate(VirtualMachine* vm)
vmpool->update_history(vm);
vm->get_requirements(cpu, mem, disk, pci);
vmpool->update(vm);
vm->get_capacity(sr);
if ( vm->get_hid() != vm->get_previous_hid() )
{
hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem,
disk, pci);
hpool->del_capacity(vm->get_previous_hid(), sr);
}
vmpool->update(vm);
@ -69,58 +69,57 @@ void LifeCycleManager::start_prolog_migrate(VirtualMachine* vm)
void LifeCycleManager::revert_migrate_after_failure(VirtualMachine* vm)
{
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
time_t the_time = time(0);
time_t the_time = time(0);
//----------------------------------------------------
// RUNNING STATE FROM SAVE_MIGRATE
//----------------------------------------------------
//----------------------------------------------------
// RUNNING STATE FROM SAVE_MIGRATE
//----------------------------------------------------
vm->set_state(VirtualMachine::RUNNING);
vm->set_state(VirtualMachine::RUNNING);
vm->set_etime(the_time);
vm->set_etime(the_time);
vm->set_vm_info();
vm->set_vm_info();
vmpool->update_history(vm);
vmpool->update_history(vm);
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
if ( vm->get_hid() != vm->get_previous_hid() )
{
hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
}
if ( vm->get_hid() != vm->get_previous_hid() )
{
hpool->del_capacity(vm->get_hid(), sr);
}
vm->set_previous_etime(the_time);
vm->set_previous_etime(the_time);
vm->set_previous_vm_info();
vm->set_previous_vm_info();
vm->set_previous_running_etime(the_time);
vm->set_previous_running_etime(the_time);
vmpool->update_previous_history(vm);
vmpool->update_previous_history(vm);
// --- Add new record by copying the previous one
// --- Add new record by copying the previous one
vm->cp_previous_history();
vm->cp_previous_history();
vm->set_stime(the_time);
vm->set_stime(the_time);
vm->set_running_stime(the_time);
vm->set_running_stime(the_time);
vm->set_last_poll(0);
vm->set_last_poll(0);
vmpool->insert_history(vm);
vmpool->insert_history(vm);
vmpool->update(vm);
vmpool->update(vm);
vm->log("LCM", Log::INFO, "Fail to save VM state while migrating."
" Assuming that the VM is still RUNNING (will poll VM).");
vm->log("LCM", Log::INFO, "Fail to save VM state while migrating."
" Assuming that the VM is still RUNNING (will poll VM).");
//----------------------------------------------------
//----------------------------------------------------
vmm->trigger(VMMAction::POLL,vm->get_oid());
vmm->trigger(VMMAction::POLL,vm->get_oid());
}
/* -------------------------------------------------------------------------- */
@ -276,8 +275,7 @@ void LifeCycleManager::deploy_success_action(int vid)
if ( vm->get_lcm_state() == VirtualMachine::MIGRATE )
{
int cpu,mem,disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
time_t the_time = time(0);
@ -295,9 +293,9 @@ void LifeCycleManager::deploy_success_action(int vid)
vmpool->update_previous_history(vm);
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->del_capacity(vm->get_previous_hid(),vm->get_oid(),cpu,mem,disk,pci);
hpool->del_capacity(vm->get_previous_hid(), sr);
vm->set_state(VirtualMachine::RUNNING);
@ -355,8 +353,7 @@ void LifeCycleManager::deploy_failure_action(int vid)
if ( vm->get_lcm_state() == VirtualMachine::MIGRATE )
{
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
time_t the_time = time(0);
@ -380,9 +377,9 @@ void LifeCycleManager::deploy_failure_action(int vid)
vmpool->update_previous_history(vm);
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
hpool->del_capacity(vm->get_hid(), sr);
// --- Add new record by copying the previous one
@ -765,8 +762,7 @@ void LifeCycleManager::prolog_success_action(int vid)
void LifeCycleManager::prolog_failure_action(int vid)
{
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
time_t t = time(0);
@ -846,9 +842,9 @@ void LifeCycleManager::prolog_failure_action(int vid)
break;
}
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu,mem,disk,pci);
hpool->del_capacity(vm->get_hid(), sr);
// Clone previous history record into a new one
vm->cp_previous_history();
@ -857,7 +853,7 @@ void LifeCycleManager::prolog_failure_action(int vid)
vm->set_prolog_stime(t);
vm->set_last_poll(0);
hpool->add_capacity(vm->get_hid(),vm->get_oid(),cpu,mem,disk,pci);
hpool->add_capacity(vm->get_hid(), sr);
vmpool->insert_history(vm);
@ -886,11 +882,11 @@ void LifeCycleManager::prolog_failure_action(int vid)
void LifeCycleManager::epilog_success_action(int vid)
{
VirtualMachine * vm;
vector<VectorAttribute *> pci;
VirtualMachine * vm;
HostShareCapacity sr;
time_t the_time = time(0);
int cpu,mem,disk;
unsigned int port;
VirtualMachine::LcmState state;
@ -971,9 +967,9 @@ void LifeCycleManager::epilog_success_action(int vid)
vmpool->update(vm);
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci);
hpool->del_capacity(vm->get_hid(), sr);
//----------------------------------------------------

View File

@ -26,7 +26,7 @@
/* -------------------------------------------------------------------------- */
bool RequestManagerAllocate::allocate_authorization(
xmlrpc_c::paramList const& paramList,
xmlrpc_c::paramList const& paramList,
Template * tmpl,
RequestAttributes& att,
PoolObjectAuth * cluster_perms)
@ -62,7 +62,7 @@ bool RequestManagerAllocate::allocate_authorization(
/* -------------------------------------------------------------------------- */
bool VirtualMachineAllocate::allocate_authorization(
xmlrpc_c::paramList const& paramList,
xmlrpc_c::paramList const& paramList,
Template * tmpl,
RequestAttributes& att,
PoolObjectAuth * cluster_perms)
@ -102,7 +102,13 @@ bool VirtualMachineAllocate::allocate_authorization(
return false;
}
// -------------------------- Check Quotas ----------------------------
// ---------------------- Check Quotas & Topology --------------------------
if (VirtualMachine::parse_topology(ttmpl, att.resp_msg) != 0)
{
failure_response(ALLOCATE, att);
return false;
}
VirtualMachineTemplate aux_tmpl(*ttmpl);

View File

@ -82,7 +82,7 @@ void VMTemplateInstantiate::request_execute(xmlrpc_c::paramList const& paramList
}
ErrorCode ec = tmpl_clone.request_execute(id, tmpl_name, new_id, true,
str_uattrs, att);
str_uattrs, att);
if (ec != SUCCESS)
{
@ -199,6 +199,12 @@ Request::ErrorCode VMTemplateInstantiate::request_execute(int id, string name,
tmpl->set(new SingleAttribute("NAME",name));
}
if (VirtualMachine::parse_topology(tmpl, att.resp_msg) != 0)
{
delete tmpl;
return ALLOCATE;
}
//--------------------------------------------------------------------------
AuthRequest ar(att.uid, att.group_ids);

View File

@ -367,19 +367,16 @@ int RequestManagerVirtualMachine::get_host_information(
bool RequestManagerVirtualMachine::check_host(
int hid, bool enforce, VirtualMachine* vm, string& error)
{
Nebula& nd = Nebula::instance();
HostPool * hpool = nd.get_hpool();
HostPool * hpool = Nebula::instance().get_hpool();
Host * host;
bool test;
bool test = true;
string capacity_error;
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
host = hpool->get_ro(hid);
Host * host = hpool->get_ro(hid);
if (host == 0)
{
@ -387,16 +384,14 @@ bool RequestManagerVirtualMachine::check_host(
return false;
}
if (enforce)
if ( enforce )
{
test = host->test_capacity(cpu, mem, disk, pci, capacity_error);
}
else
{
test = host->test_capacity(pci, capacity_error);
test = host->test_capacity(sr, capacity_error);
}
if (!test)
host->unlock();
if (enforce && !test)
{
ostringstream oss;
@ -407,8 +402,6 @@ bool RequestManagerVirtualMachine::check_host(
error = oss.str();
}
host->unlock();
return test;
}
@ -834,6 +827,8 @@ void VirtualMachineDeploy::request_execute(xmlrpc_c::paramList const& paramList,
int uid = vm->get_uid();
int gid = vm->get_gid();
enforce = enforce || vm->is_pinned();
vm->unlock();
if (is_public_cloud) // Set ds_id to -1 and tm_mad empty(). This is used by
@ -967,7 +962,7 @@ void VirtualMachineDeploy::request_execute(xmlrpc_c::paramList const& paramList,
return;
}
if (check_host(hid, enforce, vm, att.resp_msg) == false)
if (check_host(hid, enforce || vm->is_pinned(), vm, att.resp_msg) == false)
{
vm->unlock();
@ -1180,6 +1175,15 @@ void VirtualMachineMigrate::request_execute(xmlrpc_c::paramList const& paramList
if (live)
{
action = History::LIVE_MIGRATE_ACTION;
if ( vm->is_pinned() )
{
att.resp_msg = "VM with a pinned NUMA topology cannot be live-migrated";
failure_response(ACTION, att);
vm->unlock();
return;
}
}
else
{
@ -1239,16 +1243,17 @@ void VirtualMachineMigrate::request_execute(xmlrpc_c::paramList const& paramList
}
//Check PCI devices are compatible with migration type
int cpu, mem, disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
if ((pci.size() > 0) && (!poffmgr && vm->get_state() != VirtualMachine::POWEROFF))
if ((sr.pci.size() > 0) && (!poffmgr &&
vm->get_state() != VirtualMachine::POWEROFF))
{
ostringstream oss;
oss << "Cannot migrate VM [" << id << "], for migrating a VM with PCI devices attached it's necessary either the poweroff or poweroff-hard flag";
oss << "Cannot migrate VM [" << id << "], use poweroff or poweroff-hard"
" flag for migrating a VM with PCI devices";
att.resp_msg = oss.str();
failure_response(ACTION, att);
@ -1948,43 +1953,81 @@ void VirtualMachineDetach::request_execute(xmlrpc_c::paramList const& paramList,
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int test_set_capacity(VirtualMachine * vm, float cpu, long mem, int vcpu,
string& error)
{
HostPool * hpool = Nebula::instance().get_hpool();
int rc;
if ( vm->get_state() == VirtualMachine::POWEROFF )
{
HostShareCapacity sr;
Host * host = hpool->get(vm->get_hid());
if ( host == 0 )
{
error = "Could not update host";
return -1;
}
vm->get_capacity(sr);
host->del_capacity(sr);
rc = vm->resize(cpu, mem, vcpu, error);
if ( rc == -1 )
{
host->unlock();
return -1;
}
vm->get_capacity(sr);
if (!host->test_capacity(sr, error))
{
host->unlock();
return -1;
}
host->add_capacity(sr);
hpool->update(host);
host->unlock();
}
else
{
rc = vm->resize(cpu, mem, vcpu, error);
}
return rc;
}
void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList,
RequestAttributes& att)
{
int id = xmlrpc_c::value_int(paramList.getInt(1));
string str_tmpl = xmlrpc_c::value_string(paramList.getString(2));
bool enforce_param = xmlrpc_c::value_boolean(paramList.getBoolean(3));
int id = xmlrpc_c::value_int(paramList.getInt(1));
std::string str_tmpl = xmlrpc_c::value_string(paramList.getString(2));
//Argument 3 enforce deprecated to check/re-evaluate NUMA topology
float ncpu, ocpu, dcpu;
int nmemory, omemory, dmemory;
long nmemory, omemory, dmemory;
int nvcpu, ovcpu;
Nebula& nd = Nebula::instance();
HostPool * hpool = nd.get_hpool();
Host * host;
Template deltas;
bool rc;
int ret;
int hid = -1;
PoolObjectAuth vm_perms;
VirtualMachinePool * vmpool = static_cast<VirtualMachinePool *>(pool);
VirtualMachine * vm;
VirtualMachineTemplate tmpl;
bool enforce = true;
if (att.is_admin())
{
enforce = enforce_param;
}
// -------------------------------------------------------------------------
// Parse template
// -------------------------------------------------------------------------
rc = tmpl.parse_str_or_xml(str_tmpl, att.resp_msg);
int rc = tmpl.parse_str_or_xml(str_tmpl, att.resp_msg);
if ( rc != 0 )
{
@ -2016,12 +2059,40 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList,
/* ---------------------------------------------------------------------- */
/* Get the resize values */
/* ---------------------------------------------------------------------- */
ncpu = nvcpu = nmemory = 0;
tmpl.get("CPU", ncpu);
tmpl.get("VCPU", nvcpu);
tmpl.get("MEMORY", nmemory);
vm = vmpool->get_ro(id);
if (ncpu < 0)
{
att.resp_msg = "CPU must be a positive float or integer value.";
failure_response(INTERNAL, att);
return;
}
if (nmemory < 0)
{
att.resp_msg = "MEMORY must be a positive integer value.";
failure_response(INTERNAL, att);
return;
}
if (nvcpu < 0)
{
att.resp_msg = "VCPU must be a positive integer value.";
failure_response(INTERNAL, att);
return;
}
/* ---------------------------------------------------------------------- */
/* Compute deltas and check quotas */
/* ---------------------------------------------------------------------- */
VirtualMachine * vm = vmpool->get_ro(id);
if (vm == 0)
{
@ -2036,6 +2107,13 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList,
vm->get_template_attribute("CPU", ocpu);
vm->get_template_attribute("VCPU", ovcpu);
if (vm->is_pinned())
{
ncpu = nvcpu;
}
vm->unlock();
if (nmemory == 0)
{
nmemory = omemory;
@ -2058,50 +2136,6 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList,
deltas.add("CPU", dcpu);
deltas.add("VMS", 0);
switch (vm->get_state())
{
case VirtualMachine::POWEROFF: //Only check host capacity in POWEROFF
if (vm->hasHistory() == true)
{
hid = vm->get_hid();
}
break;
case VirtualMachine::INIT:
case VirtualMachine::PENDING:
case VirtualMachine::HOLD:
case VirtualMachine::UNDEPLOYED:
case VirtualMachine::CLONING:
case VirtualMachine::CLONING_FAILURE:
break;
case VirtualMachine::STOPPED:
case VirtualMachine::DONE:
case VirtualMachine::SUSPENDED:
case VirtualMachine::ACTIVE:
att.resp_msg="Resize action is not available for state " +
vm->state_str();
failure_response(ACTION, att);
vm->unlock();
return;
}
ret = vm->check_resize(ncpu, nmemory, nvcpu, att.resp_msg);
vm->unlock();
if (ret != 0)
{
failure_response(INTERNAL, att);
return;
}
/* ---------------------------------------------------------------------- */
/* Check quotas */
/* ---------------------------------------------------------------------- */
if (quota_resize_authorization(&deltas, att, vm_perms) == false)
{
return;
@ -2110,58 +2144,8 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList,
RequestAttributes att_rollback(vm_perms.uid, vm_perms.gid, att);
/* ---------------------------------------------------------------------- */
/* Check & update host capacity */
/* Check & update VM & host capacity */
/* ---------------------------------------------------------------------- */
if (hid != -1)
{
int dcpu_host = (int) (dcpu * 100);//now in 100%
int dmem_host = dmemory * 1024; //now in Kilobytes
vector<VectorAttribute *> empty_pci;
host = hpool->get(hid);
if (host == 0)
{
att.resp_obj = PoolObjectSQL::HOST;
att.resp_id = hid;
failure_response(NO_EXISTS, att);
quota_rollback(&deltas, Quotas::VM, att_rollback);
return;
}
if ( enforce && host->test_capacity(dcpu_host, dmem_host, 0,
empty_pci, att.resp_msg) == false)
{
ostringstream oss;
oss << object_name(PoolObjectSQL::HOST) << " " << hid
<< " does not have enough capacity.";
att.resp_msg = oss.str();
failure_response(ACTION, att);
host->unlock();
quota_rollback(&deltas, Quotas::VM, att_rollback);
return;
}
host->update_capacity(dcpu_host, dmem_host, 0);
hpool->update(host);
host->unlock();
}
/* ---------------------------------------------------------------------- */
/* Resize the VM */
/* ---------------------------------------------------------------------- */
vm = vmpool->get(id);
if (vm == 0)
@ -2170,77 +2154,48 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList,
failure_response(NO_EXISTS, att);
quota_rollback(&deltas, Quotas::VM, att_rollback);
if (hid != -1)
{
host = hpool->get(hid);
if (host != 0)
{
host->update_capacity(-dcpu, -dmemory, 0);
hpool->update(host);
host->unlock();
}
}
return;
}
//Check again state as the VM may transit to active (e.g. scheduled)
switch (vm->get_state())
{
case VirtualMachine::POWEROFF: //Only check host capacity in POWEROFF
case VirtualMachine::INIT:
case VirtualMachine::PENDING:
case VirtualMachine::HOLD:
case VirtualMachine::POWEROFF:
case VirtualMachine::UNDEPLOYED:
case VirtualMachine::CLONING:
case VirtualMachine::CLONING_FAILURE:
ret = vm->resize(ncpu, nmemory, nvcpu, att.resp_msg);
if (ret != 0)
{
vm->unlock();
failure_response(INTERNAL, att);
return;
}
vmpool->update(vm);
vmpool->update_search(vm);
rc = test_set_capacity(vm, ncpu, nmemory, nvcpu, att.resp_msg);
break;
case VirtualMachine::STOPPED:
case VirtualMachine::DONE:
case VirtualMachine::SUSPENDED:
case VirtualMachine::ACTIVE:
att.resp_msg = "Resize action is not available for state " +
vm->state_str();
failure_response(ACTION, att);
vm->unlock();
quota_rollback(&deltas, Quotas::VM, att_rollback);
if (hid != -1)
{
host = hpool->get(hid);
if (host != 0)
{
host->update_capacity(ocpu - ncpu, omemory - nmemory, 0);
hpool->update(host);
host->unlock();
}
}
return;
rc = -1;
att.resp_msg = "Cannot resize a VM in state " + vm->state_str();
break;
}
vm->unlock();
if ( rc == -1 )
{
vm->unlock();
success_response(id, att);
quota_rollback(&deltas, Quotas::VM, att_rollback);
failure_response(ACTION, att);
}
else
{
vmpool->update(vm);
vm->unlock();
success_response(id, att);
}
return;
}
/* -------------------------------------------------------------------------- */

View File

@ -24,7 +24,124 @@
#include "HostShare.h"
#include "PoolObjectAuth.h"
using namespace std;
/**
* This class represents the needed information HostShare for a Host to
* perform the scheduling
*/
class HostShareXML
{
public:
HostShareXML(){};
virtual ~HostShareXML(){};
/**
* Tests whether a new VM can be hosted by the host or not
* @param sr the share request including CPU, memory, PCI and NUMA nodes
* @return true if the share can host the VM
*/
bool test_capacity(HostShareCapacity& sr, string & error);
/**
* Adds a new VM to the given share by incrementing the cpu,mem and disk
* counters
* @param cpu needed by the VM (percentage)
* @param mem needed by the VM (in KB)
* @return 0 on success
*/
void add_capacity(HostShareCapacity& sr)
{
cpu_usage += sr.cpu;
mem_usage += sr.mem;
pci.add(sr.pci, sr.vmid);
numa.add(sr);
running_vms++;
};
/**
* Deletes a VM to the given host by updating the cpu,mem and disk
* counters
* @param cpu needed by the VM (percentage)
* @param mem needed by the VM (in KB)
* @return 0 on success
*/
void del_capacity(HostShareCapacity& sr)
{
cpu_usage -= sr.cpu;
mem_usage -= sr.mem;
running_vms--;
};
/**
* Tests whether a new VM can be hosted by the local system DS or not
* @param dsid DS id
* @param vm_disk_mb System disk needed by the VM (in MB)
* @return true if the share can host the VM
*/
bool test_ds_capacity(int dsid, long long vm_disk_mb)
{
if (ds_free_disk.count(dsid) == 0)
{
ds_free_disk[dsid] = free_disk;
}
return (vm_disk_mb < ds_free_disk[dsid]);
}
/**
* Adds a new VM to the given local sytem DS share by incrementing the disk
* counter
* @param dsid DS id
* @param vm_disk_mb System disk needed by the VM (in MB)
*/
void add_ds_capacity(int dsid, long long vm_disk_mb)
{
if (ds_free_disk.count(dsid) == 0)
{
ds_free_disk[dsid] = free_disk;
}
ds_free_disk[dsid] -= vm_disk_mb;
}
/**
* Prints the share information to an output stream.
*/
friend ostream& operator<<(ostream& o, const HostShareXML& p);
private:
friend class HostXML;
// Host computing capacity and usage
long long mem_usage;
long long cpu_usage;
long long max_mem;
long long max_cpu;
long long running_vms;
// PCI devices
HostSharePCI pci;
// System datastore
long long free_disk;
map<int, long long> ds_free_disk;
//Numa Nodes
HostShareNUMA numa;
/**
* Construct the share information from the XML information in the
* <HOST> element
*/
void init_attributes(ObjectXML * host);
};
class HostXML : public ObjectXML
{
@ -39,6 +156,9 @@ public:
init_attributes();
};
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
int get_hid() const
{
return oid;
@ -54,29 +174,26 @@ public:
return dispatched_vms.size();
}
bool is_public_cloud() const
{
return public_cloud;
}
void get_permissions(PoolObjectAuth& auth);
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/**
* Tests whether a new VM can be hosted by the host or not
* @param cpu needed by the VM (percentage)
* @param mem needed by the VM (in KB)
* @param pci devices needed by the VM
* @param sr, the host share capacity request including cpu, mem, pci
* devices and numa topology
* @param error error message
* @return true if the share can host the VM
*/
bool test_capacity(long long cpu, long long mem,
vector<VectorAttribute *> &pci, string & error);
/**
* Tests whether a new VM can be hosted by the host or not
* @param cpu needed by the VM (percentage)
* @param mem needed by the VM (in KB)
* @param pci devices needed by the VM
* @return true if the share can host the VM
*/
bool test_capacity(long long cpu,long long mem,vector<VectorAttribute *> &p)
bool test_capacity(HostShareCapacity &sr, string & error)
{
string tmp_st;
return test_capacity(cpu, mem, p, tmp_st);
};
return share.test_capacity(sr, error);
}
/**
* Adds a new VM to the given share by incrementing the cpu,mem and disk
@ -85,17 +202,10 @@ public:
* @param mem needed by the VM (in KB)
* @return 0 on success
*/
void add_capacity(int vmid, long long cpu, long long mem,
vector<VectorAttribute *> &p)
void add_capacity(HostShareCapacity &sr)
{
cpu_usage += cpu;
mem_usage += mem;
pci.add(p, vmid);
dispatched_vms.insert(vmid);
running_vms++;
share.add_capacity(sr);
dispatched_vms.insert(sr.vmid);
};
/**
@ -105,12 +215,9 @@ public:
* @param mem needed by the VM (in KB)
* @return 0 on success
*/
void del_capacity(int cpu, int mem)
void del_capacity(HostShareCapacity &sr)
{
cpu_usage -= cpu;
mem_usage -= mem;
running_vms--;
share.del_capacity(sr);
};
/**
@ -119,7 +226,10 @@ public:
* @param vm_disk_mb System disk needed by the VM (in MB)
* @return true if the share can host the VM
*/
bool test_ds_capacity(int dsid, long long vm_disk_mb);
bool test_ds_capacity(int dsid, long long vm_disk_mb)
{
return share.test_ds_capacity(dsid, vm_disk_mb);
}
/**
* Adds a new VM to the given local sytem DS share by incrementing the disk
@ -127,7 +237,10 @@ public:
* @param dsid DS id
* @param vm_disk_mb System disk needed by the VM (in MB)
*/
void add_ds_capacity(int dsid, long long vm_disk_mb);
void add_ds_capacity(int dsid, long long vm_disk_mb)
{
share.add_ds_capacity(dsid, vm_disk_mb);
}
/**
* Search the Object for a given attribute in a set of object specific
@ -152,57 +265,25 @@ public:
return __search(name, value);
}
/**
* Checks if the host is a remote public cloud
* @return true if the host is a remote public cloud
*/
bool is_public_cloud() const
{
return public_cloud;
}
/**
* Prints the Host information to an output stream. This function is used
* for logging purposes.
*/
friend ostream& operator<<(ostream& o, const HostXML& p);
/**
* Fills a auth class to perform an authZ/authN request based on the object
* attributes
* @param auths to be filled
*/
void get_permissions(PoolObjectAuth& auth);
private:
int oid;
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)*/
HostShareXML share;
long long max_mem; /**< Total memory capacity (in KB) */
long long max_cpu; /**< Total cpu capacity (in percentage)*/
bool public_cloud;
HostSharePCI pci; /**< PCI devices of the host */
// ---------------------------------------------------------------------- //
// Scheduling statistics //
// ---------------------------------------------------------------------- //
set<int> dispatched_vms;
long long free_disk; /**< Free disk capacity (in MB)*/
map<int, long long> ds_free_disk; /**< Free MB for local system DS */
long long running_vms; /**< Number of running VMs in this Host */
set<int> dispatched_vms; /**< Dispatched VMs to this host in this cycle */
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 */
/* ---------------------------------------------------------------------- */
/* Functions to search for values in the HostXML object */
/* ---------------------------------------------------------------------- */
bool is_dispatched(const std::string& vm_id) const
{
std::istringstream iss(vm_id);
@ -225,6 +306,13 @@ private:
return false;
}
/* ---------------------------------------------------------------------- */
/* Functions to search for values in the HostXML object */
/* ---------------------------------------------------------------------- */
static const char *host_paths[];
static int host_num_paths;
/**
* Search the Object for a given attribute in a set of object specific
* routes. Overrite ObjectXML function to deal with pseudo-attributes

View File

@ -135,156 +135,110 @@ public:
//--------------------------------------------------------------------------
// Get Methods for VirtualMachineXML class
//--------------------------------------------------------------------------
int get_oid() const
{
return oid;
};
int get_oid() const { return oid; };
int get_uid() const
{
return uid;
};
int get_uid() const { return uid; };
int get_gid() const
{
return gid;
};
int get_gid() const { return gid; };
int get_hid() const
{
return hid;
};
int get_hid() const { return hid; };
int get_dsid() const
{
return dsid;
};
int get_dsid() const { return dsid; };
time_t get_stime() const
{
return stime;
}
time_t get_stime() const { return stime; }
bool is_resched() const
{
return (resched == 1);
}
bool is_resched() const { return resched; }
bool is_resume() const
{
return resume;
}
bool is_resume() const { return resume; }
const string& get_rank()
{
return rank;
};
bool is_public_cloud() const { return public_cloud; }
const string& get_ds_rank()
{
return ds_rank;
};
bool is_active() const { return active; }
const string& get_nic_rank(int nic_id)
{
static std::string es;
bool is_only_public_cloud() const { return only_public_cloud; }
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
void set_only_public_cloud() { only_public_cloud = true; }
if ( it != nics.end() )
{
return it->second->get_rank();
}
//--------------------------------------------------------------------------
// Scheduling requirements and rank
//--------------------------------------------------------------------------
const string& get_requirements() { return requirements; };
return es;
};
const string& get_ds_requirements() { return ds_requirements; }
const string& get_requirements()
{
return requirements;
};
const string& get_rank() { return rank; };
const string& get_ds_requirements()
{
return ds_requirements;
}
const string& get_nic_requirements(int nic_id)
{
static std::string es;
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
if ( it != nics.end() )
{
return it->second->get_requirements();
}
return es;
}
/**
* Return VM usage requirments
*/
void get_requirements(int& cpu, int& memory, long long& disk,
vector<VectorAttribute *> &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<int,long long> get_storage_usage();
/**
* Checks if the VM can be deployed in a public cloud provider
* @return true if the VM can be deployed in a public cloud provider
*/
bool is_public_cloud() const
{
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, long int m, long long d);
const string& get_ds_rank() { return ds_rank; };
/**
* 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 + ")";
}
}
void add_requirements(const string& reqs);
//--------------------------------------------------------------------------
// Functions to schedule network interfaces (NIC)
//--------------------------------------------------------------------------
VirtualMachineNicXML * get_nic(int nic_id);
const string& get_nic_rank(int nic_id);
const string& get_nic_requirements(int nic_id);
/**
* Check if the VM is ACTIVE state
* Return ids of NICs with NETWORK_MODE=auto (i.e. need to schedule networks)
*/
bool is_active() const
set<int> get_nics_ids()
{
return state == 3;
return nics_ids_auto;
}
//--------------------------------------------------------------------------
// Capacity Interface
//--------------------------------------------------------------------------
/**
* This function fills a share capacity request based on the VM information:
* - cpu
* - memory
* - system_ds disk usage
* - PCI devices
* - NUMA node topology
*/
void get_capacity(HostShareCapacity &sr);
/**
* Add capacity to (cpu, mem, system_ds disk, numa_nodes) this VM
* @param the capacity to be added
*/
void add_capacity(HostShareCapacity &sr);
/**
* Clears the capacity allocaton of this VM and return it.
* @param sr, the sorage requirements
*/
void reset_capacity(HostShareCapacity &sr);
/**
* Tests if the Image DS have enough free space to host the VM
* @param img_datastores Image Datastores
* @param error_msg error reason
* @return true if the Image Datastores can host the VM
*/
bool test_image_datastore_capacity(
ImageDatastorePoolXML * img_dspool, string & error_msg) const;
/**
* Adds the VM disk requirements to each Image Datastore counter
* @param img_datastores Image Datastores
*/
void add_image_datastore_capacity(ImageDatastorePoolXML * img_dspool);
/**
* @return storage usage for the VM
*/
map<int,long long> get_storage_usage()
{
return ds_usage;
}
//--------------------------------------------------------------------------
@ -296,7 +250,7 @@ public:
*/
void add_match_host(int oid)
{
if (( resched == 1 && hid != oid ) || ( resched == 0 ))
if ((resched && hid != oid) || !resched )
{
match_hosts.add_resource(oid);
}
@ -317,7 +271,7 @@ public:
*/
void add_match_network(int oid, int nic_id)
{
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
auto it = nics.find(nic_id);
if ( it != nics.end() )
{
@ -348,7 +302,7 @@ public:
{
static std::vector<Resource *> ev;
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
auto it = nics.find(nic_id);
if ( it != nics.end() )
{
@ -358,23 +312,6 @@ public:
return ev;
}
/**
* Returns a VirtualMachineNicXML
*/
VirtualMachineNicXML * get_nic(int nic_id)
{
VirtualMachineNicXML * n = 0;
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
if ( it != nics.end() )
{
n = it->second;
}
return n;
}
/**
* Sort the matched hosts for the VM
*/
@ -396,7 +333,7 @@ public:
*/
void sort_match_networks(int nic_id)
{
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
auto it = nics.find(nic_id);
if ( it != nics.end() )
{
@ -425,25 +362,12 @@ public:
*/
void clear_match_networks()
{
map<int, VirtualMachineNicXML *>::iterator it;
for (it = nics.begin(); it != nics.end(); it++ )
for (auto it = nics.begin(); it != nics.end(); it++ )
{
it->second->clear_match_networks();
}
}
/**
* Marks the VM to be only deployed on public cloud hosts
*/
void set_only_public_cloud();
/**
* Returns true is the VM can only be deployed in public cloud hosts
* @return true is the VM can only be deployed in public cloud hosts
*/
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.
@ -462,28 +386,8 @@ public:
}
//--------------------------------------------------------------------------
// Capacity Interface
// Scheduled Action Interface
//--------------------------------------------------------------------------
/**
* Tests if the Image DS have enough free space to host the VM
* @param img_datastores Image Datastores
* @param error_msg error reason
* @return true if the Image Datastores can host the VM
*/
bool test_image_datastore_capacity(
ImageDatastorePoolXML * img_dspool, string & error_msg) const;
/**
* Adds the VM disk requirements to each Image Datastore counter
* @param img_datastores Image Datastores
*/
void add_image_datastore_capacity(ImageDatastorePoolXML * img_dspool);
//--------------------------------------------------------------------------
// Action Interface
//--------------------------------------------------------------------------
/**
* Get the user template of the VM
* @return the template as a XML string
@ -531,6 +435,9 @@ public:
*/
static int parse_action_name(string& action_st);
//--------------------------------------------------------------------------
// Logging
//--------------------------------------------------------------------------
/**
* Function to write a Virtual Machine in an output stream
*/
@ -549,16 +456,7 @@ public:
*/
bool clear_log();
/**
* Return ids of NICs with NETWORK_MODE=auto (i.e. need to schedule networks)
*/
set<int> get_nics_ids()
{
return nics_ids_auto;
}
protected:
/**
* For constructors
*/
@ -566,30 +464,28 @@ protected:
void init_storage_usage();
/* ------------------- SCHEDULER INFORMATION --------------------------- */
/* ---------------------- SCHEDULER INFORMATION ------------------------- */
ResourceMatch match_hosts;
ResourceMatch match_datastores;
bool only_public_cloud;
set<int> affined_vms;
/* ----------------------- VIRTUAL MACHINE ATTRIBUTES ------------------- */
int oid;
int oid;
int uid;
int gid;
int uid;
int gid;
int hid;
int dsid;
int hid;
int dsid;
int resched;
bool resume;
int state;
bool resched;
bool resume;
bool active;
bool public_cloud;
long int memory;
float cpu;
@ -597,8 +493,6 @@ protected:
map<int,long long> ds_usage;
bool public_cloud;
string rank;
string requirements;

View File

@ -35,55 +35,148 @@ const char *HostXML::host_paths[] = {
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void HostXML::init_attributes()
void HostShareXML::init_attributes(ObjectXML * host)
{
xpath(oid, "/HOST/ID", -1);
xpath(cluster_id, "/HOST/CLUSTER_ID", -1);
xpath<long long>(mem_usage, "/HOST/HOST_SHARE/MEM_USAGE", 0);
xpath<long long>(cpu_usage, "/HOST/HOST_SHARE/CPU_USAGE", 0);
xpath<long long>(max_mem, "/HOST/HOST_SHARE/MAX_MEM", 0);
xpath<long long>(max_cpu, "/HOST/HOST_SHARE/MAX_CPU", 0);
xpath<long long>(free_disk, "/HOST/HOST_SHARE/FREE_DISK", 0);
xpath<long long>(running_vms, "/HOST/HOST_SHARE/RUNNING_VMS", 0);
//------------------ HostShare Computing Capacity --------------------------
host->xpath<long long>(mem_usage, "/HOST/HOST_SHARE/MEM_USAGE", 0);
host->xpath<long long>(cpu_usage, "/HOST/HOST_SHARE/CPU_USAGE", 0);
string public_cloud_st;
host->xpath<long long>(max_mem, "/HOST/HOST_SHARE/MAX_MEM", 0);
host->xpath<long long>(max_cpu, "/HOST/HOST_SHARE/MAX_CPU", 0);
xpath(public_cloud_st, "/HOST/TEMPLATE/PUBLIC_CLOUD", "");
public_cloud = (one_util::toupper(public_cloud_st) == "YES");
host->xpath<long long>(free_disk, "/HOST/HOST_SHARE/FREE_DISK", 0);
host->xpath<long long>(running_vms, "/HOST/HOST_SHARE/RUNNING_VMS", 0);
//-------------------- HostShare Datastores ------------------------------
vector<string> ds_ids;
vector<string> ds_free;
std::vector<int> ds_ids;
std::vector<long long> ds_free;
xpaths(ds_ids, "/HOST/HOST_SHARE/DATASTORES/DS/ID");
xpaths(ds_free,"/HOST/HOST_SHARE/DATASTORES/DS/FREE_MB");
int id;
long long disk;
host->xpaths<int>(ds_ids, "/HOST/HOST_SHARE/DATASTORES/DS/ID");
host->xpaths<long long>(ds_free, "/HOST/HOST_SHARE/DATASTORES/DS/FREE_MB");
for (size_t i = 0; i < ds_ids.size() && i < ds_free.size(); i++)
{
id = atoi(ds_ids[i].c_str());
disk = atoll(ds_free[i].c_str());
ds_free_disk[id] = disk;
ds_free_disk[ds_ids[i]] = ds_free[i];
}
//-------------------- HostShare PCI Devices ------------------------------
vector<xmlNodePtr> content;
get_nodes("/HOST/HOST_SHARE/PCI_DEVICES", content);
host->get_nodes("/HOST/HOST_SHARE/PCI_DEVICES", content);
if( !content.empty())
{
pci.from_xml_node(content[0]);
free_nodes(content);
host->free_nodes(content);
content.clear();
}
//---------------------- HostShare NUMA Nodes ------------------------------
host->get_nodes("/HOST/HOST_SHARE/NUMA_NODES/NODE", content);
if(!content.empty())
{
numa.from_xml_node(content);
host->free_nodes(content);
content.clear();
}
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool HostShareXML::test_capacity(HostShareCapacity &sr, string & error)
{
bool pci_fit = pci.test(sr.pci);
bool numa_fit = numa.test(sr);
bool cpu_fit = (max_cpu - cpu_usage ) >= sr.cpu;
bool mem_fit = (max_mem - mem_usage ) >= sr.mem;
bool fits = cpu_fit && mem_fit && numa_fit && pci_fit;
if ( fits )
{
return true;
}
ostringstream oss;
if (!cpu_fit)
{
oss << "Not enough CPU capacity: " << sr.cpu << "/" << max_cpu - cpu_usage;
}
else if (!mem_fit)
{
oss << "Not enough memory: " << sr.mem << "/" << max_mem - mem_usage;
}
else if (!numa_fit)
{
oss << "Cannot allocate NUMA topology";
}
else if (!pci_fit)
{
oss << "Unavailable PCI device.";
}
error = oss.str();
return false;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
ostream& operator<<(ostream& o, const HostShareXML& s)
{
o << "MEM_USAGE : " << s.mem_usage << endl;
o << "CPU_USAGE : " << s.cpu_usage << endl;
o << "MAX_MEM : " << s.max_mem << endl;
o << "MAX_CPU : " << s.max_cpu << endl;
o << "FREE_DISK : " << s.free_disk << endl;
o << "RUNNING_VMS : " << s.running_vms << endl;
o << endl;
o << right << setw(5) << "DSID" << " " << right << setw(15) << "FREE_MB"
<< " " << endl << setw(30) << setfill('-') << "-" << setfill (' ') << endl;
for (auto it = s.ds_free_disk.begin() ; it != s.ds_free_disk.end() ; ++it)
{
o << right << setw(5) << it->first << " "
<< right << setw(15)<< it->second<< " " << endl;
}
o << endl << s.pci;
o << endl << s.numa;
return o;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void HostXML::init_attributes()
{
std::string public_cloud_st;
//--------------------- Init host attributes -------------------------------
xpath(oid, "/HOST/ID", -1);
xpath(cluster_id, "/HOST/CLUSTER_ID", -1);
xpath(public_cloud_st, "/HOST/TEMPLATE/PUBLIC_CLOUD", "");
public_cloud = (one_util::toupper(public_cloud_st) == "YES");
share.init_attributes(this);
//-------------------- Init search xpath routes ---------------------------
ObjectXML::paths = host_paths;
ObjectXML::num_paths = host_num_paths;
@ -92,107 +185,12 @@ void HostXML::init_attributes()
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool HostXML::test_capacity(long long cpu, long long mem,
vector<VectorAttribute *>& p, string & error)
{
bool pci_fits = pci.test(p);
bool fits = ((max_cpu - cpu_usage ) >= cpu) &&
((max_mem - mem_usage ) >= mem) &&
pci_fits;
if (!fits)
{
if (NebulaLog::log_level() >= Log::DDEBUG)
{
if ( pci_fits )
{
ostringstream oss;
oss << "Not enough capacity. "
<< "Requested: "
<< cpu << " CPU, "
<< mem << " KB MEM; "
<< "Available: "
<< (max_cpu - cpu_usage ) << " CPU, "
<< (max_mem - mem_usage ) << " KB MEM";
error = oss.str();
}
else
{
error = "Unavailable PCI device.";
}
}
else
{
if ( pci_fits )
{
error = "Not enough capacity.";
}
else
{
error = "Unavailable PCI device.";
}
}
}
return fits;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool HostXML::test_ds_capacity(int dsid, long long vm_disk_mb)
{
if (ds_free_disk.count(dsid) == 0)
{
ds_free_disk[dsid] = free_disk;
}
return (vm_disk_mb < ds_free_disk[dsid]);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void HostXML::add_ds_capacity(int dsid, long long vm_disk_mb)
{
if (ds_free_disk.count(dsid) == 0)
{
ds_free_disk[dsid] = free_disk;
}
ds_free_disk[dsid] -= vm_disk_mb;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
ostream& operator<<(ostream& o, const HostXML& p)
{
map<int, long long>::const_iterator it;
o << "ID : " << p.oid << endl;
o << "CLUSTER_ID : " << p.cluster_id << endl;
o << "MEM_USAGE : " << p.mem_usage << endl;
o << "CPU_USAGE : " << p.cpu_usage << endl;
o << "MAX_MEM : " << p.max_mem << endl;
o << "MAX_CPU : " << p.max_cpu << endl;
o << "FREE_DISK : " << p.free_disk << endl;
o << "RUNNING_VMS : " << p.running_vms << endl;
o << "ID : " << p.oid << endl;
o << "CLUSTER_ID : " << p.cluster_id << endl;
o << "PUBLIC : " << p.public_cloud << endl;
o << endl
<< right << setw(5) << "DSID" << " "
<< right << setw(15) << "FREE_MB" << " "
<< endl << setw(30) << setfill('-') << "-" << setfill (' ') << endl;
for (it = p.ds_free_disk.begin() ; it != p.ds_free_disk.end() ; it++)
{
o << right << setw(5) << it->first << " "
<< right << setw(15)<< it->second<< " " << endl;
}
o << endl << p.pci;
o << p.share;
return o;
}
@ -210,3 +208,4 @@ void HostXML::get_permissions(PoolObjectAuth& auth)
auth.cids = cids;
auth.obj_type = PoolObjectSQL::HOST;
}

View File

@ -330,10 +330,7 @@ static void schecule_affined_set(const std::set<int>& vms,
for ( ++it ; it != vms.end() ; ++it )
{
float cpu;
int memory;
long long disk;
HostShareCapacity sr;
VirtualMachineXML * tmp = vmpool->get(*it);
@ -342,9 +339,9 @@ static void schecule_affined_set(const std::set<int>& vms,
continue;
}
tmp->reset_requirements(cpu, memory, disk);
tmp->reset_capacity(sr);
vm->add_requirements(cpu, memory, disk);
vm->add_capacity(sr);
vm->add_requirements(tmp->get_requirements());
vm->add_affined(*it);

View File

@ -56,9 +56,7 @@ int VirtualMachinePoolXML::set_up()
for (it = objects.begin() ; it != objects.end() ; ++it)
{
int cpu, mem;
long long disk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
string action = "DEPLOY";
@ -66,7 +64,7 @@ int VirtualMachinePoolXML::set_up()
vm = static_cast<VirtualMachineXML *>(it->second);
vm->get_requirements(cpu, mem, disk, pci);
vm->get_capacity(sr);
if (vm->is_resched())
{
@ -79,10 +77,10 @@ int VirtualMachinePoolXML::set_up()
oss << right << setw(8) << action << " "
<< right << setw(8) << it->first << " "
<< right << setw(4) << cpu << " "
<< right << setw(11) << mem << " "
<< right << setw(3) << pci.size() << " "
<< right << setw(11) << disk << " ";
<< right << setw(4) << sr.cpu << " "
<< right << setw(11) << sr.mem << " "
<< right << setw(3) << sr.pci.size() << " "
<< right << setw(11) << sr.disk << " ";
map<int,long long> ds_usage = vm->get_storage_usage();
map<int,long long>::const_iterator ds_it;
@ -120,7 +118,7 @@ void VirtualMachinePoolXML::add_object(xmlNodePtr node)
VirtualMachineXML* vm = new VirtualMachineXML(node);
objects.insert(pair<int,ObjectXML*>(vm->get_oid(),vm));
objects.insert(pair<int,ObjectXML*>(vm->get_oid(), vm));
}
/* -------------------------------------------------------------------------- */
@ -130,7 +128,7 @@ int VirtualMachinePoolXML::load_info(xmlrpc_c::value &result)
{
try
{
client->call("one.vmpool.info", "iiii", &result, -2, -1, -1, -1);
client->call("one.vmpool.infoextended", "iiii", &result, -2, -1, -1, -1);
return 0;
}

View File

@ -24,28 +24,59 @@
#include "History.h"
#include "RankScheduler.h"
/******************************************************************************/
/******************************************************************************/
/* INITIALIZE VM object attributes from its XML representation */
/******************************************************************************/
/******************************************************************************/
void VirtualMachineXML::init_attributes()
{
vector<xmlNodePtr> nodes;
std::vector<xmlNodePtr> nodes;
std::vector<VectorAttribute*> attrs;
int rc;
int action;
int tmp;
string automatic_requirements;
string automatic_ds_requirements;
string automatic_nic_requirements;
std::string automatic_requirements;
std::string automatic_ds_requirements;
std::string automatic_nic_requirements;
/**************************************************************************/
/* VM attributes and flags */
/**************************************************************************/
xpath(oid, "/VM/ID", -1);
xpath(uid, "/VM/UID", -1);
xpath(gid, "/VM/GID", -1);
xpath(state, "/VM/STATE", -1);
xpath(tmp, "/VM/STATE", -1);
active = tmp == 3;
xpath(tmp, "/VM/RESCHED", 0);
resched = tmp == 1;
xpath(action, "/VM/HISTORY_RECORDS/HISTORY/ACTION", -1);
resume = (action == History::STOP_ACTION || action == History::UNDEPLOY_ACTION
|| action == History::UNDEPLOY_HARD_ACTION );
xpath(hid, "/VM/HISTORY_RECORDS/HISTORY/HID", -1);
xpath(dsid, "/VM/HISTORY_RECORDS/HISTORY/DS_ID", -1);
xpath(stime, "/VM/STIME", (time_t) 0);
/**************************************************************************/
/* VM Capacity memory, cpu and disk (system ds storage) */
/**************************************************************************/
xpath<long int>(memory, "/VM/TEMPLATE/MEMORY", 0);
xpath<float>(cpu, "/VM/TEMPLATE/CPU", 0);
// ------------------------ RANK & DS_RANK ---------------------------------
/**************************************************************************/
/* Scheduling rank expresions for: */
/* - host */
/* - datastore */
/**************************************************************************/
rc = xpath(rank, "/VM/USER_TEMPLATE/SCHED_RANK", "");
if (rc != 0)
@ -56,8 +87,15 @@ void VirtualMachineXML::init_attributes()
xpath(ds_rank, "/VM/USER_TEMPLATE/SCHED_DS_RANK", "");
// ------------------- HOST REQUIREMENTS -----------------------------------
/**************************************************************************/
/* Scheduling requirements for: */
/* - host */
/* - datastore */
/* - network */
/**************************************************************************/
// ---------------------------------------------------------------------- //
// Host requirements //
// ---------------------------------------------------------------------- //
xpath(automatic_requirements, "/VM/TEMPLATE/AUTOMATIC_REQUIREMENTS", "");
rc = xpath(requirements, "/VM/USER_TEMPLATE/SCHED_REQUIREMENTS", "");
@ -78,9 +116,11 @@ void VirtualMachineXML::init_attributes()
requirements = automatic_requirements;
}
// ------------------- DS REQUIREMENTS -------------------------------------
xpath(automatic_ds_requirements, "/VM/TEMPLATE/AUTOMATIC_DS_REQUIREMENTS", "");
// ---------------------------------------------------------------------- //
// Datastore requirements //
// ---------------------------------------------------------------------- //
xpath(automatic_ds_requirements, "/VM/TEMPLATE/AUTOMATIC_DS_REQUIREMENTS",
"");
rc = xpath(ds_requirements, "/VM/USER_TEMPLATE/SCHED_DS_REQUIREMENTS", "");
@ -100,17 +140,17 @@ void VirtualMachineXML::init_attributes()
ds_requirements = automatic_ds_requirements;
}
// ------------------- NIC REQUIREMENTS -------------------------------------
xpath(automatic_nic_requirements, "/VM/TEMPLATE/AUTOMATIC_NIC_REQUIREMENTS", "");
// ---------------------------------------------------------------------- //
// Network requirements & rank //
// ---------------------------------------------------------------------- //
xpath(automatic_nic_requirements, "/VM/TEMPLATE/AUTOMATIC_NIC_REQUIREMENTS",
"");
if (get_nodes("/VM/TEMPLATE/NIC", nodes) > 0)
{
std::string net_mode;
vector<xmlNodePtr>::iterator it_nodes;
for (it_nodes = nodes.begin(); it_nodes != nodes.end(); ++it_nodes)
for (auto it_nodes = nodes.begin(); it_nodes != nodes.end(); ++it_nodes)
{
VirtualMachineTemplate * nic_template = new VirtualMachineTemplate;
@ -119,37 +159,39 @@ void VirtualMachineXML::init_attributes()
bool rc = nic_template->get("NETWORK_MODE", net_mode);
one_util::toupper(net_mode);
if ( rc && net_mode == "AUTO" )
if ( !rc || net_mode != "AUTO" )
{
std::string requirements, rank;
int nic_id;
continue;
}
nic_template->get("NIC_ID", nic_id);
std::string reqs, rank;
int nic_id;
nics_ids_auto.insert(nic_id);
nic_template->get("NIC_ID", nic_id);
VirtualMachineNicXML * the_nic = new VirtualMachineNicXML();
nics_ids_auto.insert(nic_id);
nics.insert(make_pair(nic_id, the_nic));
VirtualMachineNicXML * the_nic = new VirtualMachineNicXML();
if ( nic_template->get("SCHED_REQUIREMENTS", requirements) )
nics.insert(make_pair(nic_id, the_nic));
if ( nic_template->get("SCHED_REQUIREMENTS", reqs) )
{
if ( !automatic_nic_requirements.empty() )
{
if ( !automatic_nic_requirements.empty() )
{
ostringstream oss;
ostringstream oss;
oss << automatic_nic_requirements << " & ( " << requirements << " )";
oss << automatic_nic_requirements <<" & ( " << reqs << " )";
requirements = oss.str();
}
the_nic->set_requirements(requirements);
reqs = oss.str();
}
if ( nic_template->get("SCHED_RANK", rank) )
{
the_nic->set_rank(rank);
}
the_nic->set_requirements(reqs);
}
if ( nic_template->get("SCHED_RANK", rank) )
{
the_nic->set_rank(rank);
}
delete nic_template;
@ -160,21 +202,9 @@ void VirtualMachineXML::init_attributes()
nodes.clear();
// ---------------- HISTORY HID, DSID, RESCHED & TEMPLATE ------------------
xpath(hid, "/VM/HISTORY_RECORDS/HISTORY/HID", -1);
xpath(dsid, "/VM/HISTORY_RECORDS/HISTORY/DS_ID", -1);
xpath(resched, "/VM/RESCHED", 0);
xpath(action, "/VM/HISTORY_RECORDS/HISTORY/ACTION", -1);
xpath(stime, "/VM/STIME", (time_t) 0);
resume = (action == History::STOP_ACTION ||
action == History::UNDEPLOY_ACTION ||
action == History::UNDEPLOY_HARD_ACTION );
/**************************************************************************/
/* Template, user template, history information and rescheduling flag */
/**************************************************************************/
if (get_nodes("/VM/TEMPLATE", nodes) > 0)
{
vm_template = new VirtualMachineTemplate;
@ -203,17 +233,6 @@ void VirtualMachineXML::init_attributes()
user_template = 0;
}
if (vm_template != 0)
{
init_storage_usage();
}
else
{
system_ds_usage = 0;
}
vector<VectorAttribute*> attrs;
public_cloud = (user_template->get("PUBLIC_CLOUD", attrs) > 0);
if (public_cloud == false)
@ -223,124 +242,22 @@ void VirtualMachineXML::init_attributes()
}
only_public_cloud = false;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
ostream& operator<<(ostream& os, VirtualMachineXML& vm)
{
const vector<Resource *> resources = vm.match_hosts.get_resources();
vector<Resource *>::const_reverse_iterator i;
if (resources.empty())
{
return os;
}
os << "Virtual Machine: " << vm.oid << endl << endl;
os << "\tPRI\tID - HOSTS"<< endl
<< "\t------------------------" << endl;
for (i = resources.rbegin(); i != resources.rend() ; i++)
{
os << "\t" << (*i)->priority << "\t" << (*i)->oid << endl;
}
os << endl;
os << "\tPRI\tID - DATASTORES"<< endl
<< "\t------------------------" << endl;
const vector<Resource *> ds_resources = vm.match_datastores.get_resources();
for (i = ds_resources.rbegin(); i != ds_resources.rend() ; i++)
{
os << "\t" << (*i)->priority << "\t" << (*i)->oid << endl;
}
os << endl;
set<int> nics_ids = vm.get_nics_ids();
for (set<int>::iterator it = nics_ids.begin(); it != nics_ids.end(); it++)
{
os << "\tNIC_ID: "<< *it << endl
<< "\t-----------------------------------" << endl;
os << "\tPRI\tID - NETWORKS"<< endl
<< "\t------------------------" << endl;
const vector<Resource *> net_resources = vm.nics[*it]->get_match_networks();
for (i = net_resources.rbegin(); i != net_resources.rend() ; i++)
{
os << "\t" << (*i)->priority << "\t" << (*i)->oid << endl;
}
os << endl;
}
return os;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::add_requirements(float c, long 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<VectorAttribute *> &pci)
{
pci.clear();
if (vm_template != 0)
{
vm_template->get("PCI", pci);
init_storage_usage();
}
if (this->memory == 0 || this->cpu == 0)
else
{
cpu = 0;
memory = 0;
disk = 0;
return;
system_ds_usage = 0;
}
cpu = (int) (this->cpu * 100);//now in 100%
memory = this->memory * 1024; //now in Kilobytes
disk = this->system_ds_usage; // MB
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
// TODO: use VirtualMachine::isVolatile(disk)
bool isVolatile(const VectorAttribute * disk)
static bool isVolatile(const VectorAttribute * disk)
{
string type = disk->vector_value("TYPE");
@ -349,10 +266,7 @@ bool isVolatile(const VectorAttribute * disk)
return ( type == "SWAP" || type == "FS");
}
map<int,long long> VirtualMachineXML::get_storage_usage()
{
return ds_usage;
}
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::init_storage_usage()
{
@ -444,6 +358,243 @@ void VirtualMachineXML::init_storage_usage()
}
}
/******************************************************************************/
/******************************************************************************/
/* VM requirements and capacity interface */
/******************************************************************************/
/******************************************************************************/
void VirtualMachineXML::add_requirements(const string& reqs)
{
if ( reqs.empty() )
{
return;
}
else if ( requirements.empty() )
{
requirements = reqs;
}
else
{
requirements += " & (" + reqs + ")";
}
}
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::get_capacity(HostShareCapacity &sr)
{
sr.vmid = oid;
sr.pci.clear();
if (vm_template != 0)
{
vm_template->get("PCI", sr.pci);
vm_template->get("NUMA_NODE", sr.nodes);
sr.topology = vm_template->get("TOPOLOGY");
}
if ( memory == 0 || cpu == 0 )
{
sr.cpu = 0;
sr.mem = 0;
sr.disk = 0;
return;
}
sr.cpu = (int) (cpu * 100); //100%
sr.mem = memory * 1024; //Kilobytes
sr.disk = system_ds_usage; //MB
}
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::add_capacity(HostShareCapacity &sr)
{
cpu += sr.cpu;
memory += sr.mem;
system_ds_usage += sr.disk;
vm_template->set(sr.nodes);
}
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::reset_capacity(HostShareCapacity &sr)
{
std::vector<VectorAttribute *> numa_nodes;
sr.cpu = cpu;
sr.mem = memory;
sr.disk = system_ds_usage;
if ( vm_template != 0 )
{
vm_template->remove("NUMA_NODE", sr.nodes);
}
cpu = 0;
memory = 0;
system_ds_usage = 0;
}
/* -------------------------------------------------------------------------- */
bool VirtualMachineXML::test_image_datastore_capacity(
ImageDatastorePoolXML * img_dspool, string & error_msg) const
{
for (auto ds_it = ds_usage.begin(); ds_it != ds_usage.end(); ++ds_it)
{
DatastoreXML* ds = img_dspool->get(ds_it->first);
if (ds == 0 || !ds->test_capacity(ds_it->second))
{
ostringstream oss;
oss << "Image Datastore " << ds->get_oid()
<< " does not have enough capacity";
error_msg = oss.str();
return false;
}
}
return true;
}
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::add_image_datastore_capacity(
ImageDatastorePoolXML * img_dspool)
{
for (auto ds_it = ds_usage.begin(); ds_it != ds_usage.end(); ++ds_it)
{
DatastoreXML *ds = img_dspool->get(ds_it->first);
if (ds == 0)
{
continue;
}
ds->add_capacity(ds_it->second);
}
}
//******************************************************************************
// Functions to schedule network interfaces (NIC)
//******************************************************************************
VirtualMachineNicXML * VirtualMachineXML::get_nic(int nic_id)
{
VirtualMachineNicXML * n = 0;
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
if ( it != nics.end() )
{
n = it->second;
}
return n;
}
/* -------------------------------------------------------------------------- */
const string& VirtualMachineXML::get_nic_rank(int nic_id)
{
static std::string es;
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
if ( it != nics.end() )
{
return it->second->get_rank();
}
return es;
};
/* -------------------------------------------------------------------------- */
const string& VirtualMachineXML::get_nic_requirements(int nic_id)
{
static std::string es;
std::map<int, VirtualMachineNicXML *>::iterator it = nics.find(nic_id);
if ( it != nics.end() )
{
return it->second->get_requirements();
}
return es;
}
//******************************************************************************
// Logging
//******************************************************************************
ostream& operator<<(ostream& os, VirtualMachineXML& vm)
{
const vector<Resource *> resources = vm.match_hosts.get_resources();
vector<Resource *>::const_reverse_iterator i;
if (resources.empty())
{
return os;
}
os << "Virtual Machine: " << vm.oid << endl << endl;
os << "\tPRI\tID - HOSTS"<< endl
<< "\t------------------------" << endl;
for (i = resources.rbegin(); i != resources.rend() ; i++)
{
os << "\t" << (*i)->priority << "\t" << (*i)->oid << endl;
}
os << endl;
os << "\tPRI\tID - DATASTORES"<< endl
<< "\t------------------------" << endl;
const vector<Resource *> ds_resources = vm.match_datastores.get_resources();
for (i = ds_resources.rbegin(); i != ds_resources.rend() ; i++)
{
os << "\t" << (*i)->priority << "\t" << (*i)->oid << endl;
}
os << endl;
set<int> nics_ids = vm.get_nics_ids();
for (set<int>::iterator it = nics_ids.begin(); it != nics_ids.end(); it++)
{
os << "\tNIC_ID: "<< *it << endl
<< "\t-----------------------------------" << endl;
os << "\tPRI\tID - NETWORKS"<< endl
<< "\t------------------------" << endl;
const vector<Resource *> net_resources = vm.nics[*it]->get_match_networks();
for (i = net_resources.rbegin(); i != net_resources.rend() ; i++)
{
os << "\t" << (*i)->priority << "\t" << (*i)->oid << endl;
}
os << endl;
}
return os;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
@ -517,79 +668,3 @@ int VirtualMachineXML::parse_action_name(string& action_st)
return 0;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool VirtualMachineXML::test_image_datastore_capacity(
ImageDatastorePoolXML * img_dspool, string & error_msg) const
{
map<int,long long>::const_iterator ds_it;
DatastoreXML* ds;
for (ds_it = ds_usage.begin(); ds_it != ds_usage.end(); ++ds_it)
{
ds = img_dspool->get(ds_it->first);
if (ds == 0 || !ds->test_capacity(ds_it->second))
{
ostringstream oss;
oss << "Image Datastore " << ds->get_oid()
<< " does not have enough capacity";
error_msg = oss.str();
return false;
}
}
return true;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::add_image_datastore_capacity(
ImageDatastorePoolXML * img_dspool)
{
map<int,long long>::const_iterator ds_it;
DatastoreXML *ds;
for (ds_it = ds_usage.begin(); ds_it != ds_usage.end(); ++ds_it)
{
ds = img_dspool->get(ds_it->first);
if (ds == 0) //Should never reach here
{
continue;
}
ds->add_capacity(ds_it->second);
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachineXML::set_only_public_cloud()
{
only_public_cloud = true;
ostringstream oss;
oss << "VM " << oid << ": Local Datastores do not have enough capacity. "
<< "This VM can be only deployed in a Public Cloud Host.";
NebulaLog::log("SCHED",Log::INFO,oss);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool VirtualMachineXML::is_only_public_cloud() const
{
return only_public_cloud;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -510,9 +510,7 @@ int Scheduler::set_up_pools()
* @param acl pool
* @param users the user pool
* @param vm the virtual machine
* @param vm_memory vm requirement
* @param vm_cpu vm requirement
* @param vm_pci vm requirement
* @param sr share capacity request
* @param host to evaluate vm assgiment
* @param n_auth number of hosts authorized for the user, incremented if needed
* @param n_error number of requirement errors, incremented if needed
@ -522,8 +520,8 @@ int Scheduler::set_up_pools()
* @return true for a positive match
*/
static bool match_host(AclXML * acls, UserPoolXML * upool, VirtualMachineXML* vm,
int vmem, int vcpu, vector<VectorAttribute *>& vpci, HostXML * host,
int &n_auth, int& n_error, int &n_fits, int &n_matched, string &error)
HostShareCapacity &sr, HostXML * host, int &n_auth, int& n_error, int &n_fits,
int &n_matched, string &error)
{
// -------------------------------------------------------------------------
// Filter current Hosts for resched VMs
@ -576,7 +574,7 @@ static bool match_host(AclXML * acls, UserPoolXML * upool, VirtualMachineXML* vm
// -------------------------------------------------------------------------
// Check host capacity
// -------------------------------------------------------------------------
if (host->test_capacity(vcpu, vmem, vpci, error) != true)
if (host->test_capacity(sr, error) != true)
{
return false;
}
@ -846,10 +844,7 @@ void Scheduler::match_schedule()
{
VirtualMachineXML * vm;
int vm_memory;
int vm_cpu;
long long vm_disk;
vector<VectorAttribute *> vm_pci;
HostShareCapacity sr;
int n_resources;
int n_matched;
@ -888,7 +883,7 @@ void Scheduler::match_schedule()
{
vm = static_cast<VirtualMachineXML*>(vm_it->second);
vm->get_requirements(vm_cpu, vm_memory, vm_disk, vm_pci);
vm->get_capacity(sr);
n_resources = 0;
n_fits = 0;
@ -928,8 +923,8 @@ void Scheduler::match_schedule()
{
host = static_cast<HostXML *>(obj_it->second);
if (match_host(acls, upool, vm, vm_memory, vm_cpu, vm_pci, host,
n_auth, n_error, n_fits, n_matched, m_error))
if (match_host(acls, upool, vm, sr, host, n_auth, n_error, n_fits,
n_matched, m_error))
{
vm->add_match_host(host->get_hid());
@ -1035,7 +1030,7 @@ void Scheduler::match_schedule()
{
ds = static_cast<DatastoreXML *>(obj_it->second);
if (match_system_ds(acls, upool, vm, vm_disk, ds, n_auth, n_error,
if (match_system_ds(acls, upool, vm, sr.disk, ds, n_auth, n_error,
n_fits, n_matched, m_error))
{
vm->add_match_datastore(ds->get_oid());
@ -1301,9 +1296,7 @@ void Scheduler::dispatch()
ostringstream dss;
string error;
int cpu, mem;
long long dsk;
vector<VectorAttribute *> pci;
HostShareCapacity sr;
int hid, dsid, cid, netid;
@ -1369,7 +1362,7 @@ void Scheduler::dispatch()
}
}
vm->get_requirements(cpu, mem, dsk, pci);
vm->get_capacity(sr);
//----------------------------------------------------------------------
// Get the highest ranked host and best System DS for it
@ -1415,7 +1408,7 @@ void Scheduler::dispatch()
//------------------------------------------------------------------
// Test host capacity
//------------------------------------------------------------------
if (host->test_capacity(cpu, mem, pci) != true)
if (host->test_capacity(sr, error) != true)
{
continue;
}
@ -1490,12 +1483,12 @@ void Scheduler::dispatch()
}
else
{
ds_capacity = ds->test_capacity(dsk);
ds_capacity = ds->test_capacity(sr.disk);
}
}
else
{
ds_capacity = host->test_ds_capacity(ds->get_oid(), dsk);
ds_capacity = host->test_ds_capacity(ds->get_oid(), sr.disk);
}
if (!ds_capacity)
@ -1642,12 +1635,12 @@ void Scheduler::dispatch()
{
if (!vm->is_resched() && !vm->is_resume())
{
ds->add_capacity(dsk);
ds->add_capacity(sr.disk);
}
}
else
{
host->add_ds_capacity(ds->get_oid(), dsk);
host->add_ds_capacity(ds->get_oid(), sr.disk);
}
// ---------- Add image DS usage (i.e. clone = self) ----------
@ -1683,7 +1676,7 @@ void Scheduler::dispatch()
//------------------------------------------------------------------
// Update usage and statistics counters
//------------------------------------------------------------------
host->add_capacity(vm->get_oid(), cpu, mem, pci);
host->add_capacity(sr);
dispatched_vms++;

View File

@ -103,25 +103,25 @@ int VirtualMachine::vm_state_from_str(string& st, VmState& state)
if ( st == "INIT" ) {
state = INIT;
} else if ( st == "PENDING" ) {
state = PENDING;
state = PENDING;
} else if ( st == "HOLD" ) {
state = HOLD;
state = HOLD;
} else if ( st == "ACTIVE" ) {
state = ACTIVE;
state = ACTIVE;
} else if ( st == "STOPPED" ) {
state = STOPPED;
state = STOPPED;
} else if ( st == "SUSPENDED" ) {
state = SUSPENDED;
state = SUSPENDED;
} else if ( st == "DONE" ) {
state = DONE;
state = DONE;
} else if ( st == "POWEROFF" ) {
state = POWEROFF;
state = POWEROFF;
} else if ( st == "UNDEPLOYED" ) {
state = UNDEPLOYED;
state = UNDEPLOYED;
} else if ( st == "CLONING" ) {
state = CLONING;
state = CLONING;
} else if ( st == "CLONING_FAILURE" ) {
state = CLONING_FAILURE;
state = CLONING_FAILURE;
} else {
return -1;
}
@ -136,27 +136,27 @@ string& VirtualMachine::vm_state_to_str(string& st, VmState state)
switch (state)
{
case INIT:
st = "INIT"; break;
st = "INIT"; break;
case PENDING:
st = "PENDING"; break;
st = "PENDING"; break;
case HOLD:
st = "HOLD"; break;
st = "HOLD"; break;
case ACTIVE:
st = "ACTIVE"; break;
st = "ACTIVE"; break;
case STOPPED:
st = "STOPPED"; break;
st = "STOPPED"; break;
case SUSPENDED:
st = "SUSPENDED"; break;
st = "SUSPENDED"; break;
case DONE:
st = "DONE"; break;
st = "DONE"; break;
case POWEROFF:
st = "POWEROFF"; break;
st = "POWEROFF"; break;
case UNDEPLOYED:
st = "UNDEPLOYED"; break;
st = "UNDEPLOYED"; break;
case CLONING:
st = "CLONING"; break;
st = "CLONING"; break;
case CLONING_FAILURE:
st = "CLONING_FAILURE"; break;
st = "CLONING_FAILURE"; break;
}
return st;
@ -180,119 +180,119 @@ int VirtualMachine::lcm_state_from_str(string& st, LcmState& state)
} else if ( st == "MIGRATE") {
state = MIGRATE;
} else if ( st == "SAVE_STOP") {
state = SAVE_STOP;
state = SAVE_STOP;
} else if ( st == "SAVE_SUSPEND") {
state = SAVE_SUSPEND;
state = SAVE_SUSPEND;
} else if ( st == "SAVE_MIGRATE") {
state = SAVE_MIGRATE;
state = SAVE_MIGRATE;
} else if ( st == "PROLOG_MIGRATE") {
state = PROLOG_MIGRATE;
state = PROLOG_MIGRATE;
} else if ( st == "PROLOG_RESUME") {
state = PROLOG_RESUME;
state = PROLOG_RESUME;
} else if ( st == "EPILOG_STOP") {
state = EPILOG_STOP;
state = EPILOG_STOP;
} else if ( st == "EPILOG") {
state = EPILOG;
state = EPILOG;
} else if ( st == "SHUTDOWN") {
state = SHUTDOWN;
state = SHUTDOWN;
} else if ( st == "CLEANUP_RESUBMIT") {
state = CLEANUP_RESUBMIT;
state = CLEANUP_RESUBMIT;
} else if ( st == "UNKNOWN") {
state = UNKNOWN;
state = UNKNOWN;
} else if ( st == "HOTPLUG") {
state = HOTPLUG;
state = HOTPLUG;
} else if ( st == "SHUTDOWN_POWEROFF") {
state = SHUTDOWN_POWEROFF;
state = SHUTDOWN_POWEROFF;
} else if ( st == "BOOT_UNKNOWN") {
state = BOOT_UNKNOWN;
state = BOOT_UNKNOWN;
} else if ( st == "BOOT_POWEROFF") {
state = BOOT_POWEROFF;
state = BOOT_POWEROFF;
} else if ( st == "BOOT_SUSPENDED") {
state = BOOT_SUSPENDED;
state = BOOT_SUSPENDED;
} else if ( st == "BOOT_STOPPED") {
state = BOOT_STOPPED;
state = BOOT_STOPPED;
} else if ( st == "CLEANUP_DELETE") {
state = CLEANUP_DELETE;
state = CLEANUP_DELETE;
} else if ( st == "HOTPLUG_SNAPSHOT") {
state = HOTPLUG_SNAPSHOT;
state = HOTPLUG_SNAPSHOT;
} else if ( st == "HOTPLUG_NIC") {
state = HOTPLUG_NIC;
state = HOTPLUG_NIC;
} else if ( st == "HOTPLUG_SAVEAS") {
state = HOTPLUG_SAVEAS;
state = HOTPLUG_SAVEAS;
} else if ( st == "HOTPLUG_SAVEAS_POWEROFF") {
state = HOTPLUG_SAVEAS_POWEROFF;
state = HOTPLUG_SAVEAS_POWEROFF;
} else if ( st == "HOTPLUG_SAVEAS_SUSPENDED") {
state = HOTPLUG_SAVEAS_SUSPENDED;
state = HOTPLUG_SAVEAS_SUSPENDED;
} else if ( st == "SHUTDOWN_UNDEPLOY") {
state = SHUTDOWN_UNDEPLOY;
state = SHUTDOWN_UNDEPLOY;
} else if ( st == "EPILOG_UNDEPLOY") {
state = EPILOG_UNDEPLOY;
state = EPILOG_UNDEPLOY;
} else if ( st == "PROLOG_UNDEPLOY") {
state = PROLOG_UNDEPLOY;
state = PROLOG_UNDEPLOY;
} else if ( st == "BOOT_UNDEPLOY") {
state = BOOT_UNDEPLOY;
state = BOOT_UNDEPLOY;
} else if ( st == "HOTPLUG_PROLOG_POWEROFF") {
state = HOTPLUG_PROLOG_POWEROFF;
state = HOTPLUG_PROLOG_POWEROFF;
} else if ( st == "HOTPLUG_EPILOG_POWEROFF") {
state = HOTPLUG_EPILOG_POWEROFF;
state = HOTPLUG_EPILOG_POWEROFF;
} else if ( st == "BOOT_MIGRATE") {
state = BOOT_MIGRATE;
state = BOOT_MIGRATE;
} else if ( st == "BOOT_FAILURE") {
state = BOOT_FAILURE;
state = BOOT_FAILURE;
} else if ( st == "BOOT_MIGRATE_FAILURE") {
state = BOOT_MIGRATE_FAILURE;
state = BOOT_MIGRATE_FAILURE;
} else if ( st == "PROLOG_MIGRATE_FAILURE") {
state = PROLOG_MIGRATE_FAILURE;
state = PROLOG_MIGRATE_FAILURE;
} else if ( st == "PROLOG_FAILURE") {
state = PROLOG_FAILURE;
state = PROLOG_FAILURE;
} else if ( st == "EPILOG_FAILURE") {
state = EPILOG_FAILURE;
state = EPILOG_FAILURE;
} else if ( st == "EPILOG_STOP_FAILURE") {
state = EPILOG_STOP_FAILURE;
state = EPILOG_STOP_FAILURE;
} else if ( st == "EPILOG_UNDEPLOY_FAILURE") {
state = EPILOG_UNDEPLOY_FAILURE;
state = EPILOG_UNDEPLOY_FAILURE;
} else if ( st == "PROLOG_MIGRATE_POWEROFF") {
state = PROLOG_MIGRATE_POWEROFF;
state = PROLOG_MIGRATE_POWEROFF;
} else if ( st == "PROLOG_MIGRATE_POWEROFF_FAILURE") {
state = PROLOG_MIGRATE_POWEROFF_FAILURE;
state = PROLOG_MIGRATE_POWEROFF_FAILURE;
} else if ( st == "PROLOG_MIGRATE_SUSPEND") {
state = PROLOG_MIGRATE_SUSPEND;
state = PROLOG_MIGRATE_SUSPEND;
} else if ( st == "PROLOG_MIGRATE_SUSPEND_FAILURE") {
state = PROLOG_MIGRATE_SUSPEND_FAILURE;
state = PROLOG_MIGRATE_SUSPEND_FAILURE;
} else if ( st == "BOOT_STOPPED_FAILURE") {
state = BOOT_STOPPED_FAILURE;
state = BOOT_STOPPED_FAILURE;
} else if ( st == "BOOT_UNDEPLOY_FAILURE") {
state = BOOT_UNDEPLOY_FAILURE;
state = BOOT_UNDEPLOY_FAILURE;
} else if ( st == "PROLOG_RESUME_FAILURE") {
state = PROLOG_RESUME_FAILURE;
state = PROLOG_RESUME_FAILURE;
} else if ( st == "PROLOG_UNDEPLOY_FAILURE") {
state = PROLOG_UNDEPLOY_FAILURE;
state = PROLOG_UNDEPLOY_FAILURE;
} else if ( st == "DISK_SNAPSHOT_POWEROFF") {
state = DISK_SNAPSHOT_POWEROFF;
state = DISK_SNAPSHOT_POWEROFF;
} else if ( st == "DISK_SNAPSHOT_REVERT_POWEROFF") {
state = DISK_SNAPSHOT_REVERT_POWEROFF;
state = DISK_SNAPSHOT_REVERT_POWEROFF;
} else if ( st == "DISK_SNAPSHOT_DELETE_POWEROFF") {
state = DISK_SNAPSHOT_DELETE_POWEROFF;
state = DISK_SNAPSHOT_DELETE_POWEROFF;
} else if ( st == "DISK_SNAPSHOT_SUSPENDED") {
state = DISK_SNAPSHOT_SUSPENDED;
state = DISK_SNAPSHOT_SUSPENDED;
} else if ( st == "DISK_SNAPSHOT_REVERT_SUSPENDED") {
state = DISK_SNAPSHOT_REVERT_SUSPENDED;
state = DISK_SNAPSHOT_REVERT_SUSPENDED;
} else if ( st == "DISK_SNAPSHOT_DELETE_SUSPENDED") {
state = DISK_SNAPSHOT_DELETE_SUSPENDED;
state = DISK_SNAPSHOT_DELETE_SUSPENDED;
} else if ( st == "DISK_SNAPSHOT") {
state = DISK_SNAPSHOT;
state = DISK_SNAPSHOT;
} else if ( st == "DISK_SNAPSHOT_DELETE") {
state = DISK_SNAPSHOT_DELETE;
state = DISK_SNAPSHOT_DELETE;
} else if ( st == "PROLOG_MIGRATE_UNKNOWN") {
state = PROLOG_MIGRATE_UNKNOWN;
state = PROLOG_MIGRATE_UNKNOWN;
} else if ( st == "PROLOG_MIGRATE_UNKNOWN_FAILURE") {
state = PROLOG_MIGRATE_UNKNOWN_FAILURE;
state = PROLOG_MIGRATE_UNKNOWN_FAILURE;
} else if ( st == "DISK_RESIZE") {
state = DISK_RESIZE;
state = DISK_RESIZE;
} else if ( st == "DISK_RESIZE_POWEROFF") {
state = DISK_RESIZE_POWEROFF;
state = DISK_RESIZE_POWEROFF;
} else if ( st == "DISK_RESIZE_UNDEPLOYED") {
state = DISK_RESIZE_UNDEPLOYED;
state = DISK_RESIZE_UNDEPLOYED;
} else {
return -1;
}
@ -315,124 +315,124 @@ string& VirtualMachine::lcm_state_to_str(string& st, LcmState state)
case RUNNING:
st = "RUNNING"; break;
case MIGRATE:
st = "MIGRATE"; break;
st = "MIGRATE"; break;
case SAVE_STOP:
st = "SAVE_STOP"; break;
st = "SAVE_STOP"; break;
case SAVE_SUSPEND:
st = "SAVE_SUSPEND"; break;
st = "SAVE_SUSPEND"; break;
case SAVE_MIGRATE:
st = "SAVE_MIGRATE"; break;
st = "SAVE_MIGRATE"; break;
case PROLOG_MIGRATE:
st = "PROLOG_MIGRATE"; break;
st = "PROLOG_MIGRATE"; break;
case PROLOG_RESUME:
st = "PROLOG_RESUME"; break;
st = "PROLOG_RESUME"; break;
case EPILOG_STOP:
st = "EPILOG_STOP"; break;
st = "EPILOG_STOP"; break;
case EPILOG:
st = "EPILOG"; break;
st = "EPILOG"; break;
case SHUTDOWN:
st = "SHUTDOWN"; break;
st = "SHUTDOWN"; break;
case CLEANUP_RESUBMIT:
st = "CLEANUP_RESUBMIT"; break;
st = "CLEANUP_RESUBMIT"; break;
case UNKNOWN:
st = "UNKNOWN"; break;
st = "UNKNOWN"; break;
case HOTPLUG:
st = "HOTPLUG"; break;
st = "HOTPLUG"; break;
case SHUTDOWN_POWEROFF:
st = "SHUTDOWN_POWEROFF"; break;
st = "SHUTDOWN_POWEROFF"; break;
case BOOT_UNKNOWN:
st = "BOOT_UNKNOWN"; break;
st = "BOOT_UNKNOWN"; break;
case BOOT_POWEROFF:
st = "BOOT_POWEROFF"; break;
st = "BOOT_POWEROFF"; break;
case BOOT_SUSPENDED:
st = "BOOT_SUSPENDED"; break;
st = "BOOT_SUSPENDED"; break;
case BOOT_STOPPED:
st = "BOOT_STOPPED"; break;
st = "BOOT_STOPPED"; break;
case CLEANUP_DELETE:
st = "CLEANUP_DELETE"; break;
st = "CLEANUP_DELETE"; break;
case HOTPLUG_SNAPSHOT:
st = "HOTPLUG_SNAPSHOT"; break;
st = "HOTPLUG_SNAPSHOT"; break;
case HOTPLUG_NIC:
st = "HOTPLUG_NIC"; break;
st = "HOTPLUG_NIC"; break;
case HOTPLUG_SAVEAS:
st = "HOTPLUG_SAVEAS"; break;
st = "HOTPLUG_SAVEAS"; break;
case HOTPLUG_SAVEAS_POWEROFF:
st = "HOTPLUG_SAVEAS_POWEROFF"; break;
st = "HOTPLUG_SAVEAS_POWEROFF"; break;
case HOTPLUG_SAVEAS_SUSPENDED:
st = "HOTPLUG_SAVEAS_SUSPENDED"; break;
st = "HOTPLUG_SAVEAS_SUSPENDED"; break;
case SHUTDOWN_UNDEPLOY:
st = "SHUTDOWN_UNDEPLOY"; break;
st = "SHUTDOWN_UNDEPLOY"; break;
case EPILOG_UNDEPLOY:
st = "EPILOG_UNDEPLOY"; break;
st = "EPILOG_UNDEPLOY"; break;
case PROLOG_UNDEPLOY:
st = "PROLOG_UNDEPLOY"; break;
st = "PROLOG_UNDEPLOY"; break;
case BOOT_UNDEPLOY:
st = "BOOT_UNDEPLOY"; break;
st = "BOOT_UNDEPLOY"; break;
case HOTPLUG_PROLOG_POWEROFF:
st = "HOTPLUG_PROLOG_POWEROFF"; break;
st = "HOTPLUG_PROLOG_POWEROFF"; break;
case HOTPLUG_EPILOG_POWEROFF:
st = "HOTPLUG_EPILOG_POWEROFF"; break;
st = "HOTPLUG_EPILOG_POWEROFF"; break;
case BOOT_MIGRATE:
st = "BOOT_MIGRATE"; break;
st = "BOOT_MIGRATE"; break;
case BOOT_FAILURE:
st = "BOOT_FAILURE"; break;
st = "BOOT_FAILURE"; break;
case BOOT_MIGRATE_FAILURE:
st = "BOOT_MIGRATE_FAILURE"; break;
st = "BOOT_MIGRATE_FAILURE"; break;
case PROLOG_MIGRATE_FAILURE:
st = "PROLOG_MIGRATE_FAILURE"; break;
st = "PROLOG_MIGRATE_FAILURE"; break;
case PROLOG_FAILURE:
st = "PROLOG_FAILURE"; break;
st = "PROLOG_FAILURE"; break;
case EPILOG_FAILURE:
st = "EPILOG_FAILURE"; break;
st = "EPILOG_FAILURE"; break;
case EPILOG_STOP_FAILURE:
st = "EPILOG_STOP_FAILURE"; break;
st = "EPILOG_STOP_FAILURE"; break;
case EPILOG_UNDEPLOY_FAILURE:
st = "EPILOG_UNDEPLOY_FAILURE"; break;
st = "EPILOG_UNDEPLOY_FAILURE"; break;
case PROLOG_MIGRATE_POWEROFF:
st = "PROLOG_MIGRATE_POWEROFF"; break;
st = "PROLOG_MIGRATE_POWEROFF"; break;
case PROLOG_MIGRATE_POWEROFF_FAILURE:
st = "PROLOG_MIGRATE_POWEROFF_FAILURE"; break;
st = "PROLOG_MIGRATE_POWEROFF_FAILURE"; break;
case PROLOG_MIGRATE_SUSPEND:
st = "PROLOG_MIGRATE_SUSPEND"; break;
st = "PROLOG_MIGRATE_SUSPEND"; break;
case PROLOG_MIGRATE_SUSPEND_FAILURE:
st = "PROLOG_MIGRATE_SUSPEND_FAILURE"; break;
st = "PROLOG_MIGRATE_SUSPEND_FAILURE"; break;
case BOOT_STOPPED_FAILURE:
st = "BOOT_STOPPED_FAILURE"; break;
st = "BOOT_STOPPED_FAILURE"; break;
case BOOT_UNDEPLOY_FAILURE:
st = "BOOT_UNDEPLOY_FAILURE"; break;
st = "BOOT_UNDEPLOY_FAILURE"; break;
case PROLOG_RESUME_FAILURE:
st = "PROLOG_RESUME_FAILURE"; break;
st = "PROLOG_RESUME_FAILURE"; break;
case PROLOG_UNDEPLOY_FAILURE:
st = "PROLOG_UNDEPLOY_FAILURE"; break;
st = "PROLOG_UNDEPLOY_FAILURE"; break;
case DISK_SNAPSHOT_POWEROFF:
st = "DISK_SNAPSHOT_POWEROFF"; break;
st = "DISK_SNAPSHOT_POWEROFF"; break;
case DISK_SNAPSHOT_REVERT_POWEROFF:
st = "DISK_SNAPSHOT_REVERT_POWEROFF"; break;
st = "DISK_SNAPSHOT_REVERT_POWEROFF"; break;
case DISK_SNAPSHOT_DELETE_POWEROFF:
st = "DISK_SNAPSHOT_DELETE_POWEROFF"; break;
st = "DISK_SNAPSHOT_DELETE_POWEROFF"; break;
case DISK_SNAPSHOT_SUSPENDED:
st = "DISK_SNAPSHOT_SUSPENDED"; break;
st = "DISK_SNAPSHOT_SUSPENDED"; break;
case DISK_SNAPSHOT_REVERT_SUSPENDED:
st = "DISK_SNAPSHOT_REVERT_SUSPENDED"; break;
st = "DISK_SNAPSHOT_REVERT_SUSPENDED"; break;
case DISK_SNAPSHOT_DELETE_SUSPENDED:
st = "DISK_SNAPSHOT_DELETE_SUSPENDED"; break;
st = "DISK_SNAPSHOT_DELETE_SUSPENDED"; break;
case DISK_SNAPSHOT:
st = "DISK_SNAPSHOT"; break;
st = "DISK_SNAPSHOT"; break;
case DISK_SNAPSHOT_DELETE:
st = "DISK_SNAPSHOT_DELETE"; break;
st = "DISK_SNAPSHOT_DELETE"; break;
case PROLOG_MIGRATE_UNKNOWN:
st = "PROLOG_MIGRATE_UNKNOWN"; break;
st = "PROLOG_MIGRATE_UNKNOWN"; break;
case PROLOG_MIGRATE_UNKNOWN_FAILURE:
st = "PROLOG_MIGRATE_UNKNOWN_FAILURE"; break;
st = "PROLOG_MIGRATE_UNKNOWN_FAILURE"; break;
case DISK_RESIZE:
st = "DISK_RESIZE"; break;
st = "DISK_RESIZE"; break;
case DISK_RESIZE_POWEROFF:
st = "DISK_RESIZE_POWEROFF"; break;
st = "DISK_RESIZE_POWEROFF"; break;
case DISK_RESIZE_UNDEPLOYED:
st = "DISK_RESIZE_UNDEPLOYED"; break;
st = "DISK_RESIZE_UNDEPLOYED"; break;
}
return st;
return st;
}
/* -------------------------------------------------------------------------- */
@ -819,10 +819,14 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
this->name = name;
// -------------------------------------------------------------------------
// Move known attributes that doesn't need additional procesing to template
// -------------------------------------------------------------------------
parse_well_known_attributes();
// ------------------------------------------------------------------------
// Parse the Public Cloud specs for this VM
// ------------------------------------------------------------------------
if (parse_public_clouds(error_str) != 0)
{
goto error_public;
@ -831,7 +835,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// Check for EMULATOR attribute
// ------------------------------------------------------------------------
user_obj_template->get("EMULATOR", value);
if (!value.empty())
@ -841,10 +844,9 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
}
// ------------------------------------------------------------------------
// Check for CPU, VCPU and MEMORY attributes
// Check for CPU, VCPU, MEMORY and TOPOLOGY attributes
// ------------------------------------------------------------------------
if ( user_obj_template->get("MEMORY", ivalue) == false || (ivalue * 1024) <= 0 )
if ( user_obj_template->get("MEMORY", ivalue) == false || ivalue <= 0 )
{
goto error_memory;
}
@ -878,7 +880,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// Check the cost attributes
// ------------------------------------------------------------------------
if ( user_obj_template->get("CPU_COST", fvalue) == true )
{
if ( fvalue < 0 )
@ -915,7 +916,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// Check the OS attribute
// ------------------------------------------------------------------------
rc = parse_os(error_str);
if ( rc != 0 )
@ -931,7 +931,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// PCI Devices (Needs to be parsed before network)
// ------------------------------------------------------------------------
rc = parse_pci(error_str, user_obj_template);
if ( rc != 0 )
@ -942,7 +941,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// Parse the defaults to merge
// ------------------------------------------------------------------------
rc = parse_defaults(error_str, user_obj_template);
if ( rc != 0 )
@ -953,7 +951,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// Parse the virtual router attributes
// ------------------------------------------------------------------------
rc = parse_vrouter(error_str, user_obj_template);
if ( rc != 0 )
@ -964,7 +961,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// Get network leases
// ------------------------------------------------------------------------
rc = get_network_leases(error_str);
if ( rc != 0 )
@ -975,7 +971,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// ------------------------------------------------------------------------
// Get disk images
// ------------------------------------------------------------------------
rc = get_disk_images(error_str);
if ( rc != 0 )
@ -1002,7 +997,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// -------------------------------------------------------------------------
// Set boot order
// -------------------------------------------------------------------------
rc = set_boot_order(obj_template, error_str);
if ( rc != 0 )
@ -1013,7 +1007,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// -------------------------------------------------------------------------
// Parse the context & requirements
// -------------------------------------------------------------------------
rc = parse_context(error_str, false); //Don't generate context for auto NICs
if ( rc != 0 )
@ -1043,7 +1036,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
// -------------------------------------------------------------------------
// Get and set DEPLOY_ID for imported VMs
// -------------------------------------------------------------------------
user_obj_template->get("IMPORT_VM_ID", value);
user_obj_template->erase("IMPORT_VM_ID");
@ -1078,14 +1070,9 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
goto error_rollback;
}
// ------------------------------------------------------------------------
parse_well_known_attributes();
// ------------------------------------------------------------------------
// Insert the VM
// ------------------------------------------------------------------------
rc = insert_replace(db, false, error_str);
if ( rc != 0 )
@ -1096,7 +1083,6 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
//-------------------------------------------------------------------------
//Check for missing unlock callbacks during creation
//-------------------------------------------------------------------------
if ( state == VirtualMachine::CLONING )
{
Nebula::instance().get_lcm()->trigger(LCMAction::DISK_LOCK_SUCCESS,oid);
@ -1599,6 +1585,15 @@ int VirtualMachine::automatic_requirements(set<int>& cluster_ids,
oss << "!(PUBLIC_CLOUD = YES)";
}
if ( is_pinned() )
{
oss << " & (PIN_POLICY = PINNED)";
}
else
{
oss << " & !(PIN_POLICY = PINNED)";
}
int num_public = get_public_clouds(clouds);
if (num_public != 0)
@ -1988,95 +1983,103 @@ void VirtualMachine::cp_previous_history()
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk,
vector<VectorAttribute *>& pci_devs)
void VirtualMachine::get_capacity(HostShareCapacity& sr)
{
istringstream iss;
float fcpu;
float fcpu;
pci_devs.clear();
sr.pci.clear();
sr.nodes.clear();
if ((get_template_attribute("MEMORY",memory) == false) ||
(get_template_attribute("CPU",fcpu) == false))
sr.vmid = oid;
if ((get_template_attribute("MEMORY", sr.mem) == false) ||
(get_template_attribute("CPU", fcpu) == false))
{
cpu = 0;
memory = 0;
disk = 0;
sr.cpu = 0;
sr.mem = 0;
sr.disk = 0;
sr.vcpu = 0;
return;
}
cpu = (int) (fcpu * 100);//now in 100%
memory = memory * 1024; //now in Kilobytes
disk = 0;
sr.cpu = (int) (fcpu * 100); //%
sr.mem = sr.mem * 1024; //Kb
sr.disk = 0;
obj_template->get("PCI", pci_devs);
get_template_attribute("VCPU", sr.vcpu);
obj_template->get("PCI", sr.pci);
obj_template->get("NUMA_NODE", sr.nodes);
sr.topology = obj_template->get("TOPOLOGY");
return;
}
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachine::check_resize (
float cpu, long int memory, int vcpu, string& error_str)
int VirtualMachine::resize(float cpu, long int memory, unsigned int vcpu,
string& error)
{
if (cpu < 0)
{
error_str = "CPU must be a positive float or integer value.";
return -1;
}
unsigned int s, c, t;
if (memory < 0)
{
error_str = "MEMORY must be a positive integer value.";
return -1;
}
if (vcpu < 0)
{
error_str = "VCPU must be a positive integer value.";
return -1;
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachine::resize(float cpu, long int memory, int vcpu, string& error_str)
{
ostringstream oss;
int rc = check_resize(cpu, memory, vcpu, error_str);
if (rc != 0)
{
return rc;
}
s = c = t = 0;
if (cpu > 0)
{
oss << cpu;
replace_template_attribute("CPU", oss.str());
oss.str("");
replace_template_attribute("CPU", cpu);
}
if (memory > 0)
{
oss << memory;
replace_template_attribute("MEMORY", oss.str());
oss.str("");
replace_template_attribute("MEMORY", memory);
}
if (vcpu > 0)
{
oss << vcpu;
replace_template_attribute("VCPU", oss.str());
replace_template_attribute("VCPU", vcpu);
}
else
{
get_template_attribute("VCPU", vcpu);
}
return 0;
/* ---------------------------------------------------------------------- */
/* Update the NUMA topology with new size: */
/* 1. Increase number of cores for new VCPU (keep threads and sockets) */
/* 2. Clear current nodes and build new ones with new MEMORY/VCPU */
/* ---------------------------------------------------------------------- */
VectorAttribute * vtopol = get_template_attribute("TOPOLOGY");
if ( vtopol == 0 )
{
return 0;
}
vtopol->vector_value("SOCKETS", s);
vtopol->vector_value("CORES", c);
vtopol->vector_value("THREADS", t);
if ( s != 0 && c != 0 && t != 0 && (s * c * t) != vcpu)
{
if ( vcpu%(t * s) != 0 )
{
error = "new VCPU is not multiple of the total number of threads";
return -1;
}
c = vcpu/(t * s);
vtopol->replace("CORES", c);
}
remove_template_attribute("NUMA_NODE");
return parse_topology(obj_template, error);
}
/* -------------------------------------------------------------------------- */
@ -2361,14 +2364,12 @@ string& VirtualMachine::to_token(string& text) const
string& VirtualMachine::to_xml_short(string& xml)
{
string disks_xml, monitoring_xml, user_template_xml, history_xml, nics_xml;
string cpu_tmpl, mem_tmpl;
ostringstream oss;
string cpu_tmpl, mem_tmpl, auto_reqs, auto_ds_reqs, auto_nic_reqs;
obj_template->get("CPU", cpu_tmpl);
obj_template->get("MEMORY", mem_tmpl);
obj_template->get("AUTOMATIC_REQUIREMENTS", auto_reqs);
obj_template->get("AUTOMATIC_DS_REQUIREMENTS", auto_ds_reqs);
obj_template->get("AUTOMATIC_NIC_REQUIREMENTS", auto_nic_reqs);
oss << "<VM>"
<< "<ID>" << oid << "</ID>"
@ -2403,27 +2404,6 @@ string& VirtualMachine::to_xml_short(string& xml)
graph->to_xml(oss);
}
if (!auto_reqs.empty())
{
oss << "<AUTOMATIC_REQUIREMENTS>";
oss << one_util::escape_xml(auto_reqs);
oss << "</AUTOMATIC_REQUIREMENTS>";
}
if (!auto_ds_reqs.empty())
{
oss << "<AUTOMATIC_DS_REQUIREMENTS>";
oss << one_util::escape_xml(auto_ds_reqs);
oss << "</AUTOMATIC_DS_REQUIREMENTS>";
}
if (!auto_nic_reqs.empty())
{
oss << "<AUTOMATIC_NIC_REQUIREMENTS>";
oss << one_util::escape_xml(auto_nic_reqs);
oss << "</AUTOMATIC_NIC_REQUIREMENTS>";
}
oss << "</TEMPLATE>"
<< monitoring.to_xml_short(monitoring_xml)
<< user_obj_template->to_xml_short(user_template_xml);
@ -2439,17 +2419,6 @@ string& VirtualMachine::to_xml_short(string& xml)
oss << "<HISTORY_RECORDS/>";
}
std::vector<VectorAttribute *> vm_groups;
if (obj_template->get("VMGROUP", vm_groups) > 0)
{
for (std::vector<VectorAttribute *>::iterator it = vm_groups.begin();
it != vm_groups.end() ; it++)
{
(*it)->to_xml(oss);
}
}
oss << "</VM>";
xml = oss.str();

View File

@ -318,12 +318,12 @@ int VirtualMachine::parse_vrouter(string& error_str, Template * tmpl)
/* -------------------------------------------------------------------------- */
static int check_pci_attributes(VectorAttribute * pci, const string& default_bus,
string& error_str)
string& error_str)
{
static string attrs[] = {"VENDOR", "DEVICE", "CLASS"};
static int num_attrs = 3;
string bus;
string bus;
bool found = false;
for (int i = 0; i < num_attrs; i++)
@ -348,11 +348,11 @@ static int check_pci_attributes(VectorAttribute * pci, const string& default_bus
return -1;
}
if ( HostSharePCI::set_pci_address(pci, default_bus) != 0 )
{
error_str = "Wrong BUS in PCI attribute";
return -1;
}
if ( HostSharePCI::set_pci_address(pci, default_bus) != 0 )
{
error_str = "Wrong BUS in PCI attribute";
return -1;
}
return 0;
}
@ -373,10 +373,10 @@ int VirtualMachine::parse_pci(string& error_str, Template * tmpl)
obj_template->set(*it);
}
Nebula& nd = Nebula::instance();
string default_bus;
Nebula& nd = Nebula::instance();
string default_bus;
nd.get_configuration_attribute("PCI_PASSTHROUGH_BUS", default_bus);
nd.get_configuration_attribute("PCI_PASSTHROUGH_BUS", default_bus);
for (it = array_pci.begin(); it !=array_pci.end(); ++it)
{
@ -519,22 +519,21 @@ void VirtualMachine::parse_well_known_attributes()
* FEATURES
* RAW
* CLONING_TEMPLATE_ID
* TOPOLOGY
* NUMA_NODE
*/
std::vector<std::string> names = {"INPUT", "FEATURES", "RAW",
"CLONING_TEMPLATE_ID", "TOPOLOGY", "NUMA_NODE"};
vector<Attribute *> v_attr;
vector<Attribute *>::iterator it;
string names[] = {"INPUT", "FEATURES", "RAW", "CLONING_TEMPLATE_ID"};
for (int i=0; i<4; i++)
for (auto it = names.begin(); it != names.end() ; ++it)
{
v_attr.clear();
vector<Attribute *> v_attr;
user_obj_template->remove(names[i], v_attr);
user_obj_template->remove(*it, v_attr);
for (it=v_attr.begin(); it != v_attr.end(); it++)
for (auto jt=v_attr.begin(); jt != v_attr.end(); jt++)
{
obj_template->set(*it);
obj_template->set(*jt);
}
}
}
@ -732,3 +731,280 @@ int VirtualMachine::parse_cpu_model(Template * tmpl)
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachine::parse_topology(Template * tmpl, std::string &error)
{
/**
* TOPOLOGY
* - NUMA_NODES: number of numa nodes
* - PIN_POLICY: CORE, THREAD, SHARED, NONE
* - THREADS
* - CORES
* - SOCKETS
*/
std::vector<VectorAttribute *> numa_nodes;
std::vector<VectorAttribute *> vtopol_a;
VectorAttribute * vtopol = 0;
tmpl->remove("TOPOLOGY", vtopol_a);
if ( !vtopol_a.empty() )
{
auto it = vtopol_a.begin();
vtopol = *it;
for ( ++it; it != vtopol_a.end(); ++it)
{
delete *it;
}
}
tmpl->get("NUMA_NODE", numa_nodes);
if ( vtopol == 0 && numa_nodes.empty() )
{
return 0;
}
if ( vtopol == 0 )
{
vtopol = new VectorAttribute("TOPOLOGY");
}
tmpl->set(vtopol);
std::string pp_s = vtopol->vector_value("PIN_POLICY");
HostShare::PinPolicy pp = HostShare::str_to_pin_policy(pp_s);
/* ---------------------------------------------------------------------- */
/* Set MEMORY, HUGEPAGE_SIZE, vCPU & update CPU for pinned VMS */
/* ---------------------------------------------------------------------- */
long long memory;
unsigned int vcpu = 0;
unsigned long int hpsz_mb = 0;
if (!tmpl->get("MEMORY", memory))
{
error = "VM has not MEMORY set";
return -1;
}
if (!tmpl->get("VCPU", vcpu))
{
vcpu = 1;
tmpl->replace("VCPU", 1);
}
if ( pp != HostShare::PP_NONE )
{
tmpl->replace("CPU", vcpu);
}
if ( vtopol->vector_value("HUGEPAGE_SIZE", hpsz_mb) == 0 )
{
vtopol->replace("HUGEPAGE_SIZE", hpsz_mb * 1024);
}
/* ---------------------------------------------------------------------- */
/* Check topology for non pinned & pinned VMs */
/* - non-pinned VM needs to set SOCKETS, CORES and THREADS */
/* - pinned VM */
/* 1. Set sockets to number of NUMA_NODE or 1 if not given */
/* 2. core and thread given. Check consistency */
/* 3. core given. Compute threads & check power of 2 */
/* 4. other combinations are set by the scheduler */
/* ---------------------------------------------------------------------- */
unsigned int s, c, t;
s = c = t = 0;
vtopol->vector_value("SOCKETS", s);
vtopol->vector_value("CORES", c);
vtopol->vector_value("THREADS", t);
if ( pp == HostShare::PP_NONE )
{
if ( c == 0 || t == 0 || s == 0 )
{
error = "Non-pinned VMs with a virtual topology needs to set "
" SOCKETS, CORES and THREADS numbers.";
return -1;
}
else if ((s * c * t) != vcpu)
{
error = "Total threads per core and socket needs to match VCPU";
return -1;
}
vtopol->replace("PIN_POLICY", "NONE");
tmpl->erase("NUMA_NODE");
return 0;
}
if ( s == 0 )
{
if ( numa_nodes.empty() )
{
s = 1;
}
else
{
s = numa_nodes.size();
}
vtopol->replace("SOCKETS", s);
}
if ( c != 0 && t != 0 && (s * c * t) != vcpu)
{
error = "Total threads per core and socket needs to match VCPU";
return -1;
}
if ( t == 0 && c != 0 )
{
if ( vcpu%(c * s) != 0 )
{
error = "VCPU is not multiple of the total number of cores";
return -1;
}
t = vcpu/(c * s);
if ((t & (t - 1)) != 0 )
{
error = "Computed number of threads is not power of 2";
return -1;
}
vtopol->replace("THREADS", t);
}
/* ---------------------------------------------------------------------- */
/* Build NUMA_NODE stanzas for the given topology */
/* ---------------------------------------------------------------------- */
if (numa_nodes.empty()) // Automatic Homogenous Topology
{
if ( vcpu % s != 0 )
{
error = "VCPU is not multiple of the number of NUMA nodes";
return -1;
}
if ( memory % s != 0 )
{
error = "MEMORY is not multiple of the number of NUMA nodes";
return -1;
}
long long mem_node = memory / s;
unsigned int cpu_node = vcpu / s;
for (unsigned int i = 0 ; i < s ; ++i)
{
VectorAttribute * node = new VectorAttribute("NUMA_NODE");
node->replace("TOTAL_CPUS", cpu_node);
node->replace("MEMORY", mem_node * 1024);
tmpl->set(node);
}
}
else // Manual/Asymmetric Topology, NUMA_NODE array
{
long long node_mem = 0;
unsigned int node_cpu = 0;
std::vector<VectorAttribute *> new_nodes;
for (auto it = numa_nodes.begin() ; it != numa_nodes.end() ; ++it)
{
long long nmem = 0;
unsigned int ncpu = 0;
(*it)->vector_value("TOTAL_CPUS", ncpu);
(*it)->vector_value("MEMORY", nmem);
if ( ncpu == 0 || nmem == 0)
{
break;
}
VectorAttribute * node = new VectorAttribute("NUMA_NODE");
node->replace("TOTAL_CPUS", ncpu);
node->replace("MEMORY", nmem * 1024);
new_nodes.push_back(node);
node_cpu += ncpu;
node_mem += nmem;
}
tmpl->erase("NUMA_NODE");
if (node_cpu != vcpu || node_mem != memory ||
node_cpu == 0 || node_mem == 0)
{
for (auto it = new_nodes.begin(); it != new_nodes.end(); ++it)
{
delete *it;
}
}
if (node_cpu == 0)
{
error = "NUMA_NODES cannot have 0 CPUs";
return -1;
}
if (node_mem == 0)
{
error = "NUMA_NODES cannot have 0 MEMORY";
return -1;
}
if (node_cpu != vcpu)
{
error = "Total CPUS of NUMA nodes is different from VM VCPU";
return -1;
}
if (node_mem != memory)
{
error = "Total MEMORY of NUMA nodes is different from VM MEMORY";
return -1;
}
tmpl->set(new_nodes);
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool VirtualMachine::is_pinned()
{
VectorAttribute * topology = obj_template->get("TOPOLOGY");
if ( topology == 0 )
{
return false;
}
std::string pp_s = topology->vector_value("PIN_POLICY");
HostShare::PinPolicy pp = HostShare::str_to_pin_policy(pp_s);
return pp != HostShare::PP_NONE;
}

View File

@ -156,6 +156,193 @@ static void insert_sec(ofstream& file, const string& base, const string& s,
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void pin_cpu(ofstream& file, const VectorAttribute * topology,
std::vector<const VectorAttribute *> &nodes)
{
HostShare::PinPolicy pp = HostShare::PP_NONE;
unsigned int vcpu_id = 0;
std::ostringstream oss;
if ( topology != 0 )
{
std::string pp_s;
pp_s = topology->vector_value("PIN_POLICY");
pp = HostShare::str_to_pin_policy(pp_s);
}
if ( pp == HostShare::PP_NONE )
{
return;
}
for (auto it = nodes.begin(); it != nodes.end() ; ++it)
{
unsigned int nv = 0;
std::vector<unsigned int> cpus_a;
std::string cpus = (*it)->vector_value("CPUS");
(*it)->vector_value("TOTAL_CPUS", nv);
if ( nv == 0 || cpus.empty())
{
continue;
}
one_util::split(cpus, ',', cpus_a);
for ( unsigned int i = 0; i < nv; ++i, ++vcpu_id )
{
file << "\t\t<vcpupin vcpu='" << vcpu_id << "' cpuset='";
if ( pp == HostShare::PP_SHARED )
{
file << cpus << "'/>\n";
}
else
{
file << cpus_a[i] << "'/>\n";
}
}
if ( it != nodes.begin() )
{
oss << ",";
}
oss << cpus;
}
file << "\t\t<emulatorpin cpuset='" << oss.str() << "'/>\n";
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void vtopol(ofstream& file, const VectorAttribute * topology,
std::vector<const VectorAttribute *> &nodes, std::string &numatune,
std::string &membacking)
{
std::string ma;
int hpsz_kb = 0;
if ( topology != 0 )
{
int s, c, t;
s = c = t = 1;
topology->vector_value("SOCKETS", s);
topology->vector_value("CORES", c);
topology->vector_value("THREADS", t);
topology->vector_value("HUGEPAGE_SIZE", hpsz_kb);
ma = topology->vector_value("MEMORY_ACCESS");
if (!ma.empty() && hpsz_kb != 0)
{
one_util::tolower(ma);
if (ma != "private" && ma != "shared")
{
ma = "";
}
}
file << "\t\t<topology sockets='" << s << "' cores='" << c
<< "' threads='"<< t <<"'/>\n";
}
if ( nodes.empty() )
{
return;
}
std::ostringstream oss, mnodes;
unsigned int cpuid = 0;
unsigned int cid = 0;
oss << "\t<numatune>\n";
file << "\t\t<numa>\n";
for (auto it = nodes.begin() ; it != nodes.end() ; ++it, ++cid)
{
unsigned int ncpu = 0;
std::string mem = (*it)->vector_value("MEMORY");
std::string mem_id = (*it)->vector_value("MEMORY_NODE_ID");
(*it)->vector_value("TOTAL_CPUS", ncpu);
file << "\t\t\t<cell id='" << cid << "' memory='" << mem << "'";
if ( ncpu > 0 )
{
file << " cpus='" << cpuid << "-" << cpuid + ncpu - 1 << "'";
}
if (!ma.empty())
{
file << " memAccess='" << ma << "'";
}
file << "/>\n";
cpuid += ncpu;
if (!mem_id.empty())
{
oss << "\t\t<memnode cellid='" << cid << "' mode='strict' nodeset='"
<< mem_id <<"'/>\n";
if (!mnodes.str().empty())
{
mnodes << ",";
}
mnodes << mem_id;
}
}
file << "\t\t</numa>\n";
if (!mnodes.str().empty())
{
oss << "\t\t<memory mode='strict' nodeset='" << mnodes.str() << "'/>\n";
oss << "\t</numatune>\n";
numatune = oss.str();
}
if ( hpsz_kb != 0 )
{
std::ostringstream mboss;
mboss << "\t<memoryBacking>\n";
mboss << "\t\t<hugepages>\n";
mboss << "\t\t\t<page size=" << one_util::escape_xml_attr(hpsz_kb);
if (!mnodes.str().empty())
{
mboss << " nodeset='" << mnodes.str() << "'";
}
mboss << "/>\n";
mboss << "\t\t</hugepages>\n";
mboss << "\t</memoryBacking>\n";
membacking = mboss.str();
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int LibVirtDriver::deployment_description_kvm(
const VirtualMachine * vm,
const string& file_name) const
@ -328,6 +515,12 @@ int LibVirtDriver::deployment_description_kvm(
string default_raw = "";
string data = "";
const VectorAttribute * topology;
vector<const VectorAttribute *> nodes;
std::string numa_tune = "";
std::string mbacking = "";
string vm_xml;
// ------------------------------------------------------------------------
@ -376,13 +569,18 @@ int LibVirtDriver::deployment_description_kvm(
}
//Every process gets 1024 shares by default (cgroups), scale this with CPU
if(vm->get_template_attribute("CPU", cpu))
{
file << "\t<cputune>" << endl
<< "\t\t<shares>"<< ceil( cpu * CGROUP_BASE_CPU_SHARES )
<< "</shares>" << endl
<< "\t</cputune>"<< endl;
}
cpu = 1;
vm->get_template_attribute("CPU", cpu);
topology = vm->get_template_attribute("TOPOLOGY");
vm->get_template_attribute("NUMA_NODE", nodes);
file << "\t<cputune>\n";
file << "\t\t<shares>"<< ceil(cpu * CGROUP_BASE_CPU_SHARES) << "</shares>\n";
pin_cpu(file, topology, nodes);
file << "\t</cputune>\n";
// Memory must be expressed in Kb
if (vm->get_template_attribute("MEMORY",memory))
@ -497,6 +695,7 @@ int LibVirtDriver::deployment_description_kvm(
// ------------------------------------------------------------------------
cpu_model_v = vm->get_template_attribute("CPU_MODEL");
if( cpu_model_v != 0 )
{
cpu_model = cpu_model_v->vector_value("MODEL");
@ -513,19 +712,40 @@ int LibVirtDriver::deployment_description_kvm(
//TODO #756 cache, feature
}
if ( !cpu_model.empty() )
if ( !cpu_model.empty() || topology != 0 )
{
file << "\t<cpu mode=" << one_util::escape_xml_attr(cpu_mode) << ">\n";
file << "\t<cpu" ;
if ( cpu_mode == "custom" )
if (!cpu_model.empty())
{
file << "\t\t<model fallback='forbid'>" << one_util::escape_xml(cpu_model)
<< "</model>\n";
file << " mode=" << one_util::escape_xml_attr(cpu_mode) << ">\n";
if ( cpu_mode == "custom" )
{
file << "\t\t<model fallback='forbid'>"
<< one_util::escape_xml(cpu_model) << "</model>\n";
}
}
else
{
file << ">\n";
}
vtopol(file, topology, nodes, numa_tune, mbacking);
file << "\t</cpu>\n";
}
if (!numa_tune.empty())
{
file << numa_tune;
}
if (!mbacking.empty())
{
file << mbacking;
}
// ------------------------------------------------------------------------
// DEVICES SECTION
// ------------------------------------------------------------------------