diff --git a/include/DispatchManager.h b/include/DispatchManager.h index 94811aa3ab..4a6999701d 100644 --- a/include/DispatchManager.h +++ b/include/DispatchManager.h @@ -528,6 +528,32 @@ public: int resize(int vid, float cpu, int vcpu, long memory, const RequestAttributes& ra, std::string& error_str); + /** + * Attach a new PCI device + * + * @param vid the VM id + * @param pci attribute with the PCI device info + * @param ra information about the API call request + * @param error_str Error reason, if any + * + * @return 0 on success, -1 otherwise + */ + int attach_pci(int vid, VectorAttribute * pci, const RequestAttributes& ra, + std::string& err); + /** + * Detach an existing PCI device + * + * @param vid the VM id + * @param pci_id the PCI device ID + * @param pci attribute with the PCI device info + * @param ra information about the API call request + * @param error_str Error reason, if any + * + * @return 0 on success, -1 otherwise + */ + int detach_pci(int vid, int pci_id, const RequestAttributes& ra, + std::string& err); + //-------------------------------------------------------------------------- // DM Actions associated with a VM state transition //-------------------------------------------------------------------------- diff --git a/include/RequestManagerProxy.h b/include/RequestManagerProxy.h index ad833f3ed8..d58c57526c 100644 --- a/include/RequestManagerProxy.h +++ b/include/RequestManagerProxy.h @@ -44,7 +44,7 @@ public: }; private: - std::string method; + std::string method; }; #endif diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 94e142568f..0af91b9e76 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -350,6 +350,48 @@ protected: RequestAttributes& ra) override; }; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class VirtualMachineAttachPCI : public RequestManagerVirtualMachine +{ +public: + VirtualMachineAttachPCI(): + RequestManagerVirtualMachine("one.vm.attachpci", + "Attaches a new PCI device to the virtual machine", + "A:sis") + { + vm_action = VMActions::PCI_ATTACH_ACTION; + } + + ~VirtualMachineAttachPCI() = default; + +protected: + void request_execute(xmlrpc_c::paramList const& pl, + RequestAttributes& ra) override; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VirtualMachineDetachPCI : public RequestManagerVirtualMachine +{ +public: + VirtualMachineDetachPCI(): + RequestManagerVirtualMachine("one.vm.detachpci", + "Detaches a PCI from a virtual machine", + "A:sii") + { + vm_action = VMActions::PCI_DETACH_ACTION; + } + + ~VirtualMachineDetachPCI() = default; + +protected: + void request_execute(xmlrpc_c::paramList const& pl, + RequestAttributes& ra) override; +}; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/include/VMActions.h b/include/VMActions.h index a0d18be606..f339f3d259 100644 --- a/include/VMActions.h +++ b/include/VMActions.h @@ -86,6 +86,8 @@ public: SCHED_DELETE_ACTION = 55, // "one.vm.scheddelete" SG_ATTACH_ACTION = 56, // "one.vm.attachsg" SG_DETACH_ACTION = 57, // "one.vm.detachsg" + PCI_ATTACH_ACTION = 58, // "one.vm.attachpci" + PCI_DETACH_ACTION = 59 // "one.vm.detachpci" }; static std::string action_to_str(Action action); diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 7709d5f18a..0f9d881a6e 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -1429,13 +1429,35 @@ public: } // ------------------------------------------------------------------------ - // NIC Hotplug related functions + // NIC/PCI Hotplug related functions // ------------------------------------------------------------------------ /** * Checks the attributes of a PCI device */ - int check_pci_attributes(VectorAttribute * pci, std::string& err); + static int check_pci_attributes(VectorAttribute * pci, std::string& err); + + /** + * Get PCI attribute from VM (VectorAttribute form) + * + * @param pci_id of the PCI device + * + * @return pointer to the PCI Attribute + */ + VectorAttribute * get_pci(int pci_id); + + /** + * Attach/Detach a PCI attribute to the VM it generates the PCI_ID and VM_BUS + * paratmeters. + * + * @param vpci attribute wih the PCI information + * @param err string + * + * @return 0 on success + */ + int attach_pci(VectorAttribute * vpci, std::string& err); + + void detach_pci(VectorAttribute * vpci); /** * Generate and attach a new NIC attribute to the VM. This method check @@ -2100,6 +2122,20 @@ private: */ bool generate_pci_context(VectorAttribute * context); + /** + * Deletes the PCI (non NIC) related CONTEXT section for the given nic, i.e. + * PCI_ADDRESS + * @param pciid the id of the PCI + */ + void clear_pci_context(VectorAttribute * pci); + + /** + * Deletes the PCI (non NIC) related CONTEXT section for the given nic, i.e. + * PCI_ADDRESS + * @param pci device to add context for + */ + void add_pci_context(VectorAttribute * pci); + /** * Generate the ONE_GATE token & url * @param context attribute of the VM diff --git a/include/VirtualMachinePCI.h b/include/VirtualMachinePCI.h new file mode 100644 index 0000000000..cc7557c80c --- /dev/null +++ b/include/VirtualMachinePCI.h @@ -0,0 +1,246 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#ifndef VIRTUAL_MACHINE_PCI_H_ +#define VIRTUAL_MACHINE_PCI_H_ + +#include "VirtualMachineAttribute.h" +#include "PoolObjectSQL.h" + +class AuthRequest; + +/** + * The VirtualMachine PCI attribute + */ +class VirtualMachinePCI : public VirtualMachineAttribute +{ +public: + VirtualMachinePCI(VectorAttribute *va, int id): + VirtualMachineAttribute(va, id){}; + + virtual ~VirtualMachinePCI(){}; + + /* ---------------------------------------------------------------------- */ + /* PCI get/set functions for boolean disk flags */ + /* ATTACH */ + /* ---------------------------------------------------------------------- */ + void set_attach() + { + set_flag("ATTACH"); + }; + + /* ---------------------------------------------------------------------- */ + /* PCI attributes */ + /* ---------------------------------------------------------------------- */ + /** + * Return the disk id ("PCI_ID") + */ + int get_pci_id() const + { + return get_id(); + } + + /** + * Check if a nic is alias or not + */ + bool is_alias() const + { + return name() == "NIC_ALIAS"; + } + + /** + * Check if a nic is a PCI + */ + bool is_nic() const + { + std::string type; + + if (vector_value("TYPE", type) != 0) + { + return false; + } + + one_util::toupper(type); + + return type == "NIC"; + } + +}; + + +/** + * Set of VirtualMachine NIC + */ +class VirtualMachinePCIs : public VirtualMachineAttributeSet +{ +public: + /* ---------------------------------------------------------------------- */ + /* Constructor and Initialization functions */ + /* ---------------------------------------------------------------------- */ + /** + * Creates the VirtualMachinePCI set from a Template with PCI=[...] + * attributes + * @param tmpl template with PCI + * @param has_id to use the ID's in PCI=[PCI_ID=...] or autogenerate + */ + VirtualMachinePCIs(Template * tmpl): + VirtualMachineAttributeSet(false) + { + std::vector pcis; + + tmpl->get(PCI_NAME, pcis); + + for (auto it = pcis.begin(); it != pcis.end(); ) + { + if ( (*it)->vector_value("TYPE") != "NIC" ) + { + it = pcis.erase(it); + } + else + { + ++it; + } + } + + init(pcis, false); + }; + + /** + * Creates the VirtualMachineNic set from a vector of NIC VectorAttribute + * @param va vector of NIC VectorAttribute + * @param has_id to use the ID's in NIC=[NIC_ID=...] or autogenerate + * @param dispose true to delete the VectorAttributes when the set is + * destroyed + */ + VirtualMachinePCIs(std::vector& va, bool has_id, bool dispose): + VirtualMachineAttributeSet(dispose) + { + init(va, has_id); + }; + + /** + * Creates an empty nic set + */ + VirtualMachinePCIs(bool dispose): + VirtualMachineAttributeSet(dispose){}; + + virtual ~VirtualMachinePCIs(){}; + + /** + * Function used to initialize the attribute map based on a vector of NIC + */ + void init(std::vector& vas, bool has_id) + { + if ( has_id ) + { + init_attribute_map(PCI_ID_NAME, vas); + } + else + { + init_attribute_map("", vas); + } + } + + /* ---------------------------------------------------------------------- */ + /* Iterators */ + /* ---------------------------------------------------------------------- */ + /** + * Generic iterator for the nic set. + */ + class PCIIterator : public AttributeIterator + { + public: + PCIIterator():AttributeIterator(){}; + PCIIterator(const AttributeIterator& dit):AttributeIterator(dit){}; + virtual ~PCIIterator(){}; + + VirtualMachinePCI * operator*() const + { + return static_cast(map_it->second); + } + }; + + PCIIterator begin() + { + PCIIterator it(ExtendedAttributeSet::begin()); + return it; + } + + PCIIterator end() + { + PCIIterator it(ExtendedAttributeSet::end()); + return it; + } + + typedef class PCIIterator pci_iterator; + + /* ---------------------------------------------------------------------- */ + /* NIC interface */ + /* ---------------------------------------------------------------------- */ + /** + * Returns the PCI attribute for a network interface + * @param pci_id of the PCI + * @return pointer to the attribute ir null if not found + */ + VirtualMachinePCI * get_pci(int pci_id) const + { + return static_cast(get_attribute(pci_id)); + } + + /* ---------------------------------------------------------------------- */ + /* Attach PCI interface */ + /* ---------------------------------------------------------------------- */ + /** + * Clear attach status from the attach PCI (ATTACH=YES) and removes PCI + * from the set + */ + VirtualMachinePCI * delete_attach() + { + return static_cast(remove_attribute("ATTACH")); + } + + /** + * Clear attach status from the attach PCI (ATTACH=YES) + */ + VirtualMachinePCI * clear_attach() + { + return static_cast(clear_flag("ATTACH")); + } + + /** + * Get the attach PCI (ATTACH=YES) + */ + VirtualMachinePCI * get_attach() + { + return static_cast(get_attribute("ATTACH")); + } + +protected: + + VirtualMachineAttribute * attribute_factory(VectorAttribute * va, + int id) const + { + return new VirtualMachinePCI(va, id); + }; + +private: + static const char * PCI_NAME; //"PCI" + + static const char * PCI_ID_NAME; //"PCI_ID" +}; + +#endif /*VIRTUAL_MACHINE_PCI_H_*/ + diff --git a/src/cli/onevm b/src/cli/onevm index 138a9fab4c..aa8c5cb8c4 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -1610,6 +1610,70 @@ CommandParser::CmdParser.new(ARGV) do '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' EOT + pci_attach_desc = <<-EOT.unindent + Attaches a PCI to a VM. + + You can specify the PCI device with --pci (short_address) or + --pci_device (device ID), --pci_class (class ID) and/or --pci_vendor (vendor ID). + + States: POWEROFF + EOT + + command :"pci-attach", pci_attach_desc, :vmid, + :options => [ + OpenNebulaHelper::FILE, + PCI, + PCI_CLASS, + PCI_VENDOR, + PCI_DEVICE + ] do + + + pci_attrs = [:pci, :pci_device, :pci_vendor, :pci_class].any? do |o| + !options[o].nil? + end + + if options[:file] + template = File.read(options[:file]) + elsif !(stdin = self.read_stdin).empty? + template = stdin + elsif pci_attrs + pcia = options[:pci] + pcid = options[:pci_device] + pcic = options[:pci_class] + pciv = options[:pci_vendor] + + pcis = [] + pcis << "SHORT_ADDRESS = \"#{pcia}\"" if pcia + pcis << "DEVICE = \"#{pcid}\"" if pcid + pcis << "CLASS = \"#{pcic}\"" if pcic + pcis << "VENDOR = \"#{pciv}\"" if pciv + + template = "PCI = [ #{pcis.join(',')} ]" + else + STDERR.puts 'Provide a PCI description file with --file or the relevant pci options:' + exit(-1) + end + + helper.perform_action(args[0], options, 'Attaching PCI device') do |vm| + vm.pci_attach(template) + end + end + + pci_detach_desc = <<-EOT.unindent + Detaches a PCI device from a VM + + States: POWEROFF + EOT + + command :"pci-detach", pci_detach_desc, :vmid, :pciid do + pciid = args[1].to_i + + helper.perform_action(args[0], options, 'Detaching PCI') do |vm| + vm.pci_detach(pciid) + end + end + command :ssh, ssh_desc, :vmid, diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index beda9ad684..89febb34f7 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -3012,3 +3012,158 @@ int DispatchManager::resize(int vid, float cpu, int vcpu, long memory, return rc; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int DispatchManager::attach_pci(int vid, VectorAttribute * pci, + const RequestAttributes& ra, string& error_str) +{ + ostringstream oss; + + auto vm = vmpool->get(vid); + + if ( vm == nullptr ) + { + error_str = "Virtual machine does not exist"; + return -1; + } + + if (vm->get_state() != VirtualMachine::POWEROFF || + vm->get_lcm_state() != VirtualMachine::LCM_INIT) + { + error_str = "VM in wrong state, it has to be in poweroff"; + return -1; + } + + int hid; + + if (vm->hasHistory()) + { + hid = vm->get_hid(); + } + else + { + error_str = "Could not find host information"; + return -1; + } + + HostShareCapacity sr; + + sr.vmid = vid; + sr.pci.push_back(pci); + + auto host = hpool->get(hid); + + if ( host == nullptr ) + { + error_str = "Could not find host information"; + return -1; + } + + if (!host->add_pci(sr)) + { + error_str = "Cannot assign PCI device in host. Check address and free devices"; + return -1; + } + + if ( vm->attach_pci(pci, error_str) == -1 ) + { + return -1; + } + + hpool->update(host.get()); + + close_cp_history(vmpool, vm.get(), VMActions::PCI_ATTACH_ACTION, ra); + + vm->log("DiM", Log::INFO, "PCI device successfully attached."); + + vmpool->update_search(vm.get()); + + time_t the_time = time(0); + + vm->set_running_etime(the_time); + + vmpool->update_history(vm.get()); + + vmpool->update(vm.get()); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int DispatchManager::detach_pci(int vid, int pci_id, const RequestAttributes& ra, + string& error_str) +{ + ostringstream oss; + + auto vm = vmpool->get(vid); + + if ( vm == nullptr ) + { + error_str = "Virtual machine does not exist"; + return -1; + } + + if (vm->get_state() != VirtualMachine::POWEROFF || + vm->get_lcm_state() != VirtualMachine::LCM_INIT) + { + error_str = "VM in wrong state, it has to be in poweroff"; + return -1; + } + + int hid = -1; + + if (vm->hasHistory()) + { + hid = vm->get_hid(); + } + + VectorAttribute * vpci = vm->get_pci(pci_id); + + if (vpci == nullptr) + { + error_str = "PCI device not found"; + return -1; + } + + if ( vpci->vector_value("TYPE") == "NIC" ) + { + error_str = "Use one.vm.detachnic to detach this PCI device"; + return -1; + } + + HostShareCapacity sr; + + sr.vmid = vid; + sr.pci.push_back(vpci); + + if (auto host = hpool->get(hid)) + { + host->del_pci(sr); + hpool->update(host.get()); + } + + vm->detach_pci(vpci); + + close_cp_history(vmpool, vm.get(), VMActions::PCI_DETACH_ACTION, ra); + + vm->log("DiM", Log::INFO, "PCI device successfully deatached."); + + vmpool->update_search(vm.get()); + + time_t the_time = time(0); + + vm->set_running_etime(the_time); + + vmpool->update_history(vm.get()); + + vmpool->update(vm.get()); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/oca/ruby/opennebula/virtual_machine.rb b/src/oca/ruby/opennebula/virtual_machine.rb index fcbd9c4c2b..406e0a3f44 100644 --- a/src/oca/ruby/opennebula/virtual_machine.rb +++ b/src/oca/ruby/opennebula/virtual_machine.rb @@ -61,7 +61,9 @@ module OpenNebula :detachsg => 'vm.detachsg', :backup => 'vm.backup', :updatenic => 'vm.updatenic', - :backupcancel => 'vm.backupcancel' + :backupcancel => 'vm.backupcancel', + :attachpci => 'vm.attachpci', + :detachpci => 'vm.detachpci' } VM_STATE=['INIT', 'PENDING', 'HOLD', 'ACTIVE', 'STOPPED', 'SUSPENDED', 'DONE', 'FAILED', @@ -228,8 +230,68 @@ module OpenNebula 'BACKUP_POWEROFF' => 'back' } - HISTORY_ACTION=['none', 'migrate', 'live-migrate', 'shutdown', 'shutdown-hard', 'undeploy', - 'undeploy-hard', 'hold', 'release', 'stop', 'suspend', 'resume', 'boot', 'delete', 'delete-recreate', 'reboot', 'reboot-hard', 'resched', 'unresched', 'poweroff', 'poweroff-hard', 'disk-attach', 'disk-detach', 'nic-attach', 'nic-detach', 'disk-snapshot-create', 'disk-snapshot-delete', 'terminate', 'terminate-hard', 'disk-resize', 'deploy', 'chown', 'chmod', 'updateconf', 'rename', 'resize', 'update', 'snapshot-resize', 'snapshot-delete', 'snapshot-revert', 'disk-saveas', 'disk-snapshot-revert', 'recover', 'retry', 'monitor', 'disk-snapshot-rename', 'alias-attach', 'alias-detach', 'poweroff-migrate', 'poweroff-hard-migrate', 'backup', 'nic-update'] + HISTORY_ACTION=[ + 'none', + 'migrate', + 'live-migrate', + 'shutdown', + 'shutdown-hard', + 'undeploy', + 'undeploy-hard', + 'hold', + 'release', + 'stop', + 'suspend', + 'resume', + 'boot', + 'delete', + 'delete-recreate', + 'reboot', + 'reboot-hard', + 'resched', + 'unresched', + 'poweroff', + 'poweroff-hard', + 'disk-attach', + 'disk-detach', + 'nic-attach', + 'nic-detach', + 'disk-snapshot-create', + 'disk-snapshot-delete', + 'terminate', + 'terminate-hard', + 'disk-resize', + 'deploy', + 'chown', + 'chmod', + 'updateconf', + 'rename', + 'resize', + 'update', + 'snapshot-resize', + 'snapshot-delete', + 'snapshot-revert', + 'disk-saveas', + 'disk-snapshot-revert', + 'recover', + 'retry', + 'monitor', + 'disk-snapshot-rename', + 'alias-attach', + 'alias-detach', + 'poweroff-migrate', + 'poweroff-hard-migrate', + 'backup', + 'nic-update', + 'backup-cancel', + 'sched-add', + 'sched-update', + 'sched-delete', + 'sg-attach', + 'sg-detach', + 'pci-attach', + 'pci-detach' + ] EXTERNAL_IP_ATTRS = [ 'GUEST_IP', @@ -811,6 +873,24 @@ module OpenNebula @client.call(VM_METHODS[:backupcancel], @pe_id) end + # Attaches a PCI to a VM + # + # @param pci [String] Template containing a PCI element + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def pci_attach(pci) + call(VM_METHODS[:attachpci], @pe_id, pci) + end + + # Detaches a PCI from a VM + # + # @param nic_id [Integer] Id of the PCI to be detached + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def pci_detach(pci_id) + call(VM_METHODS[:detachpci], @pe_id, pci_id) + end + ######################################################################## # Helpers to get VirtualMachine information ######################################################################## diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 90e6bd71e2..a56ca183e1 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -350,6 +350,8 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr vm_detachsg(new VirtualMachineDetachSG()); xmlrpc_c::methodPtr vm_backup(new VirtualMachineBackup()); xmlrpc_c::methodPtr vm_backupcancel(new VirtualMachineBackupCancel()); + xmlrpc_c::methodPtr vm_attachpci(new VirtualMachineAttachPCI()); + xmlrpc_c::methodPtr vm_detachpci(new VirtualMachineDetachPCI()); xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting()); xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring()); @@ -615,6 +617,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vm.detachsg", vm_detachsg); RequestManagerRegistry.addMethod("one.vm.backup", vm_backup); RequestManagerRegistry.addMethod("one.vm.backupcancel", vm_backupcancel); + RequestManagerRegistry.addMethod("one.vm.attachpci", vm_attachpci); + RequestManagerRegistry.addMethod("one.vm.detachpci", vm_detachpci); RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info); RequestManagerRegistry.addMethod("one.vmpool.infoextended", vm_pool_info_extended); @@ -1176,23 +1180,23 @@ void RequestManager::register_xml_methods() if (nebula.is_federation_slave()) { - marketapp_update_pt = new RequestManagerProxy("one.marketapp.update"); - marketapp_chmod_pt = new RequestManagerProxy("one.marketapp.chmod"); - marketapp_chown_pt = new RequestManagerProxy("one.marketapp.chown"); - marketapp_enable_pt = new RequestManagerProxy("one.marketapp.enable"); - marketapp_rename_pt = new RequestManagerProxy("one.marketapp.rename"); + marketapp_update_pt = new RequestManagerProxy("one.marketapp.update"); + marketapp_chmod_pt = new RequestManagerProxy("one.marketapp.chmod"); + marketapp_chown_pt = new RequestManagerProxy("one.marketapp.chown"); + marketapp_enable_pt = new RequestManagerProxy("one.marketapp.enable"); + marketapp_rename_pt = new RequestManagerProxy("one.marketapp.rename"); marketapp_lock_pt = new RequestManagerProxy("one.marketapp.lock"); - marketapp_unlock_pt = new RequestManagerProxy("one.marketapp.unlock"); + marketapp_unlock_pt = new RequestManagerProxy("one.marketapp.unlock"); } else { - marketapp_update_pt = new MarketPlaceAppUpdateTemplate(); - marketapp_chmod_pt = new MarketPlaceAppChmod(); - marketapp_chown_pt = new MarketPlaceAppChown(); - marketapp_enable_pt = new MarketPlaceAppEnable(); - marketapp_rename_pt = new MarketPlaceAppRename(); + marketapp_update_pt = new MarketPlaceAppUpdateTemplate(); + marketapp_chmod_pt = new MarketPlaceAppChmod(); + marketapp_chown_pt = new MarketPlaceAppChown(); + marketapp_enable_pt = new MarketPlaceAppEnable(); + marketapp_rename_pt = new MarketPlaceAppRename(); marketapp_lock_pt = new MarketPlaceAppLock(); - marketapp_unlock_pt = new MarketPlaceAppUnlock(); + marketapp_unlock_pt = new MarketPlaceAppUnlock(); xmlrpc_c::methodPtr marketapp_updatedb(new MarketPlaceAppUpdateDB()); xmlrpc_c::methodPtr marketapp_dropdb(new MarketPlaceAppDropDB()); diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index e20c2a9c0f..c12482748c 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -4076,3 +4076,163 @@ void VirtualMachineBackupCancel::request_execute( return; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VirtualMachineAttachPCI::request_execute( + xmlrpc_c::paramList const& paramList, RequestAttributes& att) +{ + int id = xmlrpc_c::value_int(paramList.getInt(1)); + string stmpl = xmlrpc_c::value_string(paramList.getString(2)); + + // ------------------------------------------------------------------------- + // Parse PCI template and check PCI attributes + // ------------------------------------------------------------------------- + VirtualMachineTemplate tmpl; + + int rc = tmpl.parse_str_or_xml(stmpl, att.resp_msg); + + if ( rc != 0 ) + { + failure_response(INTERNAL, att); + return; + } + + VectorAttribute * pci = tmpl.get("PCI"); + + if ( pci == nullptr ) + { + att.resp_msg = "PCI attribute not found"; + + failure_response(ACTION, att); + return; + } + else if ( pci->vector_value("TYPE") == "NIC" ) + { + att.resp_msg = "Use one.vm.attachnic to attach this PCI device"; + + failure_response(ACTION, att); + return; + } + else if ( VirtualMachine::check_pci_attributes(pci, att.resp_msg) != 0) + { + failure_response(ACTION, att); + return; + } + + // ------------------------------------------------------------------------- + // Authorize the operation, restricted attributes + // ------------------------------------------------------------------------- + PoolObjectAuth vm_perms; + + if (auto vm = Nebula::instance().get_vmpool()->get_ro(id)) + { + vm->get_permissions(vm_perms); + } + else + { + att.resp_id = id; + att.resp_obj = PoolObjectSQL::VM; + + failure_response(NO_EXISTS, att); + return; + } + + AuthRequest ar(att.uid, att.group_ids); + + ar.add_auth(att.auth_op, vm_perms); + + VirtualMachine::set_auth_request(att.uid, ar, &tmpl, true); + + if (UserPool::authorize(ar) == -1) + { + att.resp_msg = ar.message; + + failure_response(AUTHORIZATION, att); + return; + } + + if (!att.is_admin()) + { + string aname; + + if (tmpl.check_restricted(aname)) + { + att.resp_msg = "PCI includes a restricted attribute " + aname; + failure_response(AUTHORIZATION, att); + return; + } + } + + // ------------------------------------------------------------------------- + // Perform the attach + // ------------------------------------------------------------------------- + rc = dm->attach_pci(id, pci, att, att.resp_msg); + + if ( rc != 0 ) + { + failure_response(ACTION, att); + } + else + { + success_response(id, att); + } + + return; +} + +// ----------------------------------------------------------------------------- + +void VirtualMachineDetachPCI::request_execute( + xmlrpc_c::paramList const& paramList, RequestAttributes& att) +{ + int id = xmlrpc_c::value_int(paramList.getInt(1)); + int pci_id = xmlrpc_c::value_int(paramList.getInt(2)); + + // ------------------------------------------------------------------------- + // Authorize the operation, restricted attributes + // ------------------------------------------------------------------------- + PoolObjectAuth vm_perms; + + if (auto vm = Nebula::instance().get_vmpool()->get_ro(id)) + { + vm->get_permissions(vm_perms); + } + else + { + att.resp_id = id; + att.resp_obj = PoolObjectSQL::VM; + + failure_response(NO_EXISTS, att); + return; + } + + AuthRequest ar(att.uid, att.group_ids); + + ar.add_auth(att.auth_op, vm_perms); + + if (UserPool::authorize(ar) == -1) + { + att.resp_msg = ar.message; + + failure_response(AUTHORIZATION, att); + return; + } + + // ------------------------------------------------------------------------- + // Perform the detach + // ------------------------------------------------------------------------- + int rc = dm->detach_pci(id, pci_id, att, att.resp_msg); + + if ( rc != 0 ) + { + failure_response(ACTION, att); + } + else + { + success_response(id, att); + } + + return; +} diff --git a/src/vm/VMActions.cc b/src/vm/VMActions.cc index 18fd682d74..b64af6f7bf 100644 --- a/src/vm/VMActions.cc +++ b/src/vm/VMActions.cc @@ -220,6 +220,11 @@ int VMActions::set_auth_ops(const string& ops_str, ops_set.set(SG_ATTACH_ACTION); ops_set.set(SG_DETACH_ACTION); } + else if ( the_op == "pci-attach" ) + { + ops_set.set(PCI_ATTACH_ACTION); + ops_set.set(PCI_DETACH_ACTION); + } else { error = "Unknown vm operation: " + the_op; @@ -401,6 +406,12 @@ string VMActions::action_to_str(Action action) case SG_DETACH_ACTION: st = "sg-detach"; break; + case PCI_ATTACH_ACTION: + st = "pci-attach"; + break; + case PCI_DETACH_ACTION: + st = "pci-detach"; + break; case NONE_ACTION: st = "none"; break; @@ -627,6 +638,14 @@ int VMActions::action_from_str(const string& st, Action& action) { action = SG_DETACH_ACTION; } + else if ( st == "pci-attach") + { + action = PCI_ATTACH_ACTION; + } + else if ( st == "pci-detach") + { + action = PCI_DETACH_ACTION; + } else { action = NONE_ACTION; diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index acf375b146..203cb3f66d 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -3793,6 +3793,94 @@ void VirtualMachine::delete_attach_alias(VirtualMachineNic *nic) } } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::attach_pci(VectorAttribute * vpci, string& err) +{ + 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; + } + } + + // ------------------------------------------------------------------------- + // Setup PCI attribute & Context + // ------------------------------------------------------------------------- + Nebula& nd = Nebula::instance(); + std::unique_ptr _new_pci(vpci->clone()); + + string bus; + + _new_pci->replace("PCI_ID", max_pci_id + 1); + + nd.get_configuration_attribute("PCI_PASSTHROUGH_BUS", bus); + + if ( HostSharePCI::set_pci_address(_new_pci.get(), bus, false) != 0 ) + { + err = "Wrong BUS in PCI attribute"; + return -1; + } + + add_pci_context(_new_pci.get()); + + // ------------------------------------------------------------------------- + // Add new nic to template + // ------------------------------------------------------------------------- + obj_template->set(_new_pci.release()); + + return max_pci_id + 1; +} + +// ----------------------------------------------------------------------------- + +VectorAttribute * VirtualMachine::get_pci(int pci_id) +{ + std::vector pcis; + + obj_template->get("PCI", pcis); + + for (auto& pci: pcis) + { + int id; + + pci->vector_value("PCI_ID", id, -1); + + if (pci_id == id) + { + return pci; + } + } + + return nullptr; +} + +// ----------------------------------------------------------------------------- + +void VirtualMachine::detach_pci(VectorAttribute * pci) +{ + // ------------------------------------------------------------------------- + // Remove from Template & Context + // ------------------------------------------------------------------------- + clear_pci_context(pci); + + obj_template->remove(pci); + + delete pci; +} + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* VirtualMachine VMGroup interface */ diff --git a/src/vm/VirtualMachineContext.cc b/src/vm/VirtualMachineContext.cc index 8b503c6cdb..c10b2b72f0 100644 --- a/src/vm/VirtualMachineContext.cc +++ b/src/vm/VirtualMachineContext.cc @@ -405,7 +405,8 @@ int VirtualMachine::generate_network_context(VectorAttribute* context, clear_nic_context(nic_id); continue; } - else if (get_nic(nic_id)->is_alias()) // If nic was detached and current is alias + // If nic was detached and current is alias + else if (get_nic(nic_id)->is_alias()) { int parent_id; @@ -482,7 +483,7 @@ static void parse_pci_context_network(const std::vector& cvars, for (const auto& con : cvars) { - ostringstream cvar; + ostringstream cvar; cvar << "PCI" << pci_id << "_" << con.context_name; @@ -495,7 +496,7 @@ static void parse_pci_context_network(const std::vector& cvars, if (!cval.empty()) { - context->replace(cvar.str(), cval); + context->replace(cvar.str(), cval); } } } @@ -517,27 +518,70 @@ bool VirtualMachine::generate_pci_context(VectorAttribute * context) for(int i=0; ivector_value("TYPE") == "NIC" ) - { - parse_pci_context_network(NETWORK_CONTEXT, context, vatts[i]); - parse_pci_context_network(NETWORK6_CONTEXT, context, vatts[i]); - } + if ( net_context && vatts[i]->vector_value("TYPE") == "NIC" ) + { + parse_pci_context_network(NETWORK_CONTEXT, context, vatts[i]); + parse_pci_context_network(NETWORK6_CONTEXT, context, vatts[i]); + } - ostringstream cvar; + ostringstream cvar; - cvar << "PCI" << vatts[i]->vector_value("PCI_ID") << "_ADDRESS"; + cvar << "PCI" << vatts[i]->vector_value("PCI_ID") << "_ADDRESS"; - string cval = vatts[i]->vector_value("VM_ADDRESS"); + string cval = vatts[i]->vector_value("VM_ADDRESS"); - if (!cval.empty()) - { - context->replace(cvar.str(), cval); - } + if (!cval.empty()) + { + context->replace(cvar.str(), cval); + } } return net_context; } +/* -------------------------------------------------------------------------- */ + +void VirtualMachine::clear_pci_context(VectorAttribute * pci) +{ + VectorAttribute * context = obj_template->get("CONTEXT"); + + if (context == 0) + { + return; + } + + ostringstream att_name; + + att_name << "PCI" << pci->vector_value("PCI_ID") << "_ADDRESS"; + + context->remove(att_name.str()); +} + +/* -------------------------------------------------------------------------- */ + +void VirtualMachine::add_pci_context(VectorAttribute * pci) +{ + VectorAttribute * context = obj_template->get("CONTEXT"); + + if (context == 0) + { + return; + } + + string addr = pci->vector_value("VM_ADDRESS"); + + if (addr.empty()) + { + return; + } + + ostringstream att_name; + + att_name << "PCI" << pci->vector_value("PCI_ID") << "_ADDRESS"; + + context->replace(att_name.str(), addr); +} + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -608,7 +652,7 @@ int VirtualMachine::parse_context(string& error_str, bool all_nics) return -1; } - generate_pci_context(context); + generate_pci_context(context); // ------------------------------------------------------------------------- // Parse FILE_DS variables