Merge "Physical network aware VIF attachment"
This commit is contained in:
commit
bc5efdf459
@ -756,3 +756,8 @@ class PortgroupPhysnetInconsistent(IronicException):
|
|||||||
_msg_fmt = _("Port group %(portgroup)s has member ports with inconsistent "
|
_msg_fmt = _("Port group %(portgroup)s has member ports with inconsistent "
|
||||||
"physical networks (%(physical_networks)s). All ports in a "
|
"physical networks (%(physical_networks)s). All ports in a "
|
||||||
"port group must have the same physical network.")
|
"port group must have the same physical network.")
|
||||||
|
|
||||||
|
|
||||||
|
class VifInvalidForAttach(Conflict):
|
||||||
|
_msg_fmt = _("Unable to attach VIF %(vif)s to node %(node)s. Reason: "
|
||||||
|
"%(reason)s")
|
||||||
|
@ -64,3 +64,17 @@ def get_ports_by_portgroup_id(task, portgroup_id):
|
|||||||
:returns: A list of Port objects.
|
:returns: A list of Port objects.
|
||||||
"""
|
"""
|
||||||
return [port for port in task.ports if port.portgroup_id == portgroup_id]
|
return [port for port in task.ports if port.portgroup_id == portgroup_id]
|
||||||
|
|
||||||
|
|
||||||
|
def get_physnets_for_node(task):
|
||||||
|
"""Return the set of physical networks for a node.
|
||||||
|
|
||||||
|
Returns the set of physical networks associated with a node's ports. The
|
||||||
|
physical network None is excluded from the set.
|
||||||
|
|
||||||
|
:param task: a TaskManager instance
|
||||||
|
:returns: A set of physical networks.
|
||||||
|
"""
|
||||||
|
return set(port.physical_network
|
||||||
|
for port in task.ports
|
||||||
|
if port.physical_network is not None)
|
||||||
|
@ -26,6 +26,12 @@ DEFAULT_NEUTRON_URL = 'http://%s:9696' % CONF.my_ip
|
|||||||
|
|
||||||
_NEUTRON_SESSION = None
|
_NEUTRON_SESSION = None
|
||||||
|
|
||||||
|
PHYSNET_PARAM_NAME = 'provider:physical_network'
|
||||||
|
"""Name of the neutron network API physical network parameter."""
|
||||||
|
|
||||||
|
SEGMENTS_PARAM_NAME = 'segments'
|
||||||
|
"""Name of the neutron network API segments parameter."""
|
||||||
|
|
||||||
|
|
||||||
def _get_neutron_session():
|
def _get_neutron_session():
|
||||||
global _NEUTRON_SESSION
|
global _NEUTRON_SESSION
|
||||||
@ -365,32 +371,10 @@ def validate_network(uuid_or_name, net_type=_('network')):
|
|||||||
raise exception.MissingParameterValue(
|
raise exception.MissingParameterValue(
|
||||||
_('UUID or name of %s is not set in configuration') % net_type)
|
_('UUID or name of %s is not set in configuration') % net_type)
|
||||||
|
|
||||||
if uuidutils.is_uuid_like(uuid_or_name):
|
client = get_client()
|
||||||
filters = {'id': uuid_or_name}
|
network = _get_network_by_uuid_or_name(client, uuid_or_name,
|
||||||
else:
|
net_type=net_type, fields=['id'])
|
||||||
filters = {'name': uuid_or_name}
|
return network['id']
|
||||||
|
|
||||||
try:
|
|
||||||
client = get_client()
|
|
||||||
networks = client.list_networks(fields=['id'], **filters)
|
|
||||||
except neutron_exceptions.NeutronClientException as exc:
|
|
||||||
raise exception.NetworkError(_('Could not retrieve network list: %s') %
|
|
||||||
exc)
|
|
||||||
|
|
||||||
LOG.debug('Got list of networks matching %(cond)s: %(result)s',
|
|
||||||
{'cond': filters, 'result': networks})
|
|
||||||
networks = [n['id'] for n in networks.get('networks', [])]
|
|
||||||
if not networks:
|
|
||||||
raise exception.InvalidParameterValue(
|
|
||||||
_('%(type)s with name or UUID %(uuid_or_name)s was not found') %
|
|
||||||
{'type': net_type, 'uuid_or_name': uuid_or_name})
|
|
||||||
elif len(networks) > 1:
|
|
||||||
raise exception.InvalidParameterValue(
|
|
||||||
_('More than one %(type)s was found for name %(name)s: %(nets)s') %
|
|
||||||
{'name': uuid_or_name, 'nets': ', '.join(networks),
|
|
||||||
'type': net_type})
|
|
||||||
|
|
||||||
return networks[0]
|
|
||||||
|
|
||||||
|
|
||||||
def validate_port_info(node, port):
|
def validate_port_info(node, port):
|
||||||
@ -414,6 +398,104 @@ def validate_port_info(node, port):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _get_network_by_uuid_or_name(client, uuid_or_name, net_type=_('network'),
|
||||||
|
**params):
|
||||||
|
"""Return a neutron network by UUID or name.
|
||||||
|
|
||||||
|
:param client: A Neutron client object.
|
||||||
|
:param uuid_or_name: network UUID or name
|
||||||
|
:param net_type: human-readable network type for error messages
|
||||||
|
:param params: Additional parameters to pass to the neutron client
|
||||||
|
list_networks method.
|
||||||
|
:returns: A dict describing the neutron network.
|
||||||
|
:raises: NetworkError on failure to contact Neutron
|
||||||
|
:raises: InvalidParameterValue for missing or duplicated network
|
||||||
|
"""
|
||||||
|
if uuidutils.is_uuid_like(uuid_or_name):
|
||||||
|
params['id'] = uuid_or_name
|
||||||
|
else:
|
||||||
|
params['name'] = uuid_or_name
|
||||||
|
|
||||||
|
try:
|
||||||
|
networks = client.list_networks(**params)
|
||||||
|
except neutron_exceptions.NeutronClientException as exc:
|
||||||
|
raise exception.NetworkError(_('Could not retrieve network list: %s') %
|
||||||
|
exc)
|
||||||
|
|
||||||
|
LOG.debug('Got list of networks matching %(cond)s: %(result)s',
|
||||||
|
{'cond': params, 'result': networks})
|
||||||
|
networks = networks.get('networks', [])
|
||||||
|
if not networks:
|
||||||
|
raise exception.InvalidParameterValue(
|
||||||
|
_('%(type)s with name or UUID %(uuid_or_name)s was not found') %
|
||||||
|
{'type': net_type, 'uuid_or_name': uuid_or_name})
|
||||||
|
elif len(networks) > 1:
|
||||||
|
network_ids = [n['id'] for n in networks]
|
||||||
|
raise exception.InvalidParameterValue(
|
||||||
|
_('More than one %(type)s was found for name %(name)s: %(nets)s') %
|
||||||
|
{'name': uuid_or_name, 'nets': ', '.join(network_ids),
|
||||||
|
'type': net_type})
|
||||||
|
return networks[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_port_by_uuid(client, port_uuid, **params):
|
||||||
|
"""Return a neutron port by UUID.
|
||||||
|
|
||||||
|
:param client: A Neutron client object.
|
||||||
|
:param port_uuid: UUID of a Neutron port to query.
|
||||||
|
:param params: Additional parameters to pass to the neutron client
|
||||||
|
show_port method.
|
||||||
|
:returns: A dict describing the neutron port.
|
||||||
|
:raises: InvalidParameterValue if the port does not exist.
|
||||||
|
:raises: NetworkError on failure to contact Neutron.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
port = client.show_port(port_uuid, **params)
|
||||||
|
except neutron_exceptions.PortNotFoundClient:
|
||||||
|
raise exception.InvalidParameterValue(
|
||||||
|
_('Neutron port %(port_uuid)s was not found') %
|
||||||
|
{'port_uuid': port_uuid})
|
||||||
|
except neutron_exceptions.NeutronClientException as exc:
|
||||||
|
raise exception.NetworkError(_('Could not retrieve neutron port: %s') %
|
||||||
|
exc)
|
||||||
|
return port['port']
|
||||||
|
|
||||||
|
|
||||||
|
def get_physnets_by_port_uuid(client, port_uuid):
|
||||||
|
"""Return the set of physical networks associated with a neutron port.
|
||||||
|
|
||||||
|
Query the network to which the port is attached and return the set of
|
||||||
|
physical networks associated with the segments in that network.
|
||||||
|
|
||||||
|
:param client: A Neutron client object.
|
||||||
|
:param port_uuid: UUID of a Neutron port to query.
|
||||||
|
:returns: A set of physical networks.
|
||||||
|
:raises: NetworkError if the network query fails.
|
||||||
|
:raises: InvalidParameterValue for missing network.
|
||||||
|
"""
|
||||||
|
port = _get_port_by_uuid(client, port_uuid, fields=['network_id'])
|
||||||
|
network_uuid = port['network_id']
|
||||||
|
|
||||||
|
fields = [PHYSNET_PARAM_NAME, SEGMENTS_PARAM_NAME]
|
||||||
|
network = _get_network_by_uuid_or_name(client, network_uuid, fields=fields)
|
||||||
|
|
||||||
|
if SEGMENTS_PARAM_NAME in network:
|
||||||
|
# A network with multiple segments will have a 'segments' parameter
|
||||||
|
# which will contain a list of segments. Each segment should have a
|
||||||
|
# 'provider:physical_network' parameter which contains the physical
|
||||||
|
# network of the segment.
|
||||||
|
segments = network[SEGMENTS_PARAM_NAME]
|
||||||
|
else:
|
||||||
|
# A network with a single segment will have a
|
||||||
|
# 'provider:physical_network' parameter which contains the network's
|
||||||
|
# physical network.
|
||||||
|
segments = [network]
|
||||||
|
|
||||||
|
return set(segment[PHYSNET_PARAM_NAME]
|
||||||
|
for segment in segments
|
||||||
|
if segment[PHYSNET_PARAM_NAME])
|
||||||
|
|
||||||
|
|
||||||
class NeutronNetworkInterfaceMixin(object):
|
class NeutronNetworkInterfaceMixin(object):
|
||||||
|
|
||||||
_cleaning_network_uuid = None
|
_cleaning_network_uuid = None
|
||||||
|
@ -2582,6 +2582,8 @@ class ConductorManager(base_manager.BaseConductorManager):
|
|||||||
exception.NetworkError,
|
exception.NetworkError,
|
||||||
exception.VifAlreadyAttached,
|
exception.VifAlreadyAttached,
|
||||||
exception.NoFreePhysicalPorts,
|
exception.NoFreePhysicalPorts,
|
||||||
|
exception.PortgroupPhysnetInconsistent,
|
||||||
|
exception.VifInvalidForAttach,
|
||||||
exception.InvalidParameterValue)
|
exception.InvalidParameterValue)
|
||||||
def vif_attach(self, context, node_id, vif_info):
|
def vif_attach(self, context, node_id, vif_info):
|
||||||
"""Attach a VIF to a node
|
"""Attach a VIF to a node
|
||||||
@ -2597,6 +2599,11 @@ class ConductorManager(base_manager.BaseConductorManager):
|
|||||||
:raises: NetworkError, if an error occurs during attaching the VIF.
|
:raises: NetworkError, if an error occurs during attaching the VIF.
|
||||||
:raises: InvalidParameterValue, if a parameter that's required for
|
:raises: InvalidParameterValue, if a parameter that's required for
|
||||||
VIF attach is wrong/missing.
|
VIF attach is wrong/missing.
|
||||||
|
:raises: PortgroupPhysnetInconsistent if one of the node's portgroups
|
||||||
|
has ports which are not all assigned the same physical
|
||||||
|
network.
|
||||||
|
:raises: VifInvalidForAttach if the VIF is not valid for attachment to
|
||||||
|
the node.
|
||||||
"""
|
"""
|
||||||
LOG.debug("RPC vif_attach called for the node %(node_id)s with "
|
LOG.debug("RPC vif_attach called for the node %(node_id)s with "
|
||||||
"vif_info %(vif_info)s", {'node_id': node_id,
|
"vif_info %(vif_info)s", {'node_id': node_id,
|
||||||
|
@ -22,6 +22,7 @@ from oslo_log import log
|
|||||||
from ironic.common import dhcp_factory
|
from ironic.common import dhcp_factory
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common.i18n import _
|
from ironic.common.i18n import _
|
||||||
|
from ironic.common import network
|
||||||
from ironic.common import neutron
|
from ironic.common import neutron
|
||||||
from ironic.common import states
|
from ironic.common import states
|
||||||
from ironic.common import utils
|
from ironic.common import utils
|
||||||
@ -54,7 +55,35 @@ def _vif_attached(port_like_obj, vif_id):
|
|||||||
return attached_vif_id is not None
|
return attached_vif_id is not None
|
||||||
|
|
||||||
|
|
||||||
def _get_free_portgroups_and_ports(task, vif_id):
|
def _is_port_physnet_allowed(port, physnets):
|
||||||
|
"""Check whether a port's physical network is allowed for a VIF.
|
||||||
|
|
||||||
|
Supports VIFs on networks with no physical network configuration by
|
||||||
|
allowing all ports regardless of their physical network. This will be the
|
||||||
|
case when the port is not a neutron port because we're in standalone mode
|
||||||
|
or not using neutron.
|
||||||
|
|
||||||
|
Allows ports with physical_network=None to ensure backwards compatibility
|
||||||
|
and provide support for simple deployments with no physical network
|
||||||
|
configuration in ironic.
|
||||||
|
|
||||||
|
When the physnets set is not empty and the port's physical_network field is
|
||||||
|
not None, the port's physical_network field must be present in the physnets
|
||||||
|
set.
|
||||||
|
|
||||||
|
:param port: A Port object to check.
|
||||||
|
:param physnets: Set of physical networks on which the VIF may be
|
||||||
|
attached. This is governed by the segments of the VIF's network. An
|
||||||
|
empty set indicates that the ports' physical networks should be
|
||||||
|
ignored.
|
||||||
|
:returns: True if the port's physical network is allowed, False otherwise.
|
||||||
|
"""
|
||||||
|
return (not physnets or
|
||||||
|
port.physical_network is None or
|
||||||
|
port.physical_network in physnets)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_free_portgroups_and_ports(task, vif_id, physnets):
|
||||||
"""Get free portgroups and ports.
|
"""Get free portgroups and ports.
|
||||||
|
|
||||||
It only returns ports or portgroups that can be used for attachment of
|
It only returns ports or portgroups that can be used for attachment of
|
||||||
@ -62,13 +91,18 @@ def _get_free_portgroups_and_ports(task, vif_id):
|
|||||||
|
|
||||||
:param task: a TaskManager instance.
|
:param task: a TaskManager instance.
|
||||||
:param vif_id: Name or UUID of a VIF.
|
:param vif_id: Name or UUID of a VIF.
|
||||||
:returns: tuple of: list of free portgroups, list of free ports.
|
:param physnets: Set of physical networks on which the VIF may be
|
||||||
|
attached. This is governed by the segments of the VIF's network. An
|
||||||
|
empty set indicates that the ports' physical networks should be
|
||||||
|
ignored.
|
||||||
|
:returns: list of free ports and portgroups.
|
||||||
:raises: VifAlreadyAttached, if vif_id is attached to any of the
|
:raises: VifAlreadyAttached, if vif_id is attached to any of the
|
||||||
node's ports or portgroups.
|
node's ports or portgroups.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# This list contains ports selected as candidates for attachment
|
# This list contains ports and portgroups selected as candidates for
|
||||||
free_ports = []
|
# attachment.
|
||||||
|
free_port_like_objs = []
|
||||||
# This is a mapping of portgroup id to collection of its free ports
|
# This is a mapping of portgroup id to collection of its free ports
|
||||||
ports_by_portgroup = collections.defaultdict(list)
|
ports_by_portgroup = collections.defaultdict(list)
|
||||||
# This set contains IDs of portgroups that should be ignored, as they have
|
# This set contains IDs of portgroups that should be ignored, as they have
|
||||||
@ -84,58 +118,105 @@ def _get_free_portgroups_and_ports(task, vif_id):
|
|||||||
# added in this set is not a problem
|
# added in this set is not a problem
|
||||||
non_usable_portgroups.add(p.portgroup_id)
|
non_usable_portgroups.add(p.portgroup_id)
|
||||||
continue
|
continue
|
||||||
|
if not _is_port_physnet_allowed(p, physnets):
|
||||||
|
continue
|
||||||
if p.portgroup_id is None:
|
if p.portgroup_id is None:
|
||||||
# ports without portgroup_id are always considered candidates
|
# ports without portgroup_id are always considered candidates
|
||||||
free_ports.append(p)
|
free_port_like_objs.append(p)
|
||||||
else:
|
else:
|
||||||
ports_by_portgroup[p.portgroup_id].append(p)
|
ports_by_portgroup[p.portgroup_id].append(p)
|
||||||
|
|
||||||
# This list contains portgroups selected as candidates for attachment
|
|
||||||
free_portgroups = []
|
|
||||||
|
|
||||||
for pg in task.portgroups:
|
for pg in task.portgroups:
|
||||||
if _vif_attached(pg, vif_id):
|
if _vif_attached(pg, vif_id):
|
||||||
continue
|
continue
|
||||||
if pg.id in non_usable_portgroups:
|
if pg.id in non_usable_portgroups:
|
||||||
# This portgroup has vifs attached to its ports, consider its
|
# This portgroup has vifs attached to its ports, consider its
|
||||||
# ports instead to avoid collisions
|
# ports instead to avoid collisions
|
||||||
free_ports.extend(ports_by_portgroup[pg.id])
|
free_port_like_objs.extend(ports_by_portgroup[pg.id])
|
||||||
# Also ignore empty portgroups
|
# Also ignore empty portgroups
|
||||||
elif ports_by_portgroup[pg.id]:
|
elif ports_by_portgroup[pg.id]:
|
||||||
free_portgroups.append(pg)
|
free_port_like_objs.append(pg)
|
||||||
|
|
||||||
return free_portgroups, free_ports
|
return free_port_like_objs
|
||||||
|
|
||||||
|
|
||||||
def get_free_port_like_object(task, vif_id):
|
def _get_physnet_for_portgroup(task, portgroup):
|
||||||
|
"""Return the physical network associated with a portgroup.
|
||||||
|
|
||||||
|
:param task: a TaskManager instance.
|
||||||
|
:param portgroup: a Portgroup object.
|
||||||
|
:returns: The physical network associated with the portgroup.
|
||||||
|
:raises: PortgroupPhysnetInconsistent if the portgroup's ports are not
|
||||||
|
assigned the same physical network.
|
||||||
|
"""
|
||||||
|
pg_ports = network.get_ports_by_portgroup_id(task, portgroup.id)
|
||||||
|
pg_physnets = {port.physical_network for port in pg_ports}
|
||||||
|
# Sanity check: there should be at least one port in the portgroup and
|
||||||
|
# all ports should have the same physical network.
|
||||||
|
if len(pg_physnets) != 1:
|
||||||
|
raise exception.PortgroupPhysnetInconsistent(
|
||||||
|
portgroup=portgroup.uuid, physical_networks=", ".join(pg_physnets))
|
||||||
|
return pg_physnets.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def get_free_port_like_object(task, vif_id, physnets):
|
||||||
"""Find free port-like object (portgroup or port) VIF will be attached to.
|
"""Find free port-like object (portgroup or port) VIF will be attached to.
|
||||||
|
|
||||||
Ensures that VIF is not already attached to this node. It will return the
|
Ensures that the VIF is not already attached to this node. When selecting
|
||||||
first free port group. If there are no free port groups, then the first
|
a port or portgroup to attach the virtual interface to, the following
|
||||||
available port (pxe_enabled preferably) is used.
|
ordered criteria are applied:
|
||||||
|
|
||||||
|
* Require ports or portgroups to have a physical network that is either
|
||||||
|
None or one of the VIF's allowed physical networks.
|
||||||
|
* Prefer ports or portgroups with a physical network field which is not
|
||||||
|
None.
|
||||||
|
* Prefer portgroups to ports.
|
||||||
|
* Prefer ports with PXE enabled.
|
||||||
|
|
||||||
:param task: a TaskManager instance.
|
:param task: a TaskManager instance.
|
||||||
:param vif_id: Name or UUID of a VIF.
|
:param vif_id: Name or UUID of a VIF.
|
||||||
|
:param physnets: Set of physical networks on which the VIF may be
|
||||||
|
attached. This is governed by the segments of the VIF's network. An
|
||||||
|
empty set indicates that the ports' physical networks should be
|
||||||
|
ignored.
|
||||||
:raises: VifAlreadyAttached, if VIF is already attached to the node.
|
:raises: VifAlreadyAttached, if VIF is already attached to the node.
|
||||||
:raises: NoFreePhysicalPorts, if there is no port-like object VIF can be
|
:raises: NoFreePhysicalPorts, if there is no port-like object VIF can be
|
||||||
attached to.
|
attached to.
|
||||||
|
:raises: PortgroupPhysnetInconsistent if one of the node's portgroups
|
||||||
|
has ports which are not all assigned the same physical network.
|
||||||
:returns: port-like object VIF will be attached to.
|
:returns: port-like object VIF will be attached to.
|
||||||
"""
|
"""
|
||||||
|
free_port_like_objs = _get_free_portgroups_and_ports(task, vif_id,
|
||||||
|
physnets)
|
||||||
|
|
||||||
free_portgroups, free_ports = _get_free_portgroups_and_ports(task, vif_id)
|
if not free_port_like_objs:
|
||||||
|
|
||||||
if free_portgroups:
|
|
||||||
# portgroups are higher priority
|
|
||||||
return free_portgroups[0]
|
|
||||||
|
|
||||||
if not free_ports:
|
|
||||||
raise exception.NoFreePhysicalPorts(vif=vif_id)
|
raise exception.NoFreePhysicalPorts(vif=vif_id)
|
||||||
|
|
||||||
# Sort ports by pxe_enabled to ensure we always bind pxe_enabled ports
|
def sort_key(port_like_obj):
|
||||||
# first
|
"""Key function for sorting a combined list of ports and portgroups.
|
||||||
sorted_free_ports = sorted(free_ports, key=lambda p: p.pxe_enabled,
|
|
||||||
reverse=True)
|
We key the port-like objects using the following precedence:
|
||||||
return sorted_free_ports[0]
|
|
||||||
|
1. Prefer objects with a physical network field which is in the
|
||||||
|
physnets set.
|
||||||
|
2. Prefer portgroups to ports.
|
||||||
|
3. Prefer ports with PXE enabled.
|
||||||
|
|
||||||
|
:param port_like_obj: The port or portgroup to key.
|
||||||
|
:returns: A key value for sorting the object.
|
||||||
|
"""
|
||||||
|
is_pg = isinstance(port_like_obj, objects.Portgroup)
|
||||||
|
if is_pg:
|
||||||
|
pg_physnet = _get_physnet_for_portgroup(task, port_like_obj)
|
||||||
|
physnet_matches = pg_physnet in physnets
|
||||||
|
pxe_enabled = True
|
||||||
|
else:
|
||||||
|
physnet_matches = port_like_obj.physical_network in physnets
|
||||||
|
pxe_enabled = port_like_obj.pxe_enabled
|
||||||
|
return (physnet_matches, is_pg, pxe_enabled)
|
||||||
|
|
||||||
|
sorted_free_plos = sorted(free_port_like_objs, key=sort_key, reverse=True)
|
||||||
|
return sorted_free_plos[0]
|
||||||
|
|
||||||
|
|
||||||
def plug_port_to_tenant_network(task, port_like_obj, client=None):
|
def plug_port_to_tenant_network(task, port_like_obj, client=None):
|
||||||
@ -347,21 +428,64 @@ class VIFPortIDMixin(object):
|
|||||||
def vif_attach(self, task, vif_info):
|
def vif_attach(self, task, vif_info):
|
||||||
"""Attach a virtual network interface to a node
|
"""Attach a virtual network interface to a node
|
||||||
|
|
||||||
Attach a virtual interface to a node. It will use the first free port
|
Attach a virtual interface to a node. When selecting a port or
|
||||||
group. If there are no free port groups, then the first available port
|
portgroup to attach the virtual interface to, the following ordered
|
||||||
(pxe_enabled preferably) is used.
|
criteria are applied:
|
||||||
|
|
||||||
|
* Require ports or portgroups to have a physical network that is either
|
||||||
|
None or one of the VIF's allowed physical networks.
|
||||||
|
* Prefer ports or portgroups with a physical network field which is not
|
||||||
|
None.
|
||||||
|
* Prefer portgroups to ports.
|
||||||
|
* Prefer ports with PXE enabled.
|
||||||
|
|
||||||
:param task: A TaskManager instance.
|
:param task: A TaskManager instance.
|
||||||
:param vif_info: a dictionary of information about a VIF.
|
:param vif_info: a dictionary of information about a VIF.
|
||||||
It must have an 'id' key, whose value is a unique
|
It must have an 'id' key, whose value is a unique
|
||||||
identifier for that VIF.
|
identifier for that VIF.
|
||||||
:raises: NetworkError, VifAlreadyAttached, NoFreePhysicalPorts
|
:raises: NetworkError, VifAlreadyAttached, NoFreePhysicalPorts
|
||||||
|
:raises: PortgroupPhysnetInconsistent if one of the node's portgroups
|
||||||
|
has ports which are not all assigned the same physical
|
||||||
|
network.
|
||||||
"""
|
"""
|
||||||
vif_id = vif_info['id']
|
vif_id = vif_info['id']
|
||||||
|
|
||||||
port_like_obj = get_free_port_like_object(task, vif_id)
|
|
||||||
|
|
||||||
client = neutron.get_client()
|
client = neutron.get_client()
|
||||||
|
|
||||||
|
# Determine whether any of the node's ports have a physical network. If
|
||||||
|
# not, we don't need to check the VIF's network's physical networks as
|
||||||
|
# they will not affect the VIF to port mapping.
|
||||||
|
physnets = set()
|
||||||
|
if any(port.physical_network is not None for port in task.ports):
|
||||||
|
try:
|
||||||
|
physnets = neutron.get_physnets_by_port_uuid(client, vif_id)
|
||||||
|
except (exception.InvalidParameterValue, exception.NetworkError):
|
||||||
|
# TODO(mgoddard): Remove this except clause and handle errors
|
||||||
|
# properly. We can do this once a strategy has been determined
|
||||||
|
# for handling the tempest VIF tests in an environment that
|
||||||
|
# may not support neutron.
|
||||||
|
# NOTE(sambetts): If a client error occurs this is because
|
||||||
|
# either neutron doesn't exist because we're running in
|
||||||
|
# standalone environment or we can't find a matching neutron
|
||||||
|
# port which means a user might be requesting a non-neutron
|
||||||
|
# port. So skip trying to update the neutron port MAC address
|
||||||
|
# in these cases.
|
||||||
|
pass
|
||||||
|
|
||||||
|
if len(physnets) > 1:
|
||||||
|
# NOTE(mgoddard): Neutron cannot currently handle hosts which
|
||||||
|
# are mapped to multiple segments in the same routed network.
|
||||||
|
node_physnets = network.get_physnets_for_node(task)
|
||||||
|
if len(node_physnets.intersection(physnets)) > 1:
|
||||||
|
reason = _("Node has ports which map to multiple segments "
|
||||||
|
"of the routed network to which the VIF is "
|
||||||
|
"attached. Currently neutron only supports "
|
||||||
|
"hosts which map to one segment of a routed "
|
||||||
|
"network")
|
||||||
|
raise exception.VifInvalidForAttach(
|
||||||
|
node=task.node.uuid, vif=vif_id, reason=reason)
|
||||||
|
|
||||||
|
port_like_obj = get_free_port_like_object(task, vif_id, physnets)
|
||||||
|
|
||||||
# Address is optional for portgroups
|
# Address is optional for portgroups
|
||||||
if port_like_obj.address:
|
if port_like_obj.address:
|
||||||
# Check if the requested vif_id is a neutron port. If it is
|
# Check if the requested vif_id is a neutron port. If it is
|
||||||
@ -369,6 +493,10 @@ class VIFPortIDMixin(object):
|
|||||||
try:
|
try:
|
||||||
client.show_port(vif_id)
|
client.show_port(vif_id)
|
||||||
except neutron_exceptions.NeutronClientException:
|
except neutron_exceptions.NeutronClientException:
|
||||||
|
# TODO(mgoddard): Remove this except clause and handle errors
|
||||||
|
# properly. We can do this once a strategy has been determined
|
||||||
|
# for handling the tempest VIF tests in an environment that
|
||||||
|
# may not support neutron.
|
||||||
# NOTE(sambetts): If a client error occurs this is because
|
# NOTE(sambetts): If a client error occurs this is because
|
||||||
# either neutron doesn't exist because we're running in
|
# either neutron doesn't exist because we're running in
|
||||||
# standalone environment or we can't find a matching neutron
|
# standalone environment or we can't find a matching neutron
|
||||||
|
@ -199,3 +199,31 @@ class GetPortsByPortgroupIdTestCase(db_base.DbTestCase):
|
|||||||
with task_manager.acquire(self.context, node.uuid) as task:
|
with task_manager.acquire(self.context, node.uuid) as task:
|
||||||
res = network.get_ports_by_portgroup_id(task, portgroup.id)
|
res = network.get_ports_by_portgroup_id(task, portgroup.id)
|
||||||
self.assertEqual([], res)
|
self.assertEqual([], res)
|
||||||
|
|
||||||
|
|
||||||
|
class GetPhysnetsForNodeTestCase(db_base.DbTestCase):
|
||||||
|
|
||||||
|
def test_get_physnets_for_node_no_ports(self):
|
||||||
|
node = object_utils.create_test_node(self.context, driver='fake')
|
||||||
|
with task_manager.acquire(self.context, node.uuid) as task:
|
||||||
|
res = network.get_physnets_for_node(task)
|
||||||
|
self.assertEqual(set(), res)
|
||||||
|
|
||||||
|
def test_get_physnets_for_node_excludes_None(self):
|
||||||
|
node = object_utils.create_test_node(self.context, driver='fake')
|
||||||
|
object_utils.create_test_port(self.context, node_id=node.id)
|
||||||
|
with task_manager.acquire(self.context, node.uuid) as task:
|
||||||
|
res = network.get_physnets_for_node(task)
|
||||||
|
self.assertEqual(set(), res)
|
||||||
|
|
||||||
|
def test_get_physnets_for_node_multiple_ports(self):
|
||||||
|
node = object_utils.create_test_node(self.context, driver='fake')
|
||||||
|
object_utils.create_test_port(self.context, node_id=node.id,
|
||||||
|
physical_network='physnet1')
|
||||||
|
object_utils.create_test_port(self.context, node_id=node.id,
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
address='00:11:22:33:44:55',
|
||||||
|
physical_network='physnet2')
|
||||||
|
with task_manager.acquire(self.context, node.uuid) as task:
|
||||||
|
res = network.get_physnets_for_node(task)
|
||||||
|
self.assertEqual({'physnet1', 'physnet2'}, res)
|
||||||
|
@ -656,3 +656,228 @@ class TestUnbindPort(base.TestCase):
|
|||||||
body)
|
body)
|
||||||
mock_log.info.assert_called_once_with('Port %s was not found while '
|
mock_log.info.assert_called_once_with('Port %s was not found while '
|
||||||
'unbinding.', port_id)
|
'unbinding.', port_id)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetNetworkByUUIDOrName(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestGetNetworkByUUIDOrName, self).setUp()
|
||||||
|
self.client = mock.MagicMock()
|
||||||
|
|
||||||
|
def test__get_network_by_uuid_or_name_uuid(self):
|
||||||
|
network_uuid = '9acb0256-2c1b-420a-b9d7-62bee90b6ed7'
|
||||||
|
networks = {
|
||||||
|
'networks': [{
|
||||||
|
'field1': 'value1',
|
||||||
|
'field2': 'value2',
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
fields = ['field1', 'field2']
|
||||||
|
self.client.list_networks.return_value = networks
|
||||||
|
result = neutron._get_network_by_uuid_or_name(
|
||||||
|
self.client, network_uuid, fields=fields)
|
||||||
|
self.client.list_networks.assert_called_once_with(
|
||||||
|
id=network_uuid, fields=fields)
|
||||||
|
self.assertEqual(networks['networks'][0], result)
|
||||||
|
|
||||||
|
def test__get_network_by_uuid_or_name_name(self):
|
||||||
|
network_name = 'test-net'
|
||||||
|
networks = {
|
||||||
|
'networks': [{
|
||||||
|
'field1': 'value1',
|
||||||
|
'field2': 'value2',
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
fields = ['field1', 'field2']
|
||||||
|
self.client.list_networks.return_value = networks
|
||||||
|
result = neutron._get_network_by_uuid_or_name(
|
||||||
|
self.client, network_name, fields=fields)
|
||||||
|
self.client.list_networks.assert_called_once_with(
|
||||||
|
name=network_name, fields=fields)
|
||||||
|
self.assertEqual(networks['networks'][0], result)
|
||||||
|
|
||||||
|
def test__get_network_by_uuid_or_name_failure(self):
|
||||||
|
network_uuid = '9acb0256-2c1b-420a-b9d7-62bee90b6ed7'
|
||||||
|
self.client.list_networks.side_effect = (
|
||||||
|
neutron_client_exc.NeutronClientException())
|
||||||
|
self.assertRaises(exception.NetworkError,
|
||||||
|
neutron._get_network_by_uuid_or_name,
|
||||||
|
self.client, network_uuid)
|
||||||
|
self.client.list_networks.assert_called_once_with(id=network_uuid)
|
||||||
|
|
||||||
|
def test__get_network_by_uuid_or_name_missing(self):
|
||||||
|
network_uuid = '9acb0256-2c1b-420a-b9d7-62bee90b6ed7'
|
||||||
|
networks = {
|
||||||
|
'networks': [],
|
||||||
|
}
|
||||||
|
self.client.list_networks.return_value = networks
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
neutron._get_network_by_uuid_or_name,
|
||||||
|
self.client, network_uuid)
|
||||||
|
self.client.list_networks.assert_called_once_with(id=network_uuid)
|
||||||
|
|
||||||
|
def test__get_network_by_uuid_or_name_duplicate(self):
|
||||||
|
network_name = 'test-net'
|
||||||
|
networks = {
|
||||||
|
'networks': [
|
||||||
|
{'id': '9acb0256-2c1b-420a-b9d7-62bee90b6ed7'},
|
||||||
|
{'id': '9014b6a7-8291-4676-80b0-ab00988ce3c7'},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
self.client.list_networks.return_value = networks
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
neutron._get_network_by_uuid_or_name,
|
||||||
|
self.client, network_name)
|
||||||
|
self.client.list_networks.assert_called_once_with(name=network_name)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(neutron, '_get_network_by_uuid_or_name', autospec=True)
|
||||||
|
@mock.patch.object(neutron, '_get_port_by_uuid', autospec=True)
|
||||||
|
class TestGetPhysnetsByPortUUID(base.TestCase):
|
||||||
|
|
||||||
|
PORT_FIELDS = ['network_id']
|
||||||
|
NETWORK_FIELDS = ['provider:physical_network', 'segments']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestGetPhysnetsByPortUUID, self).setUp()
|
||||||
|
self.client = mock.MagicMock()
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_single_segment(self, mock_gp, mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
network_uuid = 'fake-network-uuid'
|
||||||
|
physnet = 'fake-physnet'
|
||||||
|
mock_gp.return_value = {
|
||||||
|
'network_id': network_uuid,
|
||||||
|
}
|
||||||
|
mock_gn.return_value = {
|
||||||
|
'provider:physical_network': physnet,
|
||||||
|
}
|
||||||
|
result = neutron.get_physnets_by_port_uuid(self.client,
|
||||||
|
port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
mock_gn.assert_called_once_with(self.client, network_uuid,
|
||||||
|
fields=self.NETWORK_FIELDS)
|
||||||
|
self.assertEqual({physnet}, result)
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_single_segment_no_physnet(
|
||||||
|
self, mock_gp, mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
network_uuid = 'fake-network-uuid'
|
||||||
|
mock_gp.return_value = {
|
||||||
|
'network_id': network_uuid,
|
||||||
|
}
|
||||||
|
mock_gn.return_value = {
|
||||||
|
'provider:physical_network': None,
|
||||||
|
}
|
||||||
|
result = neutron.get_physnets_by_port_uuid(self.client,
|
||||||
|
port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
mock_gn.assert_called_once_with(self.client, network_uuid,
|
||||||
|
fields=self.NETWORK_FIELDS)
|
||||||
|
self.assertEqual(set(), result)
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_multiple_segments(self, mock_gp,
|
||||||
|
mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
network_uuid = 'fake-network-uuid'
|
||||||
|
physnet1 = 'fake-physnet-1'
|
||||||
|
physnet2 = 'fake-physnet-2'
|
||||||
|
mock_gp.return_value = {
|
||||||
|
'network_id': network_uuid,
|
||||||
|
}
|
||||||
|
mock_gn.return_value = {
|
||||||
|
'segments': [
|
||||||
|
{
|
||||||
|
'provider:physical_network': physnet1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'provider:physical_network': physnet2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
result = neutron.get_physnets_by_port_uuid(self.client,
|
||||||
|
port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
mock_gn.assert_called_once_with(self.client, network_uuid,
|
||||||
|
fields=self.NETWORK_FIELDS)
|
||||||
|
self.assertEqual({physnet1, physnet2}, result)
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_multiple_segments_no_physnet(
|
||||||
|
self, mock_gp, mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
network_uuid = 'fake-network-uuid'
|
||||||
|
mock_gp.return_value = {
|
||||||
|
'network_id': network_uuid,
|
||||||
|
}
|
||||||
|
mock_gn.return_value = {
|
||||||
|
'segments': [
|
||||||
|
{
|
||||||
|
'provider:physical_network': None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'provider:physical_network': None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
result = neutron.get_physnets_by_port_uuid(self.client,
|
||||||
|
port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
mock_gn.assert_called_once_with(self.client, network_uuid,
|
||||||
|
fields=self.NETWORK_FIELDS)
|
||||||
|
self.assertEqual(set(), result)
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_port_missing(self, mock_gp, mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
mock_gp.side_effect = exception.InvalidParameterValue('error')
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
neutron.get_physnets_by_port_uuid,
|
||||||
|
self.client, port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
self.assertFalse(mock_gn.called)
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_port_failure(self, mock_gp, mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
mock_gp.side_effect = exception.NetworkError
|
||||||
|
self.assertRaises(exception.NetworkError,
|
||||||
|
neutron.get_physnets_by_port_uuid,
|
||||||
|
self.client, port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
self.assertFalse(mock_gn.called)
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_network_missing(
|
||||||
|
self, mock_gp, mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
network_uuid = 'fake-network-uuid'
|
||||||
|
mock_gp.return_value = {
|
||||||
|
'network_id': network_uuid,
|
||||||
|
}
|
||||||
|
mock_gn.side_effect = exception.InvalidParameterValue('error')
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
neutron.get_physnets_by_port_uuid,
|
||||||
|
self.client, port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
mock_gn.assert_called_once_with(self.client, network_uuid,
|
||||||
|
fields=self.NETWORK_FIELDS)
|
||||||
|
|
||||||
|
def test_get_physnets_by_port_uuid_network_failure(
|
||||||
|
self, mock_gp, mock_gn):
|
||||||
|
port_uuid = 'fake-port-uuid'
|
||||||
|
network_uuid = 'fake-network-uuid'
|
||||||
|
mock_gp.return_value = {
|
||||||
|
'network_id': network_uuid,
|
||||||
|
}
|
||||||
|
mock_gn.side_effect = exception.NetworkError
|
||||||
|
self.assertRaises(exception.NetworkError,
|
||||||
|
neutron.get_physnets_by_port_uuid,
|
||||||
|
self.client, port_uuid)
|
||||||
|
mock_gp.assert_called_once_with(self.client, port_uuid,
|
||||||
|
fields=self.PORT_FIELDS)
|
||||||
|
mock_gn.assert_called_once_with(self.client, network_uuid,
|
||||||
|
fields=self.NETWORK_FIELDS)
|
||||||
|
@ -3791,6 +3791,36 @@ class VifTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
|
|||||||
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
|
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
|
||||||
mock_attach.assert_called_once_with(mock.ANY, mock.ANY, self.vif)
|
mock_attach.assert_called_once_with(mock.ANY, mock.ANY, self.vif)
|
||||||
|
|
||||||
|
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autpspec=True)
|
||||||
|
def test_vif_attach_raises_portgroup_physnet_inconsistent(
|
||||||
|
self, mock_attach, mock_valid):
|
||||||
|
mock_valid.side_effect = exception.PortgroupPhysnetInconsistent(
|
||||||
|
portgroup='fake-pg', physical_networks='fake-physnet')
|
||||||
|
node = obj_utils.create_test_node(self.context, driver='fake')
|
||||||
|
exc = self.assertRaises(messaging.rpc.ExpectedException,
|
||||||
|
self.service.vif_attach,
|
||||||
|
self.context, node.uuid, self.vif)
|
||||||
|
# Compare true exception hidden by @messaging.expected_exceptions
|
||||||
|
self.assertEqual(exception.PortgroupPhysnetInconsistent,
|
||||||
|
exc.exc_info[0])
|
||||||
|
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
|
||||||
|
self.assertFalse(mock_attach.called)
|
||||||
|
|
||||||
|
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autpspec=True)
|
||||||
|
def test_vif_attach_raises_vif_invalid_for_attach(
|
||||||
|
self, mock_attach, mock_valid):
|
||||||
|
mock_valid.side_effect = exception.VifInvalidForAttach(
|
||||||
|
node='fake-node', vif='fake-vif', reason='fake-reason')
|
||||||
|
node = obj_utils.create_test_node(self.context, driver='fake')
|
||||||
|
exc = self.assertRaises(messaging.rpc.ExpectedException,
|
||||||
|
self.service.vif_attach,
|
||||||
|
self.context, node.uuid, self.vif)
|
||||||
|
# Compare true exception hidden by @messaging.expected_exceptions
|
||||||
|
self.assertEqual(exception.VifInvalidForAttach,
|
||||||
|
exc.exc_info[0])
|
||||||
|
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
|
||||||
|
self.assertFalse(mock_attach.called)
|
||||||
|
|
||||||
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autpspec=True)
|
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autpspec=True)
|
||||||
def test_vif_attach_validate_error(self, mock_attach,
|
def test_vif_attach_validate_error(self, mock_attach,
|
||||||
mock_valid):
|
mock_valid):
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
from neutronclient.common import exceptions as neutron_exceptions
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
@ -38,35 +39,45 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.port = obj_utils.create_test_port(
|
self.port = obj_utils.create_test_port(
|
||||||
self.context, node_id=self.node.id, address='52:54:00:cf:2d:32')
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:32')
|
||||||
self.vif_id = "fake_vif_id"
|
self.vif_id = "fake_vif_id"
|
||||||
|
self.client = mock.MagicMock()
|
||||||
|
|
||||||
def _objects_setup(self):
|
def _objects_setup(self, set_physnets):
|
||||||
pg1 = obj_utils.create_test_portgroup(
|
pg1 = obj_utils.create_test_portgroup(
|
||||||
self.context, node_id=self.node.id)
|
self.context, node_id=self.node.id)
|
||||||
pg1_ports = []
|
pg1_ports = []
|
||||||
# This portgroup contains 2 ports, both of them without VIF
|
# This portgroup contains 2 ports, both of them without VIF. The ports
|
||||||
|
# are assigned to physnet physnet1.
|
||||||
|
physical_network = 'physnet1' if set_physnets else None
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
pg1_ports.append(obj_utils.create_test_port(
|
pg1_ports.append(obj_utils.create_test_port(
|
||||||
self.context, node_id=self.node.id,
|
self.context, node_id=self.node.id,
|
||||||
address='52:54:00:cf:2d:0%d' % i,
|
address='52:54:00:cf:2d:0%d' % i,
|
||||||
|
physical_network=physical_network,
|
||||||
uuid=uuidutils.generate_uuid(), portgroup_id=pg1.id))
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg1.id))
|
||||||
pg2 = obj_utils.create_test_portgroup(
|
pg2 = obj_utils.create_test_portgroup(
|
||||||
self.context, node_id=self.node.id, address='00:54:00:cf:2d:04',
|
self.context, node_id=self.node.id, address='00:54:00:cf:2d:04',
|
||||||
name='foo2', uuid=uuidutils.generate_uuid())
|
name='foo2', uuid=uuidutils.generate_uuid())
|
||||||
pg2_ports = []
|
pg2_ports = []
|
||||||
# This portgroup contains 3 ports, one of them with 'some-vif'
|
# This portgroup contains 3 ports, one of them with 'some-vif'
|
||||||
# attached, so the two free ones should be considered standalone
|
# attached, so the two free ones should be considered standalone.
|
||||||
|
# The ports are assigned physnet physnet2.
|
||||||
|
physical_network = 'physnet2' if set_physnets else None
|
||||||
for i in range(2, 4):
|
for i in range(2, 4):
|
||||||
pg2_ports.append(obj_utils.create_test_port(
|
pg2_ports.append(obj_utils.create_test_port(
|
||||||
self.context, node_id=self.node.id,
|
self.context, node_id=self.node.id,
|
||||||
address='52:54:00:cf:2d:0%d' % i,
|
address='52:54:00:cf:2d:0%d' % i,
|
||||||
|
physical_network=physical_network,
|
||||||
uuid=uuidutils.generate_uuid(), portgroup_id=pg2.id))
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg2.id))
|
||||||
pg2_ports.append(obj_utils.create_test_port(
|
pg2_ports.append(obj_utils.create_test_port(
|
||||||
self.context, node_id=self.node.id,
|
self.context, node_id=self.node.id,
|
||||||
address='52:54:00:cf:2d:04',
|
address='52:54:00:cf:2d:04',
|
||||||
|
physical_network=physical_network,
|
||||||
extra={'vif_port_id': 'some-vif'},
|
extra={'vif_port_id': 'some-vif'},
|
||||||
uuid=uuidutils.generate_uuid(), portgroup_id=pg2.id))
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg2.id))
|
||||||
# This portgroup has 'some-vif-2' attached to it and contains one port,
|
# This portgroup has 'some-vif-2' attached to it and contains one port,
|
||||||
# so neither portgroup nor port can be considered free
|
# so neither portgroup nor port can be considered free. The ports are
|
||||||
|
# assigned physnet3.
|
||||||
|
physical_network = 'physnet3' if set_physnets else None
|
||||||
pg3 = obj_utils.create_test_portgroup(
|
pg3 = obj_utils.create_test_portgroup(
|
||||||
self.context, node_id=self.node.id, address='00:54:00:cf:2d:05',
|
self.context, node_id=self.node.id, address='00:54:00:cf:2d:05',
|
||||||
name='foo3', uuid=uuidutils.generate_uuid(),
|
name='foo3', uuid=uuidutils.generate_uuid(),
|
||||||
@ -74,37 +85,119 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
pg3_ports = [obj_utils.create_test_port(
|
pg3_ports = [obj_utils.create_test_port(
|
||||||
self.context, node_id=self.node.id,
|
self.context, node_id=self.node.id,
|
||||||
address='52:54:00:cf:2d:05', uuid=uuidutils.generate_uuid(),
|
address='52:54:00:cf:2d:05', uuid=uuidutils.generate_uuid(),
|
||||||
|
physical_network=physical_network,
|
||||||
portgroup_id=pg3.id)]
|
portgroup_id=pg3.id)]
|
||||||
return pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports
|
return pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports
|
||||||
|
|
||||||
def test__get_free_portgroups_and_ports(self):
|
def test__get_free_portgroups_and_ports_no_port_physnets(self):
|
||||||
self.node.network_interface = 'flat'
|
self.node.network_interface = 'flat'
|
||||||
self.node.save()
|
self.node.save()
|
||||||
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup()
|
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup(
|
||||||
|
set_physnets=False)
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
free_portgroups, free_ports = (
|
free_port_like_objs = (
|
||||||
common._get_free_portgroups_and_ports(task, self.vif_id))
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
{'anyphysnet'}))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[pg1.uuid, self.port.uuid] + [p.uuid for p in pg2_ports[:2]],
|
||||||
|
[p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
|
def test__get_free_portgroups_and_ports_no_physnets(self):
|
||||||
|
self.node.network_interface = 'flat'
|
||||||
|
self.node.save()
|
||||||
|
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup(
|
||||||
|
set_physnets=True)
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
free_port_like_objs = (
|
||||||
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
set()))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[pg1.uuid, self.port.uuid] + [p.uuid for p in pg2_ports[:2]],
|
||||||
|
[p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
|
def test__get_free_portgroups_and_ports_no_matching_physnet(self):
|
||||||
|
self.node.network_interface = 'flat'
|
||||||
|
self.node.save()
|
||||||
|
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup(
|
||||||
|
set_physnets=True)
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
free_port_like_objs = (
|
||||||
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
{'notaphysnet'}))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[self.port.uuid],
|
||||||
|
[p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
|
def test__get_free_portgroups_and_ports_physnet1(self):
|
||||||
|
self.node.network_interface = 'flat'
|
||||||
|
self.node.save()
|
||||||
|
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup(
|
||||||
|
set_physnets=True)
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
free_port_like_objs = (
|
||||||
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
{'physnet1'}))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[pg1.uuid, self.port.uuid],
|
||||||
|
[p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
|
def test__get_free_portgroups_and_ports_physnet2(self):
|
||||||
|
self.node.network_interface = 'flat'
|
||||||
|
self.node.save()
|
||||||
|
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup(
|
||||||
|
set_physnets=True)
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
free_port_like_objs = (
|
||||||
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
{'physnet2'}))
|
||||||
self.assertItemsEqual(
|
self.assertItemsEqual(
|
||||||
[self.port.uuid] + [p.uuid for p in pg2_ports[:2]],
|
[self.port.uuid] + [p.uuid for p in pg2_ports[:2]],
|
||||||
[p.uuid for p in free_ports])
|
[p.uuid for p in free_port_like_objs])
|
||||||
self.assertItemsEqual([pg1.uuid], [p.uuid for p in free_portgroups])
|
|
||||||
|
def test__get_free_portgroups_and_ports_physnet3(self):
|
||||||
|
self.node.network_interface = 'flat'
|
||||||
|
self.node.save()
|
||||||
|
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup(
|
||||||
|
set_physnets=True)
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
free_port_like_objs = (
|
||||||
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
{'physnet3'}))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[self.port.uuid], [p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
|
def test__get_free_portgroups_and_ports_all_physnets(self):
|
||||||
|
self.node.network_interface = 'flat'
|
||||||
|
self.node.save()
|
||||||
|
pg1, pg1_ports, pg2, pg2_ports, pg3, pg3_ports = self._objects_setup(
|
||||||
|
set_physnets=True)
|
||||||
|
physnets = {'physnet1', 'physnet2', 'physnet3'}
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
free_port_like_objs = (
|
||||||
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
physnets))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[pg1.uuid, self.port.uuid] + [p.uuid for p in pg2_ports[:2]],
|
||||||
|
[p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True)
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True)
|
||||||
def test__get_free_portgroups_and_ports_neutron_missed(self, vpi_mock):
|
def test__get_free_portgroups_and_ports_neutron_missed(self, vpi_mock):
|
||||||
vpi_mock.return_value = False
|
vpi_mock.return_value = False
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
free_portgroups, free_ports = (
|
free_port_like_objs = (
|
||||||
common._get_free_portgroups_and_ports(task, self.vif_id))
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
self.assertItemsEqual([], free_ports)
|
{'anyphysnet'}))
|
||||||
|
self.assertItemsEqual([], free_port_like_objs)
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True)
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True)
|
||||||
def test__get_free_portgroups_and_ports_neutron(self, vpi_mock):
|
def test__get_free_portgroups_and_ports_neutron(self, vpi_mock):
|
||||||
vpi_mock.return_value = True
|
vpi_mock.return_value = True
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
free_portgroups, free_ports = (
|
free_port_like_objs = (
|
||||||
common._get_free_portgroups_and_ports(task, self.vif_id))
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
{'anyphysnet'}))
|
||||||
self.assertItemsEqual(
|
self.assertItemsEqual(
|
||||||
[self.port.uuid], [p.uuid for p in free_ports])
|
[self.port.uuid], [p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True)
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True)
|
||||||
def test__get_free_portgroups_and_ports_flat(self, vpi_mock):
|
def test__get_free_portgroups_and_ports_flat(self, vpi_mock):
|
||||||
@ -112,14 +205,18 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.node.save()
|
self.node.save()
|
||||||
vpi_mock.return_value = True
|
vpi_mock.return_value = True
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
free_portgroups, free_ports = (
|
free_port_like_objs = (
|
||||||
common._get_free_portgroups_and_ports(task, self.vif_id))
|
common._get_free_portgroups_and_ports(task, self.vif_id,
|
||||||
|
{'anyphysnet'}))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[self.port.uuid], [p.uuid for p in free_port_like_objs])
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
return_value=True)
|
return_value=True)
|
||||||
def test_get_free_port_like_object_ports(self, vpi_mock):
|
def test_get_free_port_like_object_ports(self, vpi_mock):
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
res = common.get_free_port_like_object(task, self.vif_id)
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'anyphysnet'})
|
||||||
self.assertEqual(self.port.uuid, res.uuid)
|
self.assertEqual(self.port.uuid, res.uuid)
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
@ -131,9 +228,42 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.context, node_id=self.node.id, address='52:54:00:cf:2d:33',
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:33',
|
||||||
uuid=uuidutils.generate_uuid())
|
uuid=uuidutils.generate_uuid())
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
res = common.get_free_port_like_object(task, self.vif_id)
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'anyphysnet'})
|
||||||
self.assertEqual(other_port.uuid, res.uuid)
|
self.assertEqual(other_port.uuid, res.uuid)
|
||||||
|
|
||||||
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
|
return_value=True)
|
||||||
|
def test_get_free_port_like_object_ports_physnet_match_first(self,
|
||||||
|
vpi_mock):
|
||||||
|
self.port.pxe_enabled = False
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
|
self.port.save()
|
||||||
|
obj_utils.create_test_port(
|
||||||
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:33',
|
||||||
|
uuid=uuidutils.generate_uuid())
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'physnet1'})
|
||||||
|
self.assertEqual(self.port.uuid, res.uuid)
|
||||||
|
|
||||||
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
|
return_value=True)
|
||||||
|
def test_get_free_port_like_object_ports_physnet_match_first2(self,
|
||||||
|
vpi_mock):
|
||||||
|
self.port.pxe_enabled = False
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
|
self.port.save()
|
||||||
|
pg = obj_utils.create_test_portgroup(
|
||||||
|
self.context, node_id=self.node.id)
|
||||||
|
obj_utils.create_test_port(
|
||||||
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
|
||||||
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg.id)
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'physnet1'})
|
||||||
|
self.assertEqual(self.port.uuid, res.uuid)
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
return_value=True)
|
return_value=True)
|
||||||
def test_get_free_port_like_object_portgroup_first(self, vpi_mock):
|
def test_get_free_port_like_object_portgroup_first(self, vpi_mock):
|
||||||
@ -143,15 +273,38 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
|
||||||
uuid=uuidutils.generate_uuid(), portgroup_id=pg.id)
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg.id)
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
res = common.get_free_port_like_object(task, self.vif_id)
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'anyphysnet'})
|
||||||
self.assertEqual(pg.uuid, res.uuid)
|
self.assertEqual(pg.uuid, res.uuid)
|
||||||
|
|
||||||
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
|
return_value=True)
|
||||||
|
def test_get_free_port_like_object_portgroup_physnet_match_first(self,
|
||||||
|
vpi_mock):
|
||||||
|
pg1 = obj_utils.create_test_portgroup(
|
||||||
|
self.context, node_id=self.node.id)
|
||||||
|
obj_utils.create_test_port(
|
||||||
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
|
||||||
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg1.id)
|
||||||
|
pg2 = obj_utils.create_test_portgroup(
|
||||||
|
self.context, node_id=self.node.id, uuid=uuidutils.generate_uuid(),
|
||||||
|
name='pg2', address='52:54:00:cf:2d:01')
|
||||||
|
obj_utils.create_test_port(
|
||||||
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:02',
|
||||||
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg2.id,
|
||||||
|
physical_network='physnet1')
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'physnet1'})
|
||||||
|
self.assertEqual(pg2.uuid, res.uuid)
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
return_value=True)
|
return_value=True)
|
||||||
def test_get_free_port_like_object_ignores_empty_portgroup(self, vpi_mock):
|
def test_get_free_port_like_object_ignores_empty_portgroup(self, vpi_mock):
|
||||||
obj_utils.create_test_portgroup(self.context, node_id=self.node.id)
|
obj_utils.create_test_portgroup(self.context, node_id=self.node.id)
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
res = common.get_free_port_like_object(task, self.vif_id)
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'anyphysnet'})
|
||||||
self.assertEqual(self.port.uuid, res.uuid)
|
self.assertEqual(self.port.uuid, res.uuid)
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
@ -169,7 +322,8 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.context, node_id=self.node.id, address='52:54:00:cf:2d:02',
|
self.context, node_id=self.node.id, address='52:54:00:cf:2d:02',
|
||||||
uuid=uuidutils.generate_uuid(), portgroup_id=pg.id)
|
uuid=uuidutils.generate_uuid(), portgroup_id=pg.id)
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
res = common.get_free_port_like_object(task, self.vif_id)
|
res = common.get_free_port_like_object(task, self.vif_id,
|
||||||
|
{'anyphysnet'})
|
||||||
self.assertEqual(free_port.uuid, res.uuid)
|
self.assertEqual(free_port.uuid, res.uuid)
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
@ -186,7 +340,8 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
exception.VifAlreadyAttached,
|
exception.VifAlreadyAttached,
|
||||||
r"already attached to Ironic Portgroup",
|
r"already attached to Ironic Portgroup",
|
||||||
common.get_free_port_like_object, task, self.vif_id)
|
common.get_free_port_like_object,
|
||||||
|
task, self.vif_id, {'anyphysnet'})
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
return_value=True)
|
return_value=True)
|
||||||
@ -202,7 +357,8 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
exception.VifAlreadyAttached,
|
exception.VifAlreadyAttached,
|
||||||
r"already attached to Ironic Portgroup",
|
r"already attached to Ironic Portgroup",
|
||||||
common.get_free_port_like_object, task, self.vif_id)
|
common.get_free_port_like_object,
|
||||||
|
task, self.vif_id, {'anyphysnet'})
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
return_value=True)
|
return_value=True)
|
||||||
@ -213,7 +369,8 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
exception.VifAlreadyAttached,
|
exception.VifAlreadyAttached,
|
||||||
r"already attached to Ironic Port\b",
|
r"already attached to Ironic Port\b",
|
||||||
common.get_free_port_like_object, task, self.vif_id)
|
common.get_free_port_like_object,
|
||||||
|
task, self.vif_id, {'anyphysnet'})
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
return_value=True)
|
return_value=True)
|
||||||
@ -225,7 +382,8 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
exception.VifAlreadyAttached,
|
exception.VifAlreadyAttached,
|
||||||
r"already attached to Ironic Port\b",
|
r"already attached to Ironic Port\b",
|
||||||
common.get_free_port_like_object, task, self.vif_id)
|
common.get_free_port_like_object,
|
||||||
|
task, self.vif_id, {'anyphysnet'})
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
return_value=True)
|
return_value=True)
|
||||||
@ -235,7 +393,17 @@ class TestCommonFunctions(db_base.DbTestCase):
|
|||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
self.assertRaises(exception.NoFreePhysicalPorts,
|
self.assertRaises(exception.NoFreePhysicalPorts,
|
||||||
common.get_free_port_like_object,
|
common.get_free_port_like_object,
|
||||||
task, self.vif_id)
|
task, self.vif_id, {'anyphysnet'})
|
||||||
|
|
||||||
|
@mock.patch.object(neutron_common, 'validate_port_info', autospec=True,
|
||||||
|
return_value=True)
|
||||||
|
def test_get_free_port_like_object_no_matching_physnets(self, vpi_mock):
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
|
self.port.save()
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
self.assertRaises(exception.NoFreePhysicalPorts,
|
||||||
|
common.get_free_port_like_object,
|
||||||
|
task, self.vif_id, {'physnet2'})
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||||
def test_plug_port_to_tenant_network_client(self, mock_gc):
|
def test_plug_port_to_tenant_network_client(self, mock_gc):
|
||||||
@ -330,24 +498,58 @@ class TestVifPortIDMixin(db_base.DbTestCase):
|
|||||||
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
||||||
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||||
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
|
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
|
||||||
def test_vif_attach(self, mock_upa, mock_client, moc_gfp):
|
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
|
||||||
|
autospec=True)
|
||||||
|
def test_vif_attach(self, mock_gpbpi, mock_upa, mock_client, mock_gfp):
|
||||||
self.port.extra = {}
|
self.port.extra = {}
|
||||||
self.port.save()
|
self.port.save()
|
||||||
vif = {'id': "fake_vif_id"}
|
vif = {'id': "fake_vif_id"}
|
||||||
moc_gfp.return_value = self.port
|
mock_gfp.return_value = self.port
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
self.interface.vif_attach(task, vif)
|
self.interface.vif_attach(task, vif)
|
||||||
self.port.refresh()
|
self.port.refresh()
|
||||||
self.assertEqual("fake_vif_id", self.port.internal_info.get(
|
self.assertEqual("fake_vif_id", self.port.internal_info.get(
|
||||||
common.TENANT_VIF_KEY))
|
common.TENANT_VIF_KEY))
|
||||||
mock_client.assert_called_once_with()
|
mock_client.assert_called_once_with()
|
||||||
|
self.assertFalse(mock_gpbpi.called)
|
||||||
|
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
|
||||||
|
mock_client.return_value.show_port.assert_called_once_with(
|
||||||
|
'fake_vif_id')
|
||||||
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
|
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
|
||||||
|
|
||||||
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
||||||
@mock.patch.object(neutron_common, 'get_client')
|
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||||
|
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
|
||||||
|
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
|
||||||
|
autospec=True)
|
||||||
|
def test_vif_attach_with_physnet(self, mock_gpbpi, mock_upa, mock_client,
|
||||||
|
mock_gfp):
|
||||||
|
self.port.extra = {}
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
|
self.port.save()
|
||||||
|
vif = {'id': "fake_vif_id"}
|
||||||
|
mock_gpbpi.return_value = {'physnet1'}
|
||||||
|
mock_gfp.return_value = self.port
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
self.interface.vif_attach(task, vif)
|
||||||
|
self.port.refresh()
|
||||||
|
self.assertEqual("fake_vif_id", self.port.internal_info.get(
|
||||||
|
common.TENANT_VIF_KEY))
|
||||||
|
mock_client.assert_called_once_with()
|
||||||
|
mock_gpbpi.assert_called_once_with(mock_client.return_value,
|
||||||
|
'fake_vif_id')
|
||||||
|
mock_gfp.assert_called_once_with(task, 'fake_vif_id', {'physnet1'})
|
||||||
|
mock_client.return_value.show_port.assert_called_once_with(
|
||||||
|
'fake_vif_id')
|
||||||
|
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
|
||||||
|
|
||||||
|
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
||||||
|
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||||
@mock.patch.object(neutron_common, 'update_port_address')
|
@mock.patch.object(neutron_common, 'update_port_address')
|
||||||
def test_vif_attach_portgroup_no_address(self, mock_upa, mock_client,
|
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
|
||||||
mock_gfp):
|
autospec=True)
|
||||||
|
def test_vif_attach_portgroup_no_address(self, mock_gpbpi, mock_upa,
|
||||||
|
mock_client, mock_gfp):
|
||||||
pg = obj_utils.create_test_portgroup(
|
pg = obj_utils.create_test_portgroup(
|
||||||
self.context, node_id=self.node.id, address=None)
|
self.context, node_id=self.node.id, address=None)
|
||||||
mock_gfp.return_value = pg
|
mock_gfp.return_value = pg
|
||||||
@ -360,15 +562,23 @@ class TestVifPortIDMixin(db_base.DbTestCase):
|
|||||||
pg.refresh()
|
pg.refresh()
|
||||||
self.assertEqual(vif['id'],
|
self.assertEqual(vif['id'],
|
||||||
pg.internal_info[common.TENANT_VIF_KEY])
|
pg.internal_info[common.TENANT_VIF_KEY])
|
||||||
|
mock_client.assert_called_once_with()
|
||||||
|
self.assertFalse(mock_gpbpi.called)
|
||||||
|
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
|
||||||
|
self.assertFalse(mock_client.return_value.show_port.called)
|
||||||
self.assertFalse(mock_upa.called)
|
self.assertFalse(mock_upa.called)
|
||||||
self.assertTrue(mock_client.called)
|
|
||||||
|
|
||||||
@mock.patch.object(neutron_common, 'get_client')
|
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||||
@mock.patch.object(neutron_common, 'update_port_address')
|
@mock.patch.object(neutron_common, 'update_port_address')
|
||||||
def test_vif_attach_update_port_exception(self, mock_upa, mock_client):
|
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
|
||||||
|
autospec=True)
|
||||||
|
def test_vif_attach_update_port_exception(self, mock_gpbpi, mock_upa,
|
||||||
|
mock_client):
|
||||||
self.port.extra = {}
|
self.port.extra = {}
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
self.port.save()
|
self.port.save()
|
||||||
vif = {'id': "fake_vif_id"}
|
vif = {'id': "fake_vif_id"}
|
||||||
|
mock_gpbpi.return_value = {'physnet1'}
|
||||||
mock_upa.side_effect = (
|
mock_upa.side_effect = (
|
||||||
exception.FailedToUpdateMacOnPort(port_id='fake'))
|
exception.FailedToUpdateMacOnPort(port_id='fake'))
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
@ -376,6 +586,84 @@ class TestVifPortIDMixin(db_base.DbTestCase):
|
|||||||
exception.NetworkError, "can not update Neutron port",
|
exception.NetworkError, "can not update Neutron port",
|
||||||
self.interface.vif_attach, task, vif)
|
self.interface.vif_attach, task, vif)
|
||||||
mock_client.assert_called_once_with()
|
mock_client.assert_called_once_with()
|
||||||
|
mock_gpbpi.assert_called_once_with(mock_client.return_value,
|
||||||
|
'fake_vif_id')
|
||||||
|
mock_client.return_value.show_port.assert_called_once_with(
|
||||||
|
'fake_vif_id')
|
||||||
|
|
||||||
|
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
||||||
|
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||||
|
@mock.patch.object(neutron_common, 'update_port_address')
|
||||||
|
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
|
||||||
|
autospec=True)
|
||||||
|
def test_vif_attach_neutron_absent(self, mock_gpbpi, mock_upa,
|
||||||
|
mock_client, mock_gfp):
|
||||||
|
self.port.extra = {}
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
|
self.port.save()
|
||||||
|
vif = {'id': "fake_vif_id"}
|
||||||
|
mock_gfp.return_value = self.port
|
||||||
|
mock_client.return_value.show_port.side_effect = (
|
||||||
|
neutron_exceptions.NeutronClientException)
|
||||||
|
mock_gpbpi.side_effect = exception.NetworkError
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
self.interface.vif_attach(task, vif)
|
||||||
|
mock_client.assert_called_once_with()
|
||||||
|
mock_gpbpi.assert_called_once_with(mock_client.return_value,
|
||||||
|
'fake_vif_id')
|
||||||
|
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
|
||||||
|
mock_client.return_value.show_port.assert_called_once_with(
|
||||||
|
'fake_vif_id')
|
||||||
|
self.assertFalse(mock_upa.called)
|
||||||
|
|
||||||
|
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
||||||
|
@mock.patch.object(neutron_common, 'get_client')
|
||||||
|
@mock.patch.object(neutron_common, 'update_port_address')
|
||||||
|
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
|
||||||
|
autospec=True)
|
||||||
|
def test_vif_attach_portgroup_physnet_inconsistent(self, mock_gpbpi,
|
||||||
|
mock_upa, mock_client,
|
||||||
|
mock_gfp):
|
||||||
|
self.port.extra = {}
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
|
self.port.save()
|
||||||
|
vif = {'id': "fake_vif_id"}
|
||||||
|
mock_gpbpi.return_value = {'anyphysnet'}
|
||||||
|
mock_gfp.side_effect = exception.PortgroupPhysnetInconsistent(
|
||||||
|
portgroup='fake-portgroup-id', physical_networks='physnet1')
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
self.assertRaises(
|
||||||
|
exception.PortgroupPhysnetInconsistent,
|
||||||
|
self.interface.vif_attach, task, vif)
|
||||||
|
mock_client.assert_called_once_with()
|
||||||
|
mock_gpbpi.assert_called_once_with(mock_client.return_value,
|
||||||
|
'fake_vif_id')
|
||||||
|
self.assertFalse(mock_upa.called)
|
||||||
|
|
||||||
|
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
|
||||||
|
@mock.patch.object(neutron_common, 'get_client')
|
||||||
|
@mock.patch.object(neutron_common, 'update_port_address')
|
||||||
|
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
|
||||||
|
autospec=True)
|
||||||
|
def test_vif_attach_multiple_segment_mappings(self, mock_gpbpi, mock_upa,
|
||||||
|
mock_client, mock_gfp):
|
||||||
|
self.port.extra = {}
|
||||||
|
self.port.physical_network = 'physnet1'
|
||||||
|
self.port.save()
|
||||||
|
obj_utils.create_test_port(
|
||||||
|
self.context, node_id=self.node.id, uuid=uuidutils.generate_uuid(),
|
||||||
|
address='52:54:00:cf:2d:33', physical_network='physnet2')
|
||||||
|
vif = {'id': "fake_vif_id"}
|
||||||
|
mock_gpbpi.return_value = {'physnet1', 'physnet2'}
|
||||||
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
self.assertRaises(
|
||||||
|
exception.VifInvalidForAttach,
|
||||||
|
self.interface.vif_attach, task, vif)
|
||||||
|
mock_client.assert_called_once_with()
|
||||||
|
mock_gpbpi.assert_called_once_with(mock_client.return_value,
|
||||||
|
'fake_vif_id')
|
||||||
|
self.assertFalse(mock_gfp.called)
|
||||||
|
self.assertFalse(mock_upa.called)
|
||||||
|
|
||||||
def test_vif_detach_in_extra(self):
|
def test_vif_detach_in_extra(self):
|
||||||
with task_manager.acquire(self.context, self.node.id) as task:
|
with task_manager.acquire(self.context, self.node.id) as task:
|
||||||
|
Loading…
Reference in New Issue
Block a user