[OVN] Add vnic_type and binding profile capabilities to LSP info
In order to decide if a port is a hardware offloaded port by just reading the OVN Logical_Switch_Port register, it is needed the VNIC type and the binding profile cababilities. The maintenance task will only update those LSP ports that belong to direct Neutron ports to avoid an unnecessary database load. Related-Bug: #1998608 Change-Id: I5febe9d3eeef6c5b5f6d972b9e8ebfef541458ac
This commit is contained in:
parent
b71b25820b
commit
b6750fb2b8
|
@ -26,6 +26,8 @@ OVN_NETWORK_MTU_EXT_ID_KEY = 'neutron:mtu'
|
|||
OVN_PORT_NAME_EXT_ID_KEY = 'neutron:port_name'
|
||||
OVN_PORT_EXT_ID_KEY = 'neutron:port_id'
|
||||
OVN_PORT_FIP_EXT_ID_KEY = 'neutron:port_fip'
|
||||
OVN_PORT_BP_CAPABILITIES_KEY = 'neutron:port_capabilities'
|
||||
OVN_PORT_VNIC_TYPE_KEY = 'neutron:vnic_type'
|
||||
OVN_ROUTER_NAME_EXT_ID_KEY = 'neutron:router_name'
|
||||
OVN_ROUTER_ID_EXT_ID_KEY = 'neutron:router_id'
|
||||
OVN_AZ_HINTS_EXT_ID_KEY = 'neutron:availability_zone_hints'
|
||||
|
|
|
@ -55,6 +55,8 @@ AddrPairsDiff = collections.namedtuple(
|
|||
|
||||
PortExtraDHCPValidation = collections.namedtuple(
|
||||
'PortExtraDHCPValidation', ['failed', 'invalid_ipv4', 'invalid_ipv6'])
|
||||
BPInfo = collections.namedtuple(
|
||||
'BPInfo', ['bp_param', 'vnic_type', 'capabilities'])
|
||||
|
||||
|
||||
class OvsdbClientCommand(object):
|
||||
|
@ -309,10 +311,17 @@ def is_security_groups_enabled(port):
|
|||
|
||||
|
||||
def validate_and_get_data_from_binding_profile(port):
|
||||
"""Validate the port binding profile
|
||||
|
||||
:param port: (dict) Neutron port dictionary.
|
||||
:returns: (namedtuple BPInfo: dict, string, list) a tuple with the
|
||||
dictionary of the port profile, the VNIC type and a list of port
|
||||
capabilities.
|
||||
"""
|
||||
if (constants.OVN_PORT_BINDING_PROFILE not in port or
|
||||
not validators.is_attr_set(
|
||||
port[constants.OVN_PORT_BINDING_PROFILE])):
|
||||
return {}
|
||||
BPInfo({}, None, [])
|
||||
param_set = {}
|
||||
param_dict = {}
|
||||
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
|
||||
|
@ -354,7 +363,7 @@ def validate_and_get_data_from_binding_profile(port):
|
|||
break
|
||||
|
||||
if not param_dict:
|
||||
return {}
|
||||
return BPInfo({}, vnic_type, capabilities)
|
||||
|
||||
# With this example param_set:
|
||||
#
|
||||
|
@ -406,7 +415,7 @@ def validate_and_get_data_from_binding_profile(port):
|
|||
'an integer between 0 and 4095, inclusive') % tag
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
return param_dict
|
||||
return BPInfo(param_dict, vnic_type, capabilities)
|
||||
|
||||
|
||||
def is_dhcp_options_ignored(subnet):
|
||||
|
|
|
@ -120,6 +120,14 @@ class PortBinding(PortBindingBase):
|
|||
cls.db_model.status == constants.INACTIVE)
|
||||
return context.session.query(cls.db_model).filter(_filter).all()
|
||||
|
||||
@classmethod
|
||||
@db_api.CONTEXT_READER
|
||||
def get_port_binding_by_vnic_type(cls, context, vnic_type):
|
||||
"""Returns the port binding filtering by VNIC type."""
|
||||
query = context.session.query(cls.db_model)
|
||||
query = query.filter(cls.db_model.vnic_type == vnic_type)
|
||||
return query.all()
|
||||
|
||||
|
||||
@base.NeutronObjectRegistry.register
|
||||
class DistributedPortBinding(PortBindingBase):
|
||||
|
|
|
@ -28,6 +28,7 @@ from neutron_lib.db import api as db_api
|
|||
from neutron_lib import exceptions as n_exc
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import timeutils
|
||||
from ovsdbapp.backend.ovs_idl import event as row_event
|
||||
|
||||
|
@ -918,6 +919,38 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
|||
|
||||
raise periodics.NeverAgain()
|
||||
|
||||
# TODO(ralonsoh): Remove this in the Antelope+4 cycle
|
||||
@periodics.periodic(spacing=600, run_immediately=True)
|
||||
def add_vnic_type_and_pb_capabilities_to_lsp(self):
|
||||
"""Add the port VNIC type and port binding capabilities to the LSP.
|
||||
|
||||
This is needed to know if a port has hardware offload capabilities.
|
||||
This method is only updating those ports with VNIC type direct, in
|
||||
order to minimize the load impact of this method when updating the OVN
|
||||
database. Within the patch that adds this maintenance method, it has
|
||||
been added to the LSP the VNIC type and the port binding capabilities.
|
||||
To implement LP#1998608, only direct ports are needed.
|
||||
"""
|
||||
if not self.has_lock:
|
||||
return
|
||||
|
||||
port_bindings = ports_obj.PortBinding.get_port_binding_by_vnic_type(
|
||||
n_context.get_admin_context(), portbindings.VNIC_DIRECT)
|
||||
with self._nb_idl.transaction(check_error=True) as txn:
|
||||
for pb in port_bindings:
|
||||
profile = jsonutils.loads(pb.profile)
|
||||
capabilities = profile.get(ovn_const.PORT_CAP_PARAM, [])
|
||||
external_ids = {
|
||||
ovn_const.OVN_PORT_VNIC_TYPE_KEY: portbindings.VNIC_DIRECT,
|
||||
ovn_const.OVN_PORT_BP_CAPABILITIES_KEY:
|
||||
';'.join(capabilities)
|
||||
}
|
||||
txn.add(self._nb_idl.set_lswitch_port(
|
||||
lport_name=pb.port_id, if_exists=True,
|
||||
external_ids=external_ids))
|
||||
|
||||
raise periodics.NeverAgain()
|
||||
|
||||
|
||||
class HashRingHealthCheckPeriodics(object):
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ OvnPortInfo = collections.namedtuple(
|
|||
"security_group_ids",
|
||||
"address4_scope_id",
|
||||
"address6_scope_id",
|
||||
"vnic_type",
|
||||
"capabilities",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -263,18 +265,18 @@ class OVNClient(object):
|
|||
return port_context.host if port_context else port.get(
|
||||
portbindings.HOST_ID, '')
|
||||
|
||||
binding_prof = utils.validate_and_get_data_from_binding_profile(port)
|
||||
if ovn_const.VIF_DETAILS_CARD_SERIAL_NUMBER in binding_prof:
|
||||
bp_info = (
|
||||
utils.validate_and_get_data_from_binding_profile(port))
|
||||
if ovn_const.VIF_DETAILS_CARD_SERIAL_NUMBER in bp_info.bp_param:
|
||||
return self._sb_idl.get_chassis_by_card_serial_from_cms_options(
|
||||
binding_prof[
|
||||
bp_info.bp_param[
|
||||
ovn_const.VIF_DETAILS_CARD_SERIAL_NUMBER]).hostname
|
||||
return ''
|
||||
|
||||
def _get_port_options(self, port):
|
||||
context = n_context.get_admin_context()
|
||||
binding_prof = utils.validate_and_get_data_from_binding_profile(port)
|
||||
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
|
||||
vtep_physical_switch = binding_prof.get('vtep-physical-switch')
|
||||
bp_info = utils.validate_and_get_data_from_binding_profile(port)
|
||||
vtep_physical_switch = bp_info.bp_param.get('vtep-physical-switch')
|
||||
|
||||
port_type = ''
|
||||
cidrs = ''
|
||||
|
@ -283,7 +285,7 @@ class OVNClient(object):
|
|||
dhcpv4_options = self._get_port_dhcp_options(port, const.IP_VERSION_4)
|
||||
dhcpv6_options = self._get_port_dhcp_options(port, const.IP_VERSION_6)
|
||||
if vtep_physical_switch:
|
||||
vtep_logical_switch = binding_prof.get('vtep-logical-switch')
|
||||
vtep_logical_switch = bp_info.bp_param.get('vtep-logical-switch')
|
||||
port_type = 'vtep'
|
||||
options = {'vtep-physical-switch': vtep_physical_switch,
|
||||
'vtep-logical-switch': vtep_logical_switch}
|
||||
|
@ -293,8 +295,8 @@ class OVNClient(object):
|
|||
port_security = []
|
||||
else:
|
||||
options = {}
|
||||
parent_name = binding_prof.get('parent_name', [])
|
||||
tag = binding_prof.get('tag', [])
|
||||
parent_name = bp_info.bp_param.get('parent_name', [])
|
||||
tag = bp_info.bp_param.get('tag', [])
|
||||
address = port['mac_address']
|
||||
|
||||
ip_subnets = port.get('fixed_ips', [])
|
||||
|
@ -394,8 +396,8 @@ class OVNClient(object):
|
|||
# HA Chassis Group will bind the port to the highest
|
||||
# priority Chassis
|
||||
if port_type != ovn_const.LSP_TYPE_EXTERNAL:
|
||||
if (vnic_type == portbindings.VNIC_REMOTE_MANAGED and
|
||||
ovn_const.VIF_DETAILS_PF_MAC_ADDRESS in binding_prof):
|
||||
if (bp_info.vnic_type == portbindings.VNIC_REMOTE_MANAGED and
|
||||
ovn_const.VIF_DETAILS_PF_MAC_ADDRESS in bp_info.bp_param):
|
||||
port_net = self._plugin.get_network(
|
||||
context, port['network_id'])
|
||||
options.update({
|
||||
|
@ -403,10 +405,10 @@ class OVNClient(object):
|
|||
ovn_const.LSP_OPTIONS_VIF_PLUG_MTU_REQUEST_KEY: str(
|
||||
port_net['mtu']),
|
||||
ovn_const.LSP_OPTIONS_VIF_PLUG_REPRESENTOR_PF_MAC_KEY: (
|
||||
binding_prof.get(
|
||||
bp_info.bp_param.get(
|
||||
ovn_const.VIF_DETAILS_PF_MAC_ADDRESS)),
|
||||
ovn_const.LSP_OPTIONS_VIF_PLUG_REPRESENTOR_VF_NUM_KEY: str(
|
||||
binding_prof.get(ovn_const.VIF_DETAILS_VF_NUM))})
|
||||
bp_info.bp_param.get(ovn_const.VIF_DETAILS_VF_NUM))})
|
||||
chassis = self.determine_bind_host(port)
|
||||
if chassis:
|
||||
# If OVN supports multi-chassis port bindings, use it for live
|
||||
|
@ -440,7 +442,8 @@ class OVNClient(object):
|
|||
return OvnPortInfo(port_type, options, addresses, port_security,
|
||||
parent_name, tag, dhcpv4_options, dhcpv6_options,
|
||||
cidrs.strip(), device_owner, sg_ids,
|
||||
address4_scope_id, address6_scope_id
|
||||
address4_scope_id, address6_scope_id,
|
||||
bp_info.vnic_type, bp_info.capabilities
|
||||
)
|
||||
|
||||
def sync_ha_chassis_group(self, context, network_id, txn):
|
||||
|
@ -533,23 +536,25 @@ class OVNClient(object):
|
|||
|
||||
def get_external_ids_from_port(self, port):
|
||||
port_info = self._get_port_options(port)
|
||||
external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name'],
|
||||
ovn_const.OVN_DEVID_EXT_ID_KEY: port['device_id'],
|
||||
ovn_const.OVN_PROJID_EXT_ID_KEY: port['project_id'],
|
||||
ovn_const.OVN_CIDRS_EXT_ID_KEY: port_info.cidrs,
|
||||
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
|
||||
port_info.device_owner,
|
||||
ovn_const.OVN_SUBNET_POOL_EXT_ADDR_SCOPE4_KEY:
|
||||
port_info.address4_scope_id,
|
||||
ovn_const.OVN_SUBNET_POOL_EXT_ADDR_SCOPE6_KEY:
|
||||
port_info.address6_scope_id,
|
||||
ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
|
||||
utils.ovn_name(port['network_id']),
|
||||
ovn_const.OVN_SG_IDS_EXT_ID_KEY:
|
||||
port_info.security_group_ids,
|
||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: str(
|
||||
utils.get_revision_number(
|
||||
port, ovn_const.TYPE_PORTS))}
|
||||
external_ids = {
|
||||
ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name'],
|
||||
ovn_const.OVN_DEVID_EXT_ID_KEY: port['device_id'],
|
||||
ovn_const.OVN_PROJID_EXT_ID_KEY: port['project_id'],
|
||||
ovn_const.OVN_CIDRS_EXT_ID_KEY: port_info.cidrs,
|
||||
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY: port_info.device_owner,
|
||||
ovn_const.OVN_SUBNET_POOL_EXT_ADDR_SCOPE4_KEY:
|
||||
port_info.address4_scope_id,
|
||||
ovn_const.OVN_SUBNET_POOL_EXT_ADDR_SCOPE6_KEY:
|
||||
port_info.address6_scope_id,
|
||||
ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
|
||||
utils.ovn_name(port['network_id']),
|
||||
ovn_const.OVN_SG_IDS_EXT_ID_KEY: port_info.security_group_ids,
|
||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: str(utils.get_revision_number(
|
||||
port, ovn_const.TYPE_PORTS)),
|
||||
ovn_const.OVN_PORT_VNIC_TYPE_KEY: port_info.vnic_type,
|
||||
ovn_const.OVN_PORT_BP_CAPABILITIES_KEY:
|
||||
';'.join(port_info.capabilities),
|
||||
}
|
||||
return port_info, external_ids
|
||||
|
||||
def create_port(self, context, port):
|
||||
|
|
|
@ -588,8 +588,8 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
'parent_name': 'fake-parent-port-uuid',
|
||||
'tag': 42
|
||||
}
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, portbindings.VNIC_NORMAL, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{constants.OVN_PORT_BINDING_PROFILE: expect}))
|
||||
|
||||
|
@ -597,8 +597,8 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
'vtep-physical-switch': 'fake-physical-switch-uuid',
|
||||
'vtep-logical-switch': 'fake-logical-switch-uuid',
|
||||
}
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, portbindings.VNIC_NORMAL, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{constants.OVN_PORT_BINDING_PROFILE: expect}))
|
||||
|
||||
|
@ -611,8 +611,9 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
}
|
||||
expect = binding_profile.copy()
|
||||
del(expect[constants.PORT_CAP_PARAM])
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, portbindings.VNIC_DIRECT,
|
||||
[constants.PORT_CAP_SWITCHDEV]),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
|
@ -626,8 +627,9 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
}
|
||||
expect = binding_profile.copy()
|
||||
del(expect[constants.PORT_CAP_PARAM])
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, portbindings.VNIC_DIRECT,
|
||||
[constants.PORT_CAP_SWITCHDEV]),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
|
@ -640,11 +642,11 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
'pf_mac_address': '00:53:00:00:00:42',
|
||||
'vf_num': 42,
|
||||
}
|
||||
self.assertDictEqual(
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, portbindings.VNIC_REMOTE_MANAGED, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: portbindings.VNIC_REMOTE_MANAGED,
|
||||
constants.OVN_PORT_BINDING_PROFILE: expect}),
|
||||
expect)
|
||||
constants.OVN_PORT_BINDING_PROFILE: expect}))
|
||||
|
||||
def test_valid_input_surplus_keys(self):
|
||||
# Confirm that extra keys are allowed
|
||||
|
@ -658,8 +660,9 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
expect = binding_profile.copy()
|
||||
del(expect[constants.PORT_CAP_PARAM])
|
||||
del(expect['optional_information_provided_by_nova'])
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, portbindings.VNIC_DIRECT,
|
||||
[constants.PORT_CAP_SWITCHDEV]),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
|
@ -667,7 +670,7 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
def test_unknown_profile_items_pruned(self):
|
||||
# Confirm that unknown profile items are pruned
|
||||
self.assertEqual(
|
||||
{},
|
||||
utils.BPInfo({}, portbindings.VNIC_NORMAL, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{constants.OVN_PORT_BINDING_PROFILE: {
|
||||
'unknown-key': 'unknown-data'}}))
|
||||
|
@ -676,16 +679,16 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
expect = {
|
||||
'key': 'value',
|
||||
}
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, self.VNIC_FAKE_NORMAL, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: self.VNIC_FAKE_NORMAL,
|
||||
constants.OVN_PORT_BINDING_PROFILE: expect}))
|
||||
expect = {
|
||||
'key': None,
|
||||
}
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, self.VNIC_FAKE_NORMAL, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: self.VNIC_FAKE_NORMAL,
|
||||
constants.OVN_PORT_BINDING_PROFILE: expect}))
|
||||
|
@ -706,17 +709,17 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
'other_key': 'value',
|
||||
}
|
||||
# This param set is valid for VNIC_FAKE_NORMAL with 'other_key' pruned.
|
||||
expect = binding_profile.copy()
|
||||
del(expect['other_key'])
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
expected_bp = binding_profile.copy()
|
||||
del(expected_bp['other_key'])
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expected_bp, self.VNIC_FAKE_NORMAL, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: self.VNIC_FAKE_NORMAL,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
# It is valid for VNIC_FAKE_OTHER
|
||||
expect = binding_profile.copy()
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
expected_bp = binding_profile.copy()
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expected_bp, self.VNIC_FAKE_OTHER, []),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
|
@ -744,7 +747,7 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
'third_key': 'value',
|
||||
}
|
||||
self.assertEqual(
|
||||
{},
|
||||
utils.BPInfo({}, self.VNIC_FAKE_OTHER, ['fake-capability']),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
|
@ -757,8 +760,9 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
}
|
||||
expect = binding_profile.copy()
|
||||
del(expect[constants.PORT_CAP_PARAM])
|
||||
self.assertDictEqual(
|
||||
expect,
|
||||
self.assertEqual(
|
||||
utils.BPInfo(expect, self.VNIC_FAKE_OTHER,
|
||||
[constants.PORT_CAP_SWITCHDEV]),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
|
@ -775,7 +779,8 @@ class TestValidateAndGetDataFromBindingProfile(base.BaseTestCase):
|
|||
constants.PORT_CAP_PARAM: [constants.PORT_CAP_SWITCHDEV],
|
||||
}
|
||||
self.assertEqual(
|
||||
{},
|
||||
utils.BPInfo({}, self.VNIC_FAKE_OTHER,
|
||||
[constants.PORT_CAP_SWITCHDEV]),
|
||||
utils.validate_and_get_data_from_binding_profile(
|
||||
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
|
||||
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
|
||||
|
|
|
@ -71,6 +71,23 @@ class PortBindingDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|||
# The PB register returned is the INACTIVE one.
|
||||
self.assertEqual(self.obj_fields[1]['host'], dup_pb[0].host)
|
||||
|
||||
def test_get_port_binding_by_vnic_type(self):
|
||||
self.update_obj_fields({'vnic_type': portbindings.VNIC_NORMAL},
|
||||
obj_fields=[self.obj_fields[0]])
|
||||
self.update_obj_fields({'vnic_type': portbindings.VNIC_DIRECT},
|
||||
obj_fields=[self.obj_fields[1],
|
||||
self.obj_fields[2]])
|
||||
for i in range(3):
|
||||
_obj = self._make_object(self.obj_fields[i])
|
||||
_obj.create()
|
||||
|
||||
for vnic_type, pb_num in [(portbindings.VNIC_NORMAL, 1),
|
||||
(portbindings.VNIC_DIRECT, 2),
|
||||
(portbindings.VNIC_MACVTAP, 0)]:
|
||||
pb = ports.PortBinding.get_port_binding_by_vnic_type(self.context,
|
||||
vnic_type)
|
||||
self.assertEqual(pb_num, len(pb))
|
||||
|
||||
|
||||
class DistributedPortBindingIfaceObjTestCase(
|
||||
obj_test_base.BaseObjectIfaceTestCase):
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
from unittest import mock
|
||||
|
||||
from futurist import periodics
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib import constants as n_const
|
||||
from neutron_lib import context
|
||||
from neutron_lib.db import api as db_api
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.common.ovn import constants
|
||||
|
@ -27,6 +29,7 @@ from neutron.common.ovn import utils
|
|||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||
from neutron.db.models import ovn as ovn_models
|
||||
from neutron.db import ovn_revision_numbers_db
|
||||
from neutron.objects import ports as ports_obj
|
||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
|
||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_db_sync
|
||||
from neutron.tests.unit import fake_resources as fakes
|
||||
|
@ -831,3 +834,23 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
|||
self.assertEqual(
|
||||
2,
|
||||
nb_idl.delete_static_route.call_count)
|
||||
|
||||
@mock.patch.object(ports_obj.PortBinding, 'get_port_binding_by_vnic_type')
|
||||
def test_add_vnic_type_and_pb_capabilities_to_lsp(self, mock_get_pb):
|
||||
nb_idl = self.fake_ovn_client._nb_idl
|
||||
profile = {'capabilities': ['switchdev']}
|
||||
pb1 = mock.Mock(profile=jsonutils.dumps(profile), port_id='port1')
|
||||
pb2 = mock.Mock(profile=jsonutils.dumps(profile), port_id='port2')
|
||||
mock_get_pb.return_value = [pb1, pb2]
|
||||
|
||||
self.assertRaises(
|
||||
periodics.NeverAgain,
|
||||
self.periodic.add_vnic_type_and_pb_capabilities_to_lsp)
|
||||
external_ids = {
|
||||
constants.OVN_PORT_VNIC_TYPE_KEY: portbindings.VNIC_DIRECT,
|
||||
constants.OVN_PORT_BP_CAPABILITIES_KEY: 'switchdev'}
|
||||
expected_calls = [mock.call(lport_name='port1', if_exists=True,
|
||||
external_ids=external_ids),
|
||||
mock.call(lport_name='port2', if_exists=True,
|
||||
external_ids=external_ids)]
|
||||
nb_idl.set_lswitch_port.assert_has_calls(expected_calls)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
from unittest import mock
|
||||
|
||||
from neutron.common.ovn import constants
|
||||
from neutron.conf.plugins.ml2 import config as ml2_conf
|
||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
|
||||
from neutron.tests import base
|
||||
|
@ -28,6 +29,7 @@ from neutron_lib import constants as const
|
|||
class TestOVNClientBase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
ml2_conf.register_ml2_plugin_opts()
|
||||
ovn_conf.register_opts()
|
||||
super(TestOVNClientBase, self).setUp()
|
||||
self.nb_idl = mock.MagicMock()
|
||||
|
@ -133,6 +135,7 @@ class TestOVNClientDetermineBindHost(TestOVNClientBase):
|
|||
def test_vnic_remote_managed_unbound_port_no_binding_profile(self):
|
||||
port = {
|
||||
portbindings.VNIC_TYPE: portbindings.VNIC_REMOTE_MANAGED,
|
||||
constants.OVN_PORT_BINDING_PROFILE: {},
|
||||
}
|
||||
self.assertEqual(
|
||||
'',
|
||||
|
|
|
@ -1800,7 +1800,9 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
|||
'ip_address': '10.0.0.55'},
|
||||
{'subnet_id': 'subnet-2',
|
||||
'ip_address': '10.0.1.55'},
|
||||
]}
|
||||
],
|
||||
portbindings.PROFILE: {},
|
||||
}
|
||||
subnet_ids = [
|
||||
ip['subnet_id']
|
||||
for ip in port.get('fixed_ips')
|
||||
|
@ -1826,6 +1828,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
|||
{"subnet_id": "subnet-1", "ip_address": "10.0.0.55"},
|
||||
{"subnet_id": "subnet-2", "ip_address": "aef0::4"},
|
||||
],
|
||||
portbindings.PROFILE: {},
|
||||
}
|
||||
|
||||
subnet_ids = [ip["subnet_id"] for ip in port.get("fixed_ips")]
|
||||
|
@ -1910,6 +1913,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
|||
'network_id': 'foo',
|
||||
'fixed_ips': [],
|
||||
portbindings.HOST_ID: 'fake-src',
|
||||
portbindings.PROFILE: {},
|
||||
}
|
||||
with mock.patch.object(
|
||||
self.mech_driver._ovn_client._sb_idl, 'is_col_present',
|
||||
|
@ -1942,7 +1946,9 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
|||
'ip_address': '10.0.0.55'},
|
||||
{'subnet_id': 'subnet-2',
|
||||
'ip_address': '10.0.1.55'},
|
||||
]}
|
||||
],
|
||||
portbindings.PROFILE: {},
|
||||
}
|
||||
subnet_ids = [
|
||||
ip['subnet_id']
|
||||
for ip in port.get('fixed_ips')
|
||||
|
@ -4292,7 +4298,9 @@ class TestOVNVVirtualPort(OVNMechanismDriverTestCase):
|
|||
'device_owner': device_owner,
|
||||
'network_id': self.net['id'],
|
||||
'fixed_ips': [{'subnet_id': self.subnet['id'],
|
||||
'ip_address': '10.0.0.55'}]}
|
||||
'ip_address': '10.0.0.55'}],
|
||||
portbindings.PROFILE: {},
|
||||
}
|
||||
port_info = self.mech_driver._ovn_client._get_port_options(port)
|
||||
self.assertEqual(ovn_const.LSP_TYPE_VIRTUAL, port_info.type)
|
||||
self.assertEqual(
|
||||
|
|
Loading…
Reference in New Issue