Support removing VM interfaces by MAC

Provides a way to handle multiple interfaces in the same network
gracefully, while making the previous behaviour explicit.
This commit is contained in:
Joshua Boniface 2021-10-27 13:17:35 -04:00
parent 52c3e8ced3
commit 872f35a7ee
2 changed files with 33 additions and 9 deletions

View File

@ -813,9 +813,9 @@ def vm_networks_add(config, vm, network, macaddr, model, sriov, sriov_mode, live
return retcode, retmsg return retcode, retmsg
def vm_networks_remove(config, vm, network, sriov, live, restart): def vm_networks_remove(config, vm, network, macaddr, sriov, live, restart):
""" """
Remove a network to the VM Remove a network from the VM, optionally by MAC
Calls vm_info to get the VM XML. Calls vm_info to get the VM XML.
@ -826,6 +826,9 @@ def vm_networks_remove(config, vm, network, sriov, live, restart):
from lxml.objectify import fromstring from lxml.objectify import fromstring
from lxml.etree import tostring from lxml.etree import tostring
if network is None and macaddr is None:
return False, "A network or MAC address must be specified for removal."
status, domain_information = vm_info(config, vm) status, domain_information = vm_info(config, vm)
if not status: if not status:
return status, domain_information return status, domain_information
@ -845,17 +848,26 @@ def vm_networks_remove(config, vm, network, sriov, live, restart):
if sriov: if sriov:
if interface.attrib.get('type') == 'hostdev': if interface.attrib.get('type') == 'hostdev':
if_dev = str(interface.sriov_device) if_dev = str(interface.sriov_device)
if network == if_dev: if macaddr is None and network == if_dev:
interface.getparent().remove(interface)
changed = True
elif macaddr is not None and macaddr == interface.mac.attrib.get('address'):
interface.getparent().remove(interface) interface.getparent().remove(interface)
changed = True changed = True
elif interface.attrib.get('type') == 'direct': elif interface.attrib.get('type') == 'direct':
if_dev = str(interface.source.attrib.get('dev')) if_dev = str(interface.source.attrib.get('dev'))
if network == if_dev: if macaddr is None and network == if_dev:
interface.getparent().remove(interface)
changed = True
elif macaddr is not None and macaddr == interface.mac.attrib.get('address'):
interface.getparent().remove(interface) interface.getparent().remove(interface)
changed = True changed = True
else: else:
if_vni = re.match(r'[vm]*br([0-9a-z]+)', interface.source.attrib.get('bridge')).group(1) if_vni = re.match(r'[vm]*br([0-9a-z]+)', interface.source.attrib.get('bridge')).group(1)
if network == if_vni: if macaddr is None and network == if_vni:
interface.getparent().remove(interface)
changed = True
elif macaddr is not None and macaddr == interface.mac.attrib.get('address'):
interface.getparent().remove(interface) interface.getparent().remove(interface)
changed = True changed = True
if changed: if changed:
@ -866,8 +878,12 @@ def vm_networks_remove(config, vm, network, sriov, live, restart):
new_xml = tostring(parsed_xml, pretty_print=True) new_xml = tostring(parsed_xml, pretty_print=True)
except Exception: except Exception:
return False, 'ERROR: Failed to dump XML data.' return False, 'ERROR: Failed to dump XML data.'
else: elif not changed and network is not None:
return False, 'ERROR: Network "{}" does not exist on VM.'.format(network) return False, 'ERROR: Network "{}" does not exist on VM.'.format(network)
elif not changed and macaddr is not None:
return False, 'ERROR: Interface with MAC "{}" does not exist on VM.'.format(macaddr)
else:
return False, 'ERROR: Unspecified error finding interface to remove.'
modify_retcode, modify_retmsg = vm_modify(config, vm, new_xml, restart) modify_retcode, modify_retmsg = vm_modify(config, vm, new_xml, restart)

View File

@ -1541,7 +1541,11 @@ def vm_network_add(domain, net, macaddr, model, sriov_flag, sriov_mode, live_fla
'domain' 'domain'
) )
@click.argument( @click.argument(
'net' 'net', required=False, default=None
)
@click.option(
'-m', '--mac-address', 'macaddr', default=None,
help='Remove an interface with this MAC address; required if NET is unspecified.'
) )
@click.option( @click.option(
'-s', '--sriov', 'sriov_flag', is_flag=True, default=False, '-s', '--sriov', 'sriov_flag', is_flag=True, default=False,
@ -1561,11 +1565,15 @@ def vm_network_add(domain, net, macaddr, model, sriov_flag, sriov_mode, live_fla
help='Confirm the restart.' help='Confirm the restart.'
) )
@cluster_req @cluster_req
def vm_network_remove(domain, net, sriov_flag, live_flag, restart_flag, confirm_flag): def vm_network_remove(domain, net, macaddr, sriov_flag, live_flag, restart_flag, confirm_flag):
""" """
Remove the network NET from the virtual machine DOMAIN. Remove the network NET from the virtual machine DOMAIN.
NET may be a PVC network VNI, which is added as a bridged device, or a SR-IOV VF device connected in the given mode. NET may be a PVC network VNI, which is added as a bridged device, or a SR-IOV VF device connected in the given mode.
NET is optional if the '-m'/'--mac-address' option is specified. If it is, then the specific device with that MAC address is removed instead.
If multiple interfaces are present on the VM in network NET, and '-m'/'--mac-address' is not specified, then all interfaces in that network will be removed.
""" """
if restart_flag and live_flag: if restart_flag and live_flag:
click.echo('WARNING: Live flag and restart flag both specified; this can cause unintended behaviour. To disable live changes, use "--no-live".') click.echo('WARNING: Live flag and restart flag both specified; this can cause unintended behaviour. To disable live changes, use "--no-live".')
@ -1577,7 +1585,7 @@ def vm_network_remove(domain, net, sriov_flag, live_flag, restart_flag, confirm_
except Exception: except Exception:
restart_flag = False restart_flag = False
retcode, retmsg = pvc_vm.vm_networks_remove(config, domain, net, sriov_flag, live_flag, restart_flag) retcode, retmsg = pvc_vm.vm_networks_remove(config, domain, net, macaddr, sriov_flag, live_flag, restart_flag)
cleanup(retcode, retmsg) cleanup(retcode, retmsg)