diff --git a/include/Host.h b/include/Host.h index a4c241fa84..7874680348 100644 --- a/include/Host.h +++ b/include/Host.h @@ -379,13 +379,15 @@ public: * @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 * @return 0 on success */ - void add_capacity(int vm_id, long long cpu, long long mem, long long disk) + void add_capacity(int vm_id, long long cpu, long long mem, long long disk, + vector pci) { if ( vm_collection.add_collection_id(vm_id) == 0 ) { - host_share.add(cpu,mem,disk); + host_share.add(vm_id, cpu, mem, disk, pci); } else { @@ -404,13 +406,15 @@ public: * @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 * @return 0 on success */ - void del_capacity(int vm_id, long long cpu, long long mem, long long disk) + void del_capacity(int vm_id, long long cpu, long long mem, long long disk, + vector pci) { if ( vm_collection.del_collection_id(vm_id) == 0 ) { - host_share.del(cpu,mem,disk); + host_share.del(cpu, mem, disk, pci); } else { @@ -440,11 +444,14 @@ public: * @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 * @return true if the share can host the VM */ - bool test_capacity(long long cpu, long long mem, long long disk) + bool test_capacity(long long cpu, long long mem, long long disk, + vector &pci, string& error) { - return host_share.test(cpu, mem, disk); + return host_share.test(cpu, mem, disk, pci, error); } /** diff --git a/include/HostPool.h b/include/HostPool.h index 8a19172cc4..fa2c05a2c6 100644 --- a/include/HostPool.h +++ b/include/HostPool.h @@ -123,17 +123,19 @@ public: * @param cpu amount of CPU, in percentage * @param mem amount of main memory, in KB * @param disk amount of disk + * @param pci devices requested by the VM * * @return 0 on success -1 in case of failure */ - int add_capacity(int oid, int vm_id, int cpu, int mem, int disk) + int add_capacity(int oid, int vm_id, int cpu, int mem, int disk, + vector pci) { int rc = 0; Host * host = get(oid, true); if ( host != 0 ) { - host->add_capacity(vm_id, cpu, mem, disk); + host->add_capacity(vm_id, cpu, mem, disk, pci); update(host); @@ -154,14 +156,16 @@ public: * @param cpu amount of CPU * @param mem amount of main memory * @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) + void del_capacity(int oid, int vm_id, int cpu, int mem, int disk, + vector pci) { Host * host = get(oid, true); if ( host != 0 ) { - host->del_capacity(vm_id, cpu, mem, disk); + host->del_capacity(vm_id, cpu, mem, disk, pci); update(host); diff --git a/include/HostShare.h b/include/HostShare.h index a929ad9fc5..c980ab97f2 100644 --- a/include/HostShare.h +++ b/include/HostShare.h @@ -20,22 +20,179 @@ #include "ObjectXML.h" #include "Template.h" #include - -using namespace std; +#include /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ -class HostShareTemplate : public Template +class HostShareDatastore : public Template { public: - HostShareTemplate() : Template(false,'=',"DATASTORES"){}; + HostShareDatastore() : Template(false, '=', "DATASTORES"){}; - ~HostShareTemplate(){}; + virtual ~HostShareDatastore(){}; }; -/* ------------------------------------------------------------------------ */ -/* ------------------------------------------------------------------------ */ +/** + * This class represents a PCI DEVICE list for the host. The list is in the + * form: + * + * PCI address domain + * PCI address bus + * PCI address slot + * PCI address function + *
PCI address, bus, slot and function + * ID of PCI device vendor + * ID of PCI device + * ID of PCI device class + * ID using this device, -1 if free + * + * The monitor probe may report additional information such as VENDOR_NAME, + * DEVICE_NAME, CLASS_NAME... + */ +class HostSharePCI : public Template +{ +public: + + HostSharePCI() : Template(false, '=', "PCI_DEVICES"){}; + + virtual ~HostSharePCI() + { + map::iterator it; + + for (it=pci_devices.begin(); it != pci_devices.end(); it++) + { + delete it->second; + }; + }; + + /** + * Builds the devices list from its XML representation. This function + * is used when importing it from the DB. + * @param node xmlNode for the template + * @return 0 on success + */ + int from_xml_node(const xmlNodePtr node); + + /** + * Test whether this PCI device set has the requested devices available. + * @param devs list of requested devices by the VM. + * @return true if all the devices are available. + */ + bool test(vector &devs) const; + + /** + * Assign the requested devices to the given VM. The assigned devices will + * be labeled with the VM and the PCI attribute of the VM extended with + * the address of the assigned devices. + * @param devs list of requested PCI devices, will include address of + * assigned devices. + * @param vmid of the VM + */ + void add(vector &devs, int vmid) + { + test_set(devs, vmid); + } + + /** + * Remove the VM assignment from the PCI device list + */ + void del(const vector &devs); + + /** + * Updates the PCI list with monitor data, it will create or + * remove PCIDevices as needed. + */ + void set_monitorization(vector &pci_att); + + /** + * Prints the PCI device list to an output stream. This function is used + * for logging purposes and *not* for generating DB content. + */ + friend ostream& operator<<(ostream& o, const HostSharePCI& p); + +private: + /** + * Sets the internal class structures from the template + */ + int init(); + + /** + * Test if a PCIDevice matches the vendor, device and class request spec + * and can be assigned. If free, it is stored in the assigned set + * @param vendor_id id in uint form 0 means * + * @param device_id id in uint form 0 means * + * @param class_id id in uint form 0 means * + * @param pci requested pci device + * @param vmid if not -1 it will also assign the PCI device to the VM, + * and the pci attribute will be extended with device information. + * @param assigned set of addresses already assigned devices, it will + * include the selected device if found; useful to iterate. + * + * @return true if a device was found. + */ + bool test(unsigned int vendor_id, unsigned int device_id, + unsigned int class_id, const VectorAttribute *pci, + std::set &assigned) const; + + /** + * Test if a PCIDevice matches the vendor, device and class request spec + * and can be assigned. It will assign it if requested. + * @param vendor_id id in uint form 0 means * + * @param device_id id in uint form 0 means * + * @param class_id id in uint form 0 means * + * @param pci requested pci device + * @param vmid if not -1 it will also assign the PCI device to the VM, + * and the pci attribute will be extended with device information. + * @param assigned set of addresses already assigned devices, it will + * include the selected device if found; useful to iterate. + * + * @return true if a device was found. + */ + bool test_set(unsigned int vendor_id, unsigned int device_id, + unsigned int class_id, VectorAttribute *pci, int vmid, + std::set &assigned) const; + + /** + * Test if the given list of PCIDevices can be assigned to the VM + * @param devs, list of PCI devices + * @param vmid if not -1 it will assign the devices to the VM + * + * @return true if the PCIDevice list can be assigned. + */ + bool test_set(vector &devs, int vmid) const; + + /** + * Gets a 4 hex digits value from attribute + * @param name of the attribute + * @pci_device VectorAttribute representing the device + * @return the value as unsigned int or 0 if was not found + */ + static unsigned int get_pci_value(const char * name, + const VectorAttribute * pci_device); + /** + * Internal structure to represent PCI devices for fast look up and + * update + */ + struct PCIDevice + { + PCIDevice(VectorAttribute * _attrs); + + ~PCIDevice(){}; + + unsigned int vendor_id; + unsigned int device_id; + unsigned int class_id; + + int vmid; + + string address; + + VectorAttribute * attrs; + }; + + map pci_devices; +}; /** * The HostShare class. It represents a logical partition of a host... @@ -53,16 +210,21 @@ public: /** * 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 */ - void add(long long cpu, long long mem, long long disk) + void add(int vmid, long long cpu, long long mem, long long disk, + vector pci_devs) { cpu_usage += cpu; mem_usage += mem; disk_usage += disk; + pci.add(pci_devs, vmid); + running_vms++; } @@ -84,13 +246,16 @@ public: * @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) + void del(long long cpu, long long mem, long long disk, vector pci_devs) { cpu_usage -= cpu; mem_usage -= mem; disk_usage -= disk; + pci.del(pci_devs); + running_vms--; } @@ -99,15 +264,35 @@ public: * @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 + * @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(long long cpu, long long mem, long long disk) const + bool test(long long cpu, long long mem, long long disk, + vector& pci_devs, string& error) const { - return (((max_cpu - cpu_usage ) >= cpu) && - ((max_mem - mem_usage ) >= mem) && - ((max_disk - disk_usage) >= disk)); + 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 ( pci_fits ) + { + error = "Not enough capacity."; + } + else + { + error = "Unavailable PCI device."; + } + } + + return fits; } /** @@ -125,6 +310,11 @@ public: void set_ds_monitorization(const vector &ds_att); + void set_pci_monitorization(vector &pci_att) + { + pci.set_monitorization(pci_att); + } + private: long long disk_usage; /**< Disk allocated to VMs (in MB). */ @@ -145,7 +335,8 @@ private: long long running_vms;/**< Number of running VMs in this Host */ - HostShareTemplate ds_template; + HostShareDatastore ds; + HostSharePCI pci; // ---------------------------------------- // Friends diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 0455ec12d3..cac7729196 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -90,10 +90,9 @@ protected: string& tm_mad, RequestAttributes& att); - bool check_host(int hid, - int cpu, - int mem, - int disk, + bool check_host(int hid, + bool enforce, + VirtualMachine* vm, string& error); int add_history(VirtualMachine * vm, diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index a6ec0ed42f..a828effcfa 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -1222,8 +1222,10 @@ public: * @param cpu * @param memory * @param disk + * @param pci_dev */ - void get_requirements (int& cpu, int& memory, int& disk); + void get_requirements (int& cpu, int& memory, int& disk, + vector& pci_dev); /** * Checks if the resize parameters are valid diff --git a/install.sh b/install.sh index ad3cad4733..76a1e0b5b7 100755 --- a/install.sh +++ b/install.sh @@ -847,6 +847,7 @@ IM_PROBES_KVM_PROBES_FILES="src/im_mad/remotes/kvm-probes.d/kvm.rb \ src/im_mad/remotes/kvm-probes.d/cpu.sh \ src/im_mad/remotes/kvm-probes.d/poll.sh \ src/im_mad/remotes/kvm-probes.d/name.sh \ + src/im_mad/remotes/kvm-probes.d/pci.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" diff --git a/src/cli/one_helper/onehost_helper.rb b/src/cli/one_helper/onehost_helper.rb index 44f262daa4..60f8e56be2 100644 --- a/src/cli/one_helper/onehost_helper.rb +++ b/src/cli/one_helper/onehost_helper.rb @@ -413,11 +413,22 @@ class OneHostHelper < OpenNebulaHelper::OneHelper wilds = host.wilds + begin + pcis = [host.to_hash['HOST']['HOST_SHARE']['PCI_DEVICES']['PCI']] + pcis = pcis.flatten.compact + rescue + pcis = nil + end + host.delete_element("TEMPLATE/VM") host.delete_element("TEMPLATE_WILDS") puts host.template_str + if pcis && !pcis.empty? + print_pcis(pcis) + end + puts CLIHelper.print_header("WILD VIRTUAL MACHINES", false) puts @@ -457,4 +468,43 @@ class OneHostHelper < OpenNebulaHelper::OneHelper onevm_helper.client=@client onevm_helper.list_pool({:filter=>["HOST=#{host.name}"]}, false) end + + def print_pcis(pcis) + puts + CLIHelper.print_header("PCI DEVICES", false) + puts + + table=CLIHelper::ShowTable.new(nil, self) do + column :VM, "Used by VM", :size => 4, :left => false do |d| + if d["VMID"] == "-1" + "" + else + d["VMID"] + end + end + + column :ADDR, "PCI Address", :size => 7, :left => true do |d| + d["SHORT_ADDRESS"] + end + + column :TYPE, "Type", :size => 14, :left => true do |d| + d["TYPE"] + end + + column :CLASS, "Class", :size => 12, :left => true do |d| + d["CLASS_NAME"] + end + + column :NAME, "Name", :size => 30, :left => true do |d| + d["DEVICE_NAME"] + end + + column :VENDOR, "Vendor", :size => 8, :left => true do |d| + d["VENDOR_NAME"] + end + + end + + table.show(pcis) + end end diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 3888de3787..08bdefdb34 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -92,10 +92,11 @@ int DispatchManager::import ( time_t the_time = time(0); int cpu, mem, disk; + vector pci; - vm->get_requirements(cpu, mem, disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); vm->set_state(VirtualMachine::ACTIVE); @@ -771,6 +772,8 @@ int DispatchManager::finalize( Host * host; ostringstream oss; + vector pci; + VirtualMachine::VmState state; bool is_public_host = false; int host_id = -1; @@ -809,8 +812,8 @@ int DispatchManager::finalize( case VirtualMachine::POWEROFF: int cpu, mem, disk; - vm->get_requirements(cpu,mem,disk); - hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + vm->get_requirements(cpu, mem, disk, pci); + hpool->del_capacity(vm->get_hid(),vm->get_oid(),cpu,mem,disk,pci); if (is_public_host) { diff --git a/src/host/Host.cc b/src/host/Host.cc index 843ecb4caf..8d3273a005 100644 --- a/src/host/Host.cc +++ b/src/host/Host.cc @@ -256,6 +256,7 @@ int Host::update_info(Template &tmpl, vector::iterator it; vector vm_att; vector ds_att; + vector pci_att; vector local_ds_att; int rc; @@ -465,6 +466,10 @@ int Host::update_info(Template &tmpl, host_share.set_ds_monitorization(local_ds_att); + obj_template->remove("PCI", pci_att); + + host_share.set_pci_monitorization(pci_att); + return 0; } diff --git a/src/host/HostShare.cc b/src/host/HostShare.cc index 64b0bbb6a2..f511a7bcba 100644 --- a/src/host/HostShare.cc +++ b/src/host/HostShare.cc @@ -15,14 +15,330 @@ /* ------------------------------------------------------------------------*/ #include -#include #include #include -#include +#include +#include #include "HostShare.h" +using namespace std; + +/* ************************************************************************ */ +/* HostSharePCI */ +/* ************************************************************************ */ + +int HostSharePCI::from_xml_node(const xmlNodePtr node) +{ + int rc = Template::from_xml_node(node); + + if (rc != 0) + { + return -1; + } + + return init(); +} + +int HostSharePCI::init() +{ + vector devices; + + int num_devs = get("PCI", devices); + + for (int i=0; i < num_devs; i++) + { + VectorAttribute * pci = dynamic_cast(devices[i]); + + if (pci == 0) + { + return -1; + } + + PCIDevice * pcidev = new PCIDevice(pci); + + pci_devices.insert(make_pair(pcidev->address, pcidev)); + } + + return 0; +} + +/* ------------------------------------------------------------------------*/ +/* ------------------------------------------------------------------------*/ + +bool HostSharePCI::test(unsigned int vendor_id, unsigned int device_id, + unsigned int class_id, const VectorAttribute * devreq, + std::set& assigned) const +{ + map::const_iterator it; + + for (it=pci_devices.begin(); it!=pci_devices.end(); it++) + { + PCIDevice * dev = it->second; + + if ((class_id == 0 || dev->class_id == class_id) && + (vendor_id == 0 || dev->vendor_id == vendor_id) && + (device_id == 0 || dev->device_id == device_id) && + dev->vmid == -1 && + assigned.find(dev->address) == assigned.end()) + { + assigned.insert(dev->address); + + return true; + } + } + + return false; +} + +/* ------------------------------------------------------------------------*/ + +bool HostSharePCI::test(vector &devs) const +{ + vector::iterator it; + std::set assigned; + + unsigned int vendor_id, device_id, class_id; + + for ( it=devs.begin(); it!= devs.end(); it++) + { + VectorAttribute * pci = dynamic_cast(*it); + + if ( pci == 0 ) + { + return false; + } + + vendor_id = get_pci_value("VENDOR", pci); + device_id = get_pci_value("DEVICE", pci); + class_id = get_pci_value("CLASS", pci); + + if (!test(vendor_id, device_id, class_id, pci, assigned)) + { + return false; + } + } + + return true; +} + + +/* ------------------------------------------------------------------------*/ +/* ------------------------------------------------------------------------*/ + +bool HostSharePCI::test_set(unsigned int vendor_id, unsigned int device_id, + unsigned int class_id, VectorAttribute * devreq, int vmid, + std::set& assigned) const +{ + map::const_iterator it; + + for (it=pci_devices.begin(); it!=pci_devices.end(); it++) + { + PCIDevice * dev = it->second; + + if ((class_id == 0 || dev->class_id == class_id) && + (vendor_id == 0 || dev->vendor_id == vendor_id) && + (device_id == 0 || dev->device_id == device_id) && + dev->vmid == -1 && + assigned.find(dev->address) == assigned.end()) + { + assigned.insert(dev->address); + + if (vmid != -1) + { + dev->vmid = vmid; + dev->attrs->replace("VMID", vmid); + + devreq->replace("DOMAIN",dev->attrs->vector_value("DOMAIN")); + devreq->replace("BUS",dev->attrs->vector_value("BUS")); + devreq->replace("SLOT",dev->attrs->vector_value("SLOT")); + devreq->replace("FUNCTION",dev->attrs->vector_value("FUNCTION")); + + devreq->replace("ADDRESS",dev->attrs->vector_value("ADDRESS")); + } + + return true; + } + } + + return false; +} + +/* ------------------------------------------------------------------------*/ + +bool HostSharePCI::test_set(vector &devs, int vmid) const +{ + vector::iterator it; + std::set assigned; + + unsigned int vendor_id, device_id, class_id; + + for ( it=devs.begin(); it!= devs.end(); it++) + { + VectorAttribute * pci = dynamic_cast(*it); + + if ( pci == 0 ) + { + return false; + } + + vendor_id = get_pci_value("VENDOR", pci); + device_id = get_pci_value("DEVICE", pci); + class_id = get_pci_value("CLASS", pci); + + if (!test_set(vendor_id, device_id, class_id, pci, vmid, assigned)) + { + return false; + } + } + + return true; +} + +/* ------------------------------------------------------------------------*/ +/* ------------------------------------------------------------------------*/ + +void HostSharePCI::del(const vector &devs) +{ + vector::const_iterator it; + map::iterator pci_it; + + for ( it=devs.begin(); it!= devs.end(); it++) + { + const VectorAttribute * pci = dynamic_cast(*it); + + if ( pci == 0 ) + { + continue; + } + + pci_it = pci_devices.find(pci->vector_value("ADDRESS")); + + if (pci_it != pci_devices.end()) + { + pci_it->second->vmid = -1; + pci_it->second->attrs->replace("VMID",-1); + } + } +}; + +/* ------------------------------------------------------------------------*/ +/* ------------------------------------------------------------------------*/ + +void HostSharePCI::set_monitorization(vector &pci_att) +{ + vector::iterator it; + map::iterator pci_it; + + string address; + + for (it = pci_att.begin(); it != pci_att.end(); it++) + { + VectorAttribute * pci = dynamic_cast(*it); + + if ( pci == 0 ) + { + continue; + } + + address = pci->vector_value("ADDRESS"); + + if (address.empty()) + { + delete pci; + continue; + } + + pci_it = pci_devices.find(address); + + if (pci_it != pci_devices.end()) + { + delete pci; + continue; + } + + PCIDevice * dev = new PCIDevice(pci); + + pci_devices.insert(make_pair(address, dev)); + + set(pci); + } +}; + +/* ------------------------------------------------------------------------*/ +/* ------------------------------------------------------------------------*/ + +unsigned int HostSharePCI::get_pci_value(const char * name, + const VectorAttribute * pci_device) +{ + string temp; + + temp = pci_device->vector_value(name); + + if (temp.empty()) + { + return 0; + } + + unsigned int pci_value; + istringstream iss(temp); + + iss >> hex >> pci_value; + + if (iss.fail() || !iss.eof()) + { + return 0; + } + + return pci_value; +} + +/* ------------------------------------------------------------------------*/ +/* ------------------------------------------------------------------------*/ + +HostSharePCI::PCIDevice::PCIDevice(VectorAttribute * _attrs) + : vmid(-1), attrs(_attrs) +{ + vendor_id = get_pci_value("VENDOR", attrs); + device_id = get_pci_value("DEVICE", attrs); + class_id = get_pci_value("CLASS", attrs); + + if (attrs->vector_value("VMID", vmid) == -1) + { + attrs->replace("VMID", -1); + } + + attrs->vector_value("ADDRESS", address); +}; + +/* ------------------------------------------------------------------------*/ +/* ------------------------------------------------------------------------*/ + +ostream& operator<<(ostream& os, const HostSharePCI& pci) +{ + map::const_iterator it; + + os << right << setw(15)<< "PCI ADDRESS"<< " " + << right << setw(8) << "CLASS" << " " + << right << setw(8) << "VENDOR" << " " + << right << setw(8) << "DEVICE" << " " + << right << setw(8) << "VMID" << " " + << endl << setw(55) << setfill('-') << "-" << setfill(' ') << endl; + + for (it=pci.pci_devices.begin(); it!=pci.pci_devices.end(); it++) + { + HostSharePCI::PCIDevice * dev = it->second; + + os << right << setw(15)<< dev->address << " " + << right << setw(8) << dev->class_id << " " + << right << setw(8) << dev->vendor_id << " " + << right << setw(8) << dev->device_id << " " + << right << setw(8) << dev->vmid << " " << endl; + } + + return os; +} /* ************************************************************************ */ /* HostShare :: Constructor/Destructor */ /* ************************************************************************ */ @@ -57,7 +373,7 @@ ostream& operator<<(ostream& os, HostShare& hs) string& HostShare::to_xml(string& xml) const { - string template_xml; + string ds_xml, pci_xml; ostringstream oss; oss << "" @@ -74,7 +390,8 @@ string& HostShare::to_xml(string& xml) const << "" << used_mem << "" << "" << used_cpu << "" << ""<" - << ds_template.to_xml(template_xml) + << ds.to_xml(ds_xml) + << pci.to_xml(pci_xml) << ""; xml = oss.str(); @@ -111,7 +428,7 @@ int HostShare::from_xml_node(const xmlNodePtr node) rc += xpath(running_vms,"/HOST_SHARE/RUNNING_VMS",-1); - // ------------ DS Template --------------- + // ------------ Datastores --------------- ObjectXML::get_nodes("/HOST_SHARE/DATASTORES", content); @@ -120,7 +437,27 @@ int HostShare::from_xml_node(const xmlNodePtr node) return -1; } - rc += ds_template.from_xml_node( content[0] ); + rc += ds.from_xml_node( content[0] ); + + ObjectXML::free_nodes(content); + + content.clear(); + + if (rc != 0) + { + return -1; + } + + // ------------ PCI Devices --------------- + + ObjectXML::get_nodes("/HOST_SHARE/PCI_DEVICES", content); + + if( content.empty()) + { + return -1; + } + + rc += pci.from_xml_node( content[0] ); ObjectXML::free_nodes(content); @@ -134,14 +471,22 @@ int HostShare::from_xml_node(const xmlNodePtr node) return 0; } + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + void HostShare::set_ds_monitorization(const vector &ds_att) { vector::const_iterator it; - ds_template.erase("DS"); + ds.erase("DS"); for (it = ds_att.begin(); it != ds_att.end(); it++) { - ds_template.set(*it); + ds.set(*it); } } + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + diff --git a/src/im_mad/dummy/one_im_dummy.rb b/src/im_mad/dummy/one_im_dummy.rb index fa141c9c46..0686dfd331 100755 --- a/src/im_mad/dummy/one_im_dummy.rb +++ b/src/im_mad/dummy/one_im_dummy.rb @@ -69,6 +69,52 @@ class DummyInformationManager < OpenNebulaDriver results << "DS_LOCATION_TOTAL_MB=20480\n" results << "DS_LOCATION_FREE_MB=20480\n" + results << "PCI = [ + ADDRESS = \"0000:02:00:0\",\n + BUS = \"02\",\n + CLASS = \"0300\",\n + CLASS_NAME = \"VGA compatible controller\",\n + DEVICE = \"0863\",\n + DEVICE_NAME = \"C79 [GeForce 9400M]\",\n + DOMAIN = \"0000\",\n + FUNCTION = \"0\",\n + SHORT_ADDRESS = \"02:00.0\",\n + SLOT = \"00\",\n + TYPE = \"10de:0863:0300\",\n + VENDOR = \"10de\",\n + VENDOR_NAME = \"NVIDIA Corporation\"\n + ]\n + PCI = [ + ADDRESS = \"0000:00:06:0\",\n + BUS = \"00\",\n + CLASS = \"0c03\",\n + CLASS_NAME = \"USB controller\",\n + DEVICE = \"0aa7\",\n + DEVICE_NAME = \"MCP79 OHCI USB 1.1 Controller\",\n + DOMAIN = \"0000\",\n + FUNCTION = \"0\",\n + SHORT_ADDRESS = \"00:06.0\",\n + SLOT = \"06\",\n + TYPE = \"10de:0aa7:0c03\",\n + VENDOR = \"10de\",\n + VENDOR_NAME = \"NVIDIA Corporation\"\n + ]\n + PCI = [ + ADDRESS = \"0000:00:06:1\",\n + BUS = \"00\",\n + CLASS = \"0c03\",\n + CLASS_NAME = \"USB controller\",\n + DEVICE = \"0aa9\",\n + DEVICE_NAME = \"MCP79 EHCI USB 2.0 Controller\",\n + DOMAIN = \"0000\",\n + FUNCTION = \"1\",\n + SHORT_ADDRESS = \"00:06.1\",\n + SLOT = \"06\",\n + TYPE = \"10de:0aa9:0c03\",\n + VENDOR = \"10de\",\n + VENDOR_NAME = \"NVIDIA Corporation\"\n + ]\n" + results = Base64::encode64(results).strip.delete("\n") send_message("MONITOR", RESULT[:success], number, results) diff --git a/src/im_mad/remotes/kvm-probes.d/pci.rb b/src/im_mad/remotes/kvm-probes.d/pci.rb new file mode 100755 index 0000000000..565cc25569 --- /dev/null +++ b/src/im_mad/remotes/kvm-probes.d/pci.rb @@ -0,0 +1,114 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2015, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# 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 variable contains the filters for PCI card monitoring. The format +# is the same as lspci and several filters can be added separated by commas. +# A nil filter will retrieve all PCI cards. +# +# From lspci help: +# -d []:[][:] +# +# For example +# +# FILTER = '::0300' # all VGA cards +# FILTER = '10de::0300' # all NVIDIA VGA cards +# FILTER = '10de:11bf:0300' # only GK104GL [GRID K2] +# FILTER = '8086::0300,::0106' # all Intel VGA cards and any SATA controller + +FILTER = nil + +require 'shellwords' + +def get_pci(filter=nil) + command = "lspci -mmnn" + command << " -d #{filter}" if filter + + text = %x(#{command}) + + text.split("\n").map {|l| Shellwords.split(l) } +end + +def get_name_and_id(text) + m = text.match(/^(.*) \[(....)\]$/) + return m[1], m[2] +end + +def parse_pci(pci) + card = {} + + card[:short_address] = pci[0] + card[:libvirt_address] = + "pci_0000_#{card[:short_address].gsub(/[:.]/, '_')}" + card[:address] = "0000:#{card[:short_address].gsub(/[:.]/, ':')}" + + 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]) + + card[:bus], card[:slot], card[:function] = pci[0].split(/[:.]/) + + card[:type] = [card[:vendor], card[:device], card[:class]].join(':') + + card +end + +def get_devices(filter=nil) + if filter + filter = filter.split(',') + else + filter = [nil] + end + + filter.map do |f| + get_pci(f).map {|pci| parse_pci(pci) } + end.flatten +end + +filter = FILTER + +devices = get_devices(filter) + +def pval(name, value) + %Q( #{name} = "#{value}") +end + +devices.each do |dev| + puts "PCI = [" + values = [ + pval('TYPE', dev[:type]), + pval('VENDOR', dev[:vendor]), + pval('VENDOR_NAME', dev[:vendor_name]), + pval('DEVICE', dev[:device]), + pval('DEVICE_NAME', dev[:device_name]), + pval('CLASS', dev[:class]), + pval('CLASS_NAME', dev[:class_name]), + pval('ADDRESS', dev[:address]), + pval('SHORT_ADDRESS', dev[:short_address]), + pval('DOMAIN', '0000'), + pval('BUS', dev[:bus]), + pval('SLOT', dev[:slot]), + pval('FUNCTION', dev[:function]) + ] + + puts values.join(",\n") + puts "]" +end + + + diff --git a/src/lcm/LifeCycleActions.cc b/src/lcm/LifeCycleActions.cc index 84785d7dbb..b68f30873e 100644 --- a/src/lcm/LifeCycleActions.cc +++ b/src/lcm/LifeCycleActions.cc @@ -35,6 +35,7 @@ void LifeCycleManager::deploy_action(int vid) { time_t thetime = time(0); int cpu,mem,disk; + vector pci; VirtualMachine::LcmState vm_state; TransferManager::Actions tm_action; @@ -43,7 +44,7 @@ void LifeCycleManager::deploy_action(int vid) // PROLOG STATE //---------------------------------------------------- - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); vm_state = VirtualMachine::PROLOG; tm_action = TransferManager::PROLOG; @@ -75,7 +76,7 @@ void LifeCycleManager::deploy_action(int vid) //---------------------------------------------------- - if (hpool->add_capacity(vm->get_hid(),vm->get_oid(),cpu,mem,disk) == -1) + if (hpool->add_capacity(vm->get_hid(),vm->get_oid(),cpu,mem,disk,pci) == -1) { //The host has been deleted, move VM to FAILURE this->trigger(LifeCycleManager::PROLOG_FAILURE, vid); @@ -211,6 +212,8 @@ void LifeCycleManager::migrate_action(int vid) VirtualMachine * vm; int cpu, mem, disk; + vector pci; + time_t the_time = time(0); vm = vmpool->get(vid,true); @@ -239,9 +242,9 @@ void LifeCycleManager::migrate_action(int vid) vmpool->update_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); //---------------------------------------------------- @@ -278,11 +281,12 @@ void LifeCycleManager::migrate_action(int vid) vmpool->update_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); - hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem, disk); + hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem, + disk, pci); //---------------------------------------------------- @@ -321,11 +325,12 @@ void LifeCycleManager::migrate_action(int vid) vmpool->update_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); - hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem, disk); + hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem, + disk, pci); //---------------------------------------------------- @@ -359,7 +364,8 @@ void LifeCycleManager::live_migrate_action(int vid) if (vm->get_state() == VirtualMachine::ACTIVE && vm->get_lcm_state() == VirtualMachine::RUNNING) { - int cpu,mem,disk; + int cpu, mem, disk; + vector pci; //---------------------------------------------------- // MIGRATE STATE @@ -377,9 +383,9 @@ void LifeCycleManager::live_migrate_action(int vid) vmpool->update_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->add_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); //---------------------------------------------------- @@ -845,6 +851,7 @@ void LifeCycleManager::clean_action(int vid) void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose, int& image_id) { int cpu, mem, disk; + vector pci; time_t the_time = time(0); VirtualMachine::LcmState state = vm->get_lcm_state(); @@ -873,8 +880,8 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose, int& imag vm->set_vm_info(); vm->set_reason(History::USER); - vm->get_requirements(cpu,mem,disk); - hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + vm->get_requirements(cpu, mem, disk, pci); + hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); switch (state) { @@ -1005,7 +1012,7 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose, int& imag vmpool->update_previous_history(vm); hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, - mem, disk); + mem, disk, pci); vmm->trigger(VirtualMachineManager::DRIVER_CANCEL,vid); vmm->trigger(VirtualMachineManager::CLEANUP_BOTH,vid); @@ -1031,7 +1038,7 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose, int& imag vmpool->update_previous_history(vm); hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, - mem, disk); + mem, disk, pci); vmm->trigger(VirtualMachineManager::DRIVER_CANCEL,vid); vmm->trigger(VirtualMachineManager::CLEANUP_PREVIOUS,vid); diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 9691b5c258..44e9baeab0 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -37,8 +37,10 @@ void LifeCycleManager::save_success_action(int vid) if ( vm->get_lcm_state() == VirtualMachine::SAVE_MIGRATE ) { - int cpu,mem,disk; - time_t the_time = time(0); + int cpu, mem, disk; + vector pci; + + time_t the_time = time(0); //---------------------------------------------------- // PROLOG_MIGRATE STATE @@ -66,9 +68,9 @@ void LifeCycleManager::save_success_action(int vid) vmpool->update_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem, disk); + hpool->del_capacity(vm->get_previous_hid(),vm->get_oid(),cpu,mem,disk,pci); //---------------------------------------------------- @@ -154,8 +156,10 @@ void LifeCycleManager::save_failure_action(int vid) if ( vm->get_lcm_state() == VirtualMachine::SAVE_MIGRATE ) { - int cpu,mem,disk; - time_t the_time = time(0); + int cpu, mem, disk; + vector pci; + + time_t the_time = time(0); //---------------------------------------------------- // RUNNING STATE FROM SAVE_MIGRATE @@ -171,9 +175,9 @@ void LifeCycleManager::save_failure_action(int vid) vmpool->update_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); vm->set_previous_etime(the_time); @@ -256,8 +260,10 @@ void LifeCycleManager::deploy_success_action(int vid) if ( vm->get_lcm_state() == VirtualMachine::MIGRATE ) { - int cpu,mem,disk; - time_t the_time = time(0); + int cpu,mem,disk; + vector pci; + + time_t the_time = time(0); vm->set_running_stime(the_time); @@ -275,9 +281,9 @@ void LifeCycleManager::deploy_success_action(int vid) vmpool->update_previous_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->del_capacity(vm->get_previous_hid(), vm->get_oid(), cpu, mem, disk); + hpool->del_capacity(vm->get_previous_hid(),vm->get_oid(),cpu,mem,disk,pci); vm->set_state(VirtualMachine::RUNNING); @@ -328,8 +334,10 @@ void LifeCycleManager::deploy_failure_action(int vid) if ( vm->get_lcm_state() == VirtualMachine::MIGRATE ) { - int cpu,mem,disk; - time_t the_time = time(0); + int cpu, mem, disk; + vector pci; + + time_t the_time = time(0); //---------------------------------------------------- // RUNNING STATE FROM MIGRATE @@ -357,9 +365,9 @@ void LifeCycleManager::deploy_failure_action(int vid) vmpool->update_previous_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); // --- Add new record by copying the previous one @@ -789,8 +797,10 @@ void LifeCycleManager::prolog_failure_action(int vid) void LifeCycleManager::epilog_success_action(int vid) { VirtualMachine * vm; - time_t the_time = time(0); - int cpu,mem,disk; + vector pci; + + time_t the_time = time(0); + int cpu,mem,disk; VirtualMachine::LcmState state; DispatchManager::Actions action; @@ -859,9 +869,9 @@ void LifeCycleManager::epilog_success_action(int vid) vmpool->update_history(vm); - vm->get_requirements(cpu,mem,disk); + vm->get_requirements(cpu, mem, disk, pci); - hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk); + hpool->del_capacity(vm->get_hid(), vm->get_oid(), cpu, mem, disk, pci); //---------------------------------------------------- diff --git a/src/onedb/fsck.rb b/src/onedb/fsck.rb index 6e23dc8a8a..aec7dd5343 100644 --- a/src/onedb/fsck.rb +++ b/src/onedb/fsck.rb @@ -938,7 +938,7 @@ EOT # Calculate the host's xml and write them to host_pool_new @db.transaction do @db[:host_pool].each do |row| - host_doc = Document.new(row[:body]) + host_doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} hid = row[:oid] @@ -949,54 +949,67 @@ EOT mem_usage = counters_host[:memory]*1024 # rewrite running_vms - host_doc.root.each_element("HOST_SHARE/RUNNING_VMS") {|e| + host_doc.root.xpath("HOST_SHARE/RUNNING_VMS").each {|e| if e.text != rvms.to_s log_error( "Host #{hid} RUNNING_VMS has #{e.text} \tis\t#{rvms}") - e.text = rvms + e.content = rvms end } # re-do list of VM IDs - vms_elem = host_doc.root.elements.delete("VMS") + vms_elem = host_doc.root.at_xpath("VMS").remove - vms_new_elem = host_doc.root.add_element("VMS") + vms_new_elem = host_doc.create_element("VMS") + host_doc.root.add_child(vms_new_elem) counters_host[:rvms].each do |id| - id_elem = vms_elem.elements.delete("ID[.=#{id}]") + id_elem = vms_elem.at_xpath("ID[.=#{id}]") if id_elem.nil? log_error( "VM #{id} is missing from Host #{hid} VM id list") + else + id_elem.remove end - vms_new_elem.add_element("ID").text = id.to_s + vms_new_elem.add_child(host_doc.create_element("ID")).content = id.to_s end - vms_elem.each_element("ID") do |id_elem| + vms_elem.xpath("ID").each do |id_elem| log_error( "VM #{id_elem.text} is in Host #{hid} VM id list, "<< "but it should not") end + host_doc.root.xpath("HOST_SHARE/PCI_DEVICES/PCI").each do |pci| + if !pci.at_xpath("VMID").nil? + vmid = pci.at_xpath("VMID").text.to_i + + if vmid != -1 && !counters_host[:rvms].include?(vmid) + log_error("VM #{vmid} has a PCI device assigned in host #{hid}, but it should not. Device: #{pci.at_xpath('DEVICE_NAME').text}") + pci.at_xpath("VMID").content = "-1" + end + end + end # rewrite cpu - host_doc.root.each_element("HOST_SHARE/CPU_USAGE") {|e| + host_doc.root.xpath("HOST_SHARE/CPU_USAGE").each {|e| if e.text != cpu_usage.to_s log_error( "Host #{hid} CPU_USAGE has #{e.text} "<< "\tis\t#{cpu_usage}") - e.text = cpu_usage + e.content = cpu_usage end } # rewrite memory - host_doc.root.each_element("HOST_SHARE/MEM_USAGE") {|e| + host_doc.root.xpath("HOST_SHARE/MEM_USAGE").each {|e| if e.text != mem_usage.to_s log_error("Host #{hid} MEM_USAGE has #{e.text} "<< "\tis\t#{mem_usage}") - e.text = mem_usage + e.content = mem_usage end } diff --git a/src/onedb/local/4.13.80_to_4.13.85.rb b/src/onedb/local/4.13.80_to_4.13.85.rb index 551438276d..244492b022 100644 --- a/src/onedb/local/4.13.80_to_4.13.85.rb +++ b/src/onedb/local/4.13.80_to_4.13.85.rb @@ -113,6 +113,34 @@ module Migrator log_time() + @db.run "ALTER TABLE host_pool RENAME TO old_host_pool;" + @db.run "CREATE TABLE host_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, state INTEGER, last_mon_time INTEGER, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, cid INTEGER, UNIQUE(name));" + + @db.transaction do + @db.fetch("SELECT * FROM old_host_pool") do |row| + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + + doc.root.at_xpath("HOST_SHARE").add_child(doc.create_element("PCI_DEVICES")) + + @db[:host_pool].insert( + :oid => row[:oid], + :name => row[:name], + :body => doc.root.to_s, + :state => row[:state], + :last_mon_time => row[:last_mon_time], + :uid => row[:uid], + :gid => row[:gid], + :owner_u => row[:owner_u], + :group_u => row[:group_u], + :other_u => row[:other_u], + :cid => row[:cid]) + end + end + + @db.run "DROP TABLE old_host_pool;" + + log_time() + return true end diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index b02a5bc42e..406e948e6e 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -375,17 +375,20 @@ int RequestManagerVirtualMachine::get_host_information( /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -bool RequestManagerVirtualMachine::check_host(int hid, - int cpu, - int mem, - int disk, - string& error) +bool RequestManagerVirtualMachine::check_host( + int hid, bool enforce, VirtualMachine* vm, string& error) { Nebula& nd = Nebula::instance(); HostPool * hpool = nd.get_hpool(); Host * host; bool test; + string capacity_error; + + int cpu, mem, disk; + vector pci; + + vm->get_requirements(cpu, mem, disk, pci); host = hpool->get(hid, true); @@ -395,14 +398,22 @@ bool RequestManagerVirtualMachine::check_host(int hid, return false; } - test = host->test_capacity(cpu, mem, disk); + if (!enforce) + { + cpu = 0; + mem = 0; + disk = 0; + } + + test = host->test_capacity(cpu, mem, disk, pci, capacity_error); if (!test) { ostringstream oss; - oss << object_name(PoolObjectSQL::HOST) - << " " << hid << " does not have enough capacity."; + oss << object_name(PoolObjectSQL::VM) << " [" << vm->get_oid() + << "] does not fit in " << object_name(PoolObjectSQL::HOST) + << " [" << hid << "]. " << capacity_error; error = oss.str(); } @@ -662,6 +673,8 @@ void VirtualMachineDeploy::request_execute(xmlrpc_c::paramList const& paramList, bool auth = false; + string error; + // ------------------------------------------------------------------------ // Get request parameters and information about the target host // ------------------------------------------------------------------------ @@ -814,25 +827,12 @@ void VirtualMachineDeploy::request_execute(xmlrpc_c::paramList const& paramList, return; } - if (enforce) + if (check_host(hid, enforce, vm, error) == false) { - int cpu, mem, disk; - string error; - - vm->get_requirements(cpu, mem, disk); - vm->unlock(); - if (check_host(hid, cpu, mem, disk, error) == false) - { - failure_response(ACTION, request_error(error,""), att); - return; - } - - if ((vm = get_vm(id, att)) == 0) - { - return; - } + failure_response(ACTION, request_error(error,""), att); + return; } // ------------------------------------------------------------------------ @@ -897,6 +897,8 @@ void VirtualMachineMigrate::request_execute(xmlrpc_c::paramList const& paramList bool auth = false; + string error; + // ------------------------------------------------------------------------ // Get request parameters and information about the target host // ------------------------------------------------------------------------ @@ -1031,26 +1033,16 @@ void VirtualMachineMigrate::request_execute(xmlrpc_c::paramList const& paramList } // Check the host has enough capacity - if (enforce) - { - int cpu, mem, disk; - string error; - - vm->get_requirements(cpu, mem, disk); - - vm->unlock(); - - if (check_host(hid, cpu, mem, disk, error) == false) - { - failure_response(ACTION, request_error(error,""), att); - return; - } - } - else + if (check_host(hid, enforce, vm, error) == false) { vm->unlock(); + + failure_response(ACTION, request_error(error,""), att); + return; } + vm->unlock(); + // Check we are in the same cluster Host * host = nd.get_hpool()->get(c_hid, true); @@ -1831,6 +1823,9 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList, int dcpu_host = (int) (dcpu * 100);//now in 100% int dmem_host = dmemory * 1024; //now in Kilobytes + vector empty_pci; + string error; + host = hpool->get(hid, true); if (host == 0) @@ -1844,7 +1839,7 @@ void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList, return; } - if ( enforce && host->test_capacity(dcpu_host, dmem_host, 0) == false) + if ( enforce && host->test_capacity(dcpu_host, dmem_host, 0, empty_pci, error) == false) { ostringstream oss; diff --git a/src/scheduler/include/HostXML.h b/src/scheduler/include/HostXML.h index fb9cb2504d..ec60fb484a 100644 --- a/src/scheduler/include/HostXML.h +++ b/src/scheduler/include/HostXML.h @@ -20,6 +20,7 @@ #include #include "ObjectXML.h" +#include "HostShare.h" using namespace std; @@ -50,21 +51,24 @@ public: * 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 error error message * @return true if the share can host the VM */ - bool test_capacity(long long cpu, long long mem, string & error) const; + bool test_capacity(long long cpu, long long mem, vector &pci, + string & error); /** * Tests whether a new VM can be hosted by the host or not * @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) const + bool test_capacity(long long cpu,long long mem,vector &p) { string tmp_st; - return test_capacity(cpu, mem, tmp_st); + return test_capacity(cpu, mem, p, tmp_st); }; /** @@ -74,11 +78,14 @@ public: * @param mem needed by the VM (in KB) * @return 0 on success */ - void add_capacity(long long cpu, long long mem) + void add_capacity(int vmid, long long cpu, long long mem, + vector &p) { cpu_usage += cpu; mem_usage += mem; + pci.add(p, vmid); + running_vms++; }; @@ -131,6 +138,12 @@ public: 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); + private: int oid; int cluster_id; @@ -150,6 +163,8 @@ private: bool public_cloud; + HostSharePCI pci; + // Configuration attributes static const char *host_paths[]; /**< paths for search function */ diff --git a/src/scheduler/include/VirtualMachineXML.h b/src/scheduler/include/VirtualMachineXML.h index 4539608df3..521edf8a48 100644 --- a/src/scheduler/include/VirtualMachineXML.h +++ b/src/scheduler/include/VirtualMachineXML.h @@ -116,7 +116,8 @@ public: return ds_requirements; } - void get_requirements (int& cpu, int& memory, long long& disk); + void get_requirements (int& cpu, int& memory, long long& disk, + vector &pci); map get_storage_usage(); diff --git a/src/scheduler/src/pool/HostPoolXML.cc b/src/scheduler/src/pool/HostPoolXML.cc index df026290ea..cf54faa943 100644 --- a/src/scheduler/src/pool/HostPoolXML.cc +++ b/src/scheduler/src/pool/HostPoolXML.cc @@ -29,11 +29,13 @@ int HostPoolXML::set_up() { oss << "Discovered Hosts (enabled):" << endl; - map::iterator it; + map::iterator it; for (it=objects.begin();it!=objects.end();it++) { - oss << " " << it->first; + HostXML * h = dynamic_cast(it->second); + + oss << *h << endl; } } else diff --git a/src/scheduler/src/pool/HostXML.cc b/src/scheduler/src/pool/HostXML.cc index 17ce4fe201..a6cc8a59ff 100644 --- a/src/scheduler/src/pool/HostXML.cc +++ b/src/scheduler/src/pool/HostXML.cc @@ -16,6 +16,8 @@ #include #include +#include +#include #include "HostXML.h" #include "NebulaUtil.h" @@ -36,36 +38,50 @@ const char *HostXML::host_paths[] = { void HostXML::init_attributes() { - xpath(oid, "/HOST/ID", -1); - xpath(cluster_id, "/HOST/CLUSTER_ID", -1); - xpath(mem_usage, "/HOST/HOST_SHARE/MEM_USAGE", 0); - xpath(cpu_usage, "/HOST/HOST_SHARE/CPU_USAGE", 0); - xpath(max_mem, "/HOST/HOST_SHARE/MAX_MEM", 0); - xpath(max_cpu, "/HOST/HOST_SHARE/MAX_CPU", 0); - xpath(free_disk, "/HOST/HOST_SHARE/FREE_DISK", 0); - xpath(running_vms, "/HOST/HOST_SHARE/RUNNING_VMS", 0); + xpath(oid, "/HOST/ID", -1); + xpath(cluster_id, "/HOST/CLUSTER_ID", -1); + xpath(mem_usage, "/HOST/HOST_SHARE/MEM_USAGE", 0); + xpath(cpu_usage, "/HOST/HOST_SHARE/CPU_USAGE", 0); + xpath(max_mem, "/HOST/HOST_SHARE/MAX_MEM", 0); + xpath(max_cpu, "/HOST/HOST_SHARE/MAX_CPU", 0); + xpath(free_disk, "/HOST/HOST_SHARE/FREE_DISK", 0); + xpath(running_vms, "/HOST/HOST_SHARE/RUNNING_VMS", 0); string public_cloud_st; xpath(public_cloud_st, "/HOST/TEMPLATE/PUBLIC_CLOUD", ""); public_cloud = (one_util::toupper(public_cloud_st) == "YES"); - vector ds_ids = (*this)["/HOST/HOST_SHARE/DATASTORES/DS/ID"]; - vector ds_free_mb = (*this)["/HOST/HOST_SHARE/DATASTORES/DS/FREE_MB"]; + //-------------------- HostShare Datastores ------------------------------ + vector ds_ids = (*this)["/HOST/HOST_SHARE/DATASTORES/DS/ID"]; + vector ds_free = (*this)["/HOST/HOST_SHARE/DATASTORES/DS/FREE_MB"]; int id; long long disk; - for (size_t i = 0; i < ds_ids.size() && i < ds_free_mb.size(); i++) + 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_mb[i].c_str()); + disk = atoll(ds_free[i].c_str()); ds_free_disk[id] = disk; } - //Init search xpath routes + //-------------------- HostShare PCI Devices ------------------------------ + vector content; + get_nodes("/HOST/HOST_SHARE/PCI_DEVICES", content); + + if( !content.empty()) + { + pci.from_xml_node(content[0]); + + free_nodes(content); + + content.clear(); + } + + //-------------------- Init search xpath routes --------------------------- ObjectXML::paths = host_paths; ObjectXML::num_paths = host_num_paths; } @@ -111,30 +127,46 @@ int HostXML::search(const char *name, int& value) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -bool HostXML::test_capacity(long long cpu, long long mem, string & error) const +bool HostXML::test_capacity(long long cpu, long long mem, + vector& p, string & error) { - bool fits = (((max_cpu - cpu_usage ) >= cpu) && - ((max_mem - mem_usage ) >= mem)); - + 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) { - ostringstream oss; + 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"; + 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(); + error = oss.str(); + } + else + { + error = "Unavailable PCI device."; + } } else { - error = "Not enough capacity."; + if ( pci_fits ) + { + error = "Not enough capacity."; + } + else + { + error = "Unavailable PCI device."; + } } } @@ -169,3 +201,33 @@ void HostXML::add_ds_capacity(int dsid, long long vm_disk_mb) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +ostream& operator<<(ostream& o, const HostXML& p) +{ + map::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 << "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; + + return o; +} diff --git a/src/scheduler/src/pool/VirtualMachinePoolXML.cc b/src/scheduler/src/pool/VirtualMachinePoolXML.cc index 16c9a23e3e..e4acb9f0f2 100644 --- a/src/scheduler/src/pool/VirtualMachinePoolXML.cc +++ b/src/scheduler/src/pool/VirtualMachinePoolXML.cc @@ -39,6 +39,7 @@ int VirtualMachinePoolXML::set_up() << right << setw(8) << "VM" << " " << right << setw(4) << "CPU" << " " << right << setw(11) << "Memory" << " " + << right << setw(3) << "PCI" << " " << right << setw(11) << "System DS" << " " << " Image DS" << endl << setw(60) << setfill('-') << "-" << setfill(' '); @@ -47,11 +48,13 @@ int VirtualMachinePoolXML::set_up() { int cpu, mem; long long disk; + vector pci; + string action = "DEPLOY"; VirtualMachineXML * vm = static_cast(it->second); - vm->get_requirements(cpu, mem, disk); + vm->get_requirements(cpu, mem, disk, pci); if (vm->is_resched()) { @@ -67,6 +70,7 @@ int VirtualMachinePoolXML::set_up() << right << setw(8) << it->first << " " << right << setw(4) << cpu << " " << right << setw(11) << mem << " " + << right << setw(3) << pci.size() << " " << right << setw(11) << disk << " "; map ds_usage = vm->get_storage_usage(); diff --git a/src/scheduler/src/pool/VirtualMachineXML.cc b/src/scheduler/src/pool/VirtualMachineXML.cc index b6ab5d2c8d..97c26a1888 100644 --- a/src/scheduler/src/pool/VirtualMachineXML.cc +++ b/src/scheduler/src/pool/VirtualMachineXML.cc @@ -202,8 +202,16 @@ ostream& operator<<(ostream& os, VirtualMachineXML& vm) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void VirtualMachineXML::get_requirements (int& cpu, int& memory, long long& disk) +void VirtualMachineXML::get_requirements (int& cpu, int& memory, + long long& disk, vector &pci) { + pci.clear(); + + if (vm_template != 0) + { + vm_template->get("PCI", pci); + } + if (this->memory == 0 || this->cpu == 0) { cpu = 0; diff --git a/src/scheduler/src/sched/SConstruct b/src/scheduler/src/sched/SConstruct index c7f4c05836..1b09ba5a6d 100644 --- a/src/scheduler/src/sched/SConstruct +++ b/src/scheduler/src/sched/SConstruct @@ -39,6 +39,7 @@ sched_env.Prepend(LIBS=[ 'nebula_core', 'nebula_template', 'nebula_vm', + 'nebula_host', 'crypto', 'xml2' ]) diff --git a/src/scheduler/src/sched/Scheduler.cc b/src/scheduler/src/sched/Scheduler.cc index e059b86d3a..0ed226f76e 100644 --- a/src/scheduler/src/sched/Scheduler.cc +++ b/src/scheduler/src/sched/Scheduler.cc @@ -502,6 +502,7 @@ int Scheduler::set_up_pools() * @param vm the virtual machine * @param vm_memory vm requirement * @param vm_cpu vm requirement + * @param vm_pci vm requirement * @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 @@ -511,8 +512,8 @@ int Scheduler::set_up_pools() * @return true for a positive match */ static bool match_host(AclXML * acls, VirtualMachineXML* vm, int vmem, int vcpu, - HostXML * host, int &n_auth, int& n_error, int &n_fits, int &n_matched, - string &error) + vector& vpci, HostXML * host, int &n_auth, int& n_error, + int &n_fits, int &n_matched, string &error) { // ------------------------------------------------------------------------- // Filter current Hosts for resched VMs @@ -560,7 +561,7 @@ static bool match_host(AclXML * acls, VirtualMachineXML* vm, int vmem, int vcpu, // ------------------------------------------------------------------------- // Check host capacity // ------------------------------------------------------------------------- - if (host->test_capacity(vcpu,vmem,error) != true) + if (host->test_capacity(vcpu, vmem, vpci, error) != true) { return false; } @@ -720,6 +721,7 @@ void Scheduler::match_schedule() int vm_memory; int vm_cpu; long long vm_disk; + vector vm_pci; int n_resources; int n_matched; @@ -750,7 +752,7 @@ void Scheduler::match_schedule() { vm = static_cast(vm_it->second); - vm->get_requirements(vm_cpu,vm_memory,vm_disk); + vm->get_requirements(vm_cpu, vm_memory, vm_disk, vm_pci); n_resources = 0; n_fits = 0; @@ -790,8 +792,8 @@ void Scheduler::match_schedule() { host = static_cast(h_it->second); - if (match_host(acls, vm, vm_memory, vm_cpu, host, n_auth, n_error, - n_fits, n_matched, m_error)) + if (match_host(acls, vm, vm_memory, vm_cpu, vm_pci, host, n_auth, + n_error, n_fits, n_matched, m_error)) { vm->add_match_host(host->get_hid()); @@ -1026,6 +1028,8 @@ void Scheduler::dispatch() int cpu, mem; long long dsk; + vector pci; + int hid, dsid, cid; bool test_cap_result; @@ -1074,7 +1078,7 @@ void Scheduler::dispatch() } } - vm->get_requirements(cpu,mem,dsk); + vm->get_requirements(cpu, mem, dsk, pci); //---------------------------------------------------------------------- // Get the highest ranked host and best System DS for it @@ -1094,7 +1098,7 @@ void Scheduler::dispatch() //------------------------------------------------------------------ // Test host capacity //------------------------------------------------------------------ - if (host->test_capacity(cpu,mem) != true) + if (host->test_capacity(cpu, mem, pci) != true) { continue; } @@ -1224,7 +1228,7 @@ void Scheduler::dispatch() vm->add_image_datastore_capacity(img_dspool); } - host->add_capacity(cpu,mem); + host->add_capacity(vm->get_oid(), cpu, mem, pci); host_vms[hid]++; diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index dc9bddb73c..1169314632 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -281,6 +281,8 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) ostringstream oss; + vector pci; + // ------------------------------------------------------------------------ // Set a name if the VM has not got one and VM_ID // ------------------------------------------------------------------------ @@ -442,6 +444,26 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) goto error_leases_rollback; } + // ------------------------------------------------------------------------ + // PCI Devices + // ------------------------------------------------------------------------ + + user_obj_template->remove("PCI", pci); + + for (vector::iterator it = pci.begin(); it !=pci.end(); ) + { + if ( (*it)->type() != Attribute::VECTOR ) + { + delete *it; + it = pci.erase(it); + } + else + { + obj_template->set(*it); + ++it; + } + } + // ------------------------------------------------------------------------- // Parse the context & requirements // ------------------------------------------------------------------------- @@ -1649,11 +1671,14 @@ void VirtualMachine::cp_previous_history() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk) +void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk, + vector& pci_devs) { istringstream iss; float fcpu; + pci_devs.clear(); + if ((get_template_attribute("MEMORY",memory) == false) || (get_template_attribute("CPU",fcpu) == false)) { @@ -1668,6 +1693,8 @@ void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk) memory = memory * 1024; //now in Kilobytes disk = 0; + obj_template->get("PCI", pci_devs); + return; } diff --git a/src/vmm/LibVirtDriverKVM.cc b/src/vmm/LibVirtDriverKVM.cc index b9f4584e51..5d9ac9cb57 100644 --- a/src/vmm/LibVirtDriverKVM.cc +++ b/src/vmm/LibVirtDriverKVM.cc @@ -173,6 +173,13 @@ int LibVirtDriver::deployment_description_kvm( const VectorAttribute * input; + const VectorAttribute * pci; + + string domain = ""; + /* bus is already defined for disks */ + string slot = ""; + string func = ""; + const VectorAttribute * features; bool pae = false; @@ -949,6 +956,50 @@ int LibVirtDriver::deployment_description_kvm( attrs.clear(); + // ------------------------------------------------------------------------ + // PCI Passthrough + // ------------------------------------------------------------------------ + + num = vm->get_template_attribute("PCI",attrs); + + for (int i=0; i < num ;i++) + { + pci = dynamic_cast(attrs[i]); + + if ( pci == 0 ) + { + continue; + } + + domain = pci->vector_value("DOMAIN"); + bus = pci->vector_value("BUS"); + slot = pci->vector_value("SLOT"); + func = pci->vector_value("FUNCTION"); + + if ( domain.empty() || bus.empty() || slot.empty() || func.empty() ) + { + vm->log("VMM", Log::WARNING, + "DOMAIN, BUS, SLOT and FUNC must be defined for PCI " + "passthrough. Ignored."); + continue; + } + + file << "\t\t"; + file << endl; + file << "\t\t\t" << endl; + + file << "\t\t\t\t
" << endl; + + file << "\t\t\t" << endl; + file << "\t\t" << endl; + } + + attrs.clear(); + file << "\t" << endl; // ------------------------------------------------------------------------