Move OVN Octavia Provider driver code to this repository
This code moves OVN Octavia provider driver from networking-ovn (branch master) repository to this repository. For first step lets move code and unit tests. Previous paths in networking-ovn tree: ./networking_ovn/octavia/ovn_driver.py -> ./ovn_octavia_provider/driver.py ./networking_ovn/tests/unit/octavia/test_ovn_driver -> ./ovn_octavia_provider/tests/unit/test_driver.py There are a few files taken directly from neutron repository that could be removed when neutron-lib including those will be released: ./ovn_octavia_provider/ovsdb/impl_idl_ovn.py ./ovn_octavia_provider/ovsdb/ovsdb_monitor.py Co-Authored-By: Brian Haley <bhaley@redhat.com> Co-Authored-By: Carlos Goncalves <cgoncalves@redhat.com> Co-Authored-By: Frode Nordahl <frode.nordahl@canonical.com> Co-Authored-By: Jakub Libosvar <libosvar@redhat.com> Co-Authored-By: Maciej Józefczyk <mjozefcz@redhat.com> Co-Authored-By: Numan Siddique <nusiddiq@redhat.com> Co-Authored-By: Reedip Banerjee <rbanerje@redhat.com> Co-Authored-By: Terry Wilson <twilson@redhat.com> Co-Authored-By: Yunxiang Tao <taoyunxiang@cmss.chinamobile.com> Co-Authored-By: zhufl <zhu.fanglei@zte.com.cn> Change-Id: I9b562c4ed5f74df2c3d600356758f4648ac7770b Related-Blueprint: neutron-ovn-merge
This commit is contained in:
parent
0c84fc2048
commit
000049c15d
94
ovn_octavia_provider/common/config.py
Normal file
94
ovn_octavia_provider/common/config.py
Normal file
@ -0,0 +1,94 @@
|
||||
# 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 oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from ovn_octavia_provider.i18n import _
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
ovn_opts = [
|
||||
cfg.StrOpt('ovn_nb_connection',
|
||||
default='tcp:127.0.0.1:6641',
|
||||
help=_('The connection string for the OVN_Northbound OVSDB.\n'
|
||||
'Use tcp:IP:PORT for TCP connection.\n'
|
||||
'Use ssl:IP:PORT for SSL connection. The '
|
||||
'ovn_nb_private_key, ovn_nb_certificate and '
|
||||
'ovn_nb_ca_cert are mandatory.\n'
|
||||
'Use unix:FILE for unix domain socket connection.')),
|
||||
cfg.StrOpt('ovn_nb_private_key',
|
||||
default='',
|
||||
help=_('The PEM file with private key for SSL connection to '
|
||||
'OVN-NB-DB')),
|
||||
cfg.StrOpt('ovn_nb_certificate',
|
||||
default='',
|
||||
help=_('The PEM file with certificate that certifies the '
|
||||
'private key specified in ovn_nb_private_key')),
|
||||
cfg.StrOpt('ovn_nb_ca_cert',
|
||||
default='',
|
||||
help=_('The PEM file with CA certificate that OVN should use to'
|
||||
' verify certificates presented to it by SSL peers')),
|
||||
cfg.IntOpt('ovsdb_connection_timeout',
|
||||
default=180,
|
||||
help=_('Timeout in seconds for the OVSDB '
|
||||
'connection transaction')),
|
||||
cfg.IntOpt('ovsdb_retry_max_interval',
|
||||
default=180,
|
||||
help=_('Max interval in seconds between '
|
||||
'each retry to get the OVN NB and SB IDLs')),
|
||||
cfg.IntOpt('ovsdb_probe_interval',
|
||||
min=0,
|
||||
default=60000,
|
||||
help=_('The probe interval in for the OVSDB session in '
|
||||
'milliseconds. If this is zero, it disables the '
|
||||
'connection keepalive feature. If non-zero the value '
|
||||
'will be forced to at least 1000 milliseconds. Defaults '
|
||||
'to 60 seconds.')),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(ovn_opts, group='ovn')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [
|
||||
('ovn', ovn_opts),
|
||||
]
|
||||
|
||||
|
||||
def get_ovn_nb_connection():
|
||||
return cfg.CONF.ovn.ovn_nb_connection
|
||||
|
||||
|
||||
def get_ovn_nb_private_key():
|
||||
return cfg.CONF.ovn.ovn_nb_private_key
|
||||
|
||||
|
||||
def get_ovn_nb_certificate():
|
||||
return cfg.CONF.ovn.ovn_nb_certificate
|
||||
|
||||
|
||||
def get_ovn_nb_ca_cert():
|
||||
return cfg.CONF.ovn.ovn_nb_ca_cert
|
||||
|
||||
|
||||
def get_ovn_ovsdb_timeout():
|
||||
return cfg.CONF.ovn.ovsdb_connection_timeout
|
||||
|
||||
|
||||
def get_ovn_ovsdb_retry_max_interval():
|
||||
return cfg.CONF.ovn.ovsdb_retry_max_interval
|
||||
|
||||
|
||||
def get_ovn_ovsdb_probe_interval():
|
||||
return cfg.CONF.ovn.ovsdb_probe_interval
|
22
ovn_octavia_provider/common/constants.py
Normal file
22
ovn_octavia_provider/common/constants.py
Normal file
@ -0,0 +1,22 @@
|
||||
# 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.
|
||||
|
||||
# TODO(mjozefcz): Use those variables from neutron-lib once released.
|
||||
LRP_PREFIX = "lrp-"
|
||||
LB_VIP_PORT_PREFIX = "ovn-lb-vip-"
|
||||
OVN_PORT_NAME_EXT_ID_KEY = 'neutron:port_name'
|
||||
OVN_ROUTER_NAME_EXT_ID_KEY = 'neutron:router_name'
|
||||
OVN_PORT_NAME_EXT_ID_KEY = 'neutron:port_name'
|
||||
OVN_PORT_FIP_EXT_ID_KEY = 'neutron:port_fip'
|
||||
OVN_SUBNET_EXT_ID_KEY = 'neutron:subnet_id'
|
||||
OVN_SUBNET_EXT_IDS_KEY = 'neutron:subnet_ids'
|
||||
OVN_NETWORK_NAME_EXT_ID_KEY = 'neutron:network_name'
|
24
ovn_octavia_provider/common/exceptions.py
Normal file
24
ovn_octavia_provider/common/exceptions.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# 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 n_exc
|
||||
|
||||
from ovn_octavia_provider.i18n import _
|
||||
|
||||
|
||||
class RevisionConflict(n_exc.NeutronException):
|
||||
message = _('OVN revision number for %(resource_id)s (type: '
|
||||
'%(resource_type)s) is equal or higher than the given '
|
||||
'resource. Skipping update')
|
33
ovn_octavia_provider/common/utils.py
Normal file
33
ovn_octavia_provider/common/utils.py
Normal file
@ -0,0 +1,33 @@
|
||||
# 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 ovn_octavia_provider.common import constants
|
||||
|
||||
|
||||
def ovn_name(id):
|
||||
# The name of the OVN entry will be neutron-<UUID>
|
||||
# This is due to the fact that the OVN application checks if the name
|
||||
# is a UUID. If so then there will be no matches.
|
||||
# We prefix the UUID to enable us to use the Neutron UUID when
|
||||
# updating, deleting etc.
|
||||
return 'neutron-%s' % id
|
||||
|
||||
|
||||
def ovn_lrouter_port_name(id):
|
||||
# The name of the OVN lrouter port entry will be lrp-<UUID>
|
||||
# This is to distinguish with the name of the connected lswitch patch port,
|
||||
# which is named with neutron port uuid, so that OVS patch ports are
|
||||
# generated properly. The pairing patch port names will be:
|
||||
# - patch-lrp-<UUID>-to-<UUID>
|
||||
# - patch-<UUID>-to-lrp-<UUID>
|
||||
# lrp stands for Logical Router Port
|
||||
return constants.LRP_PREFIX + '%s' % id
|
1856
ovn_octavia_provider/driver.py
Normal file
1856
ovn_octavia_provider/driver.py
Normal file
File diff suppressed because it is too large
Load Diff
20
ovn_octavia_provider/i18n.py
Normal file
20
ovn_octavia_provider/i18n.py
Normal file
@ -0,0 +1,20 @@
|
||||
# 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 oslo_i18n as i18n
|
||||
|
||||
_translators = i18n.TranslatorFactory(domain='octavia')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
138
ovn_octavia_provider/ovsdb/impl_idl_ovn.py
Normal file
138
ovn_octavia_provider/ovsdb/impl_idl_ovn.py
Normal file
@ -0,0 +1,138 @@
|
||||
# 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 contextlib
|
||||
|
||||
from neutron_lib import exceptions as n_exc
|
||||
from oslo_log import log
|
||||
from ovsdbapp.backend import ovs_idl
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp.backend.ovs_idl import transaction as idl_trans
|
||||
from ovsdbapp.schema.ovn_northbound import impl_idl as nb_impl_idl
|
||||
import tenacity
|
||||
|
||||
from ovn_octavia_provider.common import config
|
||||
from ovn_octavia_provider.common import exceptions as ovn_exc
|
||||
from ovn_octavia_provider.i18n import _
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class OvnNbTransaction(idl_trans.Transaction):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# NOTE(lucasagomes): The bump_nb_cfg parameter is only used by
|
||||
# the agents health status check
|
||||
self.bump_nb_cfg = kwargs.pop('bump_nb_cfg', False)
|
||||
super(OvnNbTransaction, self).__init__(*args, **kwargs)
|
||||
|
||||
def pre_commit(self, txn):
|
||||
if not self.bump_nb_cfg:
|
||||
return
|
||||
self.api.nb_global.increment('nb_cfg')
|
||||
|
||||
|
||||
# This version of Backend doesn't use a class variable for ovsdb_connection
|
||||
# and therefor allows networking-ovn to manage connection scope on its own
|
||||
class Backend(ovs_idl.Backend):
|
||||
lookup_table = {}
|
||||
|
||||
def __init__(self, connection):
|
||||
self.ovsdb_connection = connection
|
||||
super(Backend, self).__init__(connection)
|
||||
|
||||
def start_connection(self, connection):
|
||||
try:
|
||||
self.ovsdb_connection.start()
|
||||
except Exception as e:
|
||||
connection_exception = OvsdbConnectionUnavailable(
|
||||
db_schema=self.schema, error=e)
|
||||
LOG.exception(connection_exception)
|
||||
raise connection_exception
|
||||
|
||||
@property
|
||||
def idl(self):
|
||||
return self.ovsdb_connection.idl
|
||||
|
||||
@property
|
||||
def tables(self):
|
||||
return self.idl.tables
|
||||
|
||||
_tables = tables
|
||||
|
||||
def is_table_present(self, table_name):
|
||||
return table_name in self._tables
|
||||
|
||||
def is_col_present(self, table_name, col_name):
|
||||
return self.is_table_present(table_name) and (
|
||||
col_name in self._tables[table_name].columns)
|
||||
|
||||
def create_transaction(self, check_error=False, log_errors=True):
|
||||
return idl_trans.Transaction(
|
||||
self, self.ovsdb_connection, self.ovsdb_connection.timeout,
|
||||
check_error, log_errors)
|
||||
|
||||
# Check for a column match in the table. If not found do a retry with
|
||||
# a stop delay of 10 secs. This function would be useful if the caller
|
||||
# wants to verify for the presence of a particular row in the table
|
||||
# with the column match before doing any transaction.
|
||||
# Eg. We can check if Logical_Switch row is present before adding a
|
||||
# logical switch port to it.
|
||||
@tenacity.retry(retry=tenacity.retry_if_exception_type(RuntimeError),
|
||||
wait=tenacity.wait_exponential(),
|
||||
stop=tenacity.stop_after_delay(10),
|
||||
reraise=True)
|
||||
def check_for_row_by_value_and_retry(self, table, column, match):
|
||||
try:
|
||||
idlutils.row_by_value(self.idl, table, column, match)
|
||||
except idlutils.RowNotFound:
|
||||
msg = (_("%(match)s does not exist in %(column)s of %(table)s")
|
||||
% {'match': match, 'column': column, 'table': table})
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
class OvsdbConnectionUnavailable(n_exc.ServiceUnavailable):
|
||||
message = _("OVS database connection to %(db_schema)s failed with error: "
|
||||
"'%(error)s'. Verify that the OVS and OVN services are "
|
||||
"available and that the 'ovn_nb_connection' and "
|
||||
"'ovn_sb_connection' configuration options are correct.")
|
||||
|
||||
|
||||
class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend):
|
||||
def __init__(self, connection):
|
||||
super(OvsdbNbOvnIdl, self).__init__(connection)
|
||||
self.idl._session.reconnect.set_probe_interval(
|
||||
config.get_ovn_ovsdb_probe_interval())
|
||||
|
||||
@property
|
||||
def nb_global(self):
|
||||
return next(iter(self.tables['NB_Global'].rows.values()))
|
||||
|
||||
def create_transaction(self, check_error=False, log_errors=True,
|
||||
bump_nb_cfg=False):
|
||||
return OvnNbTransaction(
|
||||
self, self.ovsdb_connection, self.ovsdb_connection.timeout,
|
||||
check_error, log_errors, bump_nb_cfg=bump_nb_cfg)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def transaction(self, *args, **kwargs):
|
||||
"""A wrapper on the ovsdbapp transaction to work with revisions.
|
||||
|
||||
This method is just a wrapper around the ovsdbapp transaction
|
||||
to handle revision conflicts correctly.
|
||||
"""
|
||||
try:
|
||||
with super(OvsdbNbOvnIdl, self).transaction(*args, **kwargs) as t:
|
||||
yield t
|
||||
except ovn_exc.RevisionConflict as e:
|
||||
LOG.info('Transaction aborted. Reason: %s', e)
|
90
ovn_octavia_provider/ovsdb/ovsdb_monitor.py
Normal file
90
ovn_octavia_provider/ovsdb/ovsdb_monitor.py
Normal file
@ -0,0 +1,90 @@
|
||||
# Copyright 2020 Red Hat, Inc.
|
||||
#
|
||||
# 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 abc
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from ovs.stream import Stream
|
||||
from ovsdbapp.backend.ovs_idl import connection
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp import event
|
||||
|
||||
from ovn_octavia_provider.common import config as ovn_config
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseOvnIdl(connection.OvsdbIdl):
|
||||
@classmethod
|
||||
def from_server(cls, connection_string, schema_name):
|
||||
_check_and_set_ssl_files(schema_name)
|
||||
helper = idlutils.get_schema_helper(connection_string, schema_name)
|
||||
helper.register_all()
|
||||
return cls(connection_string, helper)
|
||||
|
||||
|
||||
class OvnIdl(BaseOvnIdl):
|
||||
|
||||
def __init__(self, driver, remote, schema):
|
||||
super(OvnIdl, self).__init__(remote, schema)
|
||||
self.driver = driver
|
||||
self.notify_handler = OvnDbNotifyHandler(driver)
|
||||
# ovsdb lock name to acquire.
|
||||
# This event lock is used to handle the notify events sent by idl.Idl
|
||||
# idl.Idl will call notify function for the "update" rpc method it
|
||||
# receives from the ovsdb-server.
|
||||
# This event lock is required for the following reasons
|
||||
# - If there are multiple neutron servers running, OvnWorkers of
|
||||
# these neutron servers would receive the notify events from
|
||||
# idl.Idl
|
||||
#
|
||||
# - we do not want all the neutron servers to handle these events
|
||||
#
|
||||
# - only the neutron server which has the lock will handle the
|
||||
# notify events.
|
||||
#
|
||||
# - In case the neutron server which owns this lock goes down,
|
||||
# ovsdb server would assign the lock to one of the other neutron
|
||||
# servers.
|
||||
self.event_lock_name = "ovn_provider_driver_event_lock"
|
||||
|
||||
def notify(self, event, row, updates=None):
|
||||
# Do not handle the notification if the event lock is requested,
|
||||
# but not granted by the ovsdb-server.
|
||||
if self.is_lock_contended:
|
||||
return
|
||||
self.notify_handler.notify(event, row, updates)
|
||||
|
||||
@abc.abstractmethod
|
||||
def post_connect(self):
|
||||
"""Should be called after the idl has been initialized"""
|
||||
|
||||
|
||||
class OvnDbNotifyHandler(event.RowEventHandler):
|
||||
def __init__(self, driver):
|
||||
super(OvnDbNotifyHandler, self).__init__()
|
||||
self.driver = driver
|
||||
|
||||
|
||||
def _check_and_set_ssl_files(schema_name):
|
||||
if schema_name == 'OVN_Northbound':
|
||||
priv_key_file = ovn_config.get_ovn_nb_private_key()
|
||||
cert_file = ovn_config.get_ovn_nb_certificate()
|
||||
ca_cert_file = ovn_config.get_ovn_nb_ca_cert()
|
||||
|
||||
Stream.ssl_set_private_key_file(priv_key_file)
|
||||
Stream.ssl_set_certificate_file(cert_file)
|
||||
Stream.ssl_set_ca_cert_file(ca_cert_file)
|
222
ovn_octavia_provider/tests/unit/fakes.py
Normal file
222
ovn_octavia_provider/tests/unit/fakes.py
Normal file
@ -0,0 +1,222 @@
|
||||
#
|
||||
# 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
|
||||
|
||||
import mock
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
|
||||
class FakeResource(dict):
|
||||
|
||||
def __init__(self, manager=None, info=None, loaded=False, methods=None):
|
||||
"""Set attributes and methods for a resource.
|
||||
|
||||
:param manager:
|
||||
The resource manager
|
||||
:param Dictionary info:
|
||||
A dictionary with all attributes
|
||||
:param bool loaded:
|
||||
True if the resource is loaded in memory
|
||||
:param Dictionary methods:
|
||||
A dictionary with all methods
|
||||
"""
|
||||
info = info or {}
|
||||
super(FakeResource, self).__init__(info)
|
||||
methods = methods or {}
|
||||
|
||||
self.__name__ = type(self).__name__
|
||||
self.manager = manager
|
||||
self._info = info
|
||||
self._add_details(info)
|
||||
self._add_methods(methods)
|
||||
self._loaded = loaded
|
||||
# Add a revision number by default
|
||||
setattr(self, 'revision_number', 1)
|
||||
|
||||
@property
|
||||
def db_obj(self):
|
||||
return self
|
||||
|
||||
def _add_details(self, info):
|
||||
for (k, v) in info.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def _add_methods(self, methods):
|
||||
"""Fake methods with MagicMock objects.
|
||||
|
||||
For each <@key, @value> pairs in methods, add an callable MagicMock
|
||||
object named @key as an attribute, and set the mock's return_value to
|
||||
@value. When users access the attribute with (), @value will be
|
||||
returned, which looks like a function call.
|
||||
"""
|
||||
for (name, ret) in methods.items():
|
||||
method = mock.MagicMock(return_value=ret)
|
||||
setattr(self, name, method)
|
||||
|
||||
def __repr__(self):
|
||||
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
|
||||
k != 'manager')
|
||||
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
|
||||
return "<%s %s>" % (self.__class__.__name__, info)
|
||||
|
||||
def keys(self):
|
||||
return self._info.keys()
|
||||
|
||||
def info(self):
|
||||
return self._info
|
||||
|
||||
def update(self, info):
|
||||
super(FakeResource, self).update(info)
|
||||
self._add_details(info)
|
||||
|
||||
|
||||
class FakeOvsdbRow(FakeResource):
|
||||
"""Fake one or more OVSDB rows."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_ovsdb_row(attrs=None, methods=None):
|
||||
"""Create a fake OVSDB row.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:param Dictionary methods:
|
||||
A dictionary with all methods
|
||||
:return:
|
||||
A FakeResource object faking the OVSDB row
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
methods = methods or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
ovsdb_row_attrs = {
|
||||
'uuid': fake_uuid,
|
||||
'name': 'name-' + fake_uuid,
|
||||
'external_ids': {},
|
||||
}
|
||||
|
||||
# Set default methods.
|
||||
ovsdb_row_methods = {
|
||||
'addvalue': None,
|
||||
'delete': None,
|
||||
'delvalue': None,
|
||||
'verify': None,
|
||||
'setkey': None,
|
||||
}
|
||||
|
||||
# Overwrite default attributes and methods.
|
||||
ovsdb_row_attrs.update(attrs)
|
||||
ovsdb_row_methods.update(methods)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(ovsdb_row_attrs),
|
||||
loaded=True,
|
||||
methods=copy.deepcopy(ovsdb_row_methods))
|
||||
|
||||
|
||||
class FakeSubnet(object):
|
||||
"""Fake one or more subnets."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_subnet(attrs=None):
|
||||
"""Create a fake subnet.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the subnet
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
subnet_attrs = {
|
||||
'id': 'subnet-id-' + fake_uuid,
|
||||
'name': 'subnet-name-' + fake_uuid,
|
||||
'network_id': 'network-id-' + fake_uuid,
|
||||
'cidr': '10.10.10.0/24',
|
||||
'tenant_id': 'project-id-' + fake_uuid,
|
||||
'enable_dhcp': True,
|
||||
'dns_nameservers': [],
|
||||
'allocation_pools': [],
|
||||
'host_routes': [],
|
||||
'ip_version': 4,
|
||||
'gateway_ip': '10.10.10.1',
|
||||
'ipv6_address_mode': 'None',
|
||||
'ipv6_ra_mode': 'None',
|
||||
'subnetpool_id': None,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
subnet_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(subnet_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakeOVNPort(object):
|
||||
"""Fake one or more ports."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_port(attrs=None):
|
||||
"""Create a fake ovn port.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the port
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
port_attrs = {
|
||||
'addresses': [],
|
||||
'dhcpv4_options': '',
|
||||
'dhcpv6_options': [],
|
||||
'enabled': True,
|
||||
'external_ids': {},
|
||||
'name': fake_uuid,
|
||||
'options': {},
|
||||
'parent_name': [],
|
||||
'port_security': [],
|
||||
'tag': [],
|
||||
'tag_request': [],
|
||||
'type': '',
|
||||
'up': False,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
port_attrs.update(attrs)
|
||||
return type('Logical_Switch_Port', (object, ), port_attrs)
|
||||
|
||||
|
||||
class FakeOVNRouter(object):
|
||||
|
||||
@staticmethod
|
||||
def create_one_router(attrs=None):
|
||||
router_attrs = {
|
||||
'enabled': False,
|
||||
'external_ids': {},
|
||||
'load_balancer': [],
|
||||
'name': '',
|
||||
'nat': [],
|
||||
'options': {},
|
||||
'ports': [],
|
||||
'static_routes': [],
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
router_attrs.update(attrs)
|
||||
return type('Logical_Router', (object, ), router_attrs)
|
449
ovn_octavia_provider/tests/unit/schemas/ovn-nb.ovsschema
Normal file
449
ovn_octavia_provider/tests/unit/schemas/ovn-nb.ovsschema
Normal file
@ -0,0 +1,449 @@
|
||||
{
|
||||
"name": "OVN_Northbound",
|
||||
"version": "5.16.0",
|
||||
"cksum": "923459061 23095",
|
||||
"tables": {
|
||||
"NB_Global": {
|
||||
"columns": {
|
||||
"nb_cfg": {"type": {"key": "integer"}},
|
||||
"sb_cfg": {"type": {"key": "integer"}},
|
||||
"hv_cfg": {"type": {"key": "integer"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"connections": {
|
||||
"type": {"key": {"type": "uuid",
|
||||
"refTable": "Connection"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"ssl": {
|
||||
"type": {"key": {"type": "uuid",
|
||||
"refTable": "SSL"},
|
||||
"min": 0, "max": 1}},
|
||||
"options": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"ipsec": {"type": "boolean"}},
|
||||
"maxRows": 1,
|
||||
"isRoot": true},
|
||||
"Logical_Switch": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"ports": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "Logical_Switch_Port",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"acls": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "ACL",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"qos_rules": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "QoS",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"load_balancer": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "Load_Balancer",
|
||||
"refType": "weak"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"dns_records": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "DNS",
|
||||
"refType": "weak"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"other_config": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": true},
|
||||
"Logical_Switch_Port": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"type": {"type": "string"},
|
||||
"options": {
|
||||
"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"parent_name": {"type": {"key": "string", "min": 0, "max": 1}},
|
||||
"tag_request": {
|
||||
"type": {"key": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 4095},
|
||||
"min": 0, "max": 1}},
|
||||
"tag": {
|
||||
"type": {"key": {"type": "integer",
|
||||
"minInteger": 1,
|
||||
"maxInteger": 4095},
|
||||
"min": 0, "max": 1}},
|
||||
"addresses": {"type": {"key": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"dynamic_addresses": {"type": {"key": "string",
|
||||
"min": 0,
|
||||
"max": 1}},
|
||||
"port_security": {"type": {"key": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"up": {"type": {"key": "boolean", "min": 0, "max": 1}},
|
||||
"enabled": {"type": {"key": "boolean", "min": 0, "max": 1}},
|
||||
"dhcpv4_options": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "DHCP_Options",
|
||||
"refType": "weak"},
|
||||
"min": 0,
|
||||
"max": 1}},
|
||||
"dhcpv6_options": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "DHCP_Options",
|
||||
"refType": "weak"},
|
||||
"min": 0,
|
||||
"max": 1}},
|
||||
"ha_chassis_group": {
|
||||
"type": {"key": {"type": "uuid",
|
||||
"refTable": "HA_Chassis_Group",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": 1}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"indexes": [["name"]],
|
||||
"isRoot": false},
|
||||
"Address_Set": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"addresses": {"type": {"key": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"indexes": [["name"]],
|
||||
"isRoot": true},
|
||||
"Port_Group": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"ports": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "Logical_Switch_Port",
|
||||
"refType": "weak"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"acls": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "ACL",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"indexes": [["name"]],
|
||||
"isRoot": true},
|
||||
"Load_Balancer": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"vips": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"protocol": {
|
||||
"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["tcp", "udp"]]},
|
||||
"min": 0, "max": 1}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": true},
|
||||
"ACL": {
|
||||
"columns": {
|
||||
"name": {"type": {"key": {"type": "string",
|
||||
"maxLength": 63},
|
||||
"min": 0, "max": 1}},
|
||||
"priority": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 32767}}},
|
||||
"direction": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["from-lport", "to-lport"]]}}},
|
||||
"match": {"type": "string"},
|
||||
"action": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["allow", "allow-related", "drop", "reject"]]}}},
|
||||
"log": {"type": "boolean"},
|
||||
"severity": {"type": {"key": {"type": "string",
|
||||
"enum": ["set",
|
||||
["alert", "warning",
|
||||
"notice", "info",
|
||||
"debug"]]},
|
||||
"min": 0, "max": 1}},
|
||||
"meter": {"type": {"key": "string", "min": 0, "max": 1}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": false},
|
||||
"QoS": {
|
||||
"columns": {
|
||||
"priority": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 32767}}},
|
||||
"direction": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["from-lport", "to-lport"]]}}},
|
||||
"match": {"type": "string"},
|
||||
"action": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["dscp"]]},
|
||||
"value": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 63},
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"bandwidth": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["rate",
|
||||
"burst"]]},
|
||||
"value": {"type": "integer",
|
||||
"minInteger": 1,
|
||||
"maxInteger": 4294967295},
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": false},
|
||||
"Meter": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"unit": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["kbps", "pktps"]]}}},
|
||||
"bands": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "Meter_Band",
|
||||
"refType": "strong"},
|
||||
"min": 1,
|
||||
"max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"indexes": [["name"]],
|
||||
"isRoot": true},
|
||||
"Meter_Band": {
|
||||
"columns": {
|
||||
"action": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["drop"]]}}},
|
||||
"rate": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 1,
|
||||
"maxInteger": 4294967295}}},
|
||||
"burst_size": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 4294967295}}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": false},
|
||||
"Logical_Router": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"ports": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "Logical_Router_Port",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"static_routes": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "Logical_Router_Static_Route",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"policies": {
|
||||
"type": {"key": {"type": "uuid",
|
||||
"refTable": "Logical_Router_Policy",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"enabled": {"type": {"key": "boolean", "min": 0, "max": 1}},
|
||||
"nat": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "NAT",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"load_balancer": {"type": {"key": {"type": "uuid",
|
||||
"refTable": "Load_Balancer",
|
||||
"refType": "weak"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"options": {
|
||||
"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": true},
|
||||
"Logical_Router_Port": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"gateway_chassis": {
|
||||
"type": {"key": {"type": "uuid",
|
||||
"refTable": "Gateway_Chassis",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"ha_chassis_group": {
|
||||
"type": {"key": {"type": "uuid",
|
||||
"refTable": "HA_Chassis_Group",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": 1}},
|
||||
"options": {
|
||||
"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"networks": {"type": {"key": "string",
|
||||
"min": 1,
|
||||
"max": "unlimited"}},
|
||||
"mac": {"type": "string"},
|
||||
"peer": {"type": {"key": "string", "min": 0, "max": 1}},
|
||||
"enabled": {"type": {"key": "boolean", "min": 0, "max": 1}},
|
||||
"ipv6_ra_configs": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"indexes": [["name"]],
|
||||
"isRoot": false},
|
||||
"Logical_Router_Static_Route": {
|
||||
"columns": {
|
||||
"ip_prefix": {"type": "string"},
|
||||
"policy": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["src-ip",
|
||||
"dst-ip"]]},
|
||||
"min": 0, "max": 1}},
|
||||
"nexthop": {"type": "string"},
|
||||
"output_port": {"type": {"key": "string", "min": 0, "max": 1}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": false},
|
||||
"Logical_Router_Policy": {
|
||||
"columns": {
|
||||
"priority": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 32767}}},
|
||||
"match": {"type": "string"},
|
||||
"action": {"type": {
|
||||
"key": {"type": "string",
|
||||
"enum": ["set", ["allow", "drop", "reroute"]]}}},
|
||||
"nexthop": {"type": {"key": "string", "min": 0, "max": 1}}},
|
||||
"isRoot": false},
|
||||
"NAT": {
|
||||
"columns": {
|
||||
"external_ip": {"type": "string"},
|
||||
"external_mac": {"type": {"key": "string",
|
||||
"min": 0, "max": 1}},
|
||||
"logical_ip": {"type": "string"},
|
||||
"logical_port": {"type": {"key": "string",
|
||||
"min": 0, "max": 1}},
|
||||
"type": {"type": {"key": {"type": "string",
|
||||
"enum": ["set", ["dnat",
|
||||
"snat",
|
||||
"dnat_and_snat"
|
||||
]]}}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": false},
|
||||
"DHCP_Options": {
|
||||
"columns": {
|
||||
"cidr": {"type": "string"},
|
||||
"options": {"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": true},
|
||||
"Connection": {
|
||||
"columns": {
|
||||
"target": {"type": "string"},
|
||||
"max_backoff": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 1000},
|
||||
"min": 0,
|
||||
"max": 1}},
|
||||
"inactivity_probe": {"type": {"key": "integer",
|
||||
"min": 0,
|
||||
"max": 1}},
|
||||
"other_config": {"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"external_ids": {"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"is_connected": {"type": "boolean", "ephemeral": true},
|
||||
"status": {"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"},
|
||||
"ephemeral": true}},
|
||||
"indexes": [["target"]]},
|
||||
"DNS": {
|
||||
"columns": {
|
||||
"records": {"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"external_ids": {"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}}},
|
||||
"isRoot": true},
|
||||
"SSL": {
|
||||
"columns": {
|
||||
"private_key": {"type": "string"},
|
||||
"certificate": {"type": "string"},
|
||||
"ca_cert": {"type": "string"},
|
||||
"bootstrap_ca_cert": {"type": "boolean"},
|
||||
"ssl_protocols": {"type": "string"},
|
||||
"ssl_ciphers": {"type": "string"},
|
||||
"external_ids": {"type": {"key": "string",
|
||||
"value": "string",
|
||||
"min": 0,
|
||||
"max": "unlimited"}}},
|
||||
"maxRows": 1},
|
||||
"Gateway_Chassis": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"chassis_name": {"type": "string"},
|
||||
"priority": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 32767}}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}},
|
||||
"options": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"indexes": [["name"]],
|
||||
"isRoot": false},
|
||||
"HA_Chassis": {
|
||||
"columns": {
|
||||
"chassis_name": {"type": "string"},
|
||||
"priority": {"type": {"key": {"type": "integer",
|
||||
"minInteger": 0,
|
||||
"maxInteger": 32767}}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"isRoot": false},
|
||||
"HA_Chassis_Group": {
|
||||
"columns": {
|
||||
"name": {"type": "string"},
|
||||
"ha_chassis": {
|
||||
"type": {"key": {"type": "uuid",
|
||||
"refTable": "HA_Chassis",
|
||||
"refType": "strong"},
|
||||
"min": 0,
|
||||
"max": "unlimited"}},
|
||||
"external_ids": {
|
||||
"type": {"key": "string", "value": "string",
|
||||
"min": 0, "max": "unlimited"}}},
|
||||
"indexes": [["name"]],
|
||||
"isRoot": true}}
|
||||
}
|
2141
ovn_octavia_provider/tests/unit/test_driver.py
Normal file
2141
ovn_octavia_provider/tests/unit/test_driver.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,5 @@ ovsdbapp>=0.17.0 # Apache-2.0
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
tenacity>=4.4.0 # Apache-2.0
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
neutron>=13.0.0.0b2 # Apache-2.0
|
||||
octavia-lib>=1.3.1 # Apache-2.0
|
||||
python-neutronclient>=6.7.0 # Apache-2.0
|
||||
|
@ -25,3 +25,8 @@ packages =
|
||||
[global]
|
||||
setup-hooks =
|
||||
pbr.hooks.setup_hook
|
||||
|
||||
|
||||
[entry_points]
|
||||
octavia.api.drivers =
|
||||
ovn = ovn_octavia_provider.driver:OvnProviderDriver
|
||||
|
@ -17,3 +17,4 @@ testresources>=2.0.0 # Apache-2.0/BSD
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
||||
WebTest>=2.0.27 # MIT
|
||||
testtools>=2.2.0 # MIT
|
||||
neutron>=15.0.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user