1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-11 04:58:16 +03:00

F #5612: (De)/Attach security groups from VM NICs

Author: Pavel Czerný <pczerny@opennebula.systems>
This commit is contained in:
Ruben S. Montero 2022-01-25 18:02:10 +01:00
parent 8582d83063
commit 265d05c118
No known key found for this signature in database
GPG Key ID: A0CEA6FA880A1D87
11 changed files with 606 additions and 1 deletions

View File

@ -29,6 +29,7 @@ class HostPool;
class VirtualMachinePool;
class VirtualRouterPool;
class UserPool;
class SecurityGroupPool;
class VirtualMachine;
class VirtualMachineTemplate;
@ -426,6 +427,34 @@ public:
int live_updateconf(std::unique_ptr<VirtualMachine> vm,
const RequestAttributes& ra, std::string& error_str);
/**
* Attach a new SG to a VM NIC
*
* @param vid the VM id
* @param nicid the id of the VM NIC
* @param sgid the SG id
* @param ra information about the API call request
* @param error_str Error reason, if any
*
* @return 0 on success, -1 otherwise
*/
int attach_sg(int vid, int nicid, int sgid,
const RequestAttributes& ra, std::string& error_str);
/**
* Detach a SG from VM NIC
*
* @param vid the VM id
* @param nicid the id of the VM NIC
* @param sgid the SG id
* @param ra information about the API call request
* @param error_str Error reason, if any
*
* @return 0 on success, -1 otherwise
*/
int detach_sg(int vid, int nicid, int sgid,
const RequestAttributes& ra, std::string& error_str);
//--------------------------------------------------------------------------
// DM Actions associated with a VM state transition
//--------------------------------------------------------------------------
@ -463,6 +492,11 @@ private:
*/
ClusterPool * clpool = nullptr;
/**
* Pointer to Security Group Pool
*/
SecurityGroupPool * sgpool = nullptr;
/**
* Pointer to the Virtual Router Pool
*/

View File

@ -589,4 +589,40 @@ private:
ImagePool* ipool;
};
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class VirtualMachineAttachSG : public RequestManagerVirtualMachine
{
public:
VirtualMachineAttachSG():
RequestManagerVirtualMachine("one.vm.attachsg",
"Attaches a SG to the virtual machine NIC",
"A:siii")
{
}
protected:
void request_execute(xmlrpc_c::paramList const& pl,
RequestAttributes& ra) override;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
class VirtualMachineDetachSG : public RequestManagerVirtualMachine
{
public:
VirtualMachineDetachSG():
RequestManagerVirtualMachine("one.vm.detachsg",
"Detaches a SG form virtual machine NIC",
"A:siii")
{
}
protected:
void request_execute(xmlrpc_c::paramList const& pl,
RequestAttributes& ra) override;
};
#endif

View File

@ -139,11 +139,26 @@ public:
return outdated.pop(id);
}
bool is_outdated(int id)
{
return outdated.contains(id);
}
int add_outdated(int id)
{
return outdated.add(id);
}
int add_updating(int id)
{
return updating.add(id);
}
bool is_updating(int id)
{
return updating.contains(id);
}
int del_updating(int id)
{
return updating.del(id);

View File

@ -63,6 +63,18 @@ public:
one_util::split_unique(vector_value("SECURITY_GROUPS"), ',', sgs);
}
/**
* Add security group
* @param sgid ID of the group
*/
void add_security_group(int sgid);
/**
* Remove security group
* @param sgid ID of the group
*/
void remove_security_group(int sgid);
/**
* Get the effective uid to get the VirtualNetwork.
*/

View File

@ -909,6 +909,36 @@ CommandParser::CmdParser.new(ARGV) do
end
end
sg_attach_desc = <<-EOT.unindent
Attaches a Security Group to a VM.
States: All, except BOOT, MIGRATE and HOTPLUG_NIC
EOT
command :"sg-attach", sg_attach_desc, :vmid, :nicid, :sgid do
nic_id = args[1].to_i
sg_id = args[2].to_i
helper.perform_action(args[0], options, 'Attach SG') do |vm|
vm.sg_attach(nic_id, sg_id)
end
end
sg_detach_desc = <<-EOT.unindent
Detaches a Security Group from a VM.
States: All, except BOOT, MIGRATE and HOTPLUG_NIC
EOT
command :"sg-detach", sg_detach_desc, :vmid, :nicid, :sgid do
nic_id = args[1].to_i
sg_id = args[2].to_i
helper.perform_action(args[0], options, 'Detach SG') do |vm|
vm.sg_detach(nic_id, sg_id)
end
end
chgrp_desc = <<-EOT.unindent
Changes the VM group
EOT

View File

@ -50,6 +50,7 @@ void DispatchManager::init_managers()
clpool = nd.get_clpool();
vrouterpool = nd.get_vrouterpool();
upool = nd.get_upool();
sgpool = nd.get_secgrouppool();
}
/* -------------------------------------------------------------------------- */

View File

@ -28,6 +28,7 @@
#include "HostPool.h"
#include "VirtualMachinePool.h"
#include "VirtualRouterPool.h"
#include "SecurityGroupPool.h"
using namespace std;
@ -2256,3 +2257,274 @@ int DispatchManager::live_updateconf(std::unique_ptr<VirtualMachine> vm,
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int DispatchManager::attach_sg(int vid, int nicid, int sgid,
const RequestAttributes& ra, string& error_str)
{
ostringstream oss;
bool is_update = false;
// -------------------------------------------------------------------------
// Check action consistency:
// - Object exists VM, NIC and SG
// - State is compatible with operation
// -------------------------------------------------------------------------
if ( sgpool->exist(sgid) < 0 )
{
oss << "Could not attach SG " << sgid << " to VM, SG does not exist";
error_str = oss.str();
return -1;
}
if ( auto vm = vmpool->get(vid) )
{
VirtualMachine::LcmState lstate = vm->get_lcm_state();
switch (lstate)
{
//Cannnot update VM, SG rules being updated/created
case VirtualMachine::BOOT:
case VirtualMachine::BOOT_MIGRATE:
case VirtualMachine::BOOT_SUSPENDED:
case VirtualMachine::BOOT_STOPPED:
case VirtualMachine::BOOT_UNDEPLOY:
case VirtualMachine::BOOT_POWEROFF:
case VirtualMachine::BOOT_UNKNOWN:
case VirtualMachine::BOOT_FAILURE:
case VirtualMachine::BOOT_MIGRATE_FAILURE:
case VirtualMachine::BOOT_UNDEPLOY_FAILURE:
case VirtualMachine::BOOT_STOPPED_FAILURE:
case VirtualMachine::MIGRATE:
case VirtualMachine::HOTPLUG_NIC:
case VirtualMachine::HOTPLUG_NIC_POWEROFF:
case VirtualMachine::UNKNOWN:
oss << "VM " << vid << " is in wrong state " << vm->state_str();
error_str = oss.str();
return -1;
//Update SG rules at host
case VirtualMachine::RUNNING:
case VirtualMachine::HOTPLUG:
case VirtualMachine::HOTPLUG_SNAPSHOT:
case VirtualMachine::HOTPLUG_SAVEAS:
case VirtualMachine::HOTPLUG_RESIZE:
case VirtualMachine::DISK_SNAPSHOT:
case VirtualMachine::DISK_SNAPSHOT_DELETE:
case VirtualMachine::DISK_RESIZE:
is_update = true;
break;
default:
break;
}
auto nic = vm->get_nic(nicid);
if ( nic == nullptr )
{
oss << "VM " << vid << " doesn't have NIC id " << nicid;
error_str = oss.str();
return -1;
}
set<int> sgs;
nic->get_security_groups(sgs);
if (sgs.find(sgid) != sgs.end())
{
oss << "VM " << vid << " SG " << sgid << " already in NIC " << nicid;
error_str = oss.str();
return -1;
}
// ----------------------------------------------------------------------
// Update VM data
// - NIC to include new SG
// - Template to include new SG rules (note can be there already)
// ----------------------------------------------------------------------
vector<VectorAttribute *> sg_rules;
sgpool->get_security_group_rules(-1, sgid, sg_rules);
nic->add_security_group(sgid);
vm->remove_security_group(sgid); //duplicates
vm->add_template_attribute(sg_rules);
vmpool->update(vm.get());
if ( is_update )
{
vmm->updatesg(vm.get(), sgid);
}
}
else
{
oss << "Could not attach SG to VM " << vid << ", VM does not exist";
error_str = oss.str();
return -1;
}
if (auto sg = sgpool->get(sgid))
{
sg->del_vm(vid);
if ( is_update )
{
sg->add_updating(vid);
}
else
{
sg->add_vm(vid);
}
sgpool->update(sg.get());
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int DispatchManager::detach_sg(int vid, int nicid, int sgid,
const RequestAttributes& ra, string& error_str)
{
ostringstream oss;
bool is_update = false;
vector<VectorAttribute *> sg_rules;
// -------------------------------------------------------------------------
// Check action consistency:
// - Object exists VM, NIC and SG
// - State is compatible with operation
// -------------------------------------------------------------------------
if ( auto vm = vmpool->get(vid) )
{
VirtualMachine::LcmState lstate = vm->get_lcm_state();
switch (lstate)
{
//Cannnot update VM, SG rules being updated/created
case VirtualMachine::BOOT:
case VirtualMachine::BOOT_MIGRATE:
case VirtualMachine::BOOT_SUSPENDED:
case VirtualMachine::BOOT_STOPPED:
case VirtualMachine::BOOT_UNDEPLOY:
case VirtualMachine::BOOT_POWEROFF:
case VirtualMachine::BOOT_UNKNOWN:
case VirtualMachine::BOOT_FAILURE:
case VirtualMachine::BOOT_MIGRATE_FAILURE:
case VirtualMachine::BOOT_UNDEPLOY_FAILURE:
case VirtualMachine::BOOT_STOPPED_FAILURE:
case VirtualMachine::MIGRATE:
case VirtualMachine::HOTPLUG_NIC:
case VirtualMachine::HOTPLUG_NIC_POWEROFF:
case VirtualMachine::UNKNOWN:
oss << "VM " << vid << " is in wrong state " << vm->state_str();
error_str = oss.str();
return -1;
//Update SG rules at host
case VirtualMachine::RUNNING:
case VirtualMachine::HOTPLUG:
case VirtualMachine::HOTPLUG_SNAPSHOT:
case VirtualMachine::HOTPLUG_SAVEAS:
case VirtualMachine::HOTPLUG_RESIZE:
case VirtualMachine::DISK_SNAPSHOT:
case VirtualMachine::DISK_SNAPSHOT_DELETE:
case VirtualMachine::DISK_RESIZE:
is_update = true;
break;
default:
break;
}
auto nic = vm->get_nic(nicid);
if ( nic == nullptr )
{
oss << "VM " << vid << " doesn't have NIC id " << nicid;
error_str = oss.str();
return -1;
}
set<int> sgs;
nic->get_security_groups(sgs);
if (sgs.find(sgid) == sgs.end())
{
oss << "VM " << vid << " NIC " << nicid << " doesn't contain SG " << sgid;
error_str = oss.str();
return -1;
}
// ----------------------------------------------------------------------
// Update VM data
// - NIC to remove SG
// - Template with remaining SG rules (could be empty)
// ----------------------------------------------------------------------
nic->remove_security_group(sgid);
vm->remove_security_group(sgid);
sgs.clear();
vm->get_security_groups(sgs);
if (sgs.find(sgid) != sgs.end())
{
sgpool->get_security_group_rules(-1, sgid, sg_rules);
vm->add_template_attribute(sg_rules);
}
vmpool->update(vm.get());
if ( is_update )
{
vmm->updatesg(vm.get(), sgid);
}
}
else
{
oss << "Could not attach SG to VM " << vid << ", VM does not exist";
error_str = oss.str();
return -1;
}
if (auto sg = sgpool->get(sgid))
{
sg->del_vm(vid);
if (!sg_rules.empty())
{
// The SG is in other NIC, keep the VM in the SG list
if ( is_update )
{
sg->add_updating(vid);
}
else
{
sg->add_vm(vid);
}
}
sgpool->update(sg.get());
}
return 0;
}

View File

@ -54,7 +54,9 @@ module OpenNebula
:unlock => "vm.unlock",
:schedadd => "vm.schedadd",
:scheddelete => "vm.scheddelete",
:schedupdate => "vm.schedupdate"
:schedupdate => "vm.schedupdate",
:attachsg => "vm.attachsg",
:detachsg => "vm.detachsg"
}
VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED
@ -460,6 +462,25 @@ module OpenNebula
return call(VM_METHODS[:detachnic], @pe_id, nic_id)
end
# Attaches a Security Groupt to a running VM
#
# @param nic_id [Integer] Id of the NIC, where to attach SG
# @param sg_id [Integer] Id of the SG to be attached
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
def sg_attach(nic_id, sg_id)
return call(VM_METHODS[:attachsg], @pe_id, nic_id, sg_id)
end
# Detaches a Security Group from a running VM
#
# @param sg_id [Integer] Id of the SG to be detached
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
def sg_detach(nic_id, sg_id)
return call(VM_METHODS[:detachsg], @pe_id, nic_id, sg_id)
end
# Sets the re-scheduling flag for the VM
def resched
action('resched')

View File

@ -343,6 +343,8 @@ void RequestManager::register_xml_methods()
xmlrpc_c::methodPtr vm_sched_add(new RequestManagerSchedAdd());
xmlrpc_c::methodPtr vm_sched_delete(new RequestManagerSchedDelete());
xmlrpc_c::methodPtr vm_sched_update(new RequestManagerSchedUpdate());
xmlrpc_c::methodPtr vm_attachsg(new VirtualMachineAttachSG());
xmlrpc_c::methodPtr vm_detachsg(new VirtualMachineDetachSG());
xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting());
xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring());
@ -577,6 +579,8 @@ void RequestManager::register_xml_methods()
RequestManagerRegistry.addMethod("one.vm.schedadd", vm_sched_add);
RequestManagerRegistry.addMethod("one.vm.scheddelete", vm_sched_delete);
RequestManagerRegistry.addMethod("one.vm.schedupdate", vm_sched_update);
RequestManagerRegistry.addMethod("one.vm.attachsg", vm_attachsg);
RequestManagerRegistry.addMethod("one.vm.detachsg", vm_detachsg);
RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info);
RequestManagerRegistry.addMethod("one.vmpool.infoextended", vm_pool_info_extended);

View File

@ -23,6 +23,7 @@
#include "DatastorePool.h"
#include "HostPool.h"
#include "ImagePool.h"
#include "SecurityGroupPool.h"
#include "DispatchManager.h"
#include "VirtualMachineManager.h"
@ -3575,3 +3576,150 @@ void VirtualMachineDiskResize::request_execute(
return;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachineAttachSG::request_execute(
xmlrpc_c::paramList const& paramList,
RequestAttributes& att)
{
Nebula& nd = Nebula::instance();
DispatchManager * dm = nd.get_dm();
int vm_id = xmlrpc_c::value_int(paramList.getInt(1));
int nic_id = xmlrpc_c::value_int(paramList.getInt(2));
int sg_id = xmlrpc_c::value_int(paramList.getInt(3));
unique_ptr<VirtualMachineNic> nic_tmpl;
PoolObjectAuth vm_perms;
// Get VM attributes to authorize operation
if (auto vm = get_vm_ro(vm_id, att))
{
// Check if we can add the SG
auto nic = vm->get_nic(nic_id);
if (!nic)
{
ostringstream oss;
oss << "VM " << vm_id << " doesn't have NIC id " << nic_id;
att.resp_msg = oss.str();
failure_response(Request::INTERNAL, att);
return;
}
// Copy VM attributes
nic_tmpl.reset(new VirtualMachineNic(nic->vector_attribute(), nic_id));
nic_tmpl->add_security_group(sg_id);
vm->get_permissions(vm_perms);
}
else
{
return;
}
// Authorize the operation
AuthRequest ar(att.uid, att.group_ids);
ar.add_auth(AuthRequest::MANAGE, vm_perms);
nic_tmpl->authorize(att.uid, &ar, true);
if (UserPool::authorize(ar) == -1)
{
att.resp_msg = ar.message;
failure_response(Request::AUTHORIZATION, att);
return;
}
int rc = dm->attach_sg(vm_id, nic_id, sg_id, att, att.resp_msg);
if ( rc != 0 )
{
failure_response(ACTION, att);
}
else
{
success_response(vm_id, att);
}
return;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachineDetachSG::request_execute(
xmlrpc_c::paramList const& paramList, RequestAttributes& att)
{
auto& nd = Nebula::instance();
DispatchManager * dm = nd.get_dm();
int vm_id = xmlrpc_c::value_int(paramList.getInt(1));
int nic_id = xmlrpc_c::value_int(paramList.getInt(2));
int sg_id = xmlrpc_c::value_int(paramList.getInt(3));
PoolObjectAuth vm_perms;
if (auto vm = get_vm_ro(vm_id, att))
{
auto nic = vm->get_nic(nic_id);
if (!nic)
{
ostringstream oss;
oss << "VM " << vm_id << " doesn't have NIC id " << nic_id;
att.resp_msg = oss.str();
failure_response(Request::INTERNAL, att);
return;
}
set<int> sgs;
nic->get_security_groups(sgs);
if (sgs.find(sg_id) == sgs.end())
{
ostringstream oss;
oss << "VM " << vm_id << " NIC " << nic_id
<< " doesn't contain SG " << sg_id;
att.resp_msg = oss.str();
failure_response(INTERNAL, att);
return;
}
vm->get_permissions(vm_perms);
}
else
{
return;
}
// Authorize the operation
AuthRequest ar(att.uid, att.group_ids);
ar.add_auth(AuthRequest::MANAGE, vm_perms);
if (UserPool::authorize(ar) == -1)
{
att.resp_msg = ar.message;
failure_response(Request::AUTHORIZATION, att);
return;
}
auto rc = dm->detach_sg(vm_id, nic_id, sg_id, att, att.resp_msg);
if ( rc != 0 )
{
failure_response(ACTION, att);
}
else
{
success_response(vm_id, att);
}
}

View File

@ -70,6 +70,38 @@ int VirtualMachineNic::release_network_leases(int vmid)
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachineNic::add_security_group(int sg)
{
set<int> sgs;
get_security_groups(sgs);
auto rc = sgs.insert(sg);
if (rc.second)
{
replace("SECURITY_GROUPS", one_util::join(sgs.begin(), sgs.end(), ','));
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachineNic::remove_security_group(int sg)
{
set<int> sgs;
get_security_groups(sgs);
auto rc = sgs.erase(sg);
if (rc)
{
replace("SECURITY_GROUPS", one_util::join(sgs.begin(), sgs.end(), ','));
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachineNic::get_uid(int _uid, string& error)
{
string uid_s;