Browse Source

VM support

RHBZ# 1621913

Change-Id: I2da2165d06cf853cf435e31868cc4722701bd214
changes/78/790178/12
Guilherme Giacon 2 months ago
committed by Dan Radez
parent
commit
bb6a2c1174
8 changed files with 661 additions and 260 deletions
  1. +13
    -5
      etc/neutron/plugins/ml2/ml2_conf_ansible.ini.sample
  2. +40
    -0
      networking_ansible/config.py
  3. +10
    -1
      networking_ansible/constants.py
  4. +236
    -92
      networking_ansible/ml2/mech_driver.py
  5. +62
    -36
      networking_ansible/tests/unit/base.py
  6. +274
    -120
      networking_ansible/tests/unit/ml2/test_mech_driver.py
  7. +25
    -5
      networking_ansible/tests/unit/test_config.py
  8. +1
    -1
      requirements.txt

+ 13
- 5
etc/neutron/plugins/ml2/ml2_conf_ansible.ini.sample View File

@ -11,10 +11,15 @@ coordination_uri = etcd://127.0.0.1:2379
# These sections DO NOT adhere to ansible inventory structure
# *** Please read carefully ***
#
# - Each section represents a host.
# - the 'ansible:' tag will be stripped out and the rest of the section name
# is the name of the host. This is an arbitrary value used only by
# net-ansible internally for mapping openstack ports to switches.
# - ansible:port_mappings is a a reserved section for mapping compute service
# hosts to physical switches. Th key is the host name that the neutron port
# object will return from HOST_ID when created. The value is a switch name
# defined in a subsequent ansible:switch_name section and a port name
# separated from the switch name by a double colon (::). A single colon (:)
# can be used in naming ports.
# - All other sections represents a switch ansible will configure.
# the 'ansible:' tag will be stripped out and the rest of the section name
# is used as switch_name. switch_name cannot contain a :
# - The variables in each section will be passed directly to the inventory file
# ansible will use. The variables you can use here are defined by ansible.
# If you uses an ansible host variable that is not represented here but
@ -32,6 +37,9 @@ coordination_uri = etcd://127.0.0.1:2379
# prefix cp_ which is stripped off before passed to the ansible role.
#
#########
[ansible:port_mappings]
compute1_host_name = vyos118_rack_A::switch_port_name,openswitch230_rack_23::switch_port_name
compute2_host_name = openswitch230_rack_23::switchport_name
[ansible:vyos118_rack_A]
ansible_network_os=vyos
@ -48,7 +56,7 @@ ansible_ssh_pass=password
manage_vlans=False
[ansible:custom_platform]
ansible_network_os=openswitch
ansible_network_os=custom
ansible_host=5.6.7.8
ansible_user=ansible
ansible_ssh_pass=password


+ 40
- 0
networking_ansible/config.py View File

@ -36,12 +36,14 @@ class Config(object):
"""Get inventory list from config files
builds a Network-Runner inventory object
port_map dictionary and
and a mac_map dictionary
according to ansible inventory file yaml definition
http://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
"""
self.inventory = {}
self.mac_map = {}
self.port_mappings = {}
for conffile in CONF.config_file:
# parse each config file
@ -56,6 +58,43 @@ class Config(object):
hosts = {k: v for k, v in sections.items()
if k.startswith(c.DRIVER_TAG)}
# remember port mappings and remove from the host list
# mappings come from conf file in format:
# {'compute_host_id': ['sw_name::testport,sw_name2::testport2']}
# the list needs to be removed and a dict needs to be
# returned with a list of tuples of (connection name, port):
# {'compute_host_id': [('testhost', 'testport')]}
if c.PORT_MAPPINGS in hosts:
mappings = hosts[c.PORT_MAPPINGS]
del hosts[c.PORT_MAPPINGS]
def format_and_validate_port_mapping(mapping):
host_id = mapping[0]
ports_lst = []
# ensure the mapping is a valid format
# format the mapping to a tuple of (switch_name, port_name)
ports_split = mapping[1][0].split(',')
for port in ports_split:
port_split = mapping[1][0].split('::')
if len(port_split) == 2:
# switch_name::port_name splits to
# ['switch_name', 'port_name']
ports_lst.append((port_split[0], port_split[1]))
else:
LOG.error(
'{} is not a valid switch_name::port_name '
'formated mapping. It will not be available '
'for look up. Double check that it is using a '
'double colon :: as '
'a separator.'.format(mapping))
return (host_id, ports_lst)
mapped = map(format_and_validate_port_mapping,
mappings.items())
# prune out empty mappings
self.port_mappings = {k: v for k, v in mapped if v}
# munge the oslo_config data removing the device tag and
# turning lists with single item strings into strings
for host in hosts:
@ -70,3 +109,4 @@ class Config(object):
self.mac_map[dev_cfg['mac'].upper()] = dev_id
LOG.info('Ansible Host List: %s', ', '.join(self.inventory))
LOG.debug('Ansible Port Mappings: %s', self.port_mappings)

+ 10
- 1
networking_ansible/constants.py View File

@ -13,11 +13,20 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.api.definitions import portbindings
COORDINATION = 'networking_ansible.ml2.mech_driver.coordination'
NETWORKING_ENTITY = 'ANSIBLENETWORKING'
DRIVER_TAG = 'ansible:'
PORT_MAPPINGS = DRIVER_TAG + 'port_mappings'
LLI = 'local_link_information'
NETWORKING_ENTITY = 'ANSIBLENETWORKING'
CUSTOM_PARAM_PREFIX = 'cp_'
DEVICE_OWNER = 'device_owner'
COMPUTE_NOVA = 'compute:nova'
BAREMETAL_NONE = 'baremetal:none'
SUPPORTED_OWNERS = (BAREMETAL_NONE, COMPUTE_NOVA)
SUPPORTED_TYPES = (portbindings.VNIC_BAREMETAL,
portbindings.VNIC_NORMAL)
# values that will be cast to Bool in the conf process
BOOLEANS = ['manage_vlans', 'stp_edge']


+ 236
- 92
networking_ansible/ml2/mech_driver.py View File

@ -39,7 +39,6 @@ from network_runner.models.inventory import Inventory
from tooz import coordination
LOG = logging.getLogger(__name__)
CONF = config.CONF
@ -176,7 +175,6 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
# assuming all hosts
# TODO(radez): can we filter by physnets?
LOG.debug('ansnet:network delete')
for host_name in self.ml2config.inventory:
host = self.ml2config.inventory[host_name]
@ -243,7 +241,28 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
state. It is up to the mechanism driver to ignore state or
state changes that it does not know or care about.
"""
if self._is_port_bound(context.current):
# Handle VM ports
if self._is_port_normal(context.current):
port = context.current
network = context.network.current
mappings, segmentation_id = self.get_switch_meta(port, network)
for switch_name, switch_port in mappings:
LOG.debug('Ensuring Updated port {switch_port} on network '
'{network} {switch_name} to vlan: '
'{segmentation_id}'.format(
switch_port=switch_port,
network=network,
switch_name=switch_name,
segmentation_id=segmentation_id))
self.ensure_port(port, context._plugin_context,
switch_name, switch_port,
network[provider_net.PHYSICAL_NETWORK],
context,
segmentation_id)
# Baremetal Operations
elif self._is_port_bound(context.current):
port = context.current
provisioning_blocks.provisioning_complete(
context._plugin_context, port['id'], resources.PORT,
@ -252,20 +271,22 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
elif self._is_port_bound(context.original):
port = context.original
network = context.network.current
switch_name, switch_port, segmentation_id = \
self._link_info_from_port(context.original, network)
LOG.debug('Ensuring Updated port {switch_port} on network '
'{network} {switch_name} to vlan: '
'{segmentation_id}'.format(
switch_port=switch_port,
network=network,
switch_name=switch_name,
segmentation_id=segmentation_id))
self.ensure_port(port, context._plugin_context,
switch_name, switch_port,
network[provider_net.PHYSICAL_NETWORK], context)
mappings, segmentation_id = self.get_switch_meta(port, network)
for switch_name, switch_port in mappings:
LOG.debug('Ensuring Updated port {switch_port} on network '
'{network} {switch_name} to vlan: '
'{segmentation_id}'.format(
switch_port=switch_port,
network=network,
switch_name=switch_name,
segmentation_id=segmentation_id))
self.ensure_port(port, context._plugin_context,
switch_name, switch_port,
network[provider_net.PHYSICAL_NETWORK],
context,
segmentation_id)
def delete_port_postcommit(self, context):
"""Delete a port.
@ -284,18 +305,20 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
if self._is_port_bound(context.current):
switch_name, switch_port, segmentation_id = \
self._link_info_from_port(port, network)
mappings, segmentation_id = self.get_switch_meta(port, network)
LOG.debug('Ensuring Deleted port {switch_port} on '
'{switch_name} to vlan: {segmentation_id}'.format(
switch_port=switch_port,
switch_name=switch_name,
segmentation_id=segmentation_id))
for switch_name, switch_port in mappings:
LOG.debug('Ensuring Deleted port {switch_port} on '
'{switch_name} to vlan: {segmentation_id}'.format(
switch_port=switch_port,
switch_name=switch_name,
segmentation_id=segmentation_id))
self.ensure_port(port, context._plugin_context,
switch_name, switch_port,
network[provider_net.PHYSICAL_NETWORK], context)
self.ensure_port(port, context._plugin_context,
switch_name, switch_port,
network[provider_net.PHYSICAL_NETWORK],
context,
segmentation_id, delete=True)
def bind_port(self, context):
"""Attempt to bind a port.
@ -342,33 +365,50 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
port = context.current
network = context.network.current
# Run this before extracting lli, lli_from_port runs the same condition
# and will raise before this is checked
# Run this before getting switch meta, switch_meta_from_local_info
# runs the same condition and will raise before this is checked
if not self._is_port_supported(port):
LOG.debug('Port {} has vnic_type set to {} which is not correct '
'to work with networking-ansible driver.'.format(
port['id'],
port[portbindings.VNIC_TYPE]))
LOG.warning('Port {} has device_owner: {} and vnic_type: {}'
' which is not supported by networking-ansible'
', ignoring.'.format(
port['id'],
port[c.DEVICE_OWNER],
port[portbindings.VNIC_TYPE]))
return
switch_name, switch_port, segmentation_id = \
self._link_info_from_port(port, network)
mappings, segmentation_id = self.get_switch_meta(port, network)
LOG.debug('Plugging in port {switch_port} on '
'{switch_name} to vlan: {segmentation_id}'.format(
switch_port=switch_port,
switch_name=switch_name,
segmentation_id=segmentation_id))
for switch_name, switch_port in mappings:
provisioning_blocks.add_provisioning_component(
context._plugin_context, port['id'], resources.PORT,
c.NETWORKING_ENTITY)
LOG.debug('Plugging in port {switch_port} on '
'{switch_name} to vlan: {segmentation_id}'.format(
switch_port=switch_port,
switch_name=switch_name,
segmentation_id=segmentation_id))
self.ensure_port(port, context._plugin_context,
switch_name, switch_port,
network[provider_net.PHYSICAL_NETWORK], context)
provisioning_blocks.add_provisioning_component(
context._plugin_context, port['id'], resources.PORT,
c.NETWORKING_ENTITY)
def _link_info_from_port(self, port, network=None):
self.ensure_port(port, context._plugin_context,
switch_name, switch_port,
network[provider_net.PHYSICAL_NETWORK], context,
segmentation_id)
def get_switch_meta(self, port, network=None):
'''
port: neutron port object
network: neutron network object
returns: list of mappings tuples and segmentation_id
mapping tuple: ('switch_name', 'switch_port')
'''
if self._is_port_baremetal(port):
return self._switch_meta_from_link_info(port, network)
elif self._is_port_normal(port):
return self._switch_meta_from_port_host_id(port, network)
return None, None, None
def _switch_meta_from_link_info(self, port, network=None):
network = network or {}
local_link_info = self._get_port_lli(port)
@ -387,7 +427,16 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
if not switch_name and switch_mac in self.ml2config.mac_map:
switch_name = self.ml2config.mac_map[switch_mac]
segmentation_id = network.get(provider_net.SEGMENTATION_ID, '')
return switch_name, switch_port, segmentation_id
return [(switch_name, switch_port)], segmentation_id
def _switch_meta_from_port_host_id(self, port, network=None):
network = network or {}
# TODO What do we do if there is not a mapping available?
# should we fail in some way?
host_id = port[portbindings.HOST_ID]
mappings = self.ml2config.port_mappings.get(host_id, [])
segmentation_id = network.get('provider:segmentation_id', '')
return mappings, segmentation_id
def ensure_subports(self, port_id, db):
# set the correct state on port in the case where it has subports.
@ -401,27 +450,29 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
'that has been deleted')
return
# get switch from port bindings
switch_name, switch_port, segmentation_id = \
self._link_info_from_port(port, None)
# lock switch
lock = self.coordinator.get_lock(switch_name)
with lock:
# get updated port from db
updated_port = Port.get_object(db, id=port_id)
if updated_port:
self._set_port_state(updated_port, db)
return
else:
# port delete operation will take care of deletion
LOG.debug('Discarding attempt to ensure subports on a port {} '
'that was deleted after lock '
'acquisition'.format(port_id))
return
# get switch info
mappings, segmentation_id = self.get_switch_meta(port)
for switch_name, switch_port in mappings:
# lock switch
lock = self.coordinator.get_lock(switch_name)
with lock:
# get updated port from db
updated_port = Port.get_object(db, id=port_id)
if updated_port:
self._set_port_state(updated_port, db,
switch_name, switch_port)
return
else:
# port delete operation will take care of deletion
LOG.debug('Discarding attempt to ensure subports on a port'
' {} that was deleted after lock '
'acquisition'.format(port_id))
return
def ensure_port(self, port, db, switch_name,
switch_port, physnet, port_context):
switch_port, physnet, port_context,
segmentation_id, delete=False):
LOG.debug('Ensuring state of port {port_id} '
'with mac addr {mac} '
'on switch {switch_name} '
@ -445,63 +496,118 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
# get dlock for the switch we're working with
lock = self.coordinator.get_lock(switch_name)
with lock:
# port = get the port from the db
updated_port = Port.get_object(db, id=port['id'])
# if it exists and is bound to a port
if self._get_port_lli(updated_port):
if self._set_port_state(updated_port, db):
if self._is_port_normal(port):
# OVS handles the port binding for the VM. There's no awareness
# that the compute node port is being configured in openstack.
# By the time we get the port object it's already been handled
# by OVS so we can't detect if it's being deleted by its state.
# We have to rely on the hook that's called to indicate
# whether to do an update or delete. Since ensure port handles
# both the delete flag needs to be passed for VM ports.
if delete:
# Get active ports on this port's network
# We should not delete the vlan from the compute node's
# trunk if there are other ports still using the vlan
active_ports = Port.get_objects(
db,
network_id=port['network_id'],
device_owner=c.COMPUTE_NOVA)
LOG.debug('Active Ports: {}'.format(active_ports))
# Get_objects can't filter by binding:host_id so we use a
# python filter function to finish filtering the ports by
# compute host_id
def active_port_filter(db_port):
for binding in db_port.bindings:
if binding['host'] == port[portbindings.HOST_ID] \
and db_port['id'] != port['id']:
return True
# Default to false
return False
active_ports = list(filter(active_port_filter,
active_ports))
LOG.debug('Filtered Active Ports: {}'.format(active_ports))
# If there are other VM's active ports on this port's
# network we will skip removing the vlan from the
# compute node's trunk port
if not active_ports:
self.net_runr.delete_trunk_vlan(
switch_name,
switch_port,
segmentation_id,
**self.kwargs[switch_name])
else:
LOG.info('Skip removing Segmentation ID {} from '
'compute host {}. There are {} other '
'active ports using the VLAN.'.format(
segmentation_id,
port[portbindings.HOST_ID],
len(active_ports)))
else:
self._set_port_state(port, db, switch_name, switch_port)
return
# if baremetal port exists and is bound to a port
elif self._get_port_lli(updated_port):
if self._set_port_state(updated_port, db,
switch_name, switch_port):
if port_context and port_context.segments_to_bind:
segments = port_context.segments_to_bind
port_context.set_binding(segments[0][ml2api.ID],
portbindings.VIF_TYPE_OTHER,
{})
return
return
else:
# if the port doesn't exist, we have a mac+switch, we can look
# up whether the port needs to be deleted on the switch
if self._is_deleted_port_in_use(physnet,
port['mac_address'], db):
LOG.debug('Port {port_id} was deleted, but its switch port'
' {sp} is now in use by another port, discarding'
' request to delete'.format(port_id=port['id'],
sp=switch_port))
LOG.debug('Port {port_id} was deleted, but its switch'
'port {sp} is now in use by another port, '
'discarding request to delete'.format(
port_id=port['id'],
sp=switch_port))
return
else:
self._delete_switch_port(switch_name, switch_port)
def _set_port_state(self, port, db):
def _set_port_state(self, port, db, switch_name, switch_port):
if not port:
# error
raise ml2_exc.MechanismDriverError('Null port passed to '
'set_port_state')
# get switch name and port from port bindings
switch_name, switch_port, tmp = \
self._link_info_from_port(port, None)
if not switch_name or not switch_port:
# error/raise
raise ml2_exc.MechanismDriverError('NetAnsible: couldnt find '
'switch_name {} or switch port '
'{} to set port '
'switch_name {} or switch '
'port {} to set port '
'state'.format(switch_name,
switch_port))
if not self.net_runr.has_host(switch_name):
raise ml2_exc.MechanismDriverError('NetAnsible: couldnt find '
'switch_name {} in '
'inventory'.format(switch_name))
'inventory'.format(
switch_name))
network = Network.get_object(db, id=port.network_id)
network = Network.get_object(db, id=port['network_id'])
if not network:
raise ml2_exc.MechanismDriverError('NetAnsible: couldnt find '
'network for port '
'{}'.format(port.id))
trunk = Trunk.get_object(db, port_id=port.id)
trunk = Trunk.get_object(db, port_id=port['id'])
segmentation_id = network.segments[0].segmentation_id
# Assign port to network
@ -515,6 +621,12 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
trunked_vlans,
**self.kwargs[switch_name])
elif self._is_port_normal(port):
self.net_runr.add_trunk_vlan(switch_name,
switch_port,
segmentation_id,
**self.kwargs[switch_name])
else:
self.net_runr.conf_access_port(
switch_name,
@ -524,7 +636,7 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
LOG.info('Port {neutron_port} has been plugged into '
'switch port {sp} on device {switch_name}'.format(
neutron_port=port.id,
neutron_port=port['id'],
sp=switch_port,
switch_name=switch_name))
return True
@ -532,7 +644,7 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
LOG.error('Failed to plug port {neutron_port} into '
'switch port: {sp} on device: {sw} '
'reason: {exc}'.format(
neutron_port=port.id,
neutron_port=port['id'],
sp=switch_port,
sw=switch_name,
exc=e))
@ -605,16 +717,39 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
:param port: The port to check
:returns: Whether the port is supported
Ports supported by this driver have a VNIC type of 'baremetal'.
"""
vnic_type = port[portbindings.VNIC_TYPE]
return vnic_type == portbindings.VNIC_BAREMETAL
device_owner = port[c.DEVICE_OWNER]
return vnic_type in c.SUPPORTED_TYPES and \
device_owner in c.SUPPORTED_OWNERS
@staticmethod
def _is_port_baremetal(port):
"""Return whether a port is for baremetal server.
:param port: The port to check
:returns: Whether the port is for baremetal
"""
device_owner = port[c.DEVICE_OWNER]
return device_owner == c.BAREMETAL_NONE
@staticmethod
def _is_port_normal(port):
"""Return whether a port is for VM server.
:param port: The port to check
:returns: Whether the port is for type normal
"""
device_owner = port[c.DEVICE_OWNER]
return device_owner == c.COMPUTE_NOVA
@staticmethod
def _is_port_bound(port):
"""Return whether a port is bound by this driver.
Ports bound by this driver have their VIF type set to 'other'.
Baremetal ports bound by this driver have a VIF type 'other'.
:param port: The port to check
:returns: Whether the port is bound
@ -623,7 +758,16 @@ class AnsibleMechanismDriver(ml2api.MechanismDriver):
return False
vif_type = port[portbindings.VIF_TYPE]
return vif_type == portbindings.VIF_TYPE_OTHER
# Check type Baremetal bound
if AnsibleMechanismDriver._is_port_baremetal(port):
return vif_type == portbindings.VIF_TYPE_OTHER
# Check type OVS bound
if AnsibleMechanismDriver._is_port_normal(port):
return vif_type == portbindings.VIF_TYPE_OVS
# Default: not bound
return False
@staticmethod
def _get_port_lli(port):


+ 62
- 36
networking_ansible/tests/unit/base.py View File

@ -14,6 +14,7 @@
# under the License.
import pbr
import uuid
from neutron.objects import network
from neutron.objects import ports
@ -26,8 +27,10 @@ from oslotest import base
from tooz import coordination
from unittest import mock
from networking_ansible import constants as c
from networking_ansible import config
from networking_ansible.ml2 import mech_driver
from network_runner.types import validators
QUOTA_REGISTRIES = (
"neutron.quota.resource_registry.unregister_all_resources",
@ -51,6 +54,7 @@ class MockConfig(object):
def __init__(self, host=None, mac=None):
self.inventory = {host: {'mac': mac}} if host and mac else {}
self.mac_map = {}
self.port_mappings = {}
def add_extra_params(self):
for i in self.inventory:
@ -96,25 +100,31 @@ class NetworkingAnsibleTestCase(BaseTestCase):
patch_neutron_quotas()
super(NetworkingAnsibleTestCase, self).setUp()
config_module = 'networking_ansible.ml2.mech_driver.config.Config'
with mock.patch(config_module) as m_cfg:
m_cfg.return_value = self.m_config
self.mech = mech_driver.AnsibleMechanismDriver()
with mock.patch(COORDINATION) as m_coord:
m_coord.get_coordinator = lambda *args: mock.create_autospec(
coordination.CoordinationDriver).return_value
self.mech.initialize()
self.testsegid = '37'
self.testsegid2 = '73'
with mock.patch.object(validators.ChoiceValidator, '__call__',
return_value=None):
with mock.patch(config_module) as m_cfg:
m_cfg.return_value = self.m_config
self.mech = mech_driver.AnsibleMechanismDriver()
with mock.patch(COORDINATION) as m_coord:
m_coord.get_coordinator = \
lambda *args: mock.create_autospec(
coordination.CoordinationDriver
).return_value
self.mech.initialize()
self.testsegid = 37
self.testsegid2 = 73
self.testport = 'switchportid'
self.testid = 'aaaa-bbbb-cccc'
self.testid2 = 'cccc-bbbb-aaaa'
self.testid = uuid.uuid4()
self.testid2 = uuid.uuid4()
self.test_hostid = 'testhostid'
# Define mocked network context
self.mock_net_context = mock.create_autospec(
driver_context.NetworkContext).return_value
self.mock_net_context.current = {
'id': self.testsegid,
'binding:host_id': 'fake-host-id',
provider_net.NETWORK_TYPE: 'vlan',
provider_net.SEGMENTATION_ID: self.testsegid,
provider_net.PHYSICAL_NETWORK: self.testphysnet,
@ -142,6 +152,7 @@ class NetworkingAnsibleTestCase(BaseTestCase):
self.mock_netseg3.physical_network = 'virtual'
self.mock_netseg3.network_type = 'vxlan'
# Binding profile dicts with
# Local Link Information dicts
self.lli_no_mac = {
'local_link_information': [{
@ -153,59 +164,74 @@ class NetworkingAnsibleTestCase(BaseTestCase):
'local_link_information': [{
'switch_id': self.testmac,
'port_id': self.testport,
}]
}
}]
}
# Mocked trunk port objects
self.mock_port_trunk = mock.Mock(spec=trunk.Trunk)
# Mocked trunk port and subport objects
self.mock_trunk = mock.Mock(spec=trunk.Trunk)
self.mock_trunk.network_id = uuid.uuid4()
self.mock_subport_1 = mock.Mock(spec=trunk.SubPort)
self.mock_subport_1.segmentation_id = self.testsegid2
self.mock_port_trunk.sub_ports = [self.mock_subport_1]
self.mock_trunk.sub_ports = [self.mock_subport_1]
# Mocked port objects
self.mock_port = mock.create_autospec(
self.mock_port_bm = mock.create_autospec(
ports.Port).return_value
self.mock_port.network_id = self.testid
self.mock_port.dict = {
self.mock_port_bm.network_id = self.testid
self.mock_port_bm.dict = {
'id': self.testid,
'network_id': uuid.uuid4(),
'mac_address': self.testmac,
c.DEVICE_OWNER: c.BAREMETAL_NONE,
portbindings.VIF_TYPE: portbindings.VIF_TYPE_OTHER,
portbindings.VNIC_TYPE: portbindings.VNIC_BAREMETAL
}
self.mock_port.__getitem__ = mock.Mock(
side_effect=lambda x: self.mock_port.dict[x])
portbindings.VNIC_TYPE: portbindings.VNIC_BAREMETAL,
portbindings.HOST_ID: self.test_hostid
}
self.mock_port_bm.__getitem__ = mock.Mock(
side_effect=lambda x: self.mock_port_bm.dict[x])
self.mock_port2 = mock.create_autospec(
self.mock_port_vm = mock.create_autospec(
ports.Port).return_value
self.mock_port2.network_id = self.testid2
self.mock_port2.dict = {
'id': self.testid,
self.mock_port_vm.network_id = self.testid2
self.mock_port_vm.dict = {
'id': self.testid2,
'network_id': uuid.uuid4(),
'mac_address': self.testmac,
c.DEVICE_OWNER: c.COMPUTE_NOVA,
portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
portbindings.VNIC_TYPE: portbindings.VNIC_NORMAL
portbindings.VNIC_TYPE: portbindings.VNIC_NORMAL,
portbindings.HOST_ID: self.test_hostid
}
self.mock_port2.__getitem__ = mock.Mock(
side_effect=lambda x: self.mock_port2.dict[x])
self.mock_port_vm.__getitem__ = mock.Mock(
side_effect=lambda x: self.mock_port_vm.dict[x])
self.mock_ports = [self.mock_port]
self.mock_ports = [self.mock_port_bm]
# Mocked port bindings
self.mock_portbind = mock.Mock(spec=ports.PortBinding)
self.mock_portbind.profile = self.lli_no_mac
self.mock_portbind.dict = {'host': self.test_hostid}
self.mock_portbind.vnic_type = 'baremetal'
self.mock_portbind.vif_type = 'other'
self.mock_port.bindings = [self.mock_portbind]
self.mock_portbind.__getitem__ = mock.Mock(
side_effect=lambda x: self.mock_portbind.dict[x])
self.mock_port_bm.bindings = [self.mock_portbind]
# define mocked port context
# This isn't a true representation of a real
# port context. The components are just being
# staged for the tests to eventually move things
# around to execute
self.mock_port_context = mock.create_autospec(
driver_context.PortContext).return_value
self.mock_port_context.current = self.mock_port
self.mock_port_context.original = self.mock_port2
self.mock_port_context.current = self.mock_port_bm
self.mock_port_context.original = self.mock_port_vm
self.mock_port_context._plugin_context = mock.MagicMock()
self.mock_port_context.network = mock.Mock()
self.mock_port_context.network.current = {
'id': self.testid,
# TODO(radez) should an int be use here or str ok?
'network_id': self.testid,
provider_net.NETWORK_TYPE: 'vlan',
provider_net.SEGMENTATION_ID: self.testsegid,
provider_net.PHYSICAL_NETWORK: self.testphysnet


+ 274
- 120
networking_ansible/tests/unit/ml2/test_mech_driver.py View File

@ -14,12 +14,15 @@
# under the License.
import contextlib
import fixtures
import tempfile
import webob.exc
from tooz import coordination
from unittest import mock
import fixtures
from network_runner import api
from network_runner.types import validators
from networking_ansible import exceptions as netans_ml2exc
from neutron.common import test_lib
from neutron.objects import network
from neutron.objects import ports
@ -30,12 +33,10 @@ from neutron.tests.unit.plugins.ml2 import test_plugin
from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import provider_net
from neutron_lib.callbacks import resources
from unittest import mock
import webob.exc
from networking_ansible import constants as c
from networking_ansible import exceptions as netans_ml2exc
from networking_ansible.tests.unit import base
from tooz import coordination
class TestLibTestConfigFixture(fixtures.Fixture):
@ -89,17 +90,23 @@ class TestBindPort(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
class TestIsPortSupported(base.NetworkingAnsibleTestCase):
def test_is_port_supported(self):
def test_is_port_supported_baremetal(self):
self.assertTrue(
self.mech._is_port_supported(self.mock_port_bm))
def test_is_port_supported_normal(self):
self.assertTrue(
self.mech._is_port_supported(self.mock_port))
self.mech._is_port_supported(self.mock_port_vm))
def test_is_port_supported_not_baremetal(self):
def test_is_port_supported_invalid(self):
self.mock_port_bm.dict[c.DEVICE_OWNER] = 'invalid'
self.assertFalse(
self.mech._is_port_supported(self.mock_port2))
self.mech._is_port_supported(self.mock_port_bm))
@mock.patch('networking_ansible.ml2.mech_driver.'
@ -108,17 +115,22 @@ class TestIsPortBound(base.NetworkingAnsibleTestCase):
def test_is_port_bound(self, mock_port_supported):
mock_port_supported.return_value = True
self.assertTrue(
self.mech._is_port_bound(self.mock_port))
self.mech._is_port_bound(self.mock_port_bm))
def test_is_port_bound_normal(self, mock_port_supported):
mock_port_supported.return_value = True
self.assertTrue(
self.mech._is_port_bound(self.mock_port_vm))
def test_is_port_bound_not_other(self, mock_port_supported):
self.mock_port.dict['binding:vif_type'] = 'not-other'
self.mock_port_bm.dict['binding:vif_type'] = 'not-other'
self.assertFalse(
self.mech._is_port_bound(self.mock_port))
self.mech._is_port_bound(self.mock_port_bm))
def test_is_port_bound_port_not_supported(self, mock_port_supported):
mock_port_supported.return_value = False
self.assertFalse(
self.mech._is_port_bound(self.mock_port))
self.mech._is_port_bound(self.mock_port_bm))
@mock.patch.object(network.Network, 'get_object')
@ -254,7 +266,9 @@ class TestDeletePortPostCommit(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid,
delete=True)
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._is_port_bound')
@ -330,7 +344,8 @@ class TestUpdatePortPostCommit(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
def test_update_port_postcommit_port_not_bound(self,
mock_ensure_port,
@ -340,58 +355,90 @@ class TestUpdatePortPostCommit(base.NetworkingAnsibleTestCase):
self.mech.update_port_postcommit(self.mock_port_context)
mock_prov_blocks.provisioning_complete.assert_not_called()
def test_update_port_postcommit_port_w_normal_port(self,
mock_ensure_port,
mock_prov_blocks,
mock_port_bound):
mappings = [(self.testhost, self.testport)]
self.m_config.port_mappings = {self.test_hostid: mappings}
self.mock_port_context.current = self.mock_port_context.original
self.mech.update_port_postcommit(self.mock_port_context)
mock_ensure_port.assert_called_once_with(
self.mock_port_context.current,
self.mock_port_context._plugin_context,
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context,
self.testsegid)
class TestLinkInfo(base.NetworkingAnsibleTestCase):
def test_link_info_from_port_no_net(self):
switch_name, switch_port, segmentation_id = \
self.mech._link_info_from_port(self.mock_port)
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, '')
def test_link_info_from_port_net(self):
switch_name, switch_port, segmentation_id = \
self.mech._link_info_from_port(self.mock_port,
self.mock_net_context.current)
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, self.testsegid)
def test_link_info_from_port_context_no_net(self):
switch_name, switch_port, segmentation_id = \
self.mech._link_info_from_port(self.mock_port_context.current)
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, '')
def test_link_info_from_port_context_net(self):
switch_name, switch_port, segmentation_id = \
self.mech._link_info_from_port(self.mock_port_context.current,
self.mock_net_context.current)
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, self.testsegid)
def test_link_info_from_port_context_no_name(self):
self.mock_port_context.current.bindings[0].profile = self.lli_no_info
def test_switch_meta_from_link_info_obj_no_net(self):
mappings, segmentation_id = \
self.mech._switch_meta_from_link_info(self.mock_port_bm)
for switch_name, switch_port in mappings:
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, '')
def test_switch_meta_from_link_info_obj_net(self):
mappings, segmentation_id = \
self.mech._switch_meta_from_link_info(
self.mock_port_bm,
self.mock_net_context.current)
for switch_name, switch_port in mappings:
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, self.testsegid)
def test_switch_meta_from_link_info_context_no_net(self):
mappings, segmentation_id = \
self.mech._switch_meta_from_link_info(
self.mock_port_context.current)
for switch_name, switch_port in mappings:
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, '')
def test_switch_meta_from_link_info_context_net(self):
mappings, segmentation_id = \
self.mech._switch_meta_from_link_info(
self.mock_port_context.current,
self.mock_net_context.current)
for switch_name, switch_port in mappings:
self.assertEqual(switch_name, self.testhost)
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, self.testsegid)
def test_switch_meta_from_link_info_context_no_name(self):
self.mock_port_bm.bindings[0].profile = self.lli_no_info
self.m_config.mac_map = {self.testmac.upper(): self.testhost + '+map'}
switch_name, switch_port, segmentation_id = \
self.mech._link_info_from_port(self.mock_port_context.current)
self.assertEqual(switch_name, self.testhost + '+map')
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, '')
def test_link_info_from_port_context_no_lli(self):
self.mock_port.bindings[0].profile[c.LLI] = {}
mappings, segmentation_id = \
self.mech._switch_meta_from_link_info(
self.mock_port_bm)
for switch_name, switch_port in mappings:
self.assertEqual(switch_name, self.testhost + '+map')
self.assertEqual(switch_port, self.testport)
self.assertEqual(segmentation_id, '')
def test_switch_meta_from_link_info_context_no_lli(self):
self.mock_port_bm.bindings[0].profile[c.LLI] = {}
self.assertRaises(netans_ml2exc.LocalLinkInfoMissingException,
self.mech._link_info_from_port,
self.mock_port)
self.mech._switch_meta_from_link_info,
self.mock_port_bm)
def test_link_info_from_port_port_not_supported(self):
# If this test fails from a missing key in the future
# it's generall safe to just throw the key into this dict
# it's testing a case when the value passed it not a port
# object. So we just port properties into a dict so the
# properties are there but it's not a proper port object
port = {'id': self.testid,
portbindings.VNIC_TYPE: 'not-supported'}
portbindings.VNIC_TYPE: 'not-supported',
c.DEVICE_OWNER: c.BAREMETAL_NONE}
self.assertRaises(netans_ml2exc.LocalLinkInfoMissingException,
self.mech._link_info_from_port,
self.mech._switch_meta_from_link_info,
port)
@ -423,7 +470,7 @@ class TestIsDeletedPortInUse(base.NetworkingAnsibleTestCase):
def test_is_in_use_one_port(self,
mock_net_get_object,
mock_port_get_objects):
mock_port_get_objects.return_value = [self.mock_port]
mock_port_get_objects.return_value = [self.mock_port_bm]
mock_net_get_object.return_value = self.mock_net
self.assertTrue(
self.mech._is_deleted_port_in_use(self.testphysnet, 2, 3))
@ -431,7 +478,7 @@ class TestIsDeletedPortInUse(base.NetworkingAnsibleTestCase):
def test_is_in_use_one_port_virtnet(self,
mock_net_get_object,
mock_port_get_objects):
mock_port_get_objects.return_value = [self.mock_port]
mock_port_get_objects.return_value = [self.mock_port_bm]
mock_net_get_object.return_value = self.mock_net
self.mock_net.segments = [self.mock_netseg3]
self.assertFalse(
@ -440,8 +487,8 @@ class TestIsDeletedPortInUse(base.NetworkingAnsibleTestCase):
def test_is_in_use_two_port_virtnet_and_physnet(self,
mock_net_get_object,
mock_port_get_objects):
mock_port_get_objects.return_value = [self.mock_port,
self.mock_port2]
mock_port_get_objects.return_value = [self.mock_port_bm,
self.mock_port_vm]
self.mock_net.segments = [self.mock_netseg3]
mock_net2 = mock.create_autospec(
@ -455,7 +502,7 @@ class TestIsDeletedPortInUse(base.NetworkingAnsibleTestCase):
def test_is_in_use_one_port_no_net(self,
mock_net_get_object,
mock_port_get_objects):
mock_port_get_objects.return_value = [self.mock_port]
mock_port_get_objects.return_value = [self.mock_port_bm]
mock_net_get_object.return_value = None
self.assertFalse(
self.mech._is_deleted_port_in_use(self.testphysnet, 2, 3))
@ -463,8 +510,8 @@ class TestIsDeletedPortInUse(base.NetworkingAnsibleTestCase):
def test_is_in_use_one_port_no_binding(self,
mock_net_get_object,
mock_port_get_objects):
self.mock_port.bindings.pop()
mock_port_get_objects.return_value = [self.mock_port]
self.mock_port_bm.bindings.pop()
mock_port_get_objects.return_value = [self.mock_port_bm]
mock_net_get_object.return_value = self.mock_net
self.assertFalse(
self.mech._is_deleted_port_in_use(self.testphysnet, 2, 3))
@ -472,8 +519,8 @@ class TestIsDeletedPortInUse(base.NetworkingAnsibleTestCase):
def test_is_in_use_one_port_no_lli(self,
mock_net_get_object,
mock_port_get_objects):
self.mock_port.bindings[0].profile = {}
mock_port_get_objects.return_value = [self.mock_port]
self.mock_port_bm.bindings[0].profile = {}
mock_port_get_objects.return_value = [self.mock_port_bm]
mock_net_get_object.return_value = self.mock_net
self.assertFalse(
self.mech._is_deleted_port_in_use(self.testphysnet, 2, 3))
@ -495,7 +542,8 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._delete_switch_port')
@ -515,7 +563,8 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
mock_delete_port.assert_called_once_with(self.testhost, self.testport)
@mock.patch('networking_ansible.ml2.mech_driver.'
@ -536,7 +585,8 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
mock_delete_port.assert_not_called()
@mock.patch('networking_ansible.ml2.mech_driver.'
@ -546,17 +596,20 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
mock_has_host,
mock_port_get_object,
mock_get_lock):
mock_port_get_object.return_value = self.mock_port
mock_port_get_object.return_value = self.mock_port_bm
self.mech.ensure_port(
self.mock_port_context.current,
self.mock_port_context._plugin_context,
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
mock_set_state.assert_called_once_with(
self.mock_port,
self.mock_port_context._plugin_context)
self.mock_port_bm,
self.mock_port_context._plugin_context,
self.testhost,
self.testport)
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._set_port_state')
@ -565,7 +618,7 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
mock_has_host,
mock_port_get_object,
mock_get_lock):
mock_port_get_object.return_value = self.mock_port
mock_port_get_object.return_value = self.mock_port_bm
mock_set_state.return_value = True
self.mech.ensure_port(
self.mock_port_context.current,
@ -573,10 +626,13 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
mock_set_state.assert_called_once_with(
self.mock_port,
self.mock_port_context._plugin_context)
self.mock_port_bm,
self.mock_port_context._plugin_context,
self.testhost,
self.testport)
self.mock_port_context.set_binding.assert_called_once()
@mock.patch('networking_ansible.ml2.mech_driver.'
@ -586,7 +642,7 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
mock_has_host,
mock_port_get_object,
mock_get_lock):
mock_port_get_object.return_value = self.mock_port
mock_port_get_object.return_value = self.mock_port_bm
mock_set_state.return_value = False
self.mech.ensure_port(
self.mock_port_context.current,
@ -594,12 +650,81 @@ class TestEnsurePort(base.NetworkingAnsibleTestCase):
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_context)
self.mock_port_context,
self.testsegid)
mock_set_state.assert_called_once_with(
self.mock_port,
self.mock_port_context._plugin_context)
self.mock_port_bm,
self.mock_port_context._plugin_context,
self.testhost,
self.testport)
self.mock_port_context.set_binding.assert_not_called()
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._set_port_state')
def test_ensure_port_normal_port_delete_false(self,
mock_set_state,
mock_has_host,
mock_port_get_object,
mock_get_lock):
self.mech.ensure_port(self.mock_port_vm,
self.mock_port_vm,
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_vm,
self.testsegid,
delete=False)
mock_set_state.assert_called_with(self.mock_port_vm,
self.mock_port_vm,
self.testhost,
self.testport)
@mock.patch('network_runner.api.NetworkRunner.delete_trunk_vlan')
@mock.patch.object(ports.Port, 'get_objects')
def test_ensure_port_normal_port_delete_true(self,
mock_get_objects,
mock_delete_vlan,
mock_has_host,
mock_port_get_object,
mock_get_lock):
self.mech.ensure_port(self.mock_port_vm,
self.mock_port_vm,
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_vm,
self.testsegid,
delete=True)
mock_delete_vlan.assert_called_with(self.testhost,
self.testport,
self.testsegid)
@mock.patch('network_runner.api.NetworkRunner.delete_trunk_vlan')
@mock.patch.object(ports.Port, 'get_objects')
def test_ensure_port_no_delete_w_active_ports(self,
mock_get_objects,
mock_delete_vlan,
mock_has_host,
mock_port_get_object,
mock_get_lock):
'''
Check if there are port bindings associated with the same
compute host that the port is being deleted from. If there
are other port bindings on that compute node make sure that
the vlan won't be deleted from the compute node's switchport
so that the other portbindings don't loose connectivity
'''
mock_get_objects.return_value = [self.mock_port_bm]
self.mech.ensure_port(self.mock_port_vm,
self.mock_port_vm,
self.testhost,
self.testport,
self.testphysnet,
self.mock_port_vm,
self.testsegid,
delete=True)
mock_delete_vlan.assert_not_called()
@mock.patch.object(ports.Port, 'get_object')
class TestEnsureSubports(base.NetworkingAnsibleTestCase):
@ -616,17 +741,19 @@ class TestEnsureSubports(base.NetworkingAnsibleTestCase):
def test_ensure_subports_valid(self,
mock_set_state,
mock_port_get_object):
mock_port_get_object.return_value = self.mock_port
mock_port_get_object.return_value = self.mock_port_bm
self.mech.ensure_subports(self.testid, 'testdb')
mock_set_state.assert_called_once_with(self.mock_port,
'testdb')
mock_set_state.assert_called_once_with(self.mock_port_bm,
'testdb',
self.testhost,
self.testport)
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._set_port_state')
def test_ensure_subports_invalid(self,
mock_set_state,
mock_port_get_object):
mock_port_get_object.side_effect = [self.mock_port, None]
mock_port_get_object.side_effect = [self.mock_port_bm, None]
self.mech.ensure_subports(self.testid, 'testdb')
mock_set_state.assert_not_called()
@ -636,53 +763,61 @@ class TestSetPortState(base.NetworkingAnsibleTestCase):
self.assertRaises(ml2_exc.MechanismDriverError,
self.mech._set_port_state,
None,
'db')
'db', self.testhost, self.testport)
@mock.patch.object(network.Network, 'get_object')
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._link_info_from_port')
def test_set_port_state_no_switch_name(self, mock_link):
mock_link.return_value = (None, '123', '')
'AnsibleMechanismDriver._switch_meta_from_link_info')
def test_set_port_state_no_switch_name(self, mock_link, mock_network):
mock_network.return_value = None
mock_link.return_value = ([(None, '123')], '')
self.assertRaises(ml2_exc.MechanismDriverError,
self.mech._set_port_state,
self.mock_port,
'db')
self.mock_port_bm,
'db', self.testhost, self.testport)
@mock.patch.object(network.Network, 'get_object')
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._link_info_from_port')
def test_set_port_state_no_switch_port(self, mock_link):
mock_link.return_value = ('123', None, '')
'AnsibleMechanismDriver._switch_meta_from_link_info')
def test_set_port_state_no_switch_port(self, mock_link, mock_network):
mock_network.return_value = None
mock_link.return_value = ([('123', None)], '')
self.assertRaises(ml2_exc.MechanismDriverError,
self.mech._set_port_state,
self.mock_port,
'db')
self.mock_port_bm,
'db', self.testhost, self.testport)
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._link_info_from_port')
'AnsibleMechanismDriver._switch_meta_from_link_info')
@mock.patch('network_runner.api.NetworkRunner.has_host')
def test_set_port_state_no_inventory_switch(self, mock_host, mock_link):
mock_link.return_value = ('123', '345', '')
mock_link.return_value = ([('123', '345')], '')
mock_host.return_value = False
self.assertRaises(ml2_exc.MechanismDriverError,
self.mech._set_port_state,
self.mock_port,
'db')
self.mock_port_bm,
'db', self.testhost, self.testport)
@mock.patch.object(network.Network, 'get_object')
@mock.patch('networking_ansible.ml2.mech_driver.'
'AnsibleMechanismDriver._link_info_from_port')
def test_set_port_state_no_switch_port_or_name(self, mock_link):
mock_link.return_value = (None, None, '')
'AnsibleMechanismDriver._switch_meta_from_link_info')
def test_set_port_state_no_switch_port_or_name(self,
mock_link,
mock_network):
mock_link.return_value = ([(None, None)], '')
mock_network.return_value = None
self.assertRaises(ml2_exc.MechanismDriverError,
self.mech._set_port_state,
self.mock_port,
'db')
self.mock_port_bm,
'db', self.testhost, self.testport)
@mock.patch.object(network.Network, 'get_object')
def test_set_port_state_no_network(self, mock_network):
mock_network.return_value = None
self.assertRaises(ml2_exc.MechanismDriverError,
self.mech._set_port_state,
self.mock_port,
'db')
self.mock_port_bm,
'db', self.testhost, self.testport)
@mock.patch.object(network.Network, 'get_object')
@mock.patch.object(trunk.Trunk, 'get_object')
@ -690,12 +825,13 @@ class TestSetPortState(base.NetworkingAnsibleTestCase):
@mock.patch.object(api.NetworkRunner, 'conf_trunk_port')
def test_set_port_state_trunk(self,
mock_conf_trunk_port,
mock_port,
mock_port_bm,
mock_trunk,
mock_network):
mock_network.return_value = self.mock_net
mock_trunk.return_value = self.mock_port_trunk
self.mech._set_port_state(self.mock_port, 'db')
mock_trunk.return_value = self.mock_trunk
self.mech._set_port_state(self.mock_port_bm, 'db',
self.testhost, self.testport)
mock_conf_trunk_port.assert_called_once_with(self.testhost,
self.testport,
self.testsegid,
@ -707,12 +843,13 @@ class TestSetPortState(base.NetworkingAnsibleTestCase):
@mock.patch.object(api.NetworkRunner, 'conf_access_port')
def test_set_port_state_access(self,
mock_conf_access_port,
mock_port,
mock_port_bm,
mock_trunk,
mock_network):
mock_network.return_value = self.mock_net
mock_trunk.return_value = None
self.mech._set_port_state(self.mock_port, 'db')
self.mech._set_port_state(self.mock_port_bm, 'db',
self.testhost, self.testport)
mock_conf_access_port.assert_called_once_with(self.testhost,
self.testport,
self.testsegid)
@ -723,17 +860,34 @@ class TestSetPortState(base.NetworkingAnsibleTestCase):
@mock.patch.object(api.NetworkRunner, 'conf_access_port')
def test_set_port_state_access_failure(self,
mock_conf_access_port,
mock_port,
mock_port_bm,
mock_trunk,
mock_network):
mock_network.return_value = self.mock_net
mock_trunk.return_value = None
self.mech._set_port_state(self.mock_port, 'db')
self.mech._set_port_state(self.mock_port_bm, 'db',
self.testhost, self.testport)
mock_conf_access_port.side_effect = Exception()
self.assertRaises(netans_ml2exc.NetworkingAnsibleMechException,
self.mech._set_port_state,
self.mock_port,
'db')
self.mock_port_bm,
'db', self.testhost, self.testport)
@mock.patch.object(network.Network, 'get_object')
@mock.patch.object(trunk.Trunk, 'get_object')
@mock.patch.object(api.NetworkRunner, 'add_trunk_vlan')
def test_set_port_state_normal(self,
mock_add_trunk_vlan,
mock_trunk,
mock_network):
mock_network.return_value = self.mock_net
mock_trunk.return_value = None
self.mech._set_port_state(self.mock_port_vm, 'db',
self.testhost, self.testport)
mock_add_trunk_vlan.assert_called_once_with(self.testhost,
self.testport,
self.testsegid)
@mock.patch.object(api.NetworkRunner, 'create_vlan')
@ -766,7 +920,7 @@ class TestML2PluginIntegration(NetAnsibleML2Base):
BIND_PORT_UPDATE = {
'port': {
'binding:host_id': 'foo',
'binding:vnic_type': portbindings.VNIC_BAREMETAL,
portbindings.VNIC_TYPE: portbindings.VNIC_BAREMETAL,
'binding:profile': {
'local_link_information': LOCAL_LINK_INFORMATION,
},


+ 25
- 5
networking_ansible/tests/unit/test_config.py View File

@ -26,13 +26,23 @@ class MockedConfigParser(mock.Mock):
self.sections = sections
def parse(self):
section_data = {'ansible:testhost': {'mac': ['01:23:45:67:89:ab']}}
if self.conffile == 'foo2':
section_data = {'ansible:port_mappings':
{'localhost': ['testhost::testport']},
'ansible:testhost':
{'mac': ['01:23:45:67:89:ab']}
}