Convert plugin away from ML2
This patch converts the ovn plugin away from the ml2 plugin to be a monolithic plugin. The reason is that we not really need the overhead and complexity that ml2 adds and a monolithic plugin will serve us better. Note: there are a lot of cleanups to do after this merges. Currently, I'm skipping the existing unit tests that mocked the ovn interaction. I'll add these back in a later patch set once we get this in. Depends-On: I6ce60dad222e1e244f0d4fac9680e73de2a9b2e9 Change-Id: Ife1ac9910d4dd3af321586ec59c26df1d336d302changes/26/195326/25
parent
ff29fe24a5
commit
963d9b6a01
@ -1,9 +0,0 @@
|
||||
# Devstack settings
|
||||
|
||||
# For OVN, you can enable the OVN service
|
||||
# ovn - Add this to enable OVN locally on the host
|
||||
#
|
||||
|
||||
# Enable the OVN ML2 mechanism driver. Include the logger to assist with
|
||||
# development and debugging, but it's not required.
|
||||
Q_ML2_PLUGIN_MECHANISM_DRIVERS=ovn,logger
|
@ -1,190 +0,0 @@
|
||||
# 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 six
|
||||
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.i18n import _
|
||||
from neutron.plugins.ml2 import driver_api
|
||||
|
||||
from networking_ovn.common import config as cfg
|
||||
from networking_ovn.common import constants as ovn_const
|
||||
from networking_ovn.common import utils
|
||||
from networking_ovn.ml2 import ovn_nb_sync
|
||||
from networking_ovn.ml2 import security_groups_handler as sec_handler
|
||||
from networking_ovn.ovsdb import impl_idl_ovn
|
||||
|
||||
|
||||
class OVNMechDriver(driver_api.MechanismDriver):
|
||||
|
||||
"""OVN ML2 MechanismDriver for Neutron.
|
||||
|
||||
"""
|
||||
def initialize(self):
|
||||
self.vif_type = portbindings.VIF_TYPE_OVS
|
||||
# When set to True, Nova plugs the VIF directly into the ovs bridge
|
||||
# instead of using the hybrid mode.
|
||||
self.vif_details = {portbindings.CAP_PORT_FILTER: True}
|
||||
|
||||
self._ovn = impl_idl_ovn.OvsdbOvnIdl()
|
||||
self.security_handler = sec_handler.OvnSecurityGroupsHandler(self._ovn)
|
||||
|
||||
# Call the synchronization task, this sync neutron DB to OVN-NB DB
|
||||
# only in inconsistent states
|
||||
self.synchronizer = (
|
||||
ovn_nb_sync.OvnNbSynchronizer(self,
|
||||
self._ovn,
|
||||
cfg.get_ovn_neutron_sync_mode()))
|
||||
self.synchronizer.sync()
|
||||
|
||||
def _set_network_name(self, network):
|
||||
ext_id = [ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY, network['name']]
|
||||
self._ovn.set_lswitch_ext_id(
|
||||
utils.ovn_name(network['id']),
|
||||
ext_id).execute(check_error=True)
|
||||
|
||||
def create_network_postcommit(self, context):
|
||||
network = context.current
|
||||
# Create a logical switch with a name equal to the Neutron network
|
||||
# UUID. This provides an easy way to refer to the logical switch
|
||||
# without having to track what UUID OVN assigned to it.
|
||||
external_ids = {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: network['name']}
|
||||
self._ovn.create_lswitch(lswitch_name=utils.ovn_name(network['id']),
|
||||
external_ids=external_ids).execute(
|
||||
check_error=True)
|
||||
|
||||
def update_network_postcommit(self, context):
|
||||
network = context.current
|
||||
# The only field that might get updated that we care about right now is
|
||||
# the name.
|
||||
self._set_network_name(network)
|
||||
|
||||
def delete_network_postcommit(self, context):
|
||||
network = context.current
|
||||
self._ovn.delete_lswitch(
|
||||
utils.ovn_name(network['id'])).execute(check_error=True)
|
||||
|
||||
def create_subnet_postcommit(self, context):
|
||||
pass
|
||||
|
||||
def update_subnet_postcommit(self, context):
|
||||
pass
|
||||
|
||||
def delete_subnet_postcommit(self, context):
|
||||
pass
|
||||
|
||||
def _validate_binding_profile(self, context):
|
||||
# Validate binding:profile if it exists in precommit so that we can
|
||||
# fail port creation if the contents are invalid.
|
||||
port = context.current
|
||||
if ovn_const.OVN_PORT_BINDING_PROFILE not in port:
|
||||
return
|
||||
parent_name = (
|
||||
port[ovn_const.OVN_PORT_BINDING_PROFILE].get('parent_name'))
|
||||
tag = port[ovn_const.OVN_PORT_BINDING_PROFILE].get('tag')
|
||||
if not any((parent_name, tag)):
|
||||
# An empty profile is fine.
|
||||
return
|
||||
if not all((parent_name, tag)):
|
||||
# If one is set, they both must be set.
|
||||
msg = _('Invalid binding:profile. parent_name and tag are '
|
||||
'both required.')
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
if not isinstance(parent_name, six.string_types):
|
||||
msg = _('Invalid binding:profile. parent_name "%s" must be '
|
||||
'a string.') % parent_name
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
if not isinstance(tag, int) or tag < 0 or tag > 4095:
|
||||
# The tag range is defined by ovn-nb.ovsschema.
|
||||
# https://github.com/openvswitch/ovs/blob/ovn/ovn/ovn-nb.ovsschema
|
||||
msg = _('Invalid binding:profile. tag "%s" must be '
|
||||
'an int between 1 and 4096, inclusive.') % tag
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
# Make sure we can successfully look up the port indicated by
|
||||
# parent_name. Just let it raise the right exception if there is a
|
||||
# problem.
|
||||
context._plugin.get_port(context._plugin_context, parent_name)
|
||||
|
||||
def _get_data_from_binding_profile(self, port):
|
||||
parent_name = None
|
||||
tag = None
|
||||
if ovn_const.OVN_PORT_BINDING_PROFILE in port:
|
||||
# If binding:profile exists, we know the contents are valid as they
|
||||
# were validated in create_port_precommit().
|
||||
parent_name = (
|
||||
port[ovn_const.OVN_PORT_BINDING_PROFILE].get('parent_name'))
|
||||
tag = port[ovn_const.OVN_PORT_BINDING_PROFILE].get('tag')
|
||||
return parent_name, tag
|
||||
|
||||
def _get_allowed_mac_addresses_from_port(self, port):
|
||||
if not port.get('port_security_enabled', True):
|
||||
return []
|
||||
allowed_macs = set()
|
||||
allowed_macs.add(port['mac_address'])
|
||||
allowed_address_pairs = port.get('allowed_address_pairs', [])
|
||||
for allowed_address in allowed_address_pairs:
|
||||
allowed_macs.add(allowed_address['mac_address'])
|
||||
return list(allowed_macs)
|
||||
|
||||
def create_port_precommit(self, context):
|
||||
self._validate_binding_profile(context)
|
||||
|
||||
def create_port_postcommit(self, context):
|
||||
port = context.current
|
||||
# The port name *must* be port['id']. It must match the iface-id set
|
||||
# in the Interfaces table of the Open_vSwitch database, which nova sets
|
||||
# to be the port ID.
|
||||
external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']}
|
||||
parent_name, tag = self._get_data_from_binding_profile(port)
|
||||
allowed_macs = self._get_allowed_mac_addresses_from_port(port)
|
||||
self._ovn.create_lport(
|
||||
lport_name=port['id'],
|
||||
lswitch_name=utils.ovn_name(port['network_id']),
|
||||
macs=[port['mac_address']], external_ids=external_ids,
|
||||
parent_name=parent_name, tag=tag,
|
||||
enabled=port['admin_state_up'],
|
||||
port_security=allowed_macs).execute(check_error=True)
|
||||
|
||||
def update_port_precommit(self, context):
|
||||
self._validate_binding_profile(context)
|
||||
|
||||
def update_port_postcommit(self, context):
|
||||
port = context.current
|
||||
# Neutron allows you to update:
|
||||
# - MAC address on a port.
|
||||
# - name on a port.
|
||||
# - binding profile data (parent_name, tag)
|
||||
# - admin port state
|
||||
external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']}
|
||||
parent_name, tag = self._get_data_from_binding_profile(port)
|
||||
allowed_macs = self._get_allowed_mac_addresses_from_port(port)
|
||||
self._ovn.set_lport(lport_name=port['id'],
|
||||
macs=[port['mac_address']],
|
||||
external_ids=external_ids,
|
||||
parent_name=parent_name, tag=tag,
|
||||
enabled=port['admin_state_up'],
|
||||
port_security=allowed_macs).execute(
|
||||
check_error=True)
|
||||
|
||||
def delete_port_postcommit(self, context):
|
||||
port = context.current
|
||||
self._ovn.delete_lport(port['id']).execute(check_error=True)
|
||||
|
||||
def bind_port(self, context):
|
||||
# This is just a temp solution so that Nova can boot images
|
||||
for segment in context.segments_to_bind:
|
||||
context.set_binding(segment[driver_api.ID],
|
||||
self.vif_type,
|
||||
self.vif_details,
|
||||
status=n_const.PORT_STATUS_ACTIVE)
|
@ -0,0 +1,310 @@
|
||||
#
|
||||
# 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 six
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import api as oslo_db_api
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
from sqlalchemy.orm import exc as sa_exc
|
||||
|
||||
|
||||
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||
from neutron.api.rpc.handlers import dhcp_rpc
|
||||
from neutron.api.rpc.handlers import l3_rpc
|
||||
from neutron.api.rpc.handlers import metadata_rpc
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.extensions import extra_dhcp_opt as edo_ext
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.extensions import providernet as pnet
|
||||
|
||||
from neutron.common import constants as const
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import topics
|
||||
from neutron.db import agents_db
|
||||
from neutron.db import agentschedulers_db
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import external_net_db
|
||||
from neutron.db import extradhcpopt_db
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_agentschedulers_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import portbindings_db
|
||||
from neutron.db import securitygroups_db
|
||||
from neutron.i18n import _LE, _LI
|
||||
|
||||
from networking_ovn.common import config
|
||||
from networking_ovn.common import constants as ovn_const
|
||||
from networking_ovn.common import utils
|
||||
from networking_ovn import ovn_nb_sync
|
||||
from networking_ovn.ovsdb import impl_idl_ovn
|
||||
from networking_ovn import security_groups_handler as sec_handler
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class OVNPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
securitygroups_db.SecurityGroupDbMixin,
|
||||
l3_agentschedulers_db.L3AgentSchedulerDbMixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin,
|
||||
external_net_db.External_net_db_mixin,
|
||||
portbindings_db.PortBindingMixin,
|
||||
extradhcpopt_db.ExtraDhcpOptMixin,
|
||||
extraroute_db.ExtraRoute_db_mixin,
|
||||
agentschedulers_db.DhcpAgentSchedulerDbMixin):
|
||||
|
||||
__native_bulk_support = True
|
||||
__native_pagination_support = True
|
||||
__native_sorting_support = True
|
||||
|
||||
supported_extension_aliases = ["quotas",
|
||||
"extra_dhcp_opt",
|
||||
"binding",
|
||||
"security-group",
|
||||
"extraroute",
|
||||
"external-net",
|
||||
"router"]
|
||||
|
||||
def __init__(self):
|
||||
super(OVNPlugin, self).__init__()
|
||||
LOG.info(_("Starting OVNPlugin"))
|
||||
self.vif_type = portbindings.VIF_TYPE_OVS
|
||||
# When set to True, Nova plugs the VIF directly into the ovs bridge
|
||||
# instead of using the hybrid mode.
|
||||
self.vif_details = {portbindings.CAP_PORT_FILTER: True}
|
||||
|
||||
self._ovn = impl_idl_ovn.OvsdbOvnIdl()
|
||||
self.security_handler = sec_handler.OvnSecurityGroupsHandler(self._ovn)
|
||||
|
||||
# Call the synchronization task, this sync neutron DB to OVN-NB DB
|
||||
# only in inconsistent states
|
||||
self.synchronizer = (
|
||||
ovn_nb_sync.OvnNbSynchronizer(self,
|
||||
self._ovn,
|
||||
config.get_ovn_neutron_sync_mode()))
|
||||
self.base_binding_dict = {
|
||||
portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
|
||||
portbindings.VIF_DETAILS: {
|
||||
# TODO(rkukura): Replace with new VIF security details
|
||||
portbindings.CAP_PORT_FILTER:
|
||||
'security-group' in self.supported_extension_aliases}}
|
||||
|
||||
self.synchronizer.sync()
|
||||
self._setup_rpc()
|
||||
|
||||
def _setup_rpc(self):
|
||||
self.conn = n_rpc.create_connection(new=True)
|
||||
self.endpoints = [dhcp_rpc.DhcpRpcCallback(),
|
||||
l3_rpc.L3RpcCallback(),
|
||||
agents_db.AgentExtRpcCallback(),
|
||||
metadata_rpc.MetadataRpcCallback()]
|
||||
self.agent_notifiers[const.AGENT_TYPE_L3] = (
|
||||
l3_rpc_agent_api.L3AgentNotifyAPI()
|
||||
)
|
||||
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
|
||||
dhcp_rpc_agent_api.DhcpAgentNotifyAPI())
|
||||
self.network_scheduler = importutils.import_object(
|
||||
cfg.CONF.network_scheduler_driver
|
||||
)
|
||||
self.supported_extension_aliases.extend(
|
||||
['agent', 'dhcp_agent_scheduler'])
|
||||
self.conn.create_consumer(topics.PLUGIN, self.endpoints,
|
||||
fanout=False)
|
||||
self.conn.create_consumer(topics.L3PLUGIN, self.endpoints,
|
||||
fanout=False)
|
||||
self.conn.consume_in_threads()
|
||||
|
||||
def _delete_ports(self, context, ports):
|
||||
for port in ports:
|
||||
try:
|
||||
self.delete_port(context, port.id)
|
||||
except (n_exc.PortNotFound, sa_exc.ObjectDeletedError):
|
||||
context.session.expunge(port)
|
||||
# concurrent port deletion can be performed by
|
||||
# release_dhcp_port caused by concurrent subnet_delete
|
||||
LOG.info(_LI("Port %s was deleted concurrently"), port.id)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Exception auto-deleting port %s"),
|
||||
port.id)
|
||||
|
||||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_deadlock=True)
|
||||
def create_network(self, context, network):
|
||||
with context.session.begin(subtransactions=True):
|
||||
result = super(OVNPlugin, self).create_network(context,
|
||||
network)
|
||||
self._process_l3_create(context, result, network['network'])
|
||||
|
||||
# Create a logical switch with a name equal to the Neutron network
|
||||
# UUID. This provides an easy way to refer to the logical switch
|
||||
# without having to track what UUID OVN assigned to it.
|
||||
external_ids = {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: result['name']}
|
||||
|
||||
# TODO(arosen): Undo logical switch creation on failure
|
||||
self._ovn.create_lswitch(lswitch_name=utils.ovn_name(result['id']),
|
||||
external_ids=external_ids).execute(
|
||||
check_error=True)
|
||||
return result
|
||||
|
||||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_deadlock=True)
|
||||
def delete_network(self, context, network_id):
|
||||
with context.session.begin():
|
||||
super(OVNPlugin, self).delete_network(context,
|
||||
network_id)
|
||||
self._ovn.delete_lswitch(
|
||||
utils.ovn_name(network_id)).execute(check_error=True)
|
||||
|
||||
def _set_network_name(self, network_id, name):
|
||||
ext_id = [ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY, name]
|
||||
self._ovn.set_lswitch_ext_id(
|
||||
utils.ovn_name(network_id),
|
||||
ext_id).execute(check_error=True)
|
||||
|
||||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_deadlock=True)
|
||||
def update_network(self, context, network_id, network):
|
||||
pnet._raise_if_updates_provider_attributes(network['network'])
|
||||
# FIXME(arosen) - rollback...
|
||||
if 'name' in network['network']:
|
||||
self._set_network_name(id, network['network']['name'])
|
||||
with context.session.begin(subtransactions=True):
|
||||
return super(OVNPlugin, self).update_network(context, network_id,
|
||||
network)
|
||||
|
||||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_deadlock=True)
|
||||
def update_port(self, context, id, port):
|
||||
self._validate_binding_profile(context, port)
|
||||
with context.session.begin(subtransactions=True):
|
||||
original_port = self._get_port(context, id)
|
||||
updated_port = super(OVNPlugin, self).update_port(context, id,
|
||||
port)
|
||||
|
||||
self._process_portbindings_create_and_update(context,
|
||||
port['port'],
|
||||
updated_port)
|
||||
self.update_security_group_on_port(
|
||||
context, id, port, original_port, updated_port)
|
||||
|
||||
external_ids = {
|
||||
ovn_const.OVN_PORT_NAME_EXT_ID_KEY: updated_port['name']}
|
||||
parent_name, tag = self._get_data_from_binding_profile(updated_port)
|
||||
allowed_macs = self._get_allowed_mac_addresses_from_port(
|
||||
updated_port)
|
||||
self._ovn.set_lport(lport_name=updated_port['id'],
|
||||
macs=[updated_port['mac_address']],
|
||||
external_ids=external_ids,
|
||||
parent_name=parent_name, tag=tag,
|
||||
enabled=updated_port['admin_state_up'],
|
||||
port_security=allowed_macs).execute(
|
||||
check_error=True)
|
||||
return updated_port
|
||||
|
||||
def _validate_binding_profile(self, context, port):
|
||||
if ovn_const.OVN_PORT_BINDING_PROFILE not in port:
|
||||
return
|
||||
parent_name = (
|
||||
port[ovn_const.OVN_PORT_BINDING_PROFILE].get('parent_name'))
|
||||
tag = port[ovn_const.OVN_PORT_BINDING_PROFILE].get('tag')
|
||||
if not any((parent_name, tag)):
|
||||
# An empty profile is fine.
|
||||
return
|
||||
if not all((parent_name, tag)):
|
||||
# If one is set, they both must be set.
|
||||
msg = _('Invalid binding:profile. parent_name and tag are '
|
||||
'both required.')
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
if not isinstance(parent_name, six.string_types):
|
||||
msg = _('Invalid binding:profile. parent_name "%s" must be '
|
||||
'a string.') % parent_name
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
if not isinstance(tag, int) or tag < 0 or tag > 4095:
|
||||
# The tag range is defined by ovn-nb.ovsschema.
|
||||
# https://github.com/openvswitch/ovs/blob/ovn/ovn/ovn-nb.ovsschema
|
||||
msg = _('Invalid binding:profile. tag "%s" must be '
|
||||
'an int between 1 and 4096, inclusive.') % tag
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
# Make sure we can successfully look up the port indicated by
|
||||
# parent_name. Just let it raise the right exception if there is a
|
||||
# problem.
|
||||
self.get_port(context, parent_name)
|
||||
|
||||
def _get_data_from_binding_profile(self, port):
|
||||
parent_name = None
|
||||
tag = None
|
||||
if ovn_const.OVN_PORT_BINDING_PROFILE in port:
|
||||
# If binding:profile exists, we know the contents are valid as they
|
||||
# were validated in create_port_precommit().
|
||||
parent_name = (
|
||||
port[ovn_const.OVN_PORT_BINDING_PROFILE].get('parent_name'))
|
||||
tag = port[ovn_const.OVN_PORT_BINDING_PROFILE].get('tag')
|
||||
return parent_name, tag
|
||||
|
||||
def _get_allowed_mac_addresses_from_port(self, port):
|
||||
allowed_macs = set()
|
||||
allowed_macs.add(port['mac_address'])
|
||||
allowed_address_pairs = port.get('allowed_address_pairs', [])
|
||||
for allowed_address in allowed_address_pairs:
|
||||
allowed_macs.add(allowed_address['mac_address'])
|
||||
return list(allowed_macs)
|
||||
|
||||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_deadlock=True)
|
||||
def create_port(self, context, port):
|
||||
with context.session.begin(subtransactions=True):
|
||||
self._validate_binding_profile(context, port)
|
||||
dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
|
||||
db_port = super(OVNPlugin, self).create_port(context, port)
|
||||
sgids = self._get_security_groups_on_port(context, port)
|
||||
self._process_port_create_security_group(context, db_port,
|
||||
sgids)
|
||||
self._process_portbindings_create_and_update(context,
|
||||
port['port'],
|
||||
db_port)
|
||||
|
||||
db_port[portbindings.VNIC_TYPE] = portbindings.VNIC_NORMAL
|
||||
self._process_port_create_extra_dhcp_opts(context, db_port,
|
||||
dhcp_opts)
|
||||
# The port name *must* be port['id']. It must match the iface-id set
|
||||
# in the Interfaces table of the Open_vSwitch database, which nova sets
|
||||
# to be the port ID.
|
||||
external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: db_port['name']}
|
||||
parent_name, tag = self._get_data_from_binding_profile(db_port)
|
||||
allowed_macs = self._get_allowed_mac_addresses_from_port(db_port)
|
||||
self._ovn.create_lport(
|
||||
lport_name=db_port['id'],
|
||||
lswitch_name=utils.ovn_name(db_port['network_id']),
|
||||
macs=[db_port['mac_address']], external_ids=external_ids,
|
||||
parent_name=parent_name, tag=tag,
|
||||
port_security=allowed_macs).execute(check_error=True)
|
||||
|
||||
return db_port
|
||||
|
||||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_deadlock=True)
|
||||
def delete_port(self, context, port_id, l3_port_check=True):
|
||||
self._ovn.delete_lport(port_id).execute(check_error=True)
|
||||
with context.session.begin():
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
super(OVNPlugin, self).delete_port(context, port_id)
|
||||
|
||||
def extend_port_dict_binding(self, port_res, port_db):
|
||||
super(OVNPlugin, self).extend_port_dict_binding(port_res, port_db)
|
||||
port_res[portbindings.VNIC_TYPE] = portbindings.VNIC_NORMAL
|
@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_l3_ovn
|
||||
----------------------------------
|
||||
|
||||
Tests for the L3 service plugin for networking-ovn.
|
||||
"""
|
||||
|
||||
from networking_ovn.l3 import l3_ovn
|
||||
from networking_ovn.tests import base
|
||||
|
||||
|
||||
class TestOVN_L3(base.TestCase):
|
||||
|
||||
def test_init(self):
|
||||
# just create an instance of OVNL3RouterPlugin
|
||||
l3_ovn.OVNL3RouterPlugin()
|
Loading…
Reference in New Issue