ic: add support for OVN_IC_Northbound schema

This change provides 'OvnIcNbApiIdlImpl' class for interaction with
'ovn_ic_nb.db' and adds functions and commands to add, delete and list
rows of 'Transit_Switch' table in 'OVN_IC_Northbound' schema.

Closes-Bug: 1944441

Change-Id: I322f05c1a91113a9470218946b7ce151a63737be
This commit is contained in:
Anton Vazhnetsov 2021-09-11 03:19:09 +03:00
parent 3a5927938b
commit 39426dbd4d
10 changed files with 320 additions and 22 deletions

View File

@ -0,0 +1,61 @@
# 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 ovsdbapp import api
class API(api.API, metaclass=abc.ABCMeta):
"""An API based off of the ovn-ic-nbctl CLI interface
This API basically mirrors the ovn-ic-nbctl operations with these changes:
1. Methods that create objects will return a read-only view of the object
2. Methods which list objects will return a list of read-only view objects
"""
@abc.abstractmethod
def ts_add(self, switch, may_exist=False, **columns):
"""Create a transit switch named 'switch'
:param switch: The name of the switch
:type switch: string or uuid.UUID
:param may_exist: If True, don't fail if the switch already exists
:type may_exist: boolean
:param columns: Additional columns to directly set on the switch
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def ts_del(self, switch, if_exists=False):
"""Delete transit switch 'switch' and all its ports
:param switch: The name or uuid of the switch
:type switch: string or uuid.UUID
:param if_exists: If True, don't fail if the switch doesn't exist
:type if_exists: boolean
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def ts_list(self):
"""Get all transit switches
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
def ts_get(self, switch):
"""Get transit switch for 'switch'
:returns: :class:`Command` with RowView result
"""

View File

@ -0,0 +1,67 @@
# 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 ovsdbapp.backend.ovs_idl import command as cmd
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp.backend.ovs_idl import rowview
class TsAddCommand(cmd.AddCommand):
table_name = 'Transit_Switch'
def __init__(self, api, switch, may_exist=False, **columns):
super().__init__(api)
self.switch = switch
self.columns = columns
self.may_exist = may_exist
def run_idl(self, txn):
# There is requirement for name to be unique
# (index in ovn-ic-nb.ovsschema)
switch = idlutils.row_by_value(self.api.idl, self.table_name, 'name',
self.switch, None)
if switch:
if self.may_exist:
self.result = rowview.RowView(switch)
return
raise RuntimeError("Transit Switch %s exists" % self.switch)
switch = txn.insert(self.api.tables[self.table_name])
switch.name = self.switch
self.set_columns(switch, **self.columns)
self.result = switch.uuid
class TsDelCommand(cmd.BaseCommand):
def __init__(self, api, switch, if_exists=False):
super().__init__(api)
self.switch = switch
self.if_exists = if_exists
def run_idl(self, txn):
try:
switch = self.api.lookup('Transit_Switch', self.switch)
switch.delete()
except idlutils.RowNotFound as e:
if self.if_exists:
return
msg = "Transit Switch %s does not exist" % self.switch
raise RuntimeError(msg) from e
class TsListCommand(cmd.ReadOnlyCommand):
def run_idl(self, txn):
table = self.api.tables['Transit_Switch']
self.result = [rowview.RowView(r) for r in table.rows.values()]
class TsGetCommand(cmd.BaseGetRowCommand):
table = 'Transit_Switch'

View File

@ -0,0 +1,35 @@
# 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 ovsdbapp.backend import ovs_idl
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp.schema.ovn_ic_northbound import api
from ovsdbapp.schema.ovn_ic_northbound import commands as cmd
class OvnIcNbApiIdlImpl(ovs_idl.Backend, api.API):
schema = 'OVN_IC_Northbound'
lookup_table = {
'Transit_Switch': idlutils.RowLookup('Transit_Switch', 'name', None),
}
def ts_add(self, switch, may_exist=False, **columns):
return cmd.TsAddCommand(self, switch, may_exist, **columns)
def ts_del(self, switch, if_exists=False):
return cmd.TsDelCommand(self, switch, if_exists)
def ts_list(self):
return cmd.TsListCommand(self)
def ts_get(self, switch):
return cmd.TsGetCommand(self, switch)

View File

@ -30,7 +30,9 @@ class FunctionalTestCase(base.TestCase):
ovsvenv.setUp()
schema_map = {'Open_vSwitch': ovsvenv.ovs_connection,
'OVN_Northbound': ovsvenv.ovnnb_connection,
'OVN_Southbound': ovsvenv.ovnsb_connection}
'OVN_Southbound': ovsvenv.ovnsb_connection,
'OVN_IC_Northbound': ovsvenv.ovn_icnb_connection,
}
ovsvenvlog = None
if os.getenv('KEEP_VENV') and os.getenv('VIRTUAL_ENV'):
ovsvenvlog = open(os.path.join(os.getenv('VIRTUAL_ENV'),

View File

@ -0,0 +1,18 @@
# 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 ovsdbapp.tests.functional.schema import fixtures
class TransitSwitchesFixture(fixtures.ImplIdlFixture):
create = 'ts_add'
delete = 'ts_del'

View File

@ -0,0 +1,95 @@
# 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 ovsdbapp.schema.ovn_ic_northbound import impl_idl
from ovsdbapp.tests.functional import base
from ovsdbapp.tests.functional.schema.ovn_ic_northbound import fixtures
from ovsdbapp.tests import utils
class OvnIcNorthboundTest(base.FunctionalTestCase):
schemas = ['OVN_IC_Northbound']
def setUp(self):
super(OvnIcNorthboundTest, self).setUp()
self.api = impl_idl.OvnIcNbApiIdlImpl(self.connection)
self.table = self.api.tables['Transit_Switch']
def _ts_add(self, *args, **kwargs):
fix = self.useFixture(fixtures.TransitSwitchesFixture(self.api, *args,
**kwargs))
self.assertIn(fix.obj.uuid, self.table.rows)
return fix.obj
def _test_ts_get(self, col):
ts = self._ts_add(switch=utils.get_rand_device_name())
val = getattr(ts, col)
found = self.api.ts_get(val).execute(check_error=True)
self.assertEqual(ts, found)
def test_ts_get_uuid(self):
self._test_ts_get('uuid')
def test_ts_get_name(self):
self._test_ts_get('name')
def test_ts_add_name(self):
name = utils.get_rand_device_name()
ts = self._ts_add(name)
self.assertEqual(name, ts.name)
def test_ts_add_existing(self):
name = utils.get_rand_device_name()
self._ts_add(name)
cmd = self.api.ts_add(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_ts_add_may_exist(self):
name = utils.get_rand_device_name()
sw = self._ts_add(name)
sw2 = self.api.ts_add(name, may_exist=True).execute(check_error=True)
self.assertEqual(sw, sw2)
def test_ts_add_columns(self):
external_ids = {'mykey': 'myvalue', 'yourkey': 'yourvalue'}
ts = self._ts_add(switch=utils.get_rand_device_name(),
external_ids=external_ids)
self.assertEqual(external_ids, ts.external_ids)
def test_ts_del(self):
sw = self._ts_add(switch=utils.get_rand_device_name())
self.api.ts_del(sw.uuid).execute(check_error=True)
self.assertNotIn(sw.uuid, self.table.rows)
def test_ts_del_by_name(self):
name = utils.get_rand_device_name()
self._ts_add(name)
self.api.ts_del(name).execute(check_error=True)
found = self.api.ts_get(name).execute()
self.assertIsNone(found)
def test_ts_del_no_existing(self):
name = utils.get_rand_device_name()
cmd = self.api.ts_del(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_ts_del_if_exists(self):
name = utils.get_rand_device_name()
self.api.ts_del(name, if_exists=True).execute(check_error=True)
found = self.api.ts_get(name).execute()
self.assertIsNone(found)
def test_ts_list(self):
switches = {self._ts_add(str(i)) for i in range(3)}
switch_set = set(self.api.ts_list().execute(check_error=True))
self.assertTrue(switches.issubset(switch_set))

View File

@ -151,8 +151,10 @@ class OvsOvnVenvFixture(OvsVenvFixture):
os.path.join(os.path.sep, 'usr', 'local', 'share', 'ovn'),
os.path.join(os.path.sep, 'usr', 'share', 'ovn')) + (
OvsVenvFixture.OVS_PATHS)
NBSCHEMA = 'ovn-nb.ovsschema'
SBSCHEMA = 'ovn-sb.ovsschema'
IC_NBSCHEMA = 'ovn-ic-nb.ovsschema'
def __init__(self, venv, ovndir=None, add_chassis=False, **kwargs):
self.add_chassis = add_chassis
@ -162,7 +164,8 @@ class OvsOvnVenvFixture(OvsVenvFixture):
":{0}/controller:{0}/northd:{0}/utilities".format(ovndir))
super().__init__(venv, **kwargs)
self.ovndir = self._share_path(self.OVN_PATHS, ovndir,
[self.SBSCHEMA, self.NBSCHEMA])
[self.SBSCHEMA, self.NBSCHEMA,
self.IC_NBSCHEMA])
self.env.update({'OVN_RUNDIR': self.venv})
@property
@ -173,6 +176,10 @@ class OvsOvnVenvFixture(OvsVenvFixture):
def ovnnb_schema(self):
return os.path.join(self.ovndir, self.NBSCHEMA)
@property
def ovn_icnb_schema(self):
return os.path.join(self.ovndir, self.IC_NBSCHEMA)
@property
def ovnnb_connection(self):
return 'unix:' + os.path.join(self.venv, 'ovnnb_db.sock')
@ -181,38 +188,45 @@ class OvsOvnVenvFixture(OvsVenvFixture):
def ovnsb_connection(self):
return 'unix:' + os.path.join(self.venv, 'ovnsb_db.sock')
@property
def ovn_icnb_connection(self):
return 'unix:' + os.path.join(self.venv, 'ovn_ic_nb_db.sock')
def setup_dbs(self):
super().setup_dbs()
self.create_db('ovnsb.db', self.ovnsb_schema)
self.create_db('ovnnb.db', self.ovnnb_schema)
self.create_db('ovn_ic_nb.db', self.ovn_icnb_schema)
def start_ovsdb_processes(self):
super().start_ovsdb_processes()
self.call(['ovsdb-server', '--detach', '--no-chdir', '-vconsole:off',
'--pidfile=%s' % os.path.join(self.venv, 'ovnnb_db.pid'),
'--log-file=%s' % os.path.join(self.venv, 'ovnnb_db.log'),
'--remote=db:OVN_Northbound,NB_Global,connections',
'--private-key=db:OVN_Northbound,SSL,private_key',
'--certificate=db:OVN_Northbound,SSL,certificate',
'--ca-cert=db:OVN_Northbound,SSL,ca_cert',
'--ssl-protocols=db:OVN_Northbound,SSL,ssl_protocols',
'--ssl-ciphers=db:OVN_Northbound,SSL,ssl_ciphers',
'--remote=p' + self.ovnnb_connection, 'ovnnb.db'])
self.call(['ovsdb-server', '--detach', '--no-chdir', '-vconsole:off',
'--pidfile=%s' % os.path.join(self.venv, 'ovnsb_db.pid'),
'--log-file=%s' % os.path.join(self.venv, 'ovnsb_db.log'),
'--remote=db:OVN_Southbound,SB_Global,connections',
'--private-key=db:OVN_Southbound,SSL,private_key',
'--certificate=db:OVN_Southbound,SSL,certificate',
'--ca-cert=db:OVN_Southbound,SSL,ca_cert',
'--ssl-protocols=db:OVN_Southbound,SSL,ssl_protocols',
'--ssl-ciphers=db:OVN_Southbound,SSL,ssl_ciphers',
'--remote=p' + self.ovnsb_connection, 'ovnsb.db'])
for connection, schema, db_name, table in [
(self.ovnnb_connection,
"OVN_Northbound", "ovnnb", "NB_Global"),
(self.ovnsb_connection,
"OVN_Southbound", "ovnsb", "SB_Global"),
(self.ovn_icnb_connection,
"OVN_IC_Northbound", "ovn_ic_nb", "IC_NB_Global"),
]:
self.call(['ovsdb-server',
'--detach', '--no-chdir', '-vconsole:off',
'--pidfile=%s' % os.path.join(self.venv,
'%s_db.pid' % (db_name)),
'--log-file=%s' % os.path.join(self.venv,
'%s_db.log' % (db_name)),
'--remote=db:%s,%s,connections' % (schema, table),
'--private-key=db:%s,SSL,private_key' % (schema),
'--certificate=db:%s,SSL,certificate' % (schema),
'--ca-cert=db:%s,SSL,ca_cert' % (schema),
'--ssl-protocols=db:%s,SSL,ssl_protocols' % (schema),
'--ssl-ciphers=db:%s,SSL,ssl_ciphers' % (schema),
'--remote=p' + connection, '%s.db' % (db_name)])
def init_processes(self):
super().init_processes()
self.call(['ovn-nbctl', 'init'])
self.call(['ovn-sbctl', 'init'])
self.call(['ovn-ic-nbctl', 'init'])
if self.add_chassis:
self.call([
'ovs-vsctl', 'set', 'open', '.',

View File

@ -0,0 +1,6 @@
---
features:
- |
Added support for the "OVN_IC_Northbound" schema. The ``OvnIcNbApiIdlImpl`` class provides
interaction with "ovn_ic_nb.db" and adds support to add, delete and list registers rows of
"Transit_Switch" table.