diff --git a/include/Host.h b/include/Host.h index 6c976311a8..96d34646fc 100644 --- a/include/Host.h +++ b/include/Host.h @@ -251,6 +251,11 @@ public: } }; + bool add_pci(HostShareCapacity &sr) + { + return host_share.add_pci(sr); + } + /** * Deletes a new VM to the host share by incrementing usage counters * @param sr the capacity request of the VM @@ -271,6 +276,11 @@ public: } }; + void del_pci(HostShareCapacity& sr) + { + host_share.del_pci(sr); + } + /** * Revert changes in PCI Devices after migrate failure * @param sr host share capacity info diff --git a/include/HostShare.h b/include/HostShare.h index 83e25df78c..ebdacf25ed 100644 --- a/include/HostShare.h +++ b/include/HostShare.h @@ -65,12 +65,23 @@ public: */ void add(HostShareCapacity &sr); + bool add_pci(HostShareCapacity &sr) + { + // NOTE THIS FUNCTION DOES NOT PERFORM ANY ROLLBACK + return pci.add(sr.pci, sr.vmid); + } + /** * Delete VM capacity from this share * @param sr requested capacity by the VM */ void del(HostShareCapacity &sr); + void del_pci(HostShareCapacity &sr) + { + pci.del(sr.pci, sr.vmid); + } + /** * Revert changes in PCI Devices * @param sr capacity info by the VM diff --git a/include/HostSharePCI.h b/include/HostSharePCI.h index f281b7810f..4c08f71941 100644 --- a/include/HostSharePCI.h +++ b/include/HostSharePCI.h @@ -78,8 +78,12 @@ public: * @param devs list of requested PCI devices, will include address of * assigned devices. * @param vmid of the VM + * + * @return true if the devices where added + * + * NOTE THIS FUNCTION DOES NOT PERFORM ANY ROLLBACK */ - void add(std::vector &devs, int vmid); + bool add(std::vector &devs, int vmid); /** * Remove the VM assignment from the PCI device list @@ -87,7 +91,8 @@ public: void del(const std::vector &devs, int vmid); /** - * Revert the VM assignment from the PCI device list + * Revert the VM assignment from the PCI device list. It copies + * back the attributes from the previous PCI device */ void revert(std::vector &devs); @@ -125,19 +130,21 @@ public: * - VM_SLOT: PCI_ID + 1 * - VM_FUNCTION: 0 * - VM_ADDRESS: BUS:SLOT.0 + * + * Cleans internal attributes: + * - NUMA_NODE + * - UUID + * - BUS, SLOT, FUNCITION + * - ADDRESS, PREV_ADDRESS * @param pci_device to set the address in * @param default_bus if not set in PCI attribute (PCI_PASSTHROUGH_BUS * in oned.conf) * @return -1 if wrong bus 0 on success */ - static int set_pci_address(VectorAttribute * pci_device, const std::string& dbus); + static int set_pci_address(VectorAttribute * pci_device, const std::string& dbus, + bool clean); private: - /** - * Sets the internal class structures from the template - */ - void init(); - /** * Internal structure to represent PCI devices for fast look up and * update @@ -162,6 +169,58 @@ private: }; std::map pci_devices; + + /** + * Sets the internal class structures from the template + */ + void init(); + + /** + * Test if there is a suitable PCI device for the VM request. The test + * is done using the VENDOR/DEVICE/CLASS attributes + * + * @param dev VM attribute that represents the decive request + * @param addrs PCI addresses that should be considered in use + * + * @return true if the device can be allocated to this host + */ + bool test_by_name(const VectorAttribute *dev, std::set& addrs) const; + + /** + * Test if there is a suitable PCI device for the VM request. The test + * is done using the a specific address + * + * @param device VM attribute that represents the decive request + * @param addr the requested address + * + * @return PCI_ID of the tested device or -1 if no PCI found + */ + bool test_by_addr(const VectorAttribute *dev, const std::string& addr) const; + + /** + * Allocates the given VM device using the VENDOR/DEVICE/CLASS attributes + * @param device VM attribute that represents the decive request + * @param vmid of the VM + */ + bool add_by_name(VectorAttribute *device, int vmid); + + /** + * Allocates the given VM device using the SHORT_ADDRESS attribute + * @param device VM attribute that represents the decive request + * @param vmid of the VM + * + * @return pci_id of the allocated device or -1 if not allocated + */ + bool add_by_addr(VectorAttribute *device, const std::string& addr, int vmid); + + /** + * Adds PCI attributes of the selected PCI to the VM PCI device + * + * @param device VM attribute + * @param pci Host device + * @param sp if true set the "PREVIOUS_ADDRESS" attribute + */ + void pci_attribute(VectorAttribute *device, PCIDevice *pci, bool sp); }; #endif /*HOST_SHARE_PCI_H_*/ diff --git a/include/VirtualMachineNic.h b/include/VirtualMachineNic.h index 20070afea5..84caa7d5ec 100644 --- a/include/VirtualMachineNic.h +++ b/include/VirtualMachineNic.h @@ -121,6 +121,14 @@ public: return name() == "NIC_ALIAS"; } + /** + * Check is a nic is a PCI + */ + bool is_pci() const + { + return name() == "PCI"; + } + /* * Set nic NAME attribute if not empty, defaults to NAME = NIC${NIC_ID} */ diff --git a/install.sh b/install.sh index 5191e5492b..efd690e38d 100755 --- a/install.sh +++ b/install.sh @@ -680,6 +680,7 @@ INSTALL_FILES=( VMM_EXEC_LIB_NSX_FILES:$LIB_LOCATION/ruby/nsx_driver VMM_EXEC_LIB:$VAR_LOCATION/remotes/vmm/lib VMM_EXEC_KVM_SCRIPTS:$VAR_LOCATION/remotes/vmm/kvm + VMM_EXEC_KVM_LIB:$VAR_LOCATION/remotes/vmm/kvm VMM_EXEC_LXD_SCRIPTS:$VAR_LOCATION/remotes/vmm/lxd VMM_EXEC_LXD_LIB:$VAR_LOCATION/remotes/vmm/lxd VMM_EXEC_LXC_SCRIPTS:$VAR_LOCATION/remotes/vmm/lxc @@ -1277,6 +1278,8 @@ VMM_EXEC_KVM_SCRIPTS="src/vmm_mad/remotes/kvm/cancel \ src/vmm_mad/remotes/kvm/resize \ src/vmm_mad/remotes/kvm/resize_disk" +VMM_EXEC_KVM_LIB="src/vmm_mad/remotes/lib/kvm/opennebula_vm.rb" + #------------------------------------------------------------------------------- # VMM configuration KVM scripts, to be installed under $REMOTES_LOCATION/etc/vmm/kvm #------------------------------------------------------------------------------- @@ -1730,6 +1733,7 @@ NETWORK_FILES="src/vnm_mad/remotes/lib/vnm_driver.rb \ src/vnm_mad/remotes/lib/address.rb \ src/vnm_mad/remotes/lib/command.rb \ src/vnm_mad/remotes/lib/vm.rb \ + src/vnm_mad/remotes/lib/vf.rb \ src/vnm_mad/remotes/lib/vlan.rb \ src/vnm_mad/remotes/lib/no_vlan.rb \ src/vnm_mad/remotes/lib/security_groups.rb \ diff --git a/src/cli/onevm b/src/cli/onevm index d39d39471b..8bea0c4556 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -249,6 +249,34 @@ CommandParser::CmdParser.new(ARGV) do :description => 'VNC client to use' } + PCI = { + :name => 'pci', + :large => '--pci short_address', + :format => String, + :description => 'Select PCI device by its short address' + } + + PCI_DEVICE = { + :name => 'pci_device', + :large => '--pci_device device ID', + :format => String, + :description => 'Select PCI device by its device ID' + } + + PCI_VENDOR = { + :name => 'pci_vendor', + :large => '--pci_vendor vendor ID', + :format => String, + :description => 'Select PCI device by its vendor ID' + } + + PCI_CLASS = { + :name => 'pci_class', + :large => '--pci_class class ID', + :format => String, + :description => 'Select PCI device by its class ID' + } + OpenNebulaHelper::TEMPLATE_OPTIONS_VM.delete_if do |v| %w[as_gid as_uid].include?(v[:name]) end @@ -853,8 +881,11 @@ CommandParser::CmdParser.new(ARGV) do end nic_attach_desc = <<-EOT.unindent - Attaches a NIC to a running VM. When using --file add only one - NIC instance. + Attaches a NIC to a VM. When using --file add only one NIC instance. + + To hotplug a PCI device and use it as a NIC interface in the VM select + it with --pci (short_address) or --pci_device (device ID), + --pci_class (class ID) and/or --pci_vendor (vendor ID). To attach a nic alias, use --file or --alias option. @@ -866,7 +897,11 @@ CommandParser::CmdParser.new(ARGV) do OneVMHelper::NETWORK, OneVMHelper::IP, OneVMHelper::ALIAS, - OneVMHelper::NIC_NAME] do + OneVMHelper::NIC_NAME, + PCI, + PCI_CLASS, + PCI_VENDOR, + PCI_DEVICE] do if options[:file].nil? && options[:network].nil? STDERR.puts 'Provide a template file or a network:' STDERR.puts "\t--file " @@ -878,36 +913,36 @@ CommandParser::CmdParser.new(ARGV) do template = File.read(options[:file]) else network_id = options[:network] - ip = options[:ip] - nic_alias = options[:alias] - nic_name = options[:nic_name] + ip = options[:ip] + nic_alias = options[:alias] + nic_name = options[:nic_name] - if ip - if !nic_alias && !nic_name - template = "NIC = [ NETWORK_ID = #{network_id}, \ - IP = #{ip} ]" - elsif !nic_alias && nic_name - template = "NIC = [ NETWORK_ID = #{network_id}, - IP = #{ip}, - NAME = #{nic_name} ]" - else - template = "NIC_ALIAS = \ - [ NETWORK_ID = #{network_id},\ - IP = #{ip},\ - PARENT = #{nic_alias} ]" - end + is_pci = [:pci, :pci_device, :pci_vendor, :pci_class].any? { |o| + !options[o].nil? + } + + if is_pci + pcia = options[:pci] + pcid = options[:pci_device] + pcic = options[:pci_class] + pciv = options[:pci_vendor] + + template = "PCI = [ TYPE = NIC" + template << ", NETWORK_ID = #{network_id}" + template << ", SHORT_ADDRESS = \"#{pcia}\"" if pcia + template << ", DEVICE = \"#{pcid}\"" if pcid + template << ", CLASS = \"#{pcic}\"" if pcic + template << ", VENDOR = \"#{pciv}\"" if pciv + elsif nic_alias + template = "NIC_ALIAS = [ PARENT = #{nic_alias}" + template << ", NETWORK_ID = #{network_id}" else - if !nic_alias && !nic_name - template = "NIC = [ NETWORK_ID = #{network_id} ]" - elsif !nic_alias && nic_name - template = "NIC = [ NETWORK_ID = #{network_id}, - NAME = #{nic_name} ]" - else - template = "NIC_ALIAS = \ - [ NETWORK_ID = #{network_id},\ - PARENT = #{nic_alias} ]" - end + template = "NIC = [ NETWORK_ID = #{network_id}" end + + template << ", IP = #{ip}" if ip + template << ", NAME = #{nic_name}" if nic_name + template << "]" end helper.perform_action(args[0], options, 'Attaching NIC') do |vm| diff --git a/src/host/HostShare.cc b/src/host/HostShare.cc index 3d97b30246..a284093165 100644 --- a/src/host/HostShare.cc +++ b/src/host/HostShare.cc @@ -326,22 +326,18 @@ bool HostShare::test_compute(int cpu, long long mem, std::string &error) const bool HostShare::test_pci(vector& pci_devs, string& error) const { - bool fits = pci.test(pci_devs); - error = "Unavailable PCI device."; - return fits; + return pci.test(pci_devs); } /* -------------------------------------------------------------------------- */ bool HostShare::test_numa(HostShareCapacity &sr, string& error) const { - bool fits = numa.test(sr); - error = "Cannot allocate NUMA topology"; - return fits; + return numa.test(sr); } /* -------------------------------------------------------------------------- */ diff --git a/src/host/HostSharePCI.cc b/src/host/HostSharePCI.cc index 5b3c6c4869..4183f6a33b 100644 --- a/src/host/HostSharePCI.cc +++ b/src/host/HostSharePCI.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -75,47 +76,106 @@ void HostSharePCI::init() } } -/* ------------------------------------------------------------------------*/ -/* ------------------------------------------------------------------------*/ +/* -------------------------------------------------------------------------- */ +/* Function to test PCI availability at the host */ +/* -------------------------------------------------------------------------- */ -bool HostSharePCI::test(const vector &devs) const +bool HostSharePCI::test_by_addr(const VectorAttribute *dev, const string& short_addr) const { - std::set assigned; - - unsigned int vendor_id, device_id, class_id; - int vendor_rc, device_rc, class_rc; - bool found; - - for (auto device : devs) + for (auto jt = pci_devices.begin(); jt != pci_devices.end(); jt++) { - vendor_rc = get_pci_value("VENDOR", device, vendor_id); - device_rc = get_pci_value("DEVICE", device, device_id); - class_rc = get_pci_value("CLASS" , device, class_id); + PCIDevice * pci = jt->second; - if (vendor_rc <= 0 && device_rc <= 0 && class_rc <= 0) + if (pci->attrs->vector_value("SHORT_ADDRESS") != short_addr) + { + continue; + } + + if (pci->vmid != -1) { return false; } - found = false; - for (auto jt = pci_devices.begin(); jt != pci_devices.end(); jt++) + return true; + } + + return false; +} + +/* -------------------------------------------------------------------------- */ + +bool HostSharePCI::test_by_name(const VectorAttribute *device, std::set& assigned) const +{ + unsigned int vendor_id, device_id, class_id; + + int vendor_rc = get_pci_value("VENDOR", device, vendor_id); + int device_rc = get_pci_value("DEVICE", device, device_id); + int class_rc = get_pci_value("CLASS" , device, class_id); + + if (vendor_rc <= 0 && device_rc <= 0 && class_rc <= 0) + { + return false; + } + + for (auto jt = pci_devices.begin(); jt != pci_devices.end(); jt++) + { + PCIDevice * pci = jt->second; + + string short_addr = pci->attrs->vector_value("SHORT_ADDRESS"); + + if ((class_rc == 0 || pci->class_id == class_id) && + (vendor_rc == 0 || pci->vendor_id == vendor_id) && + (device_rc == 0 || pci->device_id == device_id) && + pci->vmid == -1 && + assigned.find(short_addr) == assigned.end()) { - PCIDevice * dev = jt->second; + assigned.insert(short_addr); - if ((class_rc == 0 || dev->class_id == class_id) && - (vendor_rc == 0 || dev->vendor_id == vendor_id) && - (device_rc == 0 || dev->device_id == device_id) && - dev->vmid == -1 && - assigned.find(dev->address) == assigned.end()) - { - assigned.insert(dev->address); - found = true; + return true; + } + } - break; - } + return false; +} + +/* -------------------------------------------------------------------------- */ + +bool HostSharePCI::test(const vector &devs) const +{ + std::set assigned; + std::set tested; + + // Test for "SHORT_ADDRESS" PCI selectio + // and pre-allocated these first + for (const auto& device : devs) + { + string short_addr = device->vector_value("SHORT_ADDRESS"); + + if (short_addr.empty()) + { + continue; } - if (!found) + if (!test_by_addr(device, short_addr)) + { + return false; + } + + tested.insert(device); + + assigned.insert(short_addr); + } + + // Test for "VENDOR/DEVICE/CLASS" PCI selection + // use any remaining free device + for (const auto& device : devs) + { + if (tested.find(device) != tested.end()) + { + continue; + } + + if (!test_by_name(device, assigned)) { return false; } @@ -124,65 +184,155 @@ bool HostSharePCI::test(const vector &devs) const return true; } -/* ------------------------------------------------------------------------*/ -/* ------------------------------------------------------------------------*/ - -void HostSharePCI::add(vector &devs, int vmid) +/* -------------------------------------------------------------------------- */ +/* Function to assign host PCI devices to a VM */ +/* -------------------------------------------------------------------------- */ +void HostSharePCI::pci_attribute(VectorAttribute *device, PCIDevice *pci, + bool set_prev) { - unsigned int vendor_id, device_id, class_id; - string address, uuid; - int vendor_rc, device_rc, class_rc, addr_rc; + static vector cp_attr = {"DOMAIN", "BUS", "SLOT", "FUNCTION", + "ADDRESS", "SHORT_ADDRESS"}; - for (auto device : devs) + static vector cp_check_attr = {"NUMA_NODE", "UUID"}; + + //Save previous address for migrations, clear on revert - failed migration + if (set_prev) { - vendor_rc = get_pci_value("VENDOR", device, vendor_id); - device_rc = get_pci_value("DEVICE", device, device_id); - class_rc = get_pci_value("CLASS" , device, class_id); + string address = device->vector_value("ADDRESS"); - addr_rc = device->vector_value("ADDRESS", address); - - for (auto jt = pci_devices.begin(); jt != pci_devices.end(); jt++) + if (!address.empty()) { - PCIDevice * dev = jt->second; - - if ((class_rc == 0 || dev->class_id == class_id) && - (vendor_rc == 0 || dev->vendor_id == vendor_id) && - (device_rc == 0 || dev->device_id == device_id) && - dev->vmid == -1 ) - { - int node = -1; - - dev->vmid = vmid; - dev->attrs->replace("VMID", vmid); - - device->replace("DOMAIN", dev->attrs->vector_value("DOMAIN")); - device->replace("BUS", dev->attrs->vector_value("BUS")); - device->replace("SLOT", dev->attrs->vector_value("SLOT")); - device->replace("FUNCTION",dev->attrs->vector_value("FUNCTION")); - - device->replace("ADDRESS", dev->attrs->vector_value("ADDRESS")); - - if (addr_rc != -1 && !address.empty()) - { - device->replace("PREV_ADDRESS", address); - } - - if (dev->attrs->vector_value("NUMA_NODE", node)==0 && node !=-1) - { - device->replace("NUMA_NODE", node); - } - - uuid = dev->attrs->vector_value("UUID"); - - if ( !uuid.empty() ) - { - device->replace("UUID", uuid); - } - - break; - } + device->replace("PREV_ADDRESS", address); } } + else + { + device->remove("PREV_ADDRESS"); + } + + //Set PCI device attributes + for (const auto& attr : cp_attr) + { + device->replace(attr, pci->attrs->vector_value(attr)); + } + + //Set Optional PCI attributes + for (const auto& attr : cp_check_attr) + { + string vvalue = pci->attrs->vector_value(attr); + + if (!vvalue.empty()) + { + device->replace(attr, vvalue); + } + } +} + +/* -------------------------------------------------------------------------- */ + +bool HostSharePCI::add_by_addr(VectorAttribute *device, const string& short_addr, + int vmid) +{ + for (auto jt = pci_devices.begin(); jt != pci_devices.end(); jt++) + { + PCIDevice * pci = jt->second; + + if (pci->attrs->vector_value("SHORT_ADDRESS") != short_addr) + { + continue; + } + + if ( pci->vmid != -1 ) + { + return false; + } + + pci->vmid = vmid; + + pci->attrs->replace("VMID", vmid); + + pci_attribute(device, pci, true); + + return true; + } + + return false; +} + +/* -------------------------------------------------------------------------- */ + +bool HostSharePCI::add_by_name(VectorAttribute *device, int vmid) +{ + unsigned int vendor_id, device_id, class_id; + + int vendor_rc = get_pci_value("VENDOR", device, vendor_id); + int device_rc = get_pci_value("DEVICE", device, device_id); + int class_rc = get_pci_value("CLASS" , device, class_id); + + if (vendor_rc <= 0 && device_rc <= 0 && class_rc <= 0) + { + return false; + } + + for (auto jt = pci_devices.begin(); jt != pci_devices.end(); jt++) + { + PCIDevice * pci = jt->second; + + if ((class_rc == 0 || pci->class_id == class_id) && + (vendor_rc == 0 || pci->vendor_id == vendor_id) && + (device_rc == 0 || pci->device_id == device_id) && + pci->vmid == -1 ) + { + pci->vmid = vmid; + + pci->attrs->replace("VMID", vmid); + + pci_attribute(device, pci, true); + + return true; + } + } + + return false; +} + +/* -------------------------------------------------------------------------- */ + +bool HostSharePCI::add(vector &devs, int vmid) +{ + std::set added; + + for (auto& device : devs) + { + string short_addr = device->vector_value("SHORT_ADDRESS"); + + if (short_addr.empty()) + { + continue; + } + + if (!add_by_addr(device, short_addr, vmid)) + { + return false; + } + + added.insert(device); + } + + for (auto& device : devs) + { + if (added.find(device) != added.end()) + { + continue; + } + + if (!add_by_name(device, vmid)) + { + return false; + } + } + + return true; } /* ------------------------------------------------------------------------*/ @@ -228,30 +378,19 @@ void HostSharePCI::revert(vector &devs) { device->vector_value("PREV_ADDRESS", address); - if (!address.empty()) + if (address.empty()) { - auto dev = pci_devices[address]; - - if (!dev) - { - continue; - } - - device->replace("DOMAIN", dev->attrs->vector_value("DOMAIN")); - device->replace("BUS", dev->attrs->vector_value("BUS")); - device->replace("SLOT", dev->attrs->vector_value("SLOT")); - device->replace("FUNCTION",dev->attrs->vector_value("FUNCTION")); - device->replace("ADDRESS", address); - device->remove("PREV_ADDRESS"); - - int node = -1; - if (dev->attrs->vector_value("NUMA_NODE", node)==0 && node !=-1) - { - device->replace("NUMA_NODE", node); - } - - break; + continue; } + + auto pci = pci_devices[address]; + + if (!pci) + { + continue; + } + + pci_attribute(device, pci, false); } } @@ -366,13 +505,25 @@ int HostSharePCI::get_pci_value(const char * name, /* ------------------------------------------------------------------------*/ int HostSharePCI::set_pci_address(VectorAttribute * pci_device, - const string& dbus) + const string& dbus, bool clean) { string bus; ostringstream oss; unsigned int ibus, slot; + // ------------------- Remove well-known attributes ----------------------- + static vector rm_attr = {"DOMAIN", "BUS", "SLOT", "FUNCTION", + "ADDRESS", "PREV_ADDRESS", "NUMA_NODE", "UUID"}; + + if (clean) + { + for (const auto& attr : rm_attr) + { + pci_device->remove(attr); + } + } + // ------------------- DOMAIN & FUNCTION ------------------------- pci_device->replace("VM_DOMAIN", "0x0000"); pci_device->replace("VM_FUNCTION", "0"); diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index 6f3d2123a3..07f3b76012 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -2534,12 +2534,14 @@ Request::ErrorCode VirtualMachineAttachNic::request_execute(int id, { Nebula& nd = Nebula::instance(); - DispatchManager * dm = nd.get_dm(); + HostPool * hpool = nd.get_hpool(); + DispatchManager* dm = nd.get_dm(); + VirtualMachinePool* vmpool = nd.get_vmpool(); - PoolObjectAuth vm_perms; + PoolObjectAuth vm_perms; - int rc; + int hid = -1; // ------------------------------------------------------------------------- // Authorize the operation, restricted attributes & check quotas @@ -2547,6 +2549,11 @@ Request::ErrorCode VirtualMachineAttachNic::request_execute(int id, if (auto vm = vmpool->get_ro(id)) { vm->get_permissions(vm_perms); + + if (vm->hasHistory()) + { + hid = vm->get_hid(); + } } else { @@ -2586,14 +2593,67 @@ Request::ErrorCode VirtualMachineAttachNic::request_execute(int id, return AUTHORIZATION; } + // ------------------------------------------------------------------------- + // PCI test and set + // ------------------------------------------------------------------------- + + VectorAttribute * pci = tmpl.get("PCI"); + HostShareCapacity sr; + + if ( pci != nullptr && hid != -1 ) + { + if ( pci->vector_value("TYPE") != "NIC" ) + { + att.resp_msg = "PCI device is not of type NIC"; + + quota_rollback(&tmpl, Quotas::NETWORK, att_quota); + return ACTION; + } + + sr.vmid = id; + sr.pci.push_back(pci); + + auto host = hpool->get(hid); + + if ( host == nullptr ) + { + att.resp_id = id; + att.resp_obj = PoolObjectSQL::HOST; + + quota_rollback(&tmpl, Quotas::NETWORK, att_quota); + return NO_EXISTS; + } + + if (!host->add_pci(sr)) + { + att.resp_msg = "Cannot assign PCI device in host. Check address " + "and free devices"; + + quota_rollback(&tmpl, Quotas::NETWORK, att_quota); + return ACTION; + } + + hpool->update(host.get()); + } + // ------------------------------------------------------------------------- // Perform the attach // ------------------------------------------------------------------------- - rc = dm->attach_nic(id, &tmpl, att, att.resp_msg); + int rc = dm->attach_nic(id, &tmpl, att, att.resp_msg); if ( rc != 0 ) { quota_rollback(&tmpl, Quotas::NETWORK, att_quota); + + if ( pci != nullptr && hid != -1 ) + { + if (auto host = hpool->get(hid)) + { + host->del_pci(sr); + hpool->update(host.get()); + } + } + return ACTION; } @@ -2606,8 +2666,8 @@ Request::ErrorCode VirtualMachineAttachNic::request_execute(int id, void VirtualMachineDetachNic::request_execute( xmlrpc_c::paramList const& paramList, RequestAttributes& att) { - int id = xmlrpc_c::value_int(paramList.getInt(1)); - int nic_id = xmlrpc_c::value_int(paramList.getInt(2)); + int id = xmlrpc_c::value_int(paramList.getInt(1)); + int nic_id = xmlrpc_c::value_int(paramList.getInt(2)); // ------------------------------------------------------------------------- // Check if the VM is a Virtual Router diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 4869e21c42..5635ef2f83 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -3375,23 +3375,74 @@ int VirtualMachine::get_network_leases(string& estr) int VirtualMachine::set_up_attach_nic(VirtualMachineTemplate * tmpl, string& err) { + bool is_pci = false; + // ------------------------------------------------------------------------- // Get the new NIC attribute from the template // ------------------------------------------------------------------------- VectorAttribute * new_nic = tmpl->get("NIC"); - if ( new_nic == 0 ) + if (new_nic == nullptr) { new_nic = tmpl->get("NIC_ALIAS"); - if ( new_nic == 0 ) + if (new_nic == nullptr) { - err = "Wrong format or missing NIC/NIC_ALIAS attribute"; - return -1; + new_nic = tmpl->get("PCI"); + + if ( new_nic != nullptr && new_nic->vector_value("TYPE") != "NIC" ) + { + new_nic = nullptr; + } + + is_pci = true; } } - new_nic = new_nic->clone(); + if ( new_nic == nullptr ) + { + err = "Wrong format or missing NIC/NIC_ALIAS/PCI attribute"; + return -1; + } + + // ------------------------------------------------------------------------- + // Setup PCI attribute + // ------------------------------------------------------------------------- + std::unique_ptr _new_nic(new_nic->clone()); + + if ( is_pci ) + { + Nebula& nd = Nebula::instance(); + string default_bus; + + std::vector pcis; + + int max_pci_id = -1; + + obj_template->get("PCI", pcis); + + for (const auto& pci: pcis) + { + int pci_id; + + pci->vector_value("PCI_ID", pci_id, -1); + + if (pci_id > max_pci_id) + { + max_pci_id = pci_id; + } + } + + _new_nic->replace("PCI_ID", max_pci_id + 1); + + nd.get_configuration_attribute("PCI_PASSTHROUGH_BUS", default_bus); + + if ( HostSharePCI::set_pci_address(_new_nic.get(), default_bus, false) != 0 ) + { + err = "Wrong BUS in PCI attribute"; + return -1; + } + } // ------------------------------------------------------------------------- // Setup nic for attachment @@ -3400,12 +3451,11 @@ int VirtualMachine::set_up_attach_nic(VirtualMachineTemplate * tmpl, string& err VectorAttribute * nic_default = obj_template->get("NIC_DEFAULT"); - int rc = nics.set_up_attach_nic(oid, uid, get_cid(), new_nic, nic_default, - sgs, err); + int rc = nics.set_up_attach_nic(oid, uid, get_cid(), _new_nic.get(), + nic_default, sgs, err); if ( rc != 0 ) { - delete new_nic; return -1; } @@ -3414,7 +3464,7 @@ int VirtualMachine::set_up_attach_nic(VirtualMachineTemplate * tmpl, string& err // ------------------------------------------------------------------------- set_vm_info(); - obj_template->set(new_nic); + obj_template->set(_new_nic.release()); for (auto vattr : sgs) { diff --git a/src/vm/VirtualMachineParser.cc b/src/vm/VirtualMachineParser.cc index 585d9d7745..3342369323 100644 --- a/src/vm/VirtualMachineParser.cc +++ b/src/vm/VirtualMachineParser.cc @@ -316,22 +316,19 @@ 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; + static std::vector attrs = {"VENDOR", "DEVICE", "CLASS"}; bool found = false; - for (int i = 0; i < num_attrs; i++) + for (const auto& attr: attrs) { unsigned int val; - int rc = HostSharePCI::get_pci_value(attrs[i].c_str(), pci, val); + int rc = HostSharePCI::get_pci_value(attr.c_str(), pci, val); if (rc == -1) { - error_str = "Wrong Hex value for PCI attribute " + attrs[i]; + error_str = "Wrong Hex value for PCI attribute " + attr; return -1; } else if ( rc != 0 ) @@ -340,13 +337,22 @@ static int check_pci_attributes(VectorAttribute * pci, const string& default_bus } } - if (!found) + string saddr; + + pci->vector_value("SHORT_ADDRESS", saddr); + + if (saddr.empty() && !found) { - error_str = "DEVICE, VENDOR or CLASS must be defined for PCI."; + error_str = "SHORT_ADDRESS, DEVICE, VENDOR or CLASS must be defined for PCI."; + return -1; + } + else if (!saddr.empty() && found) + { + error_str = "SHORT_ADDRESS cannot be set with DEVICE, VENDOR or CLASS"; return -1; } - if ( HostSharePCI::set_pci_address(pci, default_bus) != 0 ) + if ( HostSharePCI::set_pci_address(pci, default_bus, true) != 0 ) { error_str = "Wrong BUS in PCI attribute"; return -1; @@ -375,7 +381,7 @@ int VirtualMachine::parse_pci(string& error_str, Template * tmpl) nd.get_configuration_attribute("PCI_PASSTHROUGH_BUS", default_bus); - for (auto attr : array_pci) + for (auto& attr : array_pci) { if ( check_pci_attributes(attr, default_bus, error_str) != 0 ) { diff --git a/src/vm/VirtualMachinePool.cc b/src/vm/VirtualMachinePool.cc index c863b2f852..43a4fa154d 100644 --- a/src/vm/VirtualMachinePool.cc +++ b/src/vm/VirtualMachinePool.cc @@ -21,6 +21,7 @@ #include "HookStateVM.h" #include "HookManager.h" #include "ImageManager.h" +#include "HostPool.h" #include @@ -1061,19 +1062,13 @@ void VirtualMachinePool::delete_attach_disk(std::unique_ptr vm) void VirtualMachinePool::delete_attach_nic(std::unique_ptr vm) { - VirtualMachineNic * nic, * p_nic; - - int uid; - int gid; - int oid; - set pre, post; Template tmpl; vm->get_security_groups(pre); - nic = vm->delete_attach_nic(); + VirtualMachineNic * nic = vm->delete_attach_nic(); if ( nic == nullptr ) { @@ -1082,7 +1077,10 @@ void VirtualMachinePool::delete_attach_nic(std::unique_ptr vm) return; } - int nic_id = nic->get_nic_id(); + int hid = vm->get_hid(); + int vmid = vm->get_oid(); + + int nic_id = nic->get_nic_id(); if (!nic->is_alias()) { @@ -1097,7 +1095,7 @@ void VirtualMachinePool::delete_attach_nic(std::unique_ptr vm) vm->clear_nic_alias_context(parent_id, alias_id); - p_nic = vm->get_nic(parent_id); + VirtualMachineNic * p_nic = vm->get_nic(parent_id); // As NIC is an alias, parent ALIAS_IDS array should be updated // to remove the alias_id @@ -1110,9 +1108,9 @@ void VirtualMachinePool::delete_attach_nic(std::unique_ptr vm) p_nic->replace("ALIAS_IDS", one_util::join(p_a_ids, ',')); } - uid = vm->get_uid(); - gid = vm->get_gid(); - oid = vm->get_oid(); + int uid = vm->get_uid(); + int gid = vm->get_gid(); + int oid = vm->get_oid(); vm->get_security_groups(post); @@ -1151,6 +1149,24 @@ void VirtualMachinePool::delete_attach_nic(std::unique_ptr vm) vm.reset(); + //Check if PCI and delete capacity from host + if (nic->is_pci() && hid != -1) + { + HostPool * hpool = Nebula::instance().get_hpool(); + + HostShareCapacity sr; + + sr.vmid = vmid; + sr.pci.push_back(nic->vector_attribute()); + + if (auto host = hpool->get(hid)) + { + host->del_pci(sr); + hpool->update(host.get()); + } + } + + //Adjust quotas tmpl.set(nic->vector_attribute()); Quotas::quota_del(Quotas::NETWORK, uid, gid, &tmpl); diff --git a/src/vmm_mad/exec/one_vmm_exec.rb b/src/vmm_mad/exec/one_vmm_exec.rb index bbf7fef297..6c39ec7e98 100755 --- a/src/vmm_mad/exec/one_vmm_exec.rb +++ b/src/vmm_mad/exec/one_vmm_exec.rb @@ -211,7 +211,7 @@ class VmmAction end end - %w[NIC NIC_ALIAS].each do |r| + %w[NIC NIC_ALIAS PCI].each do |r| vm_template_xml.elements.each("TEMPLATE/#{r}") do |element| vn_mad = element.get_text('VN_MAD').to_s @@ -979,6 +979,8 @@ class ExecDriver < VirtualMachineDriver if xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']"] base_tmpl = "VM/TEMPLATE/NIC[ATTACH='YES']" + elsif xml_data.elements["VM/TEMPLATE/PCI[ATTACH='YES']"] + base_tmpl = "VM/TEMPLATE/PCI[ATTACH='YES']" else base_tmpl = "VM/TEMPLATE/NIC_ALIAS[ATTACH='YES']" nic_alias = true @@ -1104,6 +1106,8 @@ class ExecDriver < VirtualMachineDriver if xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']"] base_tmpl = "VM/TEMPLATE/NIC[ATTACH='YES']" + elsif xml_data.elements["VM/TEMPLATE/PCI[ATTACH='YES']"] + base_tmpl = "VM/TEMPLATE/PCI[ATTACH='YES']" else base_tmpl = "VM/TEMPLATE/NIC_ALIAS[ATTACH='YES']" nic_alias = true diff --git a/src/vmm_mad/remotes/kvm/attach_nic b/src/vmm_mad/remotes/kvm/attach_nic index 54bd9bdfad..0ba8ff32a7 100755 --- a/src/vmm_mad/remotes/kvm/attach_nic +++ b/src/vmm_mad/remotes/kvm/attach_nic @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env ruby # -------------------------------------------------------------------------- # # Copyright 2002-2022, OpenNebula Project, OpenNebula Systems # @@ -15,93 +15,75 @@ # See the License for the specific language governing permissions and # # limitations under the License. # #--------------------------------------------------------------------------- # +ONE_LOCATION = ENV['ONE_LOCATION'] -source $(dirname $0)/../../etc/vmm/kvm/kvmrc -source $(dirname $0)/../../scripts_common.sh - -DOMAIN=$1 - -get_nic_information "ATTACH='YES'" - -# defaults -MODEL=${MODEL:-${DEFAULT_ATTACH_NIC_MODEL}} -FILTER=${FILTER:-${DEFAULT_ATTACH_NIC_FILTER}} - -# interface XML -DEVICE='' - -if [ -z "${BRIDGE}" ]; then - DEVICE+="" +if !ONE_LOCATION + RUBY_LIB_LOCATION = '/usr/lib/one/ruby' + GEMS_LOCATION = '/usr/share/one/gems' + VMDIR = '/var/lib/one' + CONFIG_FILE = '/var/lib/one/config' else - DEVICE+="" - if [[ "${BRIDGE_TYPE}" = openvswitch* ]]; then - DEVICE+=" " - fi - DEVICE+=" " -fi + RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby' + GEMS_LOCATION = ONE_LOCATION + '/share/gems' + VMDIR = ONE_LOCATION + '/var' + CONFIG_FILE = ONE_LOCATION + '/var/config' +end -[ -n "${MAC}" ] && DEVICE+=" " -[ -n "${NIC_TARGET}" ] && DEVICE+=" " -[ -n "${ORDER}" ] && DEVICE+=" " -[ -n "${SCRIPT}" ] && DEVICE+="