Router flavors and service type for OVN
Support is added to the OVN L3 service plugin for the router flavors and service type framework Partial-Bug: #2020823 Change-Id: If40d7b39e7b59a39ff7622bd823dbdb14bfc69d2
This commit is contained in:
parent
b28bf2d3a1
commit
49366ecada
|
@ -0,0 +1,148 @@
|
||||||
|
.. _config-router-flavor-ovn:
|
||||||
|
|
||||||
|
===================================================
|
||||||
|
Creating a L3 OVN router with a user-defined flavor
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
In this section we describe the steps necessary to create a router with a user
|
||||||
|
defined flavor.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The following example refers to a dummy user-defined service provider,
|
||||||
|
which in a real situation must be replaced with user provided code.
|
||||||
|
|
||||||
|
#. Add the service provider to neutron.conf:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
[service_providers]
|
||||||
|
service_provider = L3_ROUTER_NAT:user-defined:neutron.services.ovn_l3.service_providers.user_defined.UserDefined
|
||||||
|
|
||||||
|
#. Re-start the neutron server and verify the user-defined provider has been
|
||||||
|
loaded:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack network service provider list
|
||||||
|
+---------------+--------------+---------+
|
||||||
|
| Service Type | Name | Default |
|
||||||
|
+---------------+--------------+---------+
|
||||||
|
| L3_ROUTER_NAT | user-defined | False |
|
||||||
|
| L3_ROUTER_NAT | ovn | True |
|
||||||
|
+---------------+--------------+---------+
|
||||||
|
|
||||||
|
#. Create a service profile for the router flavor:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack network flavor profile create --description "User-defined router flavor profile" --enable --driver neutron.services.ovn_l3.service_providers.user_defined.UserDefined
|
||||||
|
+-------------+--------------------------------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+-------------+--------------------------------------------------------------------+
|
||||||
|
| description | User-defined router flavor profile |
|
||||||
|
| driver | neutron.services.ovn_l3.service_providers.user_defined.UserDefined |
|
||||||
|
| enabled | True |
|
||||||
|
| id | a717c92c-63f7-47e8-9efb-6ad0d61c4875 |
|
||||||
|
| meta_info | |
|
||||||
|
| project_id | None |
|
||||||
|
+-------------+--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#. Create the router flavor:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack network flavor create --service-type L3_ROUTER_NAT --description "User-defined flavor for routers in the L3 OVN plugin" user-defined-router-flavor
|
||||||
|
+---------------------+------------------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+---------------------+------------------------------------------------------+
|
||||||
|
| description | User-defined flavor for routers in the L3 OVN plugin |
|
||||||
|
| enabled | True |
|
||||||
|
| id | e47c1c5c-629b-4c48-b49a-78abe6ac7696 |
|
||||||
|
| name | user-defined-router-flavor |
|
||||||
|
| service_profile_ids | [] |
|
||||||
|
| service_type | L3_ROUTER_NAT |
|
||||||
|
+---------------------+------------------------------------------------------+
|
||||||
|
|
||||||
|
#. Add service profile to router flavor:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack network flavor add profile user-defined-router-flavor a717c92c-63f7-47e8-9efb-6ad0d61c4875
|
||||||
|
|
||||||
|
#. Create router specifying user-defined flavor:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack router create router-of-user-defined-flavor --external-gateway public --flavor-id e47c1c5c-629b-4c48-b49a-78abe6ac7696 --max-width 100
|
||||||
|
+-------------------------+------------------------------------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+-------------------------+------------------------------------------------------------------------+
|
||||||
|
| admin_state_up | UP |
|
||||||
|
| availability_zone_hints | |
|
||||||
|
| availability_zones | |
|
||||||
|
| created_at | 2023-05-25T22:34:16Z |
|
||||||
|
| description | |
|
||||||
|
| enable_ndp_proxy | None |
|
||||||
|
| external_gateway_info | {"network_id": "ba485dc9-2459-41c1-9d4f-71914a7fba2a", |
|
||||||
|
| | "external_fixed_ips": [{"subnet_id": |
|
||||||
|
| | "2e3adb94-c544-4916-a9fb-27a9dea21820", "ip_address": "172.24.8.69"}, |
|
||||||
|
| | {"subnet_id": "996ed143-917b-4783-8349-03c6a6d9603e", "ip_address": |
|
||||||
|
| | "2001:db8::261"}], "enable_snat": true} |
|
||||||
|
| flavor_id | e47c1c5c-629b-4c48-b49a-78abe6ac7696 |
|
||||||
|
| id | 9f5fec56-1829-4bad-abe5-7b4221649c8e |
|
||||||
|
| name | router-of-user-defined-flavor |
|
||||||
|
| project_id | b807321af03f44dc808ff06bbc845804 |
|
||||||
|
| revision_number | 3 |
|
||||||
|
| routes | |
|
||||||
|
| status | ACTIVE |
|
||||||
|
| tags | |
|
||||||
|
| tenant_id | b807321af03f44dc808ff06bbc845804 |
|
||||||
|
| updated_at | 2023-05-25T22:34:16Z |
|
||||||
|
+-------------------------+------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
#. Create an OVN flavor router to verify they co-exist with the user-defined
|
||||||
|
flavor:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack router create ovn-flavor-router --external-gateway public --max-width 100
|
||||||
|
+-------------------------+------------------------------------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+-------------------------+------------------------------------------------------------------------+
|
||||||
|
| admin_state_up | UP |
|
||||||
|
| availability_zone_hints | |
|
||||||
|
| availability_zones | |
|
||||||
|
| created_at | 2023-05-25T23:34:20Z |
|
||||||
|
| description | |
|
||||||
|
| enable_ndp_proxy | None |
|
||||||
|
| external_gateway_info | {"network_id": "ba485dc9-2459-41c1-9d4f-71914a7fba2a", |
|
||||||
|
| | "external_fixed_ips": [{"subnet_id": |
|
||||||
|
| | "2e3adb94-c544-4916-a9fb-27a9dea21820", "ip_address": "172.24.8.195"}, |
|
||||||
|
| | {"subnet_id": "996ed143-917b-4783-8349-03c6a6d9603e", "ip_address": |
|
||||||
|
| | "2001:db8::263"}], "enable_snat": true} |
|
||||||
|
| flavor_id | None |
|
||||||
|
| id | 21889ed3-b8df-4b0e-9a64-92ba9fab655d |
|
||||||
|
| name | ovn-flavor-router |
|
||||||
|
| project_id | b807321af03f44dc808ff06bbc845804 |
|
||||||
|
| revision_number | 3 |
|
||||||
|
| routes | |
|
||||||
|
| status | ACTIVE |
|
||||||
|
| tags | |
|
||||||
|
| tenant_id | e6d6b109d16b4e5e857a10034f4ba558 |
|
||||||
|
| updated_at | 2023-07-20T23:34:21Z |
|
||||||
|
+-------------------------+------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
#. List routers to verify:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack router list
|
||||||
|
+--------------------------------------+-------------------------------+--------+-------+----------------------------------+
|
||||||
|
| ID | Name | Status | State | Project |
|
||||||
|
+--------------------------------------+-------------------------------+--------+-------+----------------------------------+
|
||||||
|
| 21889ed3-b8df-4b0e-9a64-92ba9fab655d | ovn-flavor-router | ACTIVE | UP | b807321af03f44dc808ff06bbc845804 |
|
||||||
|
| 9f5fec56-1829-4bad-abe5-7b4221649c8e | router-of-user-defined-flavor | ACTIVE | UP | b807321af03f44dc808ff06bbc845804 |
|
||||||
|
| e9f25566-ff73-4a76-aeb4-969c819f9c47 | router1 | ACTIVE | UP | 1bf97e3957654c0182a48727d619e00f |
|
||||||
|
+--------------------------------------+-------------------------------+--------+-------+----------------------------------+
|
|
@ -38,6 +38,7 @@ Configuration
|
||||||
config-qos-min-pps
|
config-qos-min-pps
|
||||||
config-rbac
|
config-rbac
|
||||||
config-routed-networks
|
config-routed-networks
|
||||||
|
config-router-flavor-ovn
|
||||||
config-sriov
|
config-sriov
|
||||||
config-sfc
|
config-sfc
|
||||||
config-service-subnets
|
config-service-subnets
|
||||||
|
|
|
@ -36,10 +36,12 @@ from neutron_lib.api.definitions import fip_pf_port_range
|
||||||
from neutron_lib.api.definitions import fip_port_details
|
from neutron_lib.api.definitions import fip_port_details
|
||||||
from neutron_lib.api.definitions import firewall_v2
|
from neutron_lib.api.definitions import firewall_v2
|
||||||
from neutron_lib.api.definitions import firewall_v2_stdattrs
|
from neutron_lib.api.definitions import firewall_v2_stdattrs
|
||||||
|
from neutron_lib.api.definitions import flavors
|
||||||
from neutron_lib.api.definitions import floating_ip_port_forwarding
|
from neutron_lib.api.definitions import floating_ip_port_forwarding
|
||||||
from neutron_lib.api.definitions import floatingip_pools
|
from neutron_lib.api.definitions import floatingip_pools
|
||||||
from neutron_lib.api.definitions import l3
|
from neutron_lib.api.definitions import l3
|
||||||
from neutron_lib.api.definitions import l3_ext_gw_mode
|
from neutron_lib.api.definitions import l3_ext_gw_mode
|
||||||
|
from neutron_lib.api.definitions import l3_flavors
|
||||||
from neutron_lib.api.definitions import logging
|
from neutron_lib.api.definitions import logging
|
||||||
from neutron_lib.api.definitions import multiprovidernet
|
from neutron_lib.api.definitions import multiprovidernet
|
||||||
from neutron_lib.api.definitions import network_availability_zone
|
from neutron_lib.api.definitions import network_availability_zone
|
||||||
|
@ -114,6 +116,8 @@ ML2_SUPPORTED_API_EXTENSIONS_OVN_L3 = [
|
||||||
agent_def.ALIAS,
|
agent_def.ALIAS,
|
||||||
az_def.ALIAS,
|
az_def.ALIAS,
|
||||||
raz_def.ALIAS,
|
raz_def.ALIAS,
|
||||||
|
flavors.ALIAS,
|
||||||
|
l3_flavors.ALIAS,
|
||||||
]
|
]
|
||||||
ML2_SUPPORTED_API_EXTENSIONS = [
|
ML2_SUPPORTED_API_EXTENSIONS = [
|
||||||
address_group.ALIAS,
|
address_group.ALIAS,
|
||||||
|
|
|
@ -1215,3 +1215,8 @@ def is_additional_chassis_supported(idl):
|
||||||
|
|
||||||
def is_nat_gateway_port_supported(idl):
|
def is_nat_gateway_port_supported(idl):
|
||||||
return idl.is_col_present('NAT', 'gateway_port')
|
return idl.is_col_present('NAT', 'gateway_port')
|
||||||
|
|
||||||
|
|
||||||
|
def is_ovn_provider_router(router):
|
||||||
|
flavor_id = router.get('flavor_id')
|
||||||
|
return flavor_id is None or flavor_id is const.ATTR_NOT_SPECIFIED
|
||||||
|
|
|
@ -1216,20 +1216,21 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
||||||
gw_ips = [x['ip_address'] for x in router.gw_port.fixed_ips]
|
gw_ips = [x['ip_address'] for x in router.gw_port.fixed_ips]
|
||||||
|
|
||||||
cidrs = [x['cidr'] for x in subnets]
|
cidrs = [x['cidr'] for x in subnets]
|
||||||
|
subnet_ids = [subnet['id'] for subnet in subnets]
|
||||||
metadata = {'interface_info': interface_info,
|
metadata = {'interface_info': interface_info,
|
||||||
'port': port, 'gateway_ips': gw_ips,
|
'port': port, 'gateway_ips': gw_ips,
|
||||||
'network_id': gw_network_id, 'cidrs': cidrs}
|
'network_id': gw_network_id, 'cidrs': cidrs,
|
||||||
|
'subnet_ids': subnet_ids}
|
||||||
registry.publish(resources.ROUTER_INTERFACE,
|
registry.publish(resources.ROUTER_INTERFACE,
|
||||||
events.AFTER_DELETE, self,
|
events.AFTER_DELETE, self,
|
||||||
payload=events.DBEventPayload(
|
payload=events.DBEventPayload(
|
||||||
context, metadata=metadata,
|
context, metadata=metadata,
|
||||||
resource_id=router_id))
|
resource_id=router_id,
|
||||||
|
states=(router,)))
|
||||||
|
|
||||||
return self._make_router_interface_info(router_id, port['tenant_id'],
|
return self._make_router_interface_info(router_id, port['tenant_id'],
|
||||||
port['id'], port['network_id'],
|
port['id'], port['network_id'],
|
||||||
subnets[0]['id'],
|
subnets[0]['id'], subnet_ids)
|
||||||
[subnet['id'] for subnet in
|
|
||||||
subnets])
|
|
||||||
|
|
||||||
def _get_floatingip(self, context, id):
|
def _get_floatingip(self, context, id):
|
||||||
floatingip = l3_obj.FloatingIP.get_object(context, id=id)
|
floatingip = l3_obj.FloatingIP.get_object(context, id=id)
|
||||||
|
@ -1571,7 +1572,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
||||||
payload=events.DBEventPayload(
|
payload=events.DBEventPayload(
|
||||||
context, states=(floatingip_dict,),
|
context, states=(floatingip_dict,),
|
||||||
resource_id=floatingip_obj.id,
|
resource_id=floatingip_obj.id,
|
||||||
metadata={'association_event': assoc_result}))
|
metadata={'association_event': assoc_result},
|
||||||
|
request_body=floatingip))
|
||||||
if assoc_result:
|
if assoc_result:
|
||||||
LOG.info(FIP_ASSOC_MSG,
|
LOG.info(FIP_ASSOC_MSG,
|
||||||
{'fip_id': floatingip_obj.id,
|
{'fip_id': floatingip_obj.id,
|
||||||
|
@ -1637,7 +1639,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
||||||
payload=events.DBEventPayload(
|
payload=events.DBEventPayload(
|
||||||
context, states=(old_floatingip, floatingip_dict),
|
context, states=(old_floatingip, floatingip_dict),
|
||||||
resource_id=floatingip_obj.id,
|
resource_id=floatingip_obj.id,
|
||||||
metadata={'association_event': assoc_result}))
|
metadata={'association_event': assoc_result},
|
||||||
|
request_body=floatingip))
|
||||||
if assoc_result is not None:
|
if assoc_result is not None:
|
||||||
port_id = old_fixed_port_id or floatingip_obj.fixed_port_id
|
port_id = old_fixed_port_id or floatingip_obj.fixed_port_id
|
||||||
assoc = 'associated' if assoc_result else 'disassociated'
|
assoc = 'associated' if assoc_result else 'disassociated'
|
||||||
|
|
|
@ -57,6 +57,7 @@ from neutron.db import ovn_hash_ring_db
|
||||||
from neutron.db import ovn_revision_numbers_db
|
from neutron.db import ovn_revision_numbers_db
|
||||||
from neutron.db import provisioning_blocks
|
from neutron.db import provisioning_blocks
|
||||||
from neutron.extensions import securitygroup as ext_sg
|
from neutron.extensions import securitygroup as ext_sg
|
||||||
|
from neutron.objects import router
|
||||||
from neutron.plugins.ml2 import db as ml2_db
|
from neutron.plugins.ml2 import db as ml2_db
|
||||||
from neutron.plugins.ml2.drivers.ovn.agent import neutron_agent as n_agent
|
from neutron.plugins.ml2.drivers.ovn.agent import neutron_agent as n_agent
|
||||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.extensions \
|
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.extensions \
|
||||||
|
@ -689,12 +690,18 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||||
|
|
||||||
# in the case of router ports we also need to
|
# in the case of router ports we also need to
|
||||||
# track the creation and update of the LRP OVN objects
|
# track the creation and update of the LRP OVN objects
|
||||||
if ovn_utils.is_lsp_router_port(port):
|
if (ovn_utils.is_lsp_router_port(port) and
|
||||||
|
self._is_ovn_router_flavor_port(context, port)):
|
||||||
ovn_revision_numbers_db.create_initial_revision(
|
ovn_revision_numbers_db.create_initial_revision(
|
||||||
context.plugin_context, port['id'],
|
context.plugin_context, port['id'],
|
||||||
ovn_const.TYPE_ROUTER_PORTS,
|
ovn_const.TYPE_ROUTER_PORTS,
|
||||||
std_attr_id=context.current['standard_attr_id'])
|
std_attr_id=context.current['standard_attr_id'])
|
||||||
|
|
||||||
|
def _is_ovn_router_flavor_port(self, context, port):
|
||||||
|
router_obj = router.Router.get_object(context.plugin_context,
|
||||||
|
id=port['device_id'])
|
||||||
|
return ovn_utils.is_ovn_provider_router(router_obj)
|
||||||
|
|
||||||
def _is_port_provisioning_required(self, port, host, original_host=None):
|
def _is_port_provisioning_required(self, port, host, original_host=None):
|
||||||
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
|
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
|
||||||
if vnic_type not in self.supported_vnic_types:
|
if vnic_type not in self.supported_vnic_types:
|
||||||
|
@ -829,7 +836,8 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||||
self._insert_port_provisioning_block(context.plugin_context,
|
self._insert_port_provisioning_block(context.plugin_context,
|
||||||
port['id'])
|
port['id'])
|
||||||
|
|
||||||
if ovn_utils.is_lsp_router_port(port):
|
if (ovn_utils.is_lsp_router_port(port) and
|
||||||
|
self._is_ovn_router_flavor_port(context, port)):
|
||||||
# handle the case when an existing port is added to a
|
# handle the case when an existing port is added to a
|
||||||
# logical router so we need to track the creation of the lrp
|
# logical router so we need to track the creation of the lrp
|
||||||
if not ovn_utils.is_lsp_router_port(original_port):
|
if not ovn_utils.is_lsp_router_port(original_port):
|
||||||
|
|
|
@ -391,7 +391,7 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
||||||
def _create_lrouter_port(self, context, port):
|
def _create_lrouter_port(self, context, port):
|
||||||
router_id = port['device_id']
|
router_id = port['device_id']
|
||||||
iface_info = self._ovn_client._l3_plugin._add_neutron_router_interface(
|
iface_info = self._ovn_client._l3_plugin._add_neutron_router_interface(
|
||||||
context, router_id, {'port_id': port['id']}, may_exist=True)
|
context, router_id, {'port_id': port['id']})
|
||||||
self._ovn_client.create_router_port(context, router_id, iface_info)
|
self._ovn_client.create_router_port(context, router_id, iface_info)
|
||||||
|
|
||||||
def _check_subnet_global_dhcp_opts(self):
|
def _check_subnet_global_dhcp_opts(self):
|
||||||
|
|
|
@ -51,6 +51,7 @@ from neutron.conf.agent import ovs_conf
|
||||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||||
from neutron.db import ovn_revision_numbers_db as db_rev
|
from neutron.db import ovn_revision_numbers_db as db_rev
|
||||||
from neutron.db import segments_db
|
from neutron.db import segments_db
|
||||||
|
from neutron.objects import router
|
||||||
from neutron.plugins.ml2 import db as ml2_db
|
from neutron.plugins.ml2 import db as ml2_db
|
||||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.extensions \
|
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.extensions \
|
||||||
import placement as placement_extension
|
import placement as placement_extension
|
||||||
|
@ -646,7 +647,10 @@ class OVNClient(object):
|
||||||
# LogicalSwitchPortUpdateDownEvent, that will most likely
|
# LogicalSwitchPortUpdateDownEvent, that will most likely
|
||||||
# cause a revision conflict.
|
# cause a revision conflict.
|
||||||
# https://bugs.launchpad.net/neutron/+bug/1955578
|
# https://bugs.launchpad.net/neutron/+bug/1955578
|
||||||
columns_dict['type'] = ovn_const.LSP_TYPE_ROUTER
|
router_obj = router.Router.get_object(context,
|
||||||
|
id=port['device_id'])
|
||||||
|
if utils.is_ovn_provider_router(router_obj):
|
||||||
|
columns_dict['type'] = ovn_const.LSP_TYPE_ROUTER
|
||||||
port_info.options.update(
|
port_info.options.update(
|
||||||
self._nb_idl.get_router_port_options(port['id']))
|
self._nb_idl.get_router_port_options(port['id']))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -87,7 +87,7 @@ class DriverController(object):
|
||||||
router = payload.latest_state
|
router = payload.latest_state
|
||||||
router_db = payload.metadata['router_db']
|
router_db = payload.metadata['router_db']
|
||||||
router_id = payload.resource_id
|
router_id = payload.resource_id
|
||||||
if _flavor_specified(router):
|
if flavor_specified(router):
|
||||||
router_db.flavor_id = router['flavor_id']
|
router_db.flavor_id = router['flavor_id']
|
||||||
drv = self._get_provider_for_create(context, router)
|
drv = self._get_provider_for_create(context, router)
|
||||||
self._stm.add_resource_association(context, plugin_constants.L3,
|
self._stm.add_resource_association(context, plugin_constants.L3,
|
||||||
|
@ -127,7 +127,7 @@ class DriverController(object):
|
||||||
drv = self.get_provider_for_router(payload.context,
|
drv = self.get_provider_for_router(payload.context,
|
||||||
payload.resource_id)
|
payload.resource_id)
|
||||||
new_drv = None
|
new_drv = None
|
||||||
if _flavor_specified(payload.request_body):
|
if flavor_specified(payload.request_body):
|
||||||
if (payload.request_body['flavor_id'] !=
|
if (payload.request_body['flavor_id'] !=
|
||||||
payload.states[0]['flavor_id']):
|
payload.states[0]['flavor_id']):
|
||||||
# TODO(kevinbenton): this is currently disallowed by the API
|
# TODO(kevinbenton): this is currently disallowed by the API
|
||||||
|
@ -210,7 +210,7 @@ class DriverController(object):
|
||||||
|
|
||||||
def _get_provider_for_create(self, context, router):
|
def _get_provider_for_create(self, context, router):
|
||||||
"""Get provider based on flavor or ha/distributed flags."""
|
"""Get provider based on flavor or ha/distributed flags."""
|
||||||
if not _flavor_specified(router):
|
if not flavor_specified(router):
|
||||||
return self._attrs_to_driver(router)
|
return self._attrs_to_driver(router)
|
||||||
return self._get_l3_driver_by_flavor(context, router['flavor_id'])
|
return self._get_l3_driver_by_flavor(context, router['flavor_id'])
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ def _is_ha(ha_attr):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _flavor_specified(router):
|
def flavor_specified(router):
|
||||||
return ('flavor_id' in router and
|
return ('flavor_id' in router and
|
||||||
router['flavor_id'] != lib_const.ATTR_NOT_SPECIFIED)
|
router['flavor_id'] != lib_const.ATTR_NOT_SPECIFIED)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from neutron_lib.api.definitions import external_net
|
from neutron_lib.api.definitions import external_net
|
||||||
|
from neutron_lib.api.definitions import l3 as l3_apidef
|
||||||
from neutron_lib.api.definitions import portbindings
|
from neutron_lib.api.definitions import portbindings
|
||||||
from neutron_lib.api.definitions import provider_net as pnet
|
from neutron_lib.api.definitions import provider_net as pnet
|
||||||
from neutron_lib.api.definitions import qos_fip as qos_fip_apidef
|
from neutron_lib.api.definitions import qos_fip as qos_fip_apidef
|
||||||
|
@ -23,19 +24,18 @@ from neutron_lib.callbacks import resources
|
||||||
from neutron_lib import constants as n_const
|
from neutron_lib import constants as n_const
|
||||||
from neutron_lib import context as n_context
|
from neutron_lib import context as n_context
|
||||||
from neutron_lib.db import api as db_api
|
from neutron_lib.db import api as db_api
|
||||||
|
from neutron_lib.db import resource_extend
|
||||||
from neutron_lib import exceptions as n_exc
|
from neutron_lib import exceptions as n_exc
|
||||||
from neutron_lib.exceptions import availability_zone as az_exc
|
from neutron_lib.exceptions import availability_zone as az_exc
|
||||||
from neutron_lib.plugins import constants as plugin_constants
|
from neutron_lib.plugins import constants as plugin_constants
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from neutron_lib.services import base as service_base
|
from neutron_lib.services import base as service_base
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils import excutils
|
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
from neutron.common.ovn import constants as ovn_const
|
from neutron.common.ovn import constants as ovn_const
|
||||||
from neutron.common.ovn import extensions
|
from neutron.common.ovn import extensions
|
||||||
from neutron.common.ovn import utils
|
from neutron.common.ovn import utils
|
||||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
|
||||||
from neutron.db.availability_zone import router as router_az_db
|
from neutron.db.availability_zone import router as router_az_db
|
||||||
from neutron.db import dns_db
|
from neutron.db import dns_db
|
||||||
from neutron.db import extraroute_db
|
from neutron.db import extraroute_db
|
||||||
|
@ -46,11 +46,11 @@ from neutron.db import l3_fip_qos
|
||||||
from neutron.db import l3_gateway_ip_qos
|
from neutron.db import l3_gateway_ip_qos
|
||||||
from neutron.db import l3_gwmode_db
|
from neutron.db import l3_gwmode_db
|
||||||
from neutron.db.models import l3 as l3_models
|
from neutron.db.models import l3 as l3_models
|
||||||
from neutron.db import ovn_revision_numbers_db as db_rev
|
|
||||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
|
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
|
||||||
from neutron.quota import resource_registry
|
from neutron.quota import resource_registry
|
||||||
from neutron.scheduler import l3_ovn_scheduler
|
from neutron.scheduler import l3_ovn_scheduler
|
||||||
from neutron.services.ovn_l3 import exceptions as ovn_l3_exc
|
from neutron.services.ovn_l3 import exceptions as ovn_l3_exc
|
||||||
|
from neutron.services.ovn_l3.service_providers import driver_controller
|
||||||
from neutron.services.portforwarding.drivers.ovn import driver \
|
from neutron.services.portforwarding.drivers.ovn import driver \
|
||||||
as port_forwarding
|
as port_forwarding
|
||||||
|
|
||||||
|
@ -94,15 +94,7 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
self._ovn_client_inst = None
|
self._ovn_client_inst = None
|
||||||
self.scheduler = l3_ovn_scheduler.get_scheduler()
|
self.scheduler = l3_ovn_scheduler.get_scheduler()
|
||||||
self.port_forwarding = port_forwarding.OVNPortForwarding(self)
|
self.port_forwarding = port_forwarding.OVNPortForwarding(self)
|
||||||
self._register_precommit_callbacks()
|
self.l3_driver_controller = driver_controller.DriverController(self)
|
||||||
|
|
||||||
def _register_precommit_callbacks(self):
|
|
||||||
registry.subscribe(
|
|
||||||
self.create_router_precommit, resources.ROUTER,
|
|
||||||
events.PRECOMMIT_CREATE)
|
|
||||||
registry.subscribe(
|
|
||||||
self.create_floatingip_precommit, resources.FLOATING_IP,
|
|
||||||
events.PRECOMMIT_CREATE)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _disable_qos_extensions_by_extension_drivers(aliases):
|
def _disable_qos_extensions_by_extension_drivers(aliases):
|
||||||
|
@ -170,67 +162,13 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
return ("L3 Router Service Plugin for basic L3 forwarding"
|
return ("L3 Router Service Plugin for basic L3 forwarding"
|
||||||
" using OVN")
|
" using OVN")
|
||||||
|
|
||||||
def create_router_precommit(self, resource, event, trigger, payload):
|
|
||||||
context = payload.context
|
|
||||||
context.session.flush()
|
|
||||||
router_id = payload.resource_id
|
|
||||||
router_db = payload.metadata['router_db']
|
|
||||||
# NOTE(ralonsoh): the "distributed" flag is a static configuration
|
|
||||||
# parameter that needs to be defined only during the router creation.
|
|
||||||
extra_attr = router_db['extra_attributes']
|
|
||||||
extra_attr.distributed = ovn_conf.is_ovn_distributed_floating_ip()
|
|
||||||
|
|
||||||
db_rev.create_initial_revision(
|
|
||||||
context, router_id, ovn_const.TYPE_ROUTERS,
|
|
||||||
std_attr_id=router_db.standard_attr.id)
|
|
||||||
|
|
||||||
def create_router(self, context, router):
|
|
||||||
router = super(OVNL3RouterPlugin, self).create_router(context, router)
|
|
||||||
try:
|
|
||||||
self._ovn_client.create_router(context, router)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
# Delete the logical router
|
|
||||||
LOG.exception('Unable to create lrouter %s', router['id'])
|
|
||||||
super(OVNL3RouterPlugin, self).delete_router(context,
|
|
||||||
router['id'])
|
|
||||||
return router
|
|
||||||
|
|
||||||
def update_router(self, context, id, router):
|
|
||||||
original_router = self.get_router(context, id)
|
|
||||||
result = super(OVNL3RouterPlugin, self).update_router(context, id,
|
|
||||||
router)
|
|
||||||
try:
|
|
||||||
self._ovn_client.update_router(context, result,
|
|
||||||
original_router)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.exception('Unable to update lrouter %s', id)
|
|
||||||
revert_router = {'router': original_router}
|
|
||||||
super(OVNL3RouterPlugin, self).update_router(context, id,
|
|
||||||
revert_router)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def delete_router(self, context, id):
|
|
||||||
original_router = self.get_router(context, id)
|
|
||||||
super(OVNL3RouterPlugin, self).delete_router(context, id)
|
|
||||||
try:
|
|
||||||
self._ovn_client.delete_router(context, id)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.exception('Unable to delete lrouter %s', id)
|
|
||||||
super(OVNL3RouterPlugin, self).create_router(
|
|
||||||
context, {'router': original_router})
|
|
||||||
|
|
||||||
def _add_neutron_router_interface(self, context, router_id,
|
def _add_neutron_router_interface(self, context, router_id,
|
||||||
interface_info, may_exist=False):
|
interface_info):
|
||||||
try:
|
try:
|
||||||
router_interface_info = (
|
router_interface_info = (
|
||||||
super(OVNL3RouterPlugin, self).add_router_interface(
|
super(OVNL3RouterPlugin, self).add_router_interface(
|
||||||
context, router_id, interface_info))
|
context, router_id, interface_info))
|
||||||
except n_exc.PortInUse:
|
except n_exc.PortInUse:
|
||||||
if not may_exist:
|
|
||||||
raise
|
|
||||||
# NOTE(lucasagomes): If the port is already being used it means
|
# NOTE(lucasagomes): If the port is already being used it means
|
||||||
# the interface has been created already, let's just fetch it from
|
# the interface has been created already, let's just fetch it from
|
||||||
# the database. Perhaps the code below should live in Neutron
|
# the database. Perhaps the code below should live in Neutron
|
||||||
|
@ -247,70 +185,24 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
|
|
||||||
return router_interface_info
|
return router_interface_info
|
||||||
|
|
||||||
def add_router_interface(self, context, router_id, interface_info=None):
|
|
||||||
router_interface_info = self._add_neutron_router_interface(
|
|
||||||
context, router_id, interface_info)
|
|
||||||
try:
|
|
||||||
self._ovn_client.create_router_port(
|
|
||||||
context, router_id, router_interface_info)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.exception(
|
|
||||||
'Unable to add router interface to lrouter %s. '
|
|
||||||
'Interface info: %s', router_id, interface_info)
|
|
||||||
super(OVNL3RouterPlugin, self).remove_router_interface(
|
|
||||||
context, router_id, router_interface_info)
|
|
||||||
|
|
||||||
return router_interface_info
|
|
||||||
|
|
||||||
def remove_router_interface(self, context, router_id, interface_info):
|
|
||||||
router_interface_info = (
|
|
||||||
super(OVNL3RouterPlugin, self).remove_router_interface(
|
|
||||||
context, router_id, interface_info))
|
|
||||||
try:
|
|
||||||
port_id = router_interface_info['port_id']
|
|
||||||
subnet_ids = router_interface_info.get('subnet_ids')
|
|
||||||
self._ovn_client.delete_router_port(
|
|
||||||
context, port_id, router_id=router_id, subnet_ids=subnet_ids)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.exception(
|
|
||||||
'Unable to remove router interface from lrouter %s. '
|
|
||||||
'Interface info: %s', router_id, interface_info)
|
|
||||||
super(OVNL3RouterPlugin, self).add_router_interface(
|
|
||||||
context, router_id, interface_info)
|
|
||||||
return router_interface_info
|
|
||||||
|
|
||||||
def create_floatingip_precommit(self, resource, event, trigger, payload):
|
|
||||||
context = payload.context
|
|
||||||
floatingip_id = payload.resource_id
|
|
||||||
floatingip_db = payload.desired_state
|
|
||||||
|
|
||||||
db_rev.create_initial_revision(
|
|
||||||
context, floatingip_id, ovn_const.TYPE_FLOATINGIPS,
|
|
||||||
std_attr_id=floatingip_db.standard_attr.id)
|
|
||||||
|
|
||||||
def create_floatingip(self, context, floatingip,
|
def create_floatingip(self, context, floatingip,
|
||||||
initial_status=n_const.FLOATINGIP_STATUS_DOWN):
|
initial_status=n_const.FLOATINGIP_STATUS_DOWN):
|
||||||
fip = super(OVNL3RouterPlugin, self).create_floatingip(
|
# The OVN L3 plugin creates floating IPs in down status by default,
|
||||||
|
# whereas the L3 DB layer creates them in active status. So we keep
|
||||||
|
# this method to create the floating IP in the DB with status down,
|
||||||
|
# while the flavor drivers are responsible for calling the correct
|
||||||
|
# backend to instatiate the floating IP in the data plane
|
||||||
|
return super(OVNL3RouterPlugin, self).create_floatingip(
|
||||||
context, floatingip, initial_status)
|
context, floatingip, initial_status)
|
||||||
self._ovn_client.create_floatingip(context, fip)
|
|
||||||
return fip
|
|
||||||
|
|
||||||
def delete_floatingip(self, context, id):
|
|
||||||
super(OVNL3RouterPlugin, self).delete_floatingip(context, id)
|
|
||||||
self._ovn_client.delete_floatingip(context, id)
|
|
||||||
|
|
||||||
def update_floatingip(self, context, id, floatingip):
|
|
||||||
fip = super(OVNL3RouterPlugin, self).update_floatingip(context, id,
|
|
||||||
floatingip)
|
|
||||||
self._ovn_client.update_floatingip(context, fip, floatingip)
|
|
||||||
return fip
|
|
||||||
|
|
||||||
def update_floatingip_status(self, context, floatingip_id, status):
|
def update_floatingip_status(self, context, floatingip_id, status):
|
||||||
fip = self.update_floatingip_status_retry(
|
fip = self.update_floatingip_status_retry(
|
||||||
context, floatingip_id, status)
|
context, floatingip_id, status)
|
||||||
self._ovn_client.update_floatingip_status(context, fip)
|
registry.publish(
|
||||||
|
resources.FLOATING_IP, events.AFTER_STATUS_UPDATE, self,
|
||||||
|
payload=events.DBEventPayload(
|
||||||
|
context, states=(fip,),
|
||||||
|
resource_id=floatingip_id))
|
||||||
return fip
|
return fip
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
|
@ -319,30 +211,6 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
return super(OVNL3RouterPlugin, self).update_floatingip_status(
|
return super(OVNL3RouterPlugin, self).update_floatingip_status(
|
||||||
context, floatingip_id, status)
|
context, floatingip_id, status)
|
||||||
|
|
||||||
def disassociate_floatingips(self, context, port_id, do_notify=True):
|
|
||||||
fips = self.get_floatingips(context.elevated(),
|
|
||||||
filters={'port_id': [port_id]})
|
|
||||||
router_ids = super(OVNL3RouterPlugin, self).disassociate_floatingips(
|
|
||||||
context, port_id, do_notify)
|
|
||||||
for fip in fips:
|
|
||||||
router_id = fip.get('router_id')
|
|
||||||
fixed_ip_address = fip.get('fixed_ip_address')
|
|
||||||
if router_id and fixed_ip_address:
|
|
||||||
update_fip = {
|
|
||||||
'id': fip['id'],
|
|
||||||
'logical_ip': fixed_ip_address,
|
|
||||||
'external_ip': fip['floating_ip_address'],
|
|
||||||
'floating_network_id': fip['floating_network_id']}
|
|
||||||
try:
|
|
||||||
self._ovn_client.disassociate_floatingip(update_fip,
|
|
||||||
router_id)
|
|
||||||
self.update_floatingip_status(
|
|
||||||
context, fip['id'], n_const.FLOATINGIP_STATUS_DOWN)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error('Error in disassociating floatingip %(id)s: '
|
|
||||||
'%(error)s', {'id': fip['id'], 'error': e})
|
|
||||||
return router_ids
|
|
||||||
|
|
||||||
def _get_gateway_port_physnet_mapping(self):
|
def _get_gateway_port_physnet_mapping(self):
|
||||||
# This function returns all gateway ports with corresponding
|
# This function returns all gateway ports with corresponding
|
||||||
# external network's physnet
|
# external network's physnet
|
||||||
|
@ -359,8 +227,10 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
net_physnet_dict[net['id']] = net.get(pnet.PHYSICAL_NETWORK)
|
net_physnet_dict[net['id']] = net.get(pnet.PHYSICAL_NETWORK)
|
||||||
for port in l3plugin._plugin.get_ports(context, filters={
|
for port in l3plugin._plugin.get_ports(context, filters={
|
||||||
'device_owner': [n_const.DEVICE_OWNER_ROUTER_GW]}):
|
'device_owner': [n_const.DEVICE_OWNER_ROUTER_GW]}):
|
||||||
port_physnet_dict[port['id']] = net_physnet_dict.get(
|
if utils.is_ovn_provider_router(
|
||||||
port['network_id'])
|
l3plugin.get_router(context, port['device_id'])):
|
||||||
|
port_physnet_dict[port['id']] = net_physnet_dict.get(
|
||||||
|
port['network_id'])
|
||||||
return port_physnet_dict
|
return port_physnet_dict
|
||||||
|
|
||||||
def update_router_gateway_port_bindings(self, router, host):
|
def update_router_gateway_port_bindings(self, router, host):
|
||||||
|
@ -474,7 +344,9 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
'device_owner': [n_const.DEVICE_OWNER_ROUTER_GW],
|
'device_owner': [n_const.DEVICE_OWNER_ROUTER_GW],
|
||||||
'fixed_ips': {'subnet_id': [orig['id']]},
|
'fixed_ips': {'subnet_id': [orig['id']]},
|
||||||
})
|
})
|
||||||
router_ids = {port['device_id'] for port in gw_ports}
|
router_ids = {port['device_id'] for port in gw_ports
|
||||||
|
if utils.is_ovn_provider_router(
|
||||||
|
l3plugin.get_router(context, port['device_id']))}
|
||||||
remove = [{'destination': '0.0.0.0/0', 'nexthop': orig_gw_ip}
|
remove = [{'destination': '0.0.0.0/0', 'nexthop': orig_gw_ip}
|
||||||
] if orig_gw_ip else []
|
] if orig_gw_ip else []
|
||||||
add = [{'destination': '0.0.0.0/0', 'nexthop': current_gw_ip}
|
add = [{'destination': '0.0.0.0/0', 'nexthop': current_gw_ip}
|
||||||
|
@ -508,11 +380,16 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
# https://bugs.launchpad.net/neutron/+bug/1948457
|
# https://bugs.launchpad.net/neutron/+bug/1948457
|
||||||
if (event == events.BEFORE_UPDATE and
|
if (event == events.BEFORE_UPDATE and
|
||||||
'fixed_ips' in current and not current['fixed_ips'] and
|
'fixed_ips' in current and not current['fixed_ips'] and
|
||||||
utils.is_lsp_router_port(original)):
|
utils.is_lsp_router_port(original) and
|
||||||
|
utils.is_ovn_provider_router(
|
||||||
|
l3plugin.get_router(context, original['device_id']))):
|
||||||
reason = _("Router port must have at least one IP.")
|
reason = _("Router port must have at least one IP.")
|
||||||
raise n_exc.ServicePortInUse(port_id=original['id'], reason=reason)
|
raise n_exc.ServicePortInUse(port_id=original['id'], reason=reason)
|
||||||
|
|
||||||
if event == events.AFTER_UPDATE and utils.is_lsp_router_port(current):
|
if (event == events.AFTER_UPDATE and
|
||||||
|
utils.is_lsp_router_port(current) and
|
||||||
|
utils.is_ovn_provider_router(
|
||||||
|
l3plugin.get_router(context, current['device_id']))):
|
||||||
# We call the update_router port with if_exists, because neutron,
|
# We call the update_router port with if_exists, because neutron,
|
||||||
# internally creates the port, and then calls update, which will
|
# internally creates the port, and then calls update, which will
|
||||||
# trigger this callback even before we had the chance to create
|
# trigger this callback even before we had the chance to create
|
||||||
|
@ -541,3 +418,8 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||||
if diff:
|
if diff:
|
||||||
raise az_exc.AvailabilityZoneNotFound(
|
raise az_exc.AvailabilityZoneNotFound(
|
||||||
availability_zone=', '.join(diff))
|
availability_zone=', '.join(diff))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@resource_extend.extends([l3_apidef.ROUTERS])
|
||||||
|
def add_flavor_id(router_res, router_db):
|
||||||
|
router_res['flavor_id'] = router_db['flavor_id']
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from neutron_lib import exceptions as lib_exc
|
||||||
|
from neutron_lib.plugins import constants as plugin_constants
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
|
from neutron.db import servicetype_db as st_db
|
||||||
|
from neutron.services.l3_router.service_providers import driver_controller
|
||||||
|
from neutron.services import provider_configuration
|
||||||
|
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DriverController(driver_controller.DriverController):
|
||||||
|
"""Driver controller for the OVN L3 service plugin.
|
||||||
|
|
||||||
|
This component is responsible for dispatching router requests to L3
|
||||||
|
service providers and for performing the bookkeeping about which
|
||||||
|
driver is associated with a given router.
|
||||||
|
|
||||||
|
This is not intended to be accessed by the drivers or the l3 plugin.
|
||||||
|
All of the methods are marked as private to reflect this.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, l3_plugin):
|
||||||
|
self.l3_plugin = l3_plugin
|
||||||
|
self._stm = st_db.ServiceTypeManager.get_instance()
|
||||||
|
self._stm.add_provider_configuration(
|
||||||
|
plugin_constants.L3, _OvnPlusProviderConfiguration())
|
||||||
|
self._load_drivers()
|
||||||
|
|
||||||
|
def _get_provider_for_create(self, context, router):
|
||||||
|
"""Get provider based on flavor or default provider."""
|
||||||
|
if not driver_controller.flavor_specified(router):
|
||||||
|
return self.drivers[self.default_provider]
|
||||||
|
return self._get_l3_driver_by_flavor(context, router['flavor_id'])
|
||||||
|
|
||||||
|
|
||||||
|
class _OvnPlusProviderConfiguration(
|
||||||
|
provider_configuration.ProviderConfiguration):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# loads up the OVN provider automatically and sets it as default.
|
||||||
|
super(_OvnPlusProviderConfiguration, self).__init__(
|
||||||
|
svc_type=plugin_constants.L3)
|
||||||
|
path = 'neutron.services.ovn_l3.service_providers.ovn.OvnDriver'
|
||||||
|
try:
|
||||||
|
self.add_provider({'service_type': plugin_constants.L3,
|
||||||
|
'name': 'ovn', 'driver': path, 'default': True})
|
||||||
|
except lib_exc.Invalid:
|
||||||
|
LOG.debug("Could not add L3 provider ovn, it may have "
|
||||||
|
"already been explicitly defined.")
|
|
@ -0,0 +1,252 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from neutron_lib.callbacks import events
|
||||||
|
from neutron_lib.callbacks import registry
|
||||||
|
from neutron_lib.callbacks import resources
|
||||||
|
from neutron_lib import constants
|
||||||
|
from neutron_lib.db import api as db_api
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import excutils
|
||||||
|
|
||||||
|
from neutron.common.ovn import constants as ovn_const
|
||||||
|
from neutron.common.ovn import utils
|
||||||
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||||
|
from neutron.db import ovn_revision_numbers_db as db_rev
|
||||||
|
from neutron.extensions import revisions
|
||||||
|
from neutron.objects import router as l3_obj
|
||||||
|
from neutron.services.l3_router.service_providers import base
|
||||||
|
from neutron.services.portforwarding import constants as pf_consts
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@registry.has_registry_receivers
|
||||||
|
class OvnDriver(base.L3ServiceProvider):
|
||||||
|
ha_support = base.MANDATORY
|
||||||
|
dvr_support = base.MANDATORY
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE])
|
||||||
|
def _process_router_create_precommit(self, resource, event, trigger,
|
||||||
|
payload):
|
||||||
|
context = payload.context
|
||||||
|
context.session.flush()
|
||||||
|
router_id = payload.resource_id
|
||||||
|
router_db = payload.metadata['router_db']
|
||||||
|
router = payload.states[0]
|
||||||
|
if not utils.is_ovn_provider_router(router):
|
||||||
|
return
|
||||||
|
|
||||||
|
# NOTE(ralonsoh): the "distributed" flag is a static configuration
|
||||||
|
# parameter that needs to be defined only during the router creation.
|
||||||
|
extra_attr = router_db['extra_attributes']
|
||||||
|
extra_attr.distributed = ovn_conf.is_ovn_distributed_floating_ip()
|
||||||
|
|
||||||
|
db_rev.create_initial_revision(
|
||||||
|
context, router_id, ovn_const.TYPE_ROUTERS,
|
||||||
|
std_attr_id=router_db.standard_attr.id)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER, [events.AFTER_CREATE])
|
||||||
|
def _process_router_create(self, resource, event, trigger, payload):
|
||||||
|
router = payload.states[0]
|
||||||
|
if not utils.is_ovn_provider_router(router):
|
||||||
|
return
|
||||||
|
context = payload.context
|
||||||
|
try:
|
||||||
|
self.l3plugin._ovn_client.create_router(context, router)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
# Delete the logical router
|
||||||
|
LOG.exception('Unable to create lrouter %s', router['id'])
|
||||||
|
self.l3plugin.delete_router(context, router['id'])
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER, [events.AFTER_UPDATE])
|
||||||
|
def _process_router_update(self, resource, event, trigger, payload):
|
||||||
|
router_id = payload.resource_id
|
||||||
|
original = payload.states[0]
|
||||||
|
updated = payload.states[1]
|
||||||
|
if not utils.is_ovn_provider_router(original):
|
||||||
|
# flavor_id attribute is not allowed in router PUTs, so we only
|
||||||
|
# need to check the original router
|
||||||
|
return
|
||||||
|
context = payload.context
|
||||||
|
try:
|
||||||
|
self.l3plugin._ovn_client.update_router(context, updated, original)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.exception('Unable to update lrouter %s', router_id)
|
||||||
|
revert_router = {'router': original}
|
||||||
|
self.l3plugin.update_router(context, router_id, revert_router)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER, [events.AFTER_DELETE])
|
||||||
|
def _process_router_delete(self, resource, event, trigger, payload):
|
||||||
|
router_id = payload.resource_id
|
||||||
|
router = payload.states[0]
|
||||||
|
if not utils.is_ovn_provider_router(router):
|
||||||
|
return
|
||||||
|
context = payload.context
|
||||||
|
try:
|
||||||
|
self.l3plugin._ovn_client.delete_router(context, router_id)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.exception('Unable to delete lrouter %s', router['id'])
|
||||||
|
self.l3plugin.create_router(context, {'router': router})
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_CREATE])
|
||||||
|
def _process_add_router_interface(self, resource, event, trigger, payload):
|
||||||
|
router = payload.states[0]
|
||||||
|
if not utils.is_ovn_provider_router(router):
|
||||||
|
return
|
||||||
|
context = payload.context
|
||||||
|
port = payload.metadata['port']
|
||||||
|
subnets = payload.metadata['subnets']
|
||||||
|
router_interface_info = self.l3plugin._make_router_interface_info(
|
||||||
|
router.id, port['tenant_id'], port['id'], port['network_id'],
|
||||||
|
subnets[-1]['id'], [subnet['id'] for subnet in subnets])
|
||||||
|
try:
|
||||||
|
self.l3plugin._ovn_client.create_router_port(context, router.id,
|
||||||
|
router_interface_info)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.exception('Unable to add router interface to lrouter %s. '
|
||||||
|
'Interface info: %s', router['id'],
|
||||||
|
router_interface_info)
|
||||||
|
self.l3plugin.remove_router_interface(context, router.id,
|
||||||
|
router_interface_info)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_DELETE])
|
||||||
|
def _process_remove_router_interface(self, resource, event, trigger,
|
||||||
|
payload):
|
||||||
|
router = payload.states[0]
|
||||||
|
if not utils.is_ovn_provider_router(router):
|
||||||
|
return
|
||||||
|
context = payload.context
|
||||||
|
port = payload.metadata['port']
|
||||||
|
subnet_ids = payload.metadata['subnet_ids']
|
||||||
|
router_interface_info = self.l3plugin._make_router_interface_info(
|
||||||
|
router.id, port['tenant_id'], port['id'], port['network_id'],
|
||||||
|
subnet_ids[0], subnet_ids)
|
||||||
|
try:
|
||||||
|
self.l3plugin._ovn_client.delete_router_port(context, port['id'],
|
||||||
|
router_id=router.id,
|
||||||
|
subnet_ids=subnet_ids)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.exception('Unable to remove router interface from lrouter '
|
||||||
|
'%s. Interface info: %s', router['id'],
|
||||||
|
router_interface_info)
|
||||||
|
self.l3plugin.add_router_interface(
|
||||||
|
context, router.id, payload.metadata['interface_info'])
|
||||||
|
|
||||||
|
def _create_floatingip_initial_revision(self, context, floatingip_db):
|
||||||
|
if not floatingip_db.router_id:
|
||||||
|
return
|
||||||
|
# We get the router with elevated context because floating IPs may be
|
||||||
|
# created to be associated with a router created by a different
|
||||||
|
# project. Please see
|
||||||
|
# https://review.opendev.org/c/openstack/neutron/+/2727B09
|
||||||
|
router = self.l3plugin.get_router(context.elevated(),
|
||||||
|
floatingip_db.router_id)
|
||||||
|
if not utils.is_ovn_provider_router(router):
|
||||||
|
return
|
||||||
|
db_rev.create_initial_revision(
|
||||||
|
context, floatingip_db.id, ovn_const.TYPE_FLOATINGIPS,
|
||||||
|
may_exist=True, std_attr_id=floatingip_db.standard_attr.id)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP,
|
||||||
|
[events.PRECOMMIT_CREATE, events.PRECOMMIT_UPDATE])
|
||||||
|
def _process_floatingip_create_update_precommit(self, resource, event,
|
||||||
|
trigger, payload):
|
||||||
|
context = payload.context
|
||||||
|
floatingip_db = payload.desired_state
|
||||||
|
self._create_floatingip_initial_revision(context, floatingip_db)
|
||||||
|
|
||||||
|
@registry.receives(pf_consts.PORT_FORWARDING, [events.AFTER_CREATE])
|
||||||
|
def _process_portforwarding_create(self, resource, event, trigger,
|
||||||
|
payload):
|
||||||
|
context = payload.context
|
||||||
|
pf_obj = payload.states[0]
|
||||||
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
|
fip_db = l3_obj.FloatingIP.get_object(
|
||||||
|
context, id=pf_obj.floatingip_id).db_obj
|
||||||
|
self._create_floatingip_initial_revision(context, fip_db)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_CREATE])
|
||||||
|
def _process_floatingip_create(self, resource, event, trigger, payload):
|
||||||
|
revision_row = db_rev.get_revision_row(payload.context,
|
||||||
|
payload.resource_id)
|
||||||
|
if not revision_row:
|
||||||
|
return
|
||||||
|
# The floating ip dictionary that is sent by the L3 DB plugin in the
|
||||||
|
# notification doesn't include the revision number yet. We add it here
|
||||||
|
# to the dictionary that is passed to the ovn client
|
||||||
|
floatingip = copy.deepcopy(payload.states[0])
|
||||||
|
floatingip[revisions.REVISION] = revision_row.revision_number
|
||||||
|
qos_policy_id = payload.request_body['floatingip'].get('qos_policy_id')
|
||||||
|
if qos_policy_id and 'qos_policy_id' not in floatingip:
|
||||||
|
floatingip['qos_policy_id'] = qos_policy_id
|
||||||
|
self.l3plugin._ovn_client.create_floatingip(payload.context,
|
||||||
|
floatingip)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_UPDATE])
|
||||||
|
def _process_floatingip_update(self, resource, event, trigger, payload):
|
||||||
|
if not db_rev.get_revision_row(payload.context, payload.resource_id):
|
||||||
|
return
|
||||||
|
fip = payload.states[1]
|
||||||
|
old_fip = payload.states[0]
|
||||||
|
fip_request = payload.request_body
|
||||||
|
if fip_request:
|
||||||
|
self.l3plugin._ovn_client.update_floatingip(payload.context, fip,
|
||||||
|
fip_request)
|
||||||
|
else:
|
||||||
|
router_id = old_fip.get('router_id')
|
||||||
|
fixed_ip_address = old_fip.get('fixed_ip_address')
|
||||||
|
if router_id and fixed_ip_address:
|
||||||
|
update_fip = {
|
||||||
|
'id': old_fip['id'],
|
||||||
|
'logical_ip': fixed_ip_address,
|
||||||
|
'external_ip': old_fip['floating_ip_address'],
|
||||||
|
'floating_network_id': old_fip['floating_network_id']
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
self.l3plugin._ovn_client.disassociate_floatingip(
|
||||||
|
update_fip, router_id)
|
||||||
|
self.l3plugin.update_floatingip_status(
|
||||||
|
payload.context, old_fip['id'],
|
||||||
|
constants.FLOATINGIP_STATUS_DOWN)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error('Error in disassociating floatingip %(id)s: '
|
||||||
|
'%(error)s', {'id': old_fip['id'], 'error': e})
|
||||||
|
if not fip['router_id']:
|
||||||
|
db_rev.delete_revision(payload.context, payload.resource_id,
|
||||||
|
ovn_const.TYPE_FLOATINGIPS)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_DELETE])
|
||||||
|
def _process_floatingip_delete(self, resource, event, trigger, payload):
|
||||||
|
if not db_rev.get_revision_row(payload.context, payload.resource_id):
|
||||||
|
return
|
||||||
|
self.l3plugin._ovn_client.delete_floatingip(payload.context,
|
||||||
|
payload.resource_id)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_STATUS_UPDATE])
|
||||||
|
def _process_floatingip_status_update(self, resource, event, trigger,
|
||||||
|
payload):
|
||||||
|
if not db_rev.get_revision_row(payload.context, payload.resource_id):
|
||||||
|
return
|
||||||
|
self.l3plugin._ovn_client.update_floatingip_status(payload.context,
|
||||||
|
payload.states[0])
|
|
@ -0,0 +1,170 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
from neutron_lib.callbacks import events
|
||||||
|
from neutron_lib.callbacks import registry
|
||||||
|
from neutron_lib.callbacks import resources
|
||||||
|
from neutron_lib.plugins import constants as plugin_constants
|
||||||
|
from neutron_lib.plugins import directory
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from neutron.services.l3_router.service_providers import base
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@registry.has_registry_receivers
|
||||||
|
class UserDefined(base.L3ServiceProvider):
|
||||||
|
|
||||||
|
def __init__(self, l3_plugin):
|
||||||
|
super(UserDefined, self).__init__(l3_plugin)
|
||||||
|
self._user_defined_provider = __name__ + "." + self.__class__.__name__
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _flavor_plugin(self):
|
||||||
|
try:
|
||||||
|
return self._flavor_plugin_ref
|
||||||
|
except AttributeError:
|
||||||
|
self._flavor_plugin_ref = directory.get_plugin(
|
||||||
|
plugin_constants.FLAVORS)
|
||||||
|
return self._flavor_plugin_ref
|
||||||
|
|
||||||
|
def _is_user_defined_provider(self, context, router):
|
||||||
|
flavor_id = router.get('flavor_id')
|
||||||
|
if flavor_id is None:
|
||||||
|
return False
|
||||||
|
flavor = self._flavor_plugin.get_flavor(context, flavor_id)
|
||||||
|
provider = self._flavor_plugin.get_flavor_next_provider(
|
||||||
|
context, flavor['id'])[0]
|
||||||
|
return str(provider['driver']) == self._user_defined_provider
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER_CONTROLLER,
|
||||||
|
[events.PRECOMMIT_ADD_ASSOCIATION])
|
||||||
|
def _process_router_add_association(self, resource, event, trigger,
|
||||||
|
payload=None):
|
||||||
|
router = payload.states[0]
|
||||||
|
context = payload.context
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
LOG.debug('Got request to associate user defined flavor to router %s',
|
||||||
|
router)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER, [events.AFTER_CREATE])
|
||||||
|
def _process_router_create(self, resource, event, trigger, payload=None):
|
||||||
|
router = payload.states[0]
|
||||||
|
context = payload.context
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
LOG.debug('Got request to create a user defined flavor router %s',
|
||||||
|
router)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER, [events.AFTER_UPDATE])
|
||||||
|
def _process_router_update(self, resource, event, trigger, payload=None):
|
||||||
|
original = payload.states[0]
|
||||||
|
updated = payload.states[1]
|
||||||
|
context = payload.context
|
||||||
|
if not self._is_user_defined_provider(context, original):
|
||||||
|
# flavor_id attribute is not allowed in router PUTs, so we only
|
||||||
|
# need to check the original router
|
||||||
|
return
|
||||||
|
router_id = payload.resource_id
|
||||||
|
LOG.debug('Got request to update a user defined flavor router with id '
|
||||||
|
'%s. Original: %s. Updated: %s', router_id, original,
|
||||||
|
updated)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER, [events.AFTER_DELETE])
|
||||||
|
def _process_router_delete(self, resource, event, trigger, payload=None):
|
||||||
|
router = payload.states[0]
|
||||||
|
context = payload.context
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
router_id = payload.resource_id
|
||||||
|
LOG.debug('Got request to delete a user defined flavor router with ',
|
||||||
|
'id %s:', router_id)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_CREATE])
|
||||||
|
def _process_add_router_interface(self, resource, event, trigger, payload):
|
||||||
|
router = payload.states[0]
|
||||||
|
context = payload.context
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
port = payload.metadata['port']
|
||||||
|
subnets = payload.metadata['subnets']
|
||||||
|
router_interface_info = self.l3plugin._make_router_interface_info(
|
||||||
|
router.id, port['tenant_id'], port['id'], port['network_id'],
|
||||||
|
subnets[-1]['id'], [subnet['id'] for subnet in subnets])
|
||||||
|
LOG.debug('Got request to add interface %s to a user defined flavor '
|
||||||
|
'router with id %s', router_interface_info, router.id)
|
||||||
|
|
||||||
|
@registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_DELETE])
|
||||||
|
def _process_remove_router_interface(self, resource, event, trigger,
|
||||||
|
payload):
|
||||||
|
router = payload.states[0]
|
||||||
|
context = payload.context
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
subnet_ids = payload.metadata['subnet_ids']
|
||||||
|
LOG.debug('Got request to remove interface to subnets %s from a user '
|
||||||
|
'defined flavor router with id %s', subnet_ids, router.id)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_CREATE])
|
||||||
|
def _process_floatingip_create(self, resource, event, trigger, payload):
|
||||||
|
context = payload.context
|
||||||
|
fip = payload.states[0]
|
||||||
|
if not fip['router_id']:
|
||||||
|
return
|
||||||
|
router = self.l3plugin.get_router(context, fip['router_id'])
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
LOG.debug('Got request to create a floating ip associated to a router '
|
||||||
|
'of user defined flavor %s', fip)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_UPDATE])
|
||||||
|
def _process_floatingip_update(self, resource, event, trigger, payload):
|
||||||
|
context = payload.context
|
||||||
|
fip = payload.states[1]
|
||||||
|
if not fip['router_id']:
|
||||||
|
return
|
||||||
|
router = self.l3plugin.get_router(context, fip['router_id'])
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
LOG.debug('Got request to update a floating ip associated to a router '
|
||||||
|
'of user defined flavor %s', fip)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_DELETE])
|
||||||
|
def _process_floatingip_delete(self, resource, event, trigger, payload):
|
||||||
|
context = payload.context
|
||||||
|
fip = payload.states[0]
|
||||||
|
if not fip['router_id']:
|
||||||
|
return
|
||||||
|
router = self.l3plugin.get_router(context, fip['router_id'])
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
LOG.debug('Got request to delete a floating ip associated to a router '
|
||||||
|
'of user defined flavor %s', fip)
|
||||||
|
|
||||||
|
@registry.receives(resources.FLOATING_IP, [events.AFTER_STATUS_UPDATE])
|
||||||
|
def _process_floatingip_status_update(self, resource, event, trigger,
|
||||||
|
payload):
|
||||||
|
context = payload.context
|
||||||
|
fip = payload.states[0]
|
||||||
|
if not fip['router_id']:
|
||||||
|
return
|
||||||
|
router = self.l3plugin.get_router(context, fip['router_id'])
|
||||||
|
if not self._is_user_defined_provider(context, router):
|
||||||
|
return
|
||||||
|
LOG.debug('Got request to update the status of a floating ip '
|
||||||
|
'associated to a router of user defined flavor %s', fip)
|
|
@ -21,6 +21,8 @@ from futurist import periodics
|
||||||
from neutron_lib.api.definitions import external_net as extnet_apidef
|
from neutron_lib.api.definitions import external_net as extnet_apidef
|
||||||
from neutron_lib.api.definitions import floating_ip_port_forwarding as pf_def
|
from neutron_lib.api.definitions import floating_ip_port_forwarding as pf_def
|
||||||
from neutron_lib.api.definitions import provider_net as provnet_apidef
|
from neutron_lib.api.definitions import provider_net as provnet_apidef
|
||||||
|
from neutron_lib.callbacks import events
|
||||||
|
from neutron_lib.callbacks import registry
|
||||||
from neutron_lib import constants as n_const
|
from neutron_lib import constants as n_const
|
||||||
from neutron_lib import context as n_context
|
from neutron_lib import context as n_context
|
||||||
from neutron_lib.exceptions import l3 as lib_l3_exc
|
from neutron_lib.exceptions import l3 as lib_l3_exc
|
||||||
|
@ -30,6 +32,7 @@ from neutron.common.ovn import utils
|
||||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as ovn_config
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as ovn_config
|
||||||
from neutron.db import ovn_revision_numbers_db as db_rev
|
from neutron.db import ovn_revision_numbers_db as db_rev
|
||||||
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
|
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
|
||||||
|
from neutron.services.portforwarding import constants as pf_consts
|
||||||
from neutron.tests.functional import base
|
from neutron.tests.functional import base
|
||||||
from neutron.tests.functional.services.logapi.drivers.ovn \
|
from neutron.tests.functional.services.logapi.drivers.ovn \
|
||||||
import test_driver as test_log_driver
|
import test_driver as test_log_driver
|
||||||
|
@ -925,65 +928,69 @@ class TestMaintenance(_TestMaintenanceHelper):
|
||||||
p1 = self._create_port('testp1', net1['id'])
|
p1 = self._create_port('testp1', net1['id'])
|
||||||
p1_ip = p1['fixed_ips'][0]['ip_address']
|
p1_ip = p1['fixed_ips'][0]['ip_address']
|
||||||
|
|
||||||
with mock.patch('neutron_lib.callbacks.registry.publish') as m_publish:
|
callbacks = registry._get_callback_manager()._callbacks
|
||||||
# > Create
|
pf_cb = callbacks[pf_consts.PORT_FORWARDING]
|
||||||
fip_pf_args = {
|
key = list(pf_cb[events.AFTER_UPDATE][0][1].keys())[0]
|
||||||
pf_def.EXTERNAL_PORT: 2222,
|
pf_cb[events.AFTER_CREATE][0][1][key] = mock.MagicMock()
|
||||||
pf_def.INTERNAL_PORT: 22,
|
|
||||||
pf_def.INTERNAL_PORT_ID: p1['id'],
|
|
||||||
pf_def.PROTOCOL: 'tcp',
|
|
||||||
pf_def.INTERNAL_IP_ADDRESS: p1_ip}
|
|
||||||
pf_obj = self.pf_plugin.create_floatingip_port_forwarding(
|
|
||||||
self.context, fip_id, **fip_attrs(fip_pf_args))
|
|
||||||
call = mock.call('port_forwarding', 'after_create', self.pf_plugin,
|
|
||||||
payload=mock.ANY)
|
|
||||||
m_publish.assert_has_calls([call])
|
|
||||||
|
|
||||||
# Assert load balancer for port forwarding was not created
|
# > Create
|
||||||
self.assertFalse(self._find_pf_lb(router_id, fip_id))
|
fip_pf_args = {
|
||||||
|
pf_def.EXTERNAL_PORT: 2222,
|
||||||
|
pf_def.INTERNAL_PORT: 22,
|
||||||
|
pf_def.INTERNAL_PORT_ID: p1['id'],
|
||||||
|
pf_def.PROTOCOL: 'tcp',
|
||||||
|
pf_def.INTERNAL_IP_ADDRESS: p1_ip}
|
||||||
|
pf_obj = self.pf_plugin.create_floatingip_port_forwarding(
|
||||||
|
self.context, fip_id, **fip_attrs(fip_pf_args))
|
||||||
|
call = mock.call('port_forwarding', 'after_create', self.pf_plugin,
|
||||||
|
payload=mock.ANY)
|
||||||
|
pf_cb[events.AFTER_CREATE][0][1][key].assert_has_calls([call])
|
||||||
|
|
||||||
# Call the maintenance thread to fix the problem
|
# Assert load balancer for port forwarding was not created
|
||||||
self.maint.check_for_inconsistencies()
|
self.assertFalse(self._find_pf_lb(router_id, fip_id))
|
||||||
|
|
||||||
# Assert load balancer for port forwarding was created
|
# Call the maintenance thread to fix the problem
|
||||||
_verify_lb(self, 'tcp', 2222, 22)
|
self.maint.check_for_inconsistencies()
|
||||||
|
|
||||||
# > Update
|
# Assert load balancer for port forwarding was created
|
||||||
fip_pf_args = {pf_def.EXTERNAL_PORT: 5353,
|
_verify_lb(self, 'tcp', 2222, 22)
|
||||||
pf_def.INTERNAL_PORT: 53,
|
|
||||||
pf_def.PROTOCOL: 'udp'}
|
|
||||||
m_publish.reset_mock()
|
|
||||||
self.pf_plugin.update_floatingip_port_forwarding(
|
|
||||||
self.context, pf_obj['id'], fip_id, **fip_attrs(fip_pf_args))
|
|
||||||
call = mock.call('port_forwarding', 'after_update', self.pf_plugin,
|
|
||||||
payload=mock.ANY)
|
|
||||||
m_publish.assert_has_calls([call])
|
|
||||||
|
|
||||||
# Assert load balancer for port forwarding is stale
|
# > Update
|
||||||
_verify_lb(self, 'tcp', 2222, 22)
|
fip_pf_args = {pf_def.EXTERNAL_PORT: 5353,
|
||||||
|
pf_def.INTERNAL_PORT: 53,
|
||||||
|
pf_def.PROTOCOL: 'udp'}
|
||||||
|
pf_cb[events.AFTER_UPDATE][0][1][key] = mock.MagicMock()
|
||||||
|
self.pf_plugin.update_floatingip_port_forwarding(
|
||||||
|
self.context, pf_obj['id'], fip_id, **fip_attrs(fip_pf_args))
|
||||||
|
call = mock.call('port_forwarding', 'after_update', self.pf_plugin,
|
||||||
|
payload=mock.ANY)
|
||||||
|
pf_cb[events.AFTER_UPDATE][0][1][key].assert_has_calls([call])
|
||||||
|
|
||||||
# Call the maintenance thread to fix the problem
|
# Assert load balancer for port forwarding is stale
|
||||||
self.maint.check_for_inconsistencies()
|
_verify_lb(self, 'tcp', 2222, 22)
|
||||||
|
|
||||||
# Assert load balancer for port forwarding was updated
|
# Call the maintenance thread to fix the problem
|
||||||
_verify_lb(self, 'udp', 5353, 53)
|
self.maint.check_for_inconsistencies()
|
||||||
|
|
||||||
# > Delete
|
# Assert load balancer for port forwarding was updated
|
||||||
m_publish.reset_mock()
|
_verify_lb(self, 'udp', 5353, 53)
|
||||||
self.pf_plugin.delete_floatingip_port_forwarding(
|
|
||||||
self.context, pf_obj['id'], fip_id)
|
|
||||||
call = mock.call('port_forwarding', 'after_delete', self.pf_plugin,
|
|
||||||
payload=mock.ANY)
|
|
||||||
m_publish.assert_has_calls([call])
|
|
||||||
|
|
||||||
# Assert load balancer for port forwarding is stale
|
# > Delete
|
||||||
_verify_lb(self, 'udp', 5353, 53)
|
pf_cb[events.AFTER_DELETE][0][1][key] = mock.MagicMock()
|
||||||
|
self.pf_plugin.delete_floatingip_port_forwarding(
|
||||||
|
self.context, pf_obj['id'], fip_id)
|
||||||
|
call = mock.call('port_forwarding', 'after_delete', self.pf_plugin,
|
||||||
|
payload=mock.ANY)
|
||||||
|
pf_cb[events.AFTER_DELETE][0][1][key].assert_has_calls([call])
|
||||||
|
|
||||||
# Call the maintenance thread to fix the problem
|
# Assert load balancer for port forwarding is stale
|
||||||
self.maint.check_for_inconsistencies()
|
_verify_lb(self, 'udp', 5353, 53)
|
||||||
|
|
||||||
# Assert load balancer for port forwarding is gone
|
# Call the maintenance thread to fix the problem
|
||||||
self.assertFalse(self._find_pf_lb(router_id, fip_id))
|
self.maint.check_for_inconsistencies()
|
||||||
|
|
||||||
|
# Assert load balancer for port forwarding is gone
|
||||||
|
self.assertFalse(self._find_pf_lb(router_id, fip_id))
|
||||||
|
|
||||||
def test_check_for_ha_chassis_group(self):
|
def test_check_for_ha_chassis_group(self):
|
||||||
net1 = self._create_network('network1test', external=False)
|
net1 = self._create_network('network1test', external=False)
|
||||||
|
|
|
@ -620,7 +620,8 @@ class TestExternalPorts(base.TestOVNFunctionalBase):
|
||||||
'ports', port_upt_data, port['id'], self.fmt)
|
'ports', port_upt_data, port['id'], self.fmt)
|
||||||
port_res = port_req.get_response(self.api)
|
port_res = port_req.get_response(self.api)
|
||||||
|
|
||||||
def test_add_external_port_avoid_flapping(self):
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
|
def test_add_external_port_avoid_flapping(self, gr):
|
||||||
class LogicalSwitchPortUpdateUpEventTest(event.RowEvent):
|
class LogicalSwitchPortUpdateUpEventTest(event.RowEvent):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.count = 0
|
self.count = 0
|
||||||
|
@ -651,6 +652,7 @@ class TestExternalPorts(base.TestOVNFunctionalBase):
|
||||||
def get_count(self):
|
def get_count(self):
|
||||||
return self.count
|
return self.count
|
||||||
|
|
||||||
|
gr.return_value = {'flavor_id': None}
|
||||||
og_up_event = ovsdb_monitor.LogicalSwitchPortUpdateUpEvent(None)
|
og_up_event = ovsdb_monitor.LogicalSwitchPortUpdateUpEvent(None)
|
||||||
og_down_event = ovsdb_monitor.LogicalSwitchPortUpdateDownEvent(None)
|
og_down_event = ovsdb_monitor.LogicalSwitchPortUpdateDownEvent(None)
|
||||||
test_down_event = LogicalSwitchPortUpdateDownEventTest()
|
test_down_event = LogicalSwitchPortUpdateDownEventTest()
|
||||||
|
@ -666,7 +668,8 @@ class TestExternalPorts(base.TestOVNFunctionalBase):
|
||||||
# status as up, triggering only a LogicalSwitchPortUpdateUpEvent.
|
# status as up, triggering only a LogicalSwitchPortUpdateUpEvent.
|
||||||
self._create_router_port(portbindings.VNIC_DIRECT)
|
self._create_router_port(portbindings.VNIC_DIRECT)
|
||||||
self.assertEqual(test_down_event.get_count(), 0)
|
self.assertEqual(test_down_event.get_count(), 0)
|
||||||
self.assertEqual(test_up_event.get_count(), 1)
|
n_utils.wait_until_true(lambda: test_up_event.get_count() == 1,
|
||||||
|
timeout=10)
|
||||||
|
|
||||||
def test_external_port_create_vnic_direct(self):
|
def test_external_port_create_vnic_direct(self):
|
||||||
self._test_external_port_create(portbindings.VNIC_DIRECT)
|
self._test_external_port_create(portbindings.VNIC_DIRECT)
|
||||||
|
|
|
@ -2224,7 +2224,9 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||||
self.assertEqual(webob.exc.HTTPClientError.code,
|
self.assertEqual(webob.exc.HTTPClientError.code,
|
||||||
res.status_int)
|
res.status_int)
|
||||||
|
|
||||||
def test_requested_fixed_ip_address_v6_slaac_router_iface(self):
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
|
def test_requested_fixed_ip_address_v6_slaac_router_iface(self, gr):
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
with self.subnet(gateway_ip='fe80::1',
|
with self.subnet(gateway_ip='fe80::1',
|
||||||
cidr='fe80::/64',
|
cidr='fe80::/64',
|
||||||
ip_version=constants.IP_VERSION_6,
|
ip_version=constants.IP_VERSION_6,
|
||||||
|
@ -2284,7 +2286,9 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||||
self.assertIn({'ip_address': eui_addr,
|
self.assertIn({'ip_address': eui_addr,
|
||||||
'subnet_id': subnet2['subnet']['id']}, ips)
|
'subnet_id': subnet2['subnet']['id']}, ips)
|
||||||
|
|
||||||
def test_create_router_port_ipv4_and_ipv6_slaac_no_fixed_ips(self):
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
|
def test_create_router_port_ipv4_and_ipv6_slaac_no_fixed_ips(self, gr):
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
with self.network() as network:
|
with self.network() as network:
|
||||||
# Create an IPv4 and an IPv6 SLAAC subnet on the network
|
# Create an IPv4 and an IPv6 SLAAC subnet on the network
|
||||||
with self.subnet(network) as subnet_v4,\
|
with self.subnet(network) as subnet_v4,\
|
||||||
|
@ -3764,8 +3768,10 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||||
sport = self.deserialize(self.fmt, req.get_response(self.api))
|
sport = self.deserialize(self.fmt, req.get_response(self.api))
|
||||||
self.assertEqual(0, len(sport['port']['fixed_ips']))
|
self.assertEqual(0, len(sport['port']['fixed_ips']))
|
||||||
|
|
||||||
def test_delete_subnet_ipv6_slaac_router_port_exists(self):
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
|
def test_delete_subnet_ipv6_slaac_router_port_exists(self, gr):
|
||||||
"""Test IPv6 SLAAC subnet delete with a router port using the subnet"""
|
"""Test IPv6 SLAAC subnet delete with a router port using the subnet"""
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
subnet, port = self._create_slaac_subnet_and_port(
|
subnet, port = self._create_slaac_subnet_and_port(
|
||||||
constants.DEVICE_OWNER_ROUTER_INTF)
|
constants.DEVICE_OWNER_ROUTER_INTF)
|
||||||
# Delete the subnet and assert that we get a HTTP 409 error
|
# Delete the subnet and assert that we get a HTTP 409 error
|
||||||
|
@ -4502,7 +4508,9 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||||
ipv6_ra_mode=constants.IPV6_SLAAC,
|
ipv6_ra_mode=constants.IPV6_SLAAC,
|
||||||
ipv6_address_mode=constants.IPV6_SLAAC)
|
ipv6_address_mode=constants.IPV6_SLAAC)
|
||||||
|
|
||||||
def test_create_subnet_ipv6_first_ip_owned_by_router(self):
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
|
def test_create_subnet_ipv6_first_ip_owned_by_router(self, gr):
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
cidr = '2001::/64'
|
cidr = '2001::/64'
|
||||||
with self.network() as network:
|
with self.network() as network:
|
||||||
net_id = network['network']['id']
|
net_id = network['network']['id']
|
||||||
|
@ -4598,10 +4606,12 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||||
self.assertEqual(webob.exc.HTTPClientError.code,
|
self.assertEqual(webob.exc.HTTPClientError.code,
|
||||||
ctx_manager.exception.code)
|
ctx_manager.exception.code)
|
||||||
|
|
||||||
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
def _test_create_subnet_ipv6_auto_addr_with_port_on_network(
|
def _test_create_subnet_ipv6_auto_addr_with_port_on_network(
|
||||||
self, addr_mode, device_owner=DEVICE_OWNER_COMPUTE,
|
self, addr_mode, gr, device_owner=DEVICE_OWNER_COMPUTE,
|
||||||
insert_db_reference_error=False, insert_port_not_found=False,
|
insert_db_reference_error=False, insert_port_not_found=False,
|
||||||
insert_address_allocated=False):
|
insert_address_allocated=False):
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
# Create a network with one IPv4 subnet and one port
|
# Create a network with one IPv4 subnet and one port
|
||||||
with self.network() as network,\
|
with self.network() as network,\
|
||||||
self.subnet(network=network) as v4_subnet,\
|
self.subnet(network=network) as v4_subnet,\
|
||||||
|
|
|
@ -2495,9 +2495,12 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
||||||
def test__update_dnat_entry_if_needed_down_no_dvr(self):
|
def test__update_dnat_entry_if_needed_down_no_dvr(self):
|
||||||
self._test__update_dnat_entry_if_needed(up=False, dvr=False)
|
self._test__update_dnat_entry_if_needed(up=False, dvr=False)
|
||||||
|
|
||||||
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
||||||
'ovn_client.OVNClient._get_router_ports')
|
'ovn_client.OVNClient._get_router_ports')
|
||||||
def _test_update_network_fragmentation(self, new_mtu, expected_opts, grps):
|
def _test_update_network_fragmentation(self, new_mtu, expected_opts, grps,
|
||||||
|
gr):
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
network_attrs = {external_net.EXTERNAL: True}
|
network_attrs = {external_net.EXTERNAL: True}
|
||||||
network = self._make_network(
|
network = self._make_network(
|
||||||
self.fmt, 'net1', True, as_admin=True,
|
self.fmt, 'net1', True, as_admin=True,
|
||||||
|
|
|
@ -1344,15 +1344,19 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
|
||||||
with self.port(device_owner="fake:test"):
|
with self.port(device_owner="fake:test"):
|
||||||
self.assertTrue(cp.called)
|
self.assertTrue(cp.called)
|
||||||
|
|
||||||
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
def _test_no_dhcp_provisioning_blocks_removed_empty_device_owner(
|
def _test_no_dhcp_provisioning_blocks_removed_empty_device_owner(
|
||||||
self, device_owner):
|
self, device_owner, gr):
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
with mock.patch.object(provisioning_blocks,
|
with mock.patch.object(provisioning_blocks,
|
||||||
'remove_provisioning_component') as cp:
|
'remove_provisioning_component') as cp:
|
||||||
with self.port(device_owner=device_owner):
|
with self.port(device_owner=device_owner):
|
||||||
self.assertFalse(cp.called)
|
self.assertFalse(cp.called)
|
||||||
|
|
||||||
|
@mock.patch('neutron.objects.router.Router.get_object')
|
||||||
def _test_no_dhcp_provisioning_blocks_added_empty_device_owner(
|
def _test_no_dhcp_provisioning_blocks_added_empty_device_owner(
|
||||||
self, device_owner):
|
self, device_owner, gr):
|
||||||
|
gr.return_value = {'flavor_id': ''}
|
||||||
with mock.patch.object(provisioning_blocks,
|
with mock.patch.object(provisioning_blocks,
|
||||||
'add_provisioning_component') as cp:
|
'add_provisioning_component') as cp:
|
||||||
with self.port(device_owner=device_owner):
|
with self.port(device_owner=device_owner):
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from neutron_lib.callbacks import events
|
||||||
|
|
||||||
|
|
||||||
|
from neutron.db.models import l3
|
||||||
|
from neutron.services.ovn_l3.service_providers import user_defined
|
||||||
|
from neutron.tests.unit import testlib_api
|
||||||
|
|
||||||
|
|
||||||
|
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
||||||
|
|
||||||
|
|
||||||
|
class TestUserDefined(testlib_api.SqlTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestUserDefined, self).setUp()
|
||||||
|
self.setup_coreplugin(DB_PLUGIN_KLASS)
|
||||||
|
self.fake_l3 = mock.MagicMock()
|
||||||
|
self.fake_l3._make_router_interface_info = mock.MagicMock(
|
||||||
|
return_value='router_interface_info')
|
||||||
|
self.provider = user_defined.UserDefined(self.fake_l3)
|
||||||
|
self.context = 'fake-context'
|
||||||
|
self.router = l3.Router(id='fake-uuid',
|
||||||
|
flavor_id='fake-uuid')
|
||||||
|
self.fake_l3.get_router = mock.MagicMock(return_value=self.router)
|
||||||
|
self.fip = {'router_id': 'fake-uuid'}
|
||||||
|
mock_flavor_plugin = mock.MagicMock()
|
||||||
|
mock_flavor_plugin.get_flavor = mock.MagicMock(
|
||||||
|
return_value={'id': 'fake-uuid'})
|
||||||
|
mock_flavor_plugin.get_flavor_next_provider = mock.MagicMock(
|
||||||
|
return_value=[{'driver': self.provider._user_defined_provider}])
|
||||||
|
self.provider._flavor_plugin_ref = mock_flavor_plugin
|
||||||
|
|
||||||
|
def test__is_user_defined_provider(self):
|
||||||
|
# test the positive case
|
||||||
|
self.assertTrue(self.provider._is_user_defined_provider(
|
||||||
|
self.context, self.router))
|
||||||
|
|
||||||
|
# test the negative case
|
||||||
|
self.provider._flavor_plugin_ref.get_flavor_next_provider = (
|
||||||
|
mock.MagicMock(return_value=[{'driver': None}]))
|
||||||
|
self.assertFalse(self.provider._is_user_defined_provider(
|
||||||
|
self.context, self.router))
|
||||||
|
|
||||||
|
def test_router_processing(self):
|
||||||
|
with mock.patch.object(user_defined.LOG, 'debug') as log:
|
||||||
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.router, self.router),
|
||||||
|
resource_id=self.router['id'],
|
||||||
|
metadata={'subnet_ids': ['subnet-id']})
|
||||||
|
fl_plg = self.provider._flavor_plugin_ref
|
||||||
|
methods = [self.provider._process_router_add_association,
|
||||||
|
self.provider._process_router_create,
|
||||||
|
self.provider._process_router_update,
|
||||||
|
self.provider._process_router_delete,
|
||||||
|
self.provider._process_remove_router_interface]
|
||||||
|
for method in methods:
|
||||||
|
method('resource', 'event', self, payload)
|
||||||
|
fl_plg.get_flavor.assert_called_once()
|
||||||
|
fl_plg.get_flavor_next_provider.assert_called_once()
|
||||||
|
log.assert_called_once()
|
||||||
|
fl_plg.get_flavor.reset_mock()
|
||||||
|
fl_plg.get_flavor_next_provider.reset_mock()
|
||||||
|
log.reset_mock()
|
||||||
|
|
||||||
|
def test_add_router_interface(self):
|
||||||
|
with mock.patch.object(user_defined.LOG, 'debug') as log:
|
||||||
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.router, self.router),
|
||||||
|
resource_id=self.router['id'],
|
||||||
|
metadata={'subnet_ids': ['subnet-id'],
|
||||||
|
'port': {'tenant_id': 'tenant-id',
|
||||||
|
'id': 'id',
|
||||||
|
'network_id': 'network-id'},
|
||||||
|
'subnets': [{'id': 'id'}]})
|
||||||
|
fl_plg = self.provider._flavor_plugin_ref
|
||||||
|
l3_plg = self.fake_l3
|
||||||
|
self.provider._process_add_router_interface('resource',
|
||||||
|
'event',
|
||||||
|
self,
|
||||||
|
payload)
|
||||||
|
l3_plg._make_router_interface_info.assert_called_once()
|
||||||
|
fl_plg.get_flavor.assert_called_once()
|
||||||
|
fl_plg.get_flavor_next_provider.assert_called_once()
|
||||||
|
log.assert_called_once()
|
||||||
|
|
||||||
|
def test_floatingip_processing(self):
|
||||||
|
# Test all the methods related to FIP processing
|
||||||
|
with mock.patch.object(user_defined.LOG, 'debug') as log:
|
||||||
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.fip, self.fip))
|
||||||
|
fl_plg = self.provider._flavor_plugin_ref
|
||||||
|
l3_plg = self.fake_l3
|
||||||
|
methods = [self.provider._process_floatingip_create,
|
||||||
|
self.provider._process_floatingip_update,
|
||||||
|
self.provider._process_floatingip_delete,
|
||||||
|
self.provider._process_floatingip_status_update]
|
||||||
|
for method in methods:
|
||||||
|
method('resource', 'event', self, payload)
|
||||||
|
l3_plg.get_router.assert_called_once()
|
||||||
|
fl_plg.get_flavor.assert_called_once()
|
||||||
|
fl_plg.get_flavor_next_provider.assert_called_once()
|
||||||
|
log.assert_called_once()
|
||||||
|
l3_plg.get_router.reset_mock()
|
||||||
|
fl_plg.get_flavor.reset_mock()
|
||||||
|
fl_plg.get_flavor_next_provider.reset_mock()
|
||||||
|
log.reset_mock()
|
|
@ -37,6 +37,8 @@ from neutron.common.ovn import utils
|
||||||
from neutron.conf.plugins.ml2.drivers import driver_type as driver_type_conf
|
from neutron.conf.plugins.ml2.drivers import driver_type as driver_type_conf
|
||||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
|
||||||
from neutron.db import extraroute_db
|
from neutron.db import extraroute_db
|
||||||
|
from neutron.db.models import l3 as l3_models
|
||||||
|
from neutron.db.models import ovn as ovn_models
|
||||||
from neutron import manager as neutron_manager
|
from neutron import manager as neutron_manager
|
||||||
from neutron.plugins.ml2 import managers
|
from neutron.plugins.ml2 import managers
|
||||||
from neutron.services.ovn_l3 import exceptions as ovn_l3_exc
|
from neutron.services.ovn_l3 import exceptions as ovn_l3_exc
|
||||||
|
@ -78,6 +80,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
attrs=network_attrs).info()
|
attrs=network_attrs).info()
|
||||||
self.fake_router_port = {'device_id': '',
|
self.fake_router_port = {'device_id': '',
|
||||||
'network_id': self.fake_network['id'],
|
'network_id': self.fake_network['id'],
|
||||||
|
'tenant_id': 'tenant-id',
|
||||||
'device_owner': 'network:router_interface',
|
'device_owner': 'network:router_interface',
|
||||||
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
||||||
'status': constants.PORT_STATUS_ACTIVE,
|
'status': constants.PORT_STATUS_ACTIVE,
|
||||||
|
@ -103,6 +106,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.fake_router = {'id': 'router-id',
|
self.fake_router = {'id': 'router-id',
|
||||||
'name': 'router',
|
'name': 'router',
|
||||||
'admin_state_up': False,
|
'admin_state_up': False,
|
||||||
|
'flavor_id': None,
|
||||||
'routes': [{'destination': '1.1.1.0/24',
|
'routes': [{'destination': '1.1.1.0/24',
|
||||||
'nexthop': '10.0.0.2'}]}
|
'nexthop': '10.0.0.2'}]}
|
||||||
self.fake_router_interface_info = {
|
self.fake_router_interface_info = {
|
||||||
|
@ -122,6 +126,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'id': 'router-id',
|
'id': 'router-id',
|
||||||
'name': 'router',
|
'name': 'router',
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
|
'flavor_id': None,
|
||||||
'external_gateway_info': self.fake_external_fixed_ips,
|
'external_gateway_info': self.fake_external_fixed_ips,
|
||||||
'gw_port_id': 'gw-port-id'
|
'gw_port_id': 'gw-port-id'
|
||||||
}
|
}
|
||||||
|
@ -179,6 +184,8 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.fake_floating_ip['router_id'])},
|
self.fake_floating_ip['router_id'])},
|
||||||
}))
|
}))
|
||||||
self.l3_inst = directory.get_plugin(plugin_constants.L3)
|
self.l3_inst = directory.get_plugin(plugin_constants.L3)
|
||||||
|
ovn_provider = self.l3_inst.l3_driver_controller.default_provider
|
||||||
|
self.ovn_drv = self.l3_inst.l3_driver_controller.drivers[ovn_provider]
|
||||||
self.lb_id = uuidutils.generate_uuid()
|
self.lb_id = uuidutils.generate_uuid()
|
||||||
self.member_subnet = {'id': 'subnet-id',
|
self.member_subnet = {'id': 'subnet-id',
|
||||||
'ip_version': 4,
|
'ip_version': 4,
|
||||||
|
@ -292,6 +299,9 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.get_rev_p = self._start_mock(
|
self.get_rev_p = self._start_mock(
|
||||||
'neutron.common.ovn.utils.get_revision_number',
|
'neutron.common.ovn.utils.get_revision_number',
|
||||||
return_value=1)
|
return_value=1)
|
||||||
|
self.get_rev_row_p = self._start_mock(
|
||||||
|
'neutron.db.ovn_revision_numbers_db.get_revision_row',
|
||||||
|
return_value=ovn_models.OVNRevisionNumbers(revision_number=1))
|
||||||
self.admin_context = mock.Mock()
|
self.admin_context = mock.Mock()
|
||||||
self._start_mock(
|
self._start_mock(
|
||||||
'neutron_lib.context.get_admin_context',
|
'neutron_lib.context.get_admin_context',
|
||||||
|
@ -326,13 +336,26 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
driver, None)
|
driver, None)
|
||||||
self.assertEqual(fake_mech_driver.obj, result)
|
self.assertEqual(fake_mech_driver.obj, result)
|
||||||
|
|
||||||
|
def _create_payload_for_router_interface(self, router_id,
|
||||||
|
pass_subnet=True):
|
||||||
|
router_obj = l3_models.Router(id=router_id, flavor_id=None)
|
||||||
|
metadata = {'port': self.fake_router_port}
|
||||||
|
if pass_subnet:
|
||||||
|
metadata['subnets'] = [self.fake_subnet]
|
||||||
|
else:
|
||||||
|
metadata['subnet_ids'] = [self.fake_subnet['id']]
|
||||||
|
return events.DBEventPayload(self.context, metadata=metadata,
|
||||||
|
states=(router_obj,),
|
||||||
|
resource_id=router_id)
|
||||||
|
|
||||||
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
||||||
def test_add_router_interface(self, func):
|
def test_add_router_interface(self, func):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
|
||||||
func.return_value = self.fake_router_interface_info
|
func.return_value = self.fake_router_interface_info
|
||||||
self.l3_inst.add_router_interface(self.context, router_id,
|
payload = self._create_payload_for_router_interface(router_id)
|
||||||
interface_info)
|
self.ovn_drv._process_add_router_interface(resources.ROUTER_INTERFACE,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
||||||
**self.fake_router_port_assert)
|
**self.fake_router_port_assert)
|
||||||
self.l3_inst._nb_ovn.set_lrouter_port_in_lswitch_port.\
|
self.l3_inst._nb_ovn.set_lrouter_port_in_lswitch_port.\
|
||||||
|
@ -346,7 +369,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
||||||
def test_add_router_interface_update_lrouter_port(self, func):
|
def test_add_router_interface_update_lrouter_port(self, func):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
|
||||||
func.return_value = {'id': router_id,
|
func.return_value = {'id': router_id,
|
||||||
'port_id': 'router-port-id',
|
'port_id': 'router-port-id',
|
||||||
'subnet_id': 'subnet-id1',
|
'subnet_id': 'subnet-id1',
|
||||||
|
@ -366,8 +388,10 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
||||||
'network_id': 'network-id1'}
|
'network_id': 'network-id1'}
|
||||||
fake_rtr_intf_networks = ['2001:db8::1/24', '2001:dba::1/24']
|
fake_rtr_intf_networks = ['2001:db8::1/24', '2001:dba::1/24']
|
||||||
self.l3_inst.add_router_interface(self.context, router_id,
|
payload = self._create_payload_for_router_interface(router_id)
|
||||||
interface_info)
|
self.ovn_drv._process_add_router_interface(resources.ROUTER_INTERFACE,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
called_args_dict = (
|
called_args_dict = (
|
||||||
self.l3_inst._nb_ovn.update_lrouter_port.call_args_list[0][1])
|
self.l3_inst._nb_ovn.update_lrouter_port.call_args_list[0][1])
|
||||||
|
|
||||||
|
@ -382,12 +406,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
|
|
||||||
def test_remove_router_interface(self):
|
def test_remove_router_interface(self):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
|
||||||
self.get_port.side_effect = n_exc.PortNotFound(
|
self.get_port.side_effect = n_exc.PortNotFound(
|
||||||
port_id='router-port-id')
|
port_id='router-port-id')
|
||||||
|
|
||||||
self.l3_inst.remove_router_interface(
|
payload = self._create_payload_for_router_interface(router_id,
|
||||||
self.context, router_id, interface_info)
|
pass_subnet=False)
|
||||||
|
self.ovn_drv._process_remove_router_interface(
|
||||||
|
resources.ROUTER_INTERFACE, events.AFTER_DELETE, self, payload)
|
||||||
|
|
||||||
self.l3_inst._nb_ovn.lrp_del.assert_called_once_with(
|
self.l3_inst._nb_ovn.lrp_del.assert_called_once_with(
|
||||||
'lrp-router-port-id', 'neutron-router-id', if_exists=True)
|
'lrp-router-port-id', 'neutron-router-id', if_exists=True)
|
||||||
|
@ -396,9 +421,10 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
|
|
||||||
def test_remove_router_interface_update_lrouter_port(self):
|
def test_remove_router_interface_update_lrouter_port(self):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
payload = self._create_payload_for_router_interface(router_id,
|
||||||
self.l3_inst.remove_router_interface(
|
pass_subnet=False)
|
||||||
self.context, router_id, interface_info)
|
self.ovn_drv._process_remove_router_interface(
|
||||||
|
resources.ROUTER_INTERFACE, events.AFTER_DELETE, self, payload)
|
||||||
|
|
||||||
self.l3_inst._nb_ovn.update_lrouter_port.assert_called_once_with(
|
self.l3_inst._nb_ovn.update_lrouter_port.assert_called_once_with(
|
||||||
if_exists=False, name='lrp-router-port-id',
|
if_exists=False, name='lrp-router-port-id',
|
||||||
|
@ -413,14 +439,15 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
|
|
||||||
def test_remove_router_interface_router_not_found(self):
|
def test_remove_router_interface_router_not_found(self):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
|
||||||
self.get_port.side_effect = n_exc.PortNotFound(
|
self.get_port.side_effect = n_exc.PortNotFound(
|
||||||
port_id='router-port-id')
|
port_id='router-port-id')
|
||||||
self.get_router.side_effect = l3_exc.RouterNotFound(
|
self.get_router.side_effect = l3_exc.RouterNotFound(
|
||||||
router_id='router-id')
|
router_id='router-id')
|
||||||
|
|
||||||
self.l3_inst.remove_router_interface(
|
payload = self._create_payload_for_router_interface(router_id,
|
||||||
self.context, router_id, interface_info)
|
pass_subnet=False)
|
||||||
|
self.ovn_drv._process_remove_router_interface(
|
||||||
|
resources.ROUTER_INTERFACE, events.AFTER_DELETE, self, payload)
|
||||||
|
|
||||||
self.get_router.assert_called_once_with(self.context, 'router-id')
|
self.get_router.assert_called_once_with(self.context, 'router-id')
|
||||||
self.l3_inst._nb_ovn.lrp_del.assert_called_once_with(
|
self.l3_inst._nb_ovn.lrp_del.assert_called_once_with(
|
||||||
|
@ -433,31 +460,40 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb'
|
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb'
|
||||||
'.ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
'.ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
||||||
def test_update_router_admin_state_change(self, get_rps, func):
|
def test_update_router_admin_state_change(self, get_rps, func):
|
||||||
router_id = 'router-id'
|
|
||||||
new_router = self.fake_router.copy()
|
new_router = self.fake_router.copy()
|
||||||
updated_data = {'admin_state_up': True}
|
updated_data = {'admin_state_up': True}
|
||||||
new_router.update(updated_data)
|
new_router.update(updated_data)
|
||||||
func.return_value = new_router
|
func.return_value = new_router
|
||||||
self.l3_inst.update_router(self.context, router_id,
|
payload = self._create_payload_for_router_update(self.fake_router,
|
||||||
{'router': updated_data})
|
new_router)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.update_lrouter.assert_called_once_with(
|
self.l3_inst._nb_ovn.update_lrouter.assert_called_once_with(
|
||||||
'neutron-router-id', enabled=True, external_ids={
|
'neutron-router-id', enabled=True, external_ids={
|
||||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
||||||
ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router',
|
ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router',
|
||||||
ovn_const.OVN_AZ_HINTS_EXT_ID_KEY: ''})
|
ovn_const.OVN_AZ_HINTS_EXT_ID_KEY: ''})
|
||||||
|
|
||||||
|
def _create_payload_for_router_update(self, original, updated):
|
||||||
|
return events.DBEventPayload(self.context,
|
||||||
|
states=(original, updated),
|
||||||
|
resource_id=original['id'])
|
||||||
|
|
||||||
@mock.patch('neutron.db.extraroute_db.ExtraRoute_dbonly_mixin.'
|
@mock.patch('neutron.db.extraroute_db.ExtraRoute_dbonly_mixin.'
|
||||||
'update_router')
|
'update_router')
|
||||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
||||||
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
||||||
def test_update_router_name_change(self, get_rps, func):
|
def test_update_router_name_change(self, get_rps, func):
|
||||||
router_id = 'router-id'
|
|
||||||
new_router = self.fake_router.copy()
|
new_router = self.fake_router.copy()
|
||||||
updated_data = {'name': 'test'}
|
updated_data = {'name': 'test'}
|
||||||
new_router.update(updated_data)
|
new_router.update(updated_data)
|
||||||
func.return_value = new_router
|
func.return_value = new_router
|
||||||
self.l3_inst.update_router(self.context, router_id,
|
payload = self._create_payload_for_router_update(self.fake_router,
|
||||||
{'router': updated_data})
|
new_router)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.update_lrouter.assert_called_once_with(
|
self.l3_inst._nb_ovn.update_lrouter.assert_called_once_with(
|
||||||
'neutron-router-id', enabled=False,
|
'neutron-router-id', enabled=False,
|
||||||
external_ids={ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'test',
|
external_ids={ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'test',
|
||||||
|
@ -492,7 +528,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
||||||
def test_update_router_static_route_change(self, get_rps, func,
|
def test_update_router_static_route_change(self, get_rps, func,
|
||||||
mock_routes):
|
mock_routes):
|
||||||
router_id = 'router-id'
|
|
||||||
get_rps.return_value = [{'device_id': '',
|
get_rps.return_value = [{'device_id': '',
|
||||||
'device_owner': 'network:router_interface',
|
'device_owner': 'network:router_interface',
|
||||||
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
||||||
|
@ -506,8 +541,11 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'nexthop': '10.0.0.3'}]}
|
'nexthop': '10.0.0.3'}]}
|
||||||
new_router.update(updated_data)
|
new_router.update(updated_data)
|
||||||
func.return_value = new_router
|
func.return_value = new_router
|
||||||
self.l3_inst.update_router(self.context, router_id,
|
payload = self._create_payload_for_router_update(self.fake_router,
|
||||||
{'router': updated_data})
|
new_router)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
|
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
|
||||||
'neutron-router-id',
|
'neutron-router-id',
|
||||||
ip_prefix='2.2.2.0/24', nexthop='10.0.0.3')
|
ip_prefix='2.2.2.0/24', nexthop='10.0.0.3')
|
||||||
|
@ -522,7 +560,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
||||||
def test_update_router_static_route_clear(self, get_rps, func,
|
def test_update_router_static_route_clear(self, get_rps, func,
|
||||||
mock_routes):
|
mock_routes):
|
||||||
router_id = 'router-id'
|
|
||||||
get_rps.return_value = [{'device_id': '',
|
get_rps.return_value = [{'device_id': '',
|
||||||
'device_owner': 'network:router_interface',
|
'device_owner': 'network:router_interface',
|
||||||
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
'mac_address': 'aa:aa:aa:aa:aa:aa',
|
||||||
|
@ -535,8 +572,11 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
updated_data = {'routes': []}
|
updated_data = {'routes': []}
|
||||||
new_router.update(updated_data)
|
new_router.update(updated_data)
|
||||||
func.return_value = new_router
|
func.return_value = new_router
|
||||||
self.l3_inst.update_router(self.context, router_id,
|
payload = self._create_payload_for_router_update(self.fake_router,
|
||||||
{'router': updated_data})
|
new_router)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.add_static_route.assert_not_called()
|
self.l3_inst._nb_ovn.add_static_route.assert_not_called()
|
||||||
self.l3_inst._nb_ovn.delete_static_route.assert_called_once_with(
|
self.l3_inst._nb_ovn.delete_static_route.assert_called_once_with(
|
||||||
'neutron-router-id',
|
'neutron-router-id',
|
||||||
|
@ -546,12 +586,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
'ovn_client.OVNClient._get_v4_network_of_all_router_ports')
|
||||||
def test_create_router_with_ext_gw(self, get_rps):
|
def test_create_router_with_ext_gw(self, get_rps):
|
||||||
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
||||||
router = {'router': {'name': 'router'}}
|
|
||||||
self.get_subnet.return_value = self.fake_ext_subnet
|
self.get_subnet.return_value = self.fake_ext_subnet
|
||||||
self.get_port.return_value = self.fake_ext_gw_port
|
self.get_port.return_value = self.fake_ext_gw_port
|
||||||
get_rps.return_value = self.fake_ext_subnet['cidr']
|
get_rps.return_value = self.fake_ext_subnet['cidr']
|
||||||
|
|
||||||
self.l3_inst.create_router(self.context, router)
|
payload = events.DBEventPayload(self.context,
|
||||||
|
states=(self.fake_router_with_ext_gw,))
|
||||||
|
self.ovn_drv._process_router_create(resources.ROUTER,
|
||||||
|
events.AFTER_CREATE, self, payload)
|
||||||
|
|
||||||
external_ids = {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router',
|
external_ids = {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router',
|
||||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
||||||
|
@ -596,7 +638,12 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
fake_resources.FakeOVNRouter.from_neutron_router(
|
fake_resources.FakeOVNRouter.from_neutron_router(
|
||||||
self.fake_router_with_ext_gw))
|
self.fake_router_with_ext_gw))
|
||||||
|
|
||||||
self.l3_inst.delete_router(self.context, 'router-id')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_router_with_ext_gw,),
|
||||||
|
resource_id=self.fake_router_with_ext_gw['id'])
|
||||||
|
self.ovn_drv._process_router_delete(resources.ROUTER,
|
||||||
|
events.AFTER_DELETE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
self.l3_inst._nb_ovn.delete_lrouter.assert_called_once_with(
|
self.l3_inst._nb_ovn.delete_lrouter.assert_called_once_with(
|
||||||
'neutron-router-id')
|
'neutron-router-id')
|
||||||
|
@ -606,12 +653,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
||||||
def test_add_router_interface_with_gateway_set(self, ari, grps):
|
def test_add_router_interface_with_gateway_set(self, ari, grps):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
|
||||||
ari.return_value = self.fake_router_interface_info
|
ari.return_value = self.fake_router_interface_info
|
||||||
self.get_router.return_value = self.fake_router_with_ext_gw
|
self.get_router.return_value = self.fake_router_with_ext_gw
|
||||||
|
|
||||||
self.l3_inst.add_router_interface(self.context, router_id,
|
payload = self._create_payload_for_router_interface(router_id)
|
||||||
interface_info)
|
self.ovn_drv._process_add_router_interface(resources.ROUTER_INTERFACE,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
||||||
**self.fake_router_port_assert)
|
**self.fake_router_port_assert)
|
||||||
|
@ -632,14 +680,15 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
def test_add_router_interface_with_gateway_set_and_snat_disabled(
|
def test_add_router_interface_with_gateway_set_and_snat_disabled(
|
||||||
self, ari, grps):
|
self, ari, grps):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
|
||||||
ari.return_value = self.fake_router_interface_info
|
ari.return_value = self.fake_router_interface_info
|
||||||
get_router = self.fake_router_with_ext_gw
|
get_router = self.fake_router_with_ext_gw
|
||||||
get_router['external_gateway_info']['enable_snat'] = False
|
get_router['external_gateway_info']['enable_snat'] = False
|
||||||
self.get_router.return_value = get_router
|
self.get_router.return_value = get_router
|
||||||
|
|
||||||
self.l3_inst.add_router_interface(self.context, router_id,
|
payload = self._create_payload_for_router_interface(router_id)
|
||||||
interface_info)
|
self.ovn_drv._process_add_router_interface(resources.ROUTER_INTERFACE,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
||||||
**self.fake_router_port_assert)
|
**self.fake_router_port_assert)
|
||||||
|
@ -655,7 +704,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.add_router_interface')
|
||||||
def test_add_router_interface_vlan_network(self, ari, grps, gn):
|
def test_add_router_interface_vlan_network(self, ari, grps, gn):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id'}
|
|
||||||
ari.return_value = self.fake_router_interface_info
|
ari.return_value = self.fake_router_interface_info
|
||||||
self.get_router.return_value = self.fake_router_with_ext_gw
|
self.get_router.return_value = self.fake_router_with_ext_gw
|
||||||
|
|
||||||
|
@ -664,8 +712,10 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
fake_network_vlan[pnet.NETWORK_TYPE] = constants.TYPE_VLAN
|
fake_network_vlan[pnet.NETWORK_TYPE] = constants.TYPE_VLAN
|
||||||
gn.return_value = fake_network_vlan
|
gn.return_value = fake_network_vlan
|
||||||
|
|
||||||
self.l3_inst.add_router_interface(self.context, router_id,
|
payload = self._create_payload_for_router_interface(router_id)
|
||||||
interface_info)
|
self.ovn_drv._process_add_router_interface(resources.ROUTER_INTERFACE,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
# Make sure that the "reside-on-redirect-chassis" option was
|
# Make sure that the "reside-on-redirect-chassis" option was
|
||||||
# set to the new router port
|
# set to the new router port
|
||||||
|
@ -689,13 +739,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
|
|
||||||
def test_remove_router_interface_with_gateway_set(self):
|
def test_remove_router_interface_with_gateway_set(self):
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id',
|
|
||||||
'subnet_id': 'subnet-id'}
|
|
||||||
self.get_router.return_value = self.fake_router_with_ext_gw
|
self.get_router.return_value = self.fake_router_with_ext_gw
|
||||||
self.get_port.side_effect = n_exc.PortNotFound(
|
self.get_port.side_effect = n_exc.PortNotFound(
|
||||||
port_id='router-port-id')
|
port_id='router-port-id')
|
||||||
self.l3_inst.remove_router_interface(
|
payload = self._create_payload_for_router_interface(router_id,
|
||||||
self.context, router_id, interface_info)
|
pass_subnet=False)
|
||||||
|
self.ovn_drv._process_remove_router_interface(
|
||||||
|
resources.ROUTER_INTERFACE, events.AFTER_DELETE, self, payload)
|
||||||
nb_ovn = self.l3_inst._nb_ovn
|
nb_ovn = self.l3_inst._nb_ovn
|
||||||
nb_ovn.lrp_del.assert_called_once_with(
|
nb_ovn.lrp_del.assert_called_once_with(
|
||||||
'lrp-router-port-id', 'neutron-router-id', if_exists=True)
|
'lrp-router-port-id', 'neutron-router-id', if_exists=True)
|
||||||
|
@ -711,15 +761,17 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'update_router')
|
'update_router')
|
||||||
def test_update_router_with_ext_gw(self, ur, grps):
|
def test_update_router_with_ext_gw(self, ur, grps):
|
||||||
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
||||||
router = {'router': {'name': 'router'}}
|
|
||||||
self.get_router.return_value = self.fake_router_without_ext_gw
|
|
||||||
ur.return_value = self.fake_router_with_ext_gw
|
ur.return_value = self.fake_router_with_ext_gw
|
||||||
self.get_subnet.side_effect = lambda ctx, sid: {
|
self.get_subnet.side_effect = lambda ctx, sid: {
|
||||||
'ext-subnet-id': self.fake_ext_subnet}.get(sid, self.fake_subnet)
|
'ext-subnet-id': self.fake_ext_subnet}.get(sid, self.fake_subnet)
|
||||||
self.get_port.return_value = self.fake_ext_gw_port
|
self.get_port.return_value = self.fake_ext_gw_port
|
||||||
grps.return_value = self.fake_router_ports
|
grps.return_value = self.fake_router_ports
|
||||||
|
|
||||||
self.l3_inst.update_router(self.context, 'router-id', router)
|
payload = self._create_payload_for_router_update(
|
||||||
|
self.fake_router_without_ext_gw, self.fake_router_with_ext_gw)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
self.l3_inst._nb_ovn.add_lrouter_port.assert_called_once_with(
|
||||||
**self.fake_ext_gw_port_assert)
|
**self.fake_ext_gw_port_assert)
|
||||||
|
@ -748,7 +800,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
grps, mock_get_gw):
|
grps, mock_get_gw):
|
||||||
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
||||||
mock_get_gw.return_value = [mock.sentinel.GwRoute]
|
mock_get_gw.return_value = [mock.sentinel.GwRoute]
|
||||||
router = {'router': {'name': 'router'}}
|
|
||||||
fake_old_ext_subnet = {'id': 'old-ext-subnet-id',
|
fake_old_ext_subnet = {'id': 'old-ext-subnet-id',
|
||||||
'ip_version': 4,
|
'ip_version': 4,
|
||||||
'cidr': '192.168.2.0/24',
|
'cidr': '192.168.2.0/24',
|
||||||
|
@ -768,7 +819,11 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.get_port.return_value = self.fake_ext_gw_port
|
self.get_port.return_value = self.fake_ext_gw_port
|
||||||
grps.return_value = self.fake_router_ports
|
grps.return_value = self.fake_router_ports
|
||||||
|
|
||||||
self.l3_inst.update_router(self.context, 'router-id', router)
|
payload = self._create_payload_for_router_update(
|
||||||
|
self.get_router.return_value, self.fake_router_with_ext_gw)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
# Check deleting old router gateway
|
# Check deleting old router gateway
|
||||||
self.l3_inst._nb_ovn.delete_lrouter_ext_gw.assert_called_once_with(
|
self.l3_inst._nb_ovn.delete_lrouter_ext_gw.assert_called_once_with(
|
||||||
|
@ -805,7 +860,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
grps, mock_get_gw):
|
grps, mock_get_gw):
|
||||||
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
||||||
mock_get_gw.return_value = [mock.sentinel.GwRoute]
|
mock_get_gw.return_value = [mock.sentinel.GwRoute]
|
||||||
router = {'router': {'name': 'router'}}
|
|
||||||
# Old gateway info with same subnet and different ip address
|
# Old gateway info with same subnet and different ip address
|
||||||
gr_value = copy.deepcopy(self.fake_router_with_ext_gw)
|
gr_value = copy.deepcopy(self.fake_router_with_ext_gw)
|
||||||
gr_value['external_gateway_info'][
|
gr_value['external_gateway_info'][
|
||||||
|
@ -818,7 +872,11 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.get_port.return_value = self.fake_ext_gw_port
|
self.get_port.return_value = self.fake_ext_gw_port
|
||||||
grps.return_value = self.fake_router_ports
|
grps.return_value = self.fake_router_ports
|
||||||
|
|
||||||
self.l3_inst.update_router(self.context, 'router-id', router)
|
payload = self._create_payload_for_router_update(
|
||||||
|
gr_value, self.fake_router_with_ext_gw)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
# Check deleting old router gateway
|
# Check deleting old router gateway
|
||||||
self.l3_inst._nb_ovn.delete_lrouter_ext_gw.assert_called_once_with(
|
self.l3_inst._nb_ovn.delete_lrouter_ext_gw.assert_called_once_with(
|
||||||
|
@ -868,8 +926,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'update_router')
|
'update_router')
|
||||||
def test_update_router_with_ext_gw_and_disabled_snat(self, ur, grps):
|
def test_update_router_with_ext_gw_and_disabled_snat(self, ur, grps):
|
||||||
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
||||||
router = {'router': {'name': 'router'}}
|
|
||||||
self.get_router.return_value = self.fake_router_without_ext_gw
|
|
||||||
ur.return_value = self.fake_router_with_ext_gw
|
ur.return_value = self.fake_router_with_ext_gw
|
||||||
ur.return_value['external_gateway_info']['enable_snat'] = False
|
ur.return_value['external_gateway_info']['enable_snat'] = False
|
||||||
self.get_subnet.side_effect = lambda ctx, sid: {
|
self.get_subnet.side_effect = lambda ctx, sid: {
|
||||||
|
@ -877,7 +933,11 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.get_port.return_value = self.fake_ext_gw_port
|
self.get_port.return_value = self.fake_ext_gw_port
|
||||||
grps.return_value = self.fake_router_ports
|
grps.return_value = self.fake_router_ports
|
||||||
|
|
||||||
self.l3_inst.update_router(self.context, 'router-id', router)
|
payload = self._create_payload_for_router_update(
|
||||||
|
self.fake_router_without_ext_gw, self.fake_router_with_ext_gw)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
# Need not check lsp and lrp here, it has been tested in other cases
|
# Need not check lsp and lrp here, it has been tested in other cases
|
||||||
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
|
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
|
||||||
|
@ -892,7 +952,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
@mock.patch('neutron.db.extraroute_db.ExtraRoute_dbonly_mixin.'
|
@mock.patch('neutron.db.extraroute_db.ExtraRoute_dbonly_mixin.'
|
||||||
'update_router')
|
'update_router')
|
||||||
def test_enable_snat(self, ur, grps):
|
def test_enable_snat(self, ur, grps):
|
||||||
router = {'router': {'name': 'router'}}
|
|
||||||
gr_value = copy.deepcopy(self.fake_router_with_ext_gw)
|
gr_value = copy.deepcopy(self.fake_router_with_ext_gw)
|
||||||
gr_value['external_gateway_info']['enable_snat'] = False
|
gr_value['external_gateway_info']['enable_snat'] = False
|
||||||
self.get_router.return_value = gr_value
|
self.get_router.return_value = gr_value
|
||||||
|
@ -905,7 +964,11 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.get_port.return_value = self.fake_ext_gw_port
|
self.get_port.return_value = self.fake_ext_gw_port
|
||||||
grps.return_value = self.fake_router_ports
|
grps.return_value = self.fake_router_ports
|
||||||
|
|
||||||
self.l3_inst.update_router(self.context, 'router-id', router)
|
payload = self._create_payload_for_router_update(
|
||||||
|
self.fake_router_with_ext_gw, ur.return_value)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
self.l3_inst._nb_ovn.delete_static_route.assert_not_called()
|
self.l3_inst._nb_ovn.delete_static_route.assert_not_called()
|
||||||
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
||||||
|
@ -927,7 +990,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
mock_get_gw.return_value = [mock.sentinel.GwRoute]
|
mock_get_gw.return_value = [mock.sentinel.GwRoute]
|
||||||
mock_snats.return_value = [mock.sentinel.NAT]
|
mock_snats.return_value = [mock.sentinel.NAT]
|
||||||
mock_ext_ips.return_value = False
|
mock_ext_ips.return_value = False
|
||||||
router = {'router': {'name': 'router'}}
|
|
||||||
self.get_router.return_value = self.fake_router_with_ext_gw
|
self.get_router.return_value = self.fake_router_with_ext_gw
|
||||||
ur.return_value = copy.deepcopy(self.fake_router_with_ext_gw)
|
ur.return_value = copy.deepcopy(self.fake_router_with_ext_gw)
|
||||||
ur.return_value['external_gateway_info']['enable_snat'] = False
|
ur.return_value['external_gateway_info']['enable_snat'] = False
|
||||||
|
@ -936,7 +998,11 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.get_port.return_value = self.fake_ext_gw_port
|
self.get_port.return_value = self.fake_ext_gw_port
|
||||||
grps.return_value = self.fake_router_ports
|
grps.return_value = self.fake_router_ports
|
||||||
|
|
||||||
self.l3_inst.update_router(self.context, 'router-id', router)
|
payload = self._create_payload_for_router_update(
|
||||||
|
self.fake_router_with_ext_gw, ur.return_value)
|
||||||
|
self.ovn_drv._process_router_update(resources.ROUTER,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
nb_ovn = self.l3_inst._nb_ovn
|
nb_ovn = self.l3_inst._nb_ovn
|
||||||
nb_ovn.delete_static_route.assert_not_called()
|
nb_ovn.delete_static_route.assert_not_called()
|
||||||
|
@ -949,7 +1015,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
def test_create_floatingip(self):
|
def test_create_floatingip(self):
|
||||||
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
||||||
self._get_floatingip.return_value = {'floating_port_id': 'fip-port-id'}
|
self._get_floatingip.return_value = {'floating_port_id': 'fip-port-id'}
|
||||||
self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
||||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
||||||
|
@ -975,7 +1047,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self._get_floatingip.return_value = {'floating_port_id': 'fip-port-id'}
|
self._get_floatingip.return_value = {'floating_port_id': 'fip-port-id'}
|
||||||
config.cfg.CONF.set_override(
|
config.cfg.CONF.set_override(
|
||||||
'enable_distributed_floating_ip', True, group='ovn')
|
'enable_distributed_floating_ip', True, group='ovn')
|
||||||
self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
||||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
||||||
|
@ -1003,7 +1081,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self._get_floatingip.return_value = {'floating_port_id': 'fip-port-id'}
|
self._get_floatingip.return_value = {'floating_port_id': 'fip-port-id'}
|
||||||
config.cfg.CONF.set_override(
|
config.cfg.CONF.set_override(
|
||||||
'enable_distributed_floating_ip', True, group='ovn')
|
'enable_distributed_floating_ip', True, group='ovn')
|
||||||
self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
||||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
||||||
|
@ -1026,7 +1110,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.l3_inst._nb_ovn.get_lrouter_nat_rules.return_value = [
|
self.l3_inst._nb_ovn.get_lrouter_nat_rules.return_value = [
|
||||||
{'external_ip': '192.168.0.10', 'logical_ip': '10.0.0.6',
|
{'external_ip': '192.168.0.10', 'logical_ip': '10.0.0.6',
|
||||||
'type': 'dnat_and_snat', 'uuid': 'uuid1'}]
|
'type': 'dnat_and_snat', 'uuid': 'uuid1'}]
|
||||||
self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
||||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
ovn_const.OVN_REV_NUM_EXT_ID_KEY: '1',
|
||||||
|
@ -1051,7 +1141,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.l3_inst._nb_ovn.get_lrouter_nat_rules.return_value = [
|
self.l3_inst._nb_ovn.get_lrouter_nat_rules.return_value = [
|
||||||
{'external_ip': '192.168.0.10', 'logical_ip': '10.0.0.0/24',
|
{'external_ip': '192.168.0.10', 'logical_ip': '10.0.0.0/24',
|
||||||
'type': 'snat', 'uuid': 'uuid1'}]
|
'type': 'snat', 'uuid': 'uuid1'}]
|
||||||
self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.set_nat_rule_in_lrouter.assert_not_called()
|
self.l3_inst._nb_ovn.set_nat_rule_in_lrouter.assert_not_called()
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip['id'],
|
||||||
|
@ -1075,7 +1171,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
foo_lport = fake_resources.FakeOvsdbRow.create_one_ovsdb_row()
|
foo_lport = fake_resources.FakeOvsdbRow.create_one_ovsdb_row()
|
||||||
foo_lport.uuid = 'foo-port'
|
foo_lport.uuid = 'foo-port'
|
||||||
self.l3_inst._nb_ovn.get_lswitch_port.return_value = foo_lport
|
self.l3_inst._nb_ovn.get_lswitch_port.return_value = foo_lport
|
||||||
self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
calls = [mock.call(
|
calls = [mock.call(
|
||||||
'Logical_Switch_Port',
|
'Logical_Switch_Port',
|
||||||
'foo-port',
|
'foo-port',
|
||||||
|
@ -1092,7 +1194,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
self.l3_inst._nb_ovn.is_col_present.return_value = True
|
||||||
self.l3_inst._nb_ovn.lookup.return_value = self.lb_network
|
self.l3_inst._nb_ovn.lookup.return_value = self.lb_network
|
||||||
self.l3_inst._nb_ovn.get_lswitch_port.return_value = self.member_lsp
|
self.l3_inst._nb_ovn.get_lswitch_port.return_value = self.member_lsp
|
||||||
fip = self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
|
fip = self.fake_floating_ip
|
||||||
# Validate that there is no external_mac and logical_port while
|
# Validate that there is no external_mac and logical_port while
|
||||||
# setting the NAT entry.
|
# setting the NAT entry.
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
|
@ -1121,7 +1230,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
[self.fake_ovn_nat_rule],
|
[self.fake_ovn_nat_rule],
|
||||||
]
|
]
|
||||||
self.l3_inst._nb_ovn.lookup.return_value = self.lb_network
|
self.l3_inst._nb_ovn.lookup.return_value = self.lb_network
|
||||||
fip = self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
|
fip = self.fake_floating_ip
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: fip['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: fip['id'],
|
||||||
ovn_const.OVN_REV_NUM_EXT_ID_KEY: mock.ANY,
|
ovn_const.OVN_REV_NUM_EXT_ID_KEY: mock.ANY,
|
||||||
|
@ -1160,7 +1276,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
_nb_ovn.get_lrouter_port.return_value = lrp
|
_nb_ovn.get_lrouter_port.return_value = lrp
|
||||||
self.l3_inst.get_router.return_value = self.fake_router_with_ext_gw
|
self.l3_inst.get_router.return_value = self.fake_router_with_ext_gw
|
||||||
|
|
||||||
self.l3_inst.create_floatingip(self.context, 'floatingip')
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(self.fake_floating_ip,),
|
||||||
|
resource_id=self.fake_floating_ip['id'],
|
||||||
|
request_body={'floatingip': self.fake_floating_ip})
|
||||||
|
self.ovn_drv._process_floatingip_create(resources.FLOATING_IP,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
_nb_ovn.set_nat_rule_in_lrouter.assert_not_called()
|
_nb_ovn.set_nat_rule_in_lrouter.assert_not_called()
|
||||||
|
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
|
@ -1278,7 +1400,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
nb_ovn.get_floatingip.return_value = (
|
nb_ovn.get_floatingip.return_value = (
|
||||||
self.fake_ovn_nat_rule)
|
self.fake_ovn_nat_rule)
|
||||||
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
||||||
self.l3_inst.update_floatingip(self.context, 'id', fip)
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.fake_floating_ip_new, self.fake_floating_ip_new),
|
||||||
|
resource_id=self.fake_floating_ip_new['id'],
|
||||||
|
request_body=fip)
|
||||||
|
self.ovn_drv._process_floatingip_update(resources.FLOATING_IP,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
nb_ovn.delete_nat_rule_in_lrouter.assert_called_once_with(
|
nb_ovn.delete_nat_rule_in_lrouter.assert_called_once_with(
|
||||||
'neutron-router-id',
|
'neutron-router-id',
|
||||||
type='dnat_and_snat',
|
type='dnat_and_snat',
|
||||||
|
@ -1311,9 +1440,16 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
nb_ovn.get_floatingip.return_value = (
|
nb_ovn.get_floatingip.return_value = (
|
||||||
self.fake_ovn_nat_rule)
|
self.fake_ovn_nat_rule)
|
||||||
fip = {l3_def.FLOATINGIP: {qos_consts.QOS_POLICY_ID: 'qos_id_1'}}
|
fip = {l3_def.FLOATINGIP: {qos_consts.QOS_POLICY_ID: 'qos_id_1'}}
|
||||||
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.fake_floating_ip_new, self.fake_floating_ip_new),
|
||||||
|
resource_id=self.fake_floating_ip_new['id'],
|
||||||
|
request_body=fip)
|
||||||
with mock.patch.object(self.l3_inst._ovn_client._qos_driver,
|
with mock.patch.object(self.l3_inst._ovn_client._qos_driver,
|
||||||
'update_floatingip') as ufip:
|
'update_floatingip') as ufip:
|
||||||
self.l3_inst.update_floatingip(self.context, 'id', fip)
|
self.ovn_drv._process_floatingip_update(resources.FLOATING_IP,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
||||||
nb_ovn.add_nat_rule_in_lrouter.assert_not_called()
|
nb_ovn.add_nat_rule_in_lrouter.assert_not_called()
|
||||||
ufip.assert_called_once_with(mock.ANY, self.fake_floating_ip_new)
|
ufip.assert_called_once_with(mock.ANY, self.fake_floating_ip_new)
|
||||||
|
@ -1325,7 +1461,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.fake_floating_ip.update({'fixed_port_id': None})
|
self.fake_floating_ip.update({'fixed_port_id': None})
|
||||||
uf.return_value = self.fake_floating_ip_new
|
uf.return_value = self.fake_floating_ip_new
|
||||||
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
||||||
self.l3_inst.update_floatingip(self.context, 'id', fip)
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.fake_floating_ip_new, self.fake_floating_ip_new),
|
||||||
|
resource_id=self.fake_floating_ip_new['id'],
|
||||||
|
request_body=fip)
|
||||||
|
self.ovn_drv._process_floatingip_update(resources.FLOATING_IP,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip_new['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip_new['id'],
|
||||||
|
@ -1362,7 +1505,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
config.cfg.CONF.set_override(
|
config.cfg.CONF.set_override(
|
||||||
'enable_distributed_floating_ip', True, group='ovn')
|
'enable_distributed_floating_ip', True, group='ovn')
|
||||||
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
||||||
self.l3_inst.update_floatingip(self.context, 'id', fip)
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.fake_floating_ip_new, self.fake_floating_ip_new),
|
||||||
|
resource_id=self.fake_floating_ip_new['id'],
|
||||||
|
request_body=fip)
|
||||||
|
self.ovn_drv._process_floatingip_update(resources.FLOATING_IP,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_not_called()
|
||||||
expected_ext_ids = {
|
expected_ext_ids = {
|
||||||
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip_new['id'],
|
ovn_const.OVN_FIP_EXT_ID_KEY: self.fake_floating_ip_new['id'],
|
||||||
|
@ -1391,7 +1541,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
self.fake_floating_ip_new.update({'port_id': 'foo'})
|
self.fake_floating_ip_new.update({'port_id': 'foo'})
|
||||||
uf.return_value = self.fake_floating_ip_new
|
uf.return_value = self.fake_floating_ip_new
|
||||||
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
||||||
self.l3_inst.update_floatingip(self.context, 'id', fip)
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.fake_floating_ip_new, self.fake_floating_ip_new),
|
||||||
|
resource_id=self.fake_floating_ip_new['id'],
|
||||||
|
request_body=fip)
|
||||||
|
self.ovn_drv._process_floatingip_update(resources.FLOATING_IP,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
nb_ovn.delete_nat_rule_in_lrouter.assert_called_once_with(
|
nb_ovn.delete_nat_rule_in_lrouter.assert_called_once_with(
|
||||||
'neutron-router-id',
|
'neutron-router-id',
|
||||||
type='dnat_and_snat',
|
type='dnat_and_snat',
|
||||||
|
@ -1427,7 +1584,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
'fixed_port_id': 'port_id'})
|
'fixed_port_id': 'port_id'})
|
||||||
uf.return_value = self.fake_floating_ip_new
|
uf.return_value = self.fake_floating_ip_new
|
||||||
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
fip = {l3_def.FLOATINGIP: {'port_id': 'port1'}}
|
||||||
self.l3_inst.update_floatingip(self.context, 'id', fip)
|
payload = events.DBEventPayload(
|
||||||
|
self.context,
|
||||||
|
states=(self.fake_floating_ip_new, self.fake_floating_ip_new),
|
||||||
|
resource_id=self.fake_floating_ip_new['id'],
|
||||||
|
request_body=fip)
|
||||||
|
self.ovn_drv._process_floatingip_update(resources.FLOATING_IP,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
nb_ovn.delete_nat_rule_in_lrouter.assert_called_once_with(
|
nb_ovn.delete_nat_rule_in_lrouter.assert_called_once_with(
|
||||||
'neutron-router-id',
|
'neutron-router-id',
|
||||||
|
@ -1452,48 +1616,58 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
logical_port='port_id',
|
logical_port='port_id',
|
||||||
external_ids=expected_ext_ids)
|
external_ids=expected_ext_ids)
|
||||||
|
|
||||||
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.get_floatingips')
|
@mock.patch('neutron.services.ovn_l3.plugin.OVNL3RouterPlugin.'
|
||||||
def test_disassociate_floatingips(self, gfs):
|
'update_floatingip_status')
|
||||||
gfs.return_value = [{'id': 'fip-id1',
|
def test_disassociate_floatingips(self, status_upd_mock):
|
||||||
'floating_ip_address': '192.168.0.10',
|
old_fips = [{'id': 'fip-id1',
|
||||||
'router_id': 'router-id',
|
'floating_ip_address': '192.168.0.10',
|
||||||
'port_id': 'port_id',
|
'router_id': 'router-id',
|
||||||
'floating_port_id': 'fip-port-id1',
|
'port_id': 'port_id',
|
||||||
'fixed_ip_address': '10.0.0.10',
|
'floating_port_id': 'fip-port-id1',
|
||||||
'floating_network_id': 'net1'},
|
'fixed_ip_address': '10.0.0.10',
|
||||||
{'id': 'fip-id2',
|
'floating_network_id': 'net1'},
|
||||||
'floating_ip_address': '192.167.0.10',
|
{'id': 'fip-id2',
|
||||||
'router_id': 'router-id',
|
'floating_ip_address': '192.167.0.10',
|
||||||
'port_id': 'port_id',
|
'router_id': 'router-id',
|
||||||
'floating_port_id': 'fip-port-id2',
|
'port_id': 'port_id',
|
||||||
'fixed_ip_address': '10.0.0.11',
|
'floating_port_id': 'fip-port-id2',
|
||||||
'floating_network_id': 'net2'}]
|
'fixed_ip_address': '10.0.0.11',
|
||||||
self.l3_inst.disassociate_floatingips(self.context, 'port_id',
|
'floating_network_id': 'net2'}]
|
||||||
do_notify=False)
|
for fip in old_fips:
|
||||||
|
payload = events.DBEventPayload(
|
||||||
|
self.context, states=(fip, self.fake_floating_ip_new),
|
||||||
|
resource_id=self.fake_floating_ip_new['id'])
|
||||||
|
self.ovn_drv._process_floatingip_update(resources.FLOATING_IP,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
delete_nat_calls = [mock.call('neutron-router-id',
|
delete_nat_calls = [mock.call('neutron-router-id',
|
||||||
type='dnat_and_snat',
|
type='dnat_and_snat',
|
||||||
logical_ip=fip['fixed_ip_address'],
|
logical_ip=fip['fixed_ip_address'],
|
||||||
external_ip=fip['floating_ip_address'])
|
external_ip=fip['floating_ip_address'])
|
||||||
for fip in gfs.return_value]
|
for fip in old_fips]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(delete_nat_calls),
|
len(delete_nat_calls),
|
||||||
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.call_count)
|
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.call_count)
|
||||||
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_has_calls(
|
self.l3_inst._nb_ovn.delete_nat_rule_in_lrouter.assert_has_calls(
|
||||||
delete_nat_calls, any_order=True)
|
delete_nat_calls, any_order=True)
|
||||||
|
self.assertEqual(len(delete_nat_calls), status_upd_mock.call_count)
|
||||||
|
|
||||||
|
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.get_router')
|
||||||
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
@mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
|
||||||
'ovn_client.OVNClient.update_router_port')
|
'ovn_client.OVNClient.update_router_port')
|
||||||
def test_port_update_postcommit(self, update_rp_mock):
|
def test_port_update_postcommit(self, update_rp_mock, gr):
|
||||||
context = 'fake_context'
|
context = 'fake_context'
|
||||||
port = {'device_owner': 'foo'}
|
port = {'device_owner': 'foo', 'device_id': 'id'}
|
||||||
|
gr.return_value = {'flavor_id': None}
|
||||||
self.l3_inst._port_update(resources.PORT, events.AFTER_UPDATE, None,
|
self.l3_inst._port_update(resources.PORT, events.AFTER_UPDATE, None,
|
||||||
payload=events.DBEventPayload(
|
payload=events.DBEventPayload(
|
||||||
context,
|
context,
|
||||||
states=(port,)))
|
states=(port,)))
|
||||||
update_rp_mock.assert_not_called()
|
update_rp_mock.assert_not_called()
|
||||||
|
|
||||||
port = {'device_owner': constants.DEVICE_OWNER_ROUTER_INTF}
|
port = {'device_owner': constants.DEVICE_OWNER_ROUTER_INTF,
|
||||||
|
'device_id': 'router_id'}
|
||||||
self.l3_inst._port_update(resources.PORT, events.AFTER_UPDATE, None,
|
self.l3_inst._port_update(resources.PORT, events.AFTER_UPDATE, None,
|
||||||
payload=events.DBEventPayload(
|
payload=events.DBEventPayload(
|
||||||
context,
|
context,
|
||||||
|
@ -1503,11 +1677,14 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
port,
|
port,
|
||||||
if_exists=True)
|
if_exists=True)
|
||||||
|
|
||||||
def test_port_update_before_update_router_port_without_ip(self):
|
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.get_router')
|
||||||
|
def test_port_update_before_update_router_port_without_ip(self, gr):
|
||||||
context = 'fake_context'
|
context = 'fake_context'
|
||||||
port = {'device_owner': constants.DEVICE_OWNER_ROUTER_INTF,
|
port = {'device_owner': constants.DEVICE_OWNER_ROUTER_INTF,
|
||||||
'fixed_ips': [],
|
'fixed_ips': [],
|
||||||
'id': 'port-id'}
|
'id': 'port-id',
|
||||||
|
'device_id': 'router_id'}
|
||||||
|
gr.return_value = {'flavor_id': None}
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
n_exc.ServicePortInUse,
|
n_exc.ServicePortInUse,
|
||||||
self.l3_inst._port_update,
|
self.l3_inst._port_update,
|
||||||
|
@ -1674,8 +1851,10 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
gn.return_value = prov_net
|
gn.return_value = prov_net
|
||||||
gns.return_value = [self.fake_network]
|
gns.return_value = [self.fake_network]
|
||||||
|
|
||||||
self.l3_inst.add_router_interface(self.context, router_id,
|
payload = self._create_payload_for_router_interface(router_id)
|
||||||
interface_info)
|
self.ovn_drv._process_add_router_interface(resources.ROUTER_INTERFACE,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
# Make sure that the "gateway_mtu" option was set to the router port
|
# Make sure that the "gateway_mtu" option was set to the router port
|
||||||
fake_router_port_assert = self.fake_router_port_assert
|
fake_router_port_assert = self.fake_router_port_assert
|
||||||
|
@ -1708,8 +1887,6 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
config.cfg.CONF.set_override(
|
config.cfg.CONF.set_override(
|
||||||
'ovn_emit_need_to_frag', True, group='ovn')
|
'ovn_emit_need_to_frag', True, group='ovn')
|
||||||
router_id = 'router-id'
|
router_id = 'router-id'
|
||||||
interface_info = {'port_id': 'router-port-id',
|
|
||||||
'network_id': 'priv-net'}
|
|
||||||
ari.return_value = self.fake_router_interface_info
|
ari.return_value = self.fake_router_interface_info
|
||||||
# If we remove the router halfway the return value of
|
# If we remove the router halfway the return value of
|
||||||
# _get_routers_ports will be RouterNotFound
|
# _get_routers_ports will be RouterNotFound
|
||||||
|
@ -1723,8 +1900,10 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
|
||||||
gn.return_value = prov_net
|
gn.return_value = prov_net
|
||||||
gns.return_value = [self.fake_network]
|
gns.return_value = [self.fake_network]
|
||||||
|
|
||||||
self.l3_inst.add_router_interface(self.context, router_id,
|
payload = self._create_payload_for_router_interface(router_id)
|
||||||
interface_info)
|
self.ovn_drv._process_add_router_interface(resources.ROUTER_INTERFACE,
|
||||||
|
events.AFTER_CREATE,
|
||||||
|
self, payload)
|
||||||
|
|
||||||
# Make sure that the "gateway_mtu" option was set to the router port
|
# Make sure that the "gateway_mtu" option was set to the router port
|
||||||
fake_router_port_assert = self.fake_router_port_assert
|
fake_router_port_assert = self.fake_router_port_assert
|
||||||
|
@ -1882,7 +2061,9 @@ class OVNL3ExtrarouteTests(test_l3_gw.ExtGwModeIntTestCase,
|
||||||
test_floatingip_update_subnet_gateway_disabled(expected_status)
|
test_floatingip_update_subnet_gateway_disabled(expected_status)
|
||||||
|
|
||||||
# Test function _subnet_update of L3 OVN plugin.
|
# Test function _subnet_update of L3 OVN plugin.
|
||||||
def test_update_subnet_gateway_for_external_net(self):
|
@mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.get_router')
|
||||||
|
def test_update_subnet_gateway_for_external_net(self, gr):
|
||||||
|
gr.return_value = {'flavor_id': None}
|
||||||
super(OVNL3ExtrarouteTests, self). \
|
super(OVNL3ExtrarouteTests, self). \
|
||||||
test_update_subnet_gateway_for_external_net()
|
test_update_subnet_gateway_for_external_net()
|
||||||
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
|
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- Neutron allows users to create routers with flavors with the L3 OVN
|
||||||
|
plugin. This new feature can be configured in the neutron.conf file.
|
||||||
|
Please see the "Creating a L3 OVN router with a user defined flavor"
|
||||||
|
section under Neutron configuration in the documentation for more
|
||||||
|
details. This document also describes the steps users have to take to
|
||||||
|
create a router with a flavor assigned to it.
|
Loading…
Reference in New Issue