Merge "vtep: add support for 'hardware_vtep' schema"

This commit is contained in:
Zuul 2022-07-20 15:08:43 +00:00 committed by Gerrit Code Review
commit d26be1cdcd
10 changed files with 1065 additions and 2 deletions

View 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 abc
from ovsdbapp import api
class API(api.API, metaclass=abc.ABCMeta):
"""An API based off of the vtep-ctl CLI interface
This API basically mirrors the vtep-ctl 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 add_ps(self, pswitch, may_exist=False, **columns):
"""Create a physical switch named 'pswitch'
:param pswitch: The name of the switch
:type pswitch: 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 del_ps(self, pswitch, if_exists=False):
"""Delete physical switch 'pswitch' and all its ports
:param pswitch: The name or uuid of the switch
:type pswitch: string or uuid.UUID
:type 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 list_ps(self):
"""Get all physical switches
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
def get_ps(self, pswitch):
"""Get physical switch for 'pswitch'
:param pswitch: The name of the pswitch
:type pswitch: string or uuid.UUID
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def add_port(self, pswitch, port, may_exist=False):
"""Add a port named 'port' to physical switch named 'pswitch'
:param pswitch: The name of the switch
:type pswitch: string or uuid.UUID
:param port: The name of the port
:type port: string or uuid.UUID
:param may_exist: If True, don't fail if the port already exists in
physical switch
:type may_exist: boolean
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def del_port(self, pswitch, port, if_exists=False):
"""Delete a port named 'port' from physical switch named 'pswitch'
:param pswitch: The name or uuid of the switch
:type pswitch: string or uuid.UUID
:param port: The name of the port
:type port: string or uuid.UUID
:type if_exists: If True, don't fail if the switch doesn't exist in
physical switch
:type if_exists: boolean
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def list_ports(self, pswitch):
"""Get all ports of physical switch 'pswitch'
:param pswitch: The name of the pswitch
:type pswitch: string or uuid.UUID
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
def get_port(self, port):
"""Get physical port for 'port'
:param port: The name of the port
:type port: string or uuid.UUID
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def add_ls(self, switch, may_exist=False, **columns):
"""Create a logical 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 del_ls(self, switch, if_exists=False):
"""Delete logical switch 'switch' and all its ports
:param switch: The name or uuid of the switch
:type switch: string or uuid.UUID
:type 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 list_ls(self):
"""Get all logical switches
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
def get_ls(self, switch):
"""Get logical switch for 'switch'
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def bind_ls(self, pswitch, port, vlan, switch):
"""Bind 'switch' to 'pswitch'
Bind logical switch to the port/vlan combination on the
physical switch 'pswitch'.
:param pswitch: The name or uuid of the physical switch
:type pswitch: string or uuid.UUID
:param port: name of port
:type port: string
:param vlan: number of VLAN
:type vlan: int
:param switch: The name or uuid of the switch
:type switch: string or uuid.UUID
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def unbind_ls(self, pswitch, port, vlan):
"""Unbind 'switch' from 'pswitch'
Remove the logical switch binding from the port/vlan combination on
the physical switch pswitch.
:param pswitch: The name or uuid of the physical switch
:type pswitch: string or uuid.UUID
:param port: name of port
:type port: string
:param vlan: number of VLAN
:type vlan: int
:param switch: The name or uuid of the switch
:type switch: string or uuid.UUID
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def list_local_macs(self, switch):
"""Get all local MACs for 'switch'
:param switch: The name of the switch
:type switch: string or uuid.UUID
:returns: :class:`Command` with list of RowView lists result.
First list contains 'Ucast_Macs_Local' table records,
second list contains 'Mcast_Macs_Local' table records.
"""
@abc.abstractmethod
def list_remote_macs(self, switch):
"""Get all remote MACs for 'switch'
:param switch: The name of the switch
:type switch: string or uuid.UUID
:returns: :class:`Command` with list of RowView lists result.
First list contains 'Ucast_Macs_Remote' table records,
second list contains 'Mcast_Macs_Remote' table records.
"""
@abc.abstractmethod
def clear_local_macs(self, switch):
"""Clear the local MAC bindings for 'switch'
:param switch: The name of the switch
:type switch: string or uuid.UUID
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def clear_remote_macs(self, switch):
"""Clear the remote MAC bindings for 'switch'
:param switch: The name of the switch
:type switch: string or uuid.UUID
:returns: :class:`Command` with RowView result
"""

View File

@ -0,0 +1,296 @@
# 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 TableGlobalIsEmpty(idlutils.RowNotFound):
message = "Table 'Global' is empty"
def get_global_record(api):
# there should be only one record in 'Global' table
try:
return next((r for r in api.tables['Global'].rows.values()))
except StopIteration as e:
raise TableGlobalIsEmpty from e
class _ListCommand(cmd.ReadOnlyCommand):
def run_idl(self, txn):
table = self.api.tables[self.table_name]
self.result = [rowview.RowView(r) for r in table.rows.values()]
class AddPsCommand(cmd.AddCommand):
table_name = 'Physical_Switch'
def __init__(self, api, pswitch, may_exist=False, **columns):
super().__init__(api)
self.pswitch = pswitch
self.columns = columns
self.may_exist = may_exist
def run_idl(self, txn):
config = get_global_record(self.api)
pswitch = idlutils.row_by_value(self.api.idl, self.table_name, 'name',
self.pswitch, None)
if pswitch:
if self.may_exist:
self.result = rowview.RowView(pswitch)
return
msg = "Physical switch %s exists" % self.pswitch
raise RuntimeError(msg)
pswitch = txn.insert(self.api.tables[self.table_name])
pswitch.name = self.pswitch
self.set_columns(pswitch, **self.columns)
config.addvalue('switches', pswitch)
self.result = pswitch.uuid
class DelPsCommand(cmd.BaseCommand):
table_name = 'Physical_Switch'
def __init__(self, api, pswitch, if_exists=False):
super().__init__(api)
self.pswitch = pswitch
self.if_exists = if_exists
def run_idl(self, txn):
config = get_global_record(self.api)
try:
pswitch = self.api.lookup(self.table_name, self.pswitch)
config.delvalue('switches', pswitch)
pswitch.delete()
except idlutils.RowNotFound as e:
if self.if_exists:
return
msg = "Physical switch %s does not exist" % self.pswitch
raise RuntimeError(msg) from e
class ListPsCommand(_ListCommand):
table_name = 'Physical_Switch'
class GetPsCommand(cmd.BaseGetRowCommand):
table = 'Physical_Switch'
class AddPortCommand(cmd.AddCommand):
table_name = 'Physical_Port'
def __init__(self, api, pswitch, port, may_exist=False):
super().__init__(api)
self.pswitch = pswitch
self.port = port
self.may_exist = may_exist
self.conditions = [('name', '=', self.port)]
def run_idl(self, txn):
pswitch = self.api.lookup('Physical_Switch', self.pswitch)
port = next((p for p in pswitch.ports
if idlutils.row_match(p, self.conditions)), None)
if port:
if self.may_exist:
self.result = rowview.RowView(port)
return
msg = "Physical port %s exists in %s" % (self.port, self.pswitch)
raise RuntimeError(msg)
port = txn.insert(self.api.tables[self.table_name])
port.name = self.port
pswitch.addvalue('ports', port)
self.result = port.uuid
class DelPortCommand(cmd.BaseCommand):
table_name = 'Physical_Port'
def __init__(self, api, pswitch, port, if_exists=False):
super().__init__(api)
self.pswitch = pswitch
self.port = port
self.if_exists = if_exists
self.conditions = [('name', '=', self.port)]
def run_idl(self, txn):
pswitch = self.api.lookup('Physical_Switch', self.pswitch)
port = next((p for p in pswitch.ports
if idlutils.row_match(p, self.conditions)), None)
if not port:
if self.if_exists:
return
msg = "Physical port %s does not exist in %s" % (self.port,
self.pswitch)
raise RuntimeError(msg)
pswitch.delvalue('ports', port)
port.delete()
class ListPortsCommand(cmd.ReadOnlyCommand):
table_name = 'Physical_Switch'
def __init__(self, api, pswitch):
super().__init__(api)
self.pswitch = pswitch
def run_idl(self, txn):
pswitch = self.api.lookup(self.table_name, self.pswitch)
self.result = [rowview.RowView(port) for port in pswitch.ports]
class GetPortCommand(cmd.BaseGetRowCommand):
table = 'Physical_Port'
class AddLsCommand(cmd.AddCommand):
table_name = 'Logical_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):
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
msg = "Logical switch %s exists" % self.switch
raise RuntimeError(msg)
switch = txn.insert(self.api.tables[self.table_name])
switch.name = self.switch
self.set_columns(switch, **self.columns)
self.result = switch.uuid
class DelLsCommand(cmd.BaseCommand):
table_name = 'Logical_Switch'
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:
entity = self.api.lookup(self.table_name, self.switch)
entity.delete()
except idlutils.RowNotFound as e:
if self.if_exists:
return
msg = "Logical switch %s does not exist" % self.switch
raise RuntimeError(msg) from e
class ListLsCommand(_ListCommand):
table_name = 'Logical_Switch'
class GetLsCommand(cmd.BaseGetRowCommand):
table = 'Logical_Switch'
class BindLsCommand(cmd.BaseCommand):
table_name = 'Physical_Port'
def __init__(self, api, pswitch, port, vlan, switch):
super().__init__(api)
self.pswitch = pswitch
self.port = port
self.vlan = vlan
self.switch = switch
self.conditions = [('name', '=', self.port)]
def run_idl(self, txn):
pswitch = self.api.lookup('Physical_Switch', self.pswitch)
switch = self.api.lookup('Logical_Switch', self.switch)
port = next((p for p in pswitch.ports
if idlutils.row_match(p, self.conditions)), None)
if not port:
raise idlutils.RowNotFound(table=self.table_name,
col='name', match=self.port)
port.setkey('vlan_bindings', self.vlan, switch.uuid)
class UnbindLsCommand(cmd.BaseCommand):
table_name = 'Physical_Port'
def __init__(self, api, pswitch, port, vlan):
super().__init__(api)
self.pswitch = pswitch
self.port = port
self.vlan = vlan
self.conditions = [('name', '=', self.port)]
def run_idl(self, txn):
pswitch = self.api.lookup('Physical_Switch', self.pswitch)
port = next((p for p in pswitch.ports
if idlutils.row_match(p, self.conditions)), None)
if not port:
raise idlutils.RowNotFound(table=self.table_name,
col='name', match=self.port)
port.delkey('vlan_bindings', self.vlan)
class _ClearMacsCommand(cmd.BaseCommand):
def __init__(self, api, switch):
super().__init__(api)
self.switch = switch
def run_idl(self, txn):
switch = self.api.lookup('Logical_Switch', self.switch)
macs = []
for table_name in self.table_names:
macs.extend(idlutils.rows_by_value(self.api.idl,
table_name,
'logical_switch', switch))
for mac in macs:
mac.delete()
class ClearLocalMacsCommand(_ClearMacsCommand):
table_names = ['Ucast_Macs_Local', 'Mcast_Macs_Local']
class ClearRemoteMacsCommand(_ClearMacsCommand):
table_names = ['Ucast_Macs_Remote', 'Mcast_Macs_Remote']
class _ListMacsCommand(cmd.ReadOnlyCommand):
def __init__(self, api, switch):
super().__init__(api)
self.switch = switch
def run_idl(self, txn):
switch = self.api.lookup('Logical_Switch', self.switch)
self.result = []
for table_name in self.table_names:
self.result.append([rowview.RowView(mac)
for mac in idlutils.rows_by_value(
self.api.idl, table_name,
'logical_switch', switch)])
class ListLocalMacsCommand(_ListMacsCommand):
table_names = ['Ucast_Macs_Local', 'Mcast_Macs_Local']
class ListRemoteMacsCommand(_ListMacsCommand):
table_names = ['Ucast_Macs_Remote', 'Mcast_Macs_Remote']

View File

@ -0,0 +1,86 @@
# 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.hardware_vtep import api
from ovsdbapp.schema.hardware_vtep import commands as cmd
class HwVtepApiIdlImpl(ovs_idl.Backend, api.API):
schema = 'hardware_vtep'
lookup_table = {
'Global': idlutils.RowLookup('Global', None, None),
'Physical_Switch': idlutils.RowLookup('Physical_Switch', 'name', None),
'Physical_Port': idlutils.RowLookup('Physical_Port', 'name', None),
'Logical_Switch': idlutils.RowLookup('Logical_Switch', 'name', None),
'Ucast_Macs_Local': idlutils.RowLookup('Ucast_Macs_Local', None, None),
'Mcast_Macs_Local': idlutils.RowLookup('Mcast_Macs_Local', None, None),
'Ucast_Macs_Remote': idlutils.RowLookup('Ucast_Macs_Remote',
None, None),
'Mcast_Macs_Remote': idlutils.RowLookup('Mcast_Macs_Remote',
None, None),
}
def add_ps(self, pswitch, may_exist=False, **columns):
return cmd.AddPsCommand(self, pswitch, may_exist, **columns)
def del_ps(self, pswitch, if_exists=False):
return cmd.DelPsCommand(self, pswitch, if_exists)
def list_ps(self):
return cmd.ListPsCommand(self)
def get_ps(self, pswitch):
return cmd.GetPsCommand(self, pswitch)
def add_port(self, pswitch, port, may_exist=False):
return cmd.AddPortCommand(self, pswitch, port, may_exist)
def del_port(self, pswitch, port, if_exists=False):
return cmd.DelPortCommand(self, pswitch, port, if_exists)
def list_ports(self, pswitch):
return cmd.ListPortsCommand(self, pswitch)
def get_port(self, port):
return cmd.GetPortCommand(self, port)
def add_ls(self, switch, may_exist=False, **columns):
return cmd.AddLsCommand(self, switch, may_exist, **columns)
def del_ls(self, switch, if_exists=False):
return cmd.DelLsCommand(self, switch, if_exists)
def list_ls(self):
return cmd.ListLsCommand(self)
def get_ls(self, switch):
return cmd.GetLsCommand(self, switch)
def bind_ls(self, pswitch, port, vlan, switch):
return cmd.BindLsCommand(self, pswitch, port, vlan, switch)
def unbind_ls(self, pswitch, port, vlan):
return cmd.UnbindLsCommand(self, pswitch, port, vlan)
def clear_local_macs(self, switch):
return cmd.ClearLocalMacsCommand(self, switch)
def clear_remote_macs(self, switch):
return cmd.ClearRemoteMacsCommand(self, switch)
def list_local_macs(self, switch):
return cmd.ListLocalMacsCommand(self, switch)
def list_remote_macs(self, switch):
return cmd.ListRemoteMacsCommand(self, switch)

View File

@ -0,0 +1,28 @@
# 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.hardware_vtep import impl_idl
from ovsdbapp.tests.functional.schema import fixtures
class HwVtepApiFixture(fixtures.ApiImplFixture):
api_cls = impl_idl.HwVtepApiIdlImpl
class PhysicalSwitchFixture(fixtures.ImplIdlFixture):
create = 'add_ps'
delete = 'del_ps'
class LogicalSwitchFixture(fixtures.ImplIdlFixture):
create = 'add_ls'
delete = 'del_ls'

View File

@ -0,0 +1,387 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017 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.
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp.schema.hardware_vtep.commands import get_global_record
from ovsdbapp.tests.functional import base
from ovsdbapp.tests.functional.schema.hardware_vtep import fixtures
from ovsdbapp.tests import utils
class HardwareVtepTest(base.FunctionalTestCase):
schemas = ["hardware_vtep"]
fixture_class = base.venv.OvsVtepVenvFixture
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.schema_map = cls.schema_map.copy()
cls.schema_map['hardware_vtep'] = cls.ovsvenv.ovs_connection
def setUp(self):
if not self.ovsvenv.has_vtep:
self.skipTest("Installed version of OVS does not support VTEP")
super().setUp()
self.api = self.useFixture(
fixtures.HwVtepApiFixture(self.connection)).obj
class TestPhysicalSwitchOps(HardwareVtepTest):
def setUp(self):
super().setUp()
self.table = self.api.tables['Physical_Switch']
self.config = get_global_record(self.api)
def _add_ps(self, *args, **kwargs):
ps = self.useFixture(fixtures.PhysicalSwitchFixture(self.api, *args,
**kwargs)).obj
self.assertIn(ps.uuid, self.table.rows)
self.assertIn(ps, self.config.switches)
return ps
def _test_get_ps(self, col):
ps = self._add_ps(pswitch=utils.get_rand_device_name())
val = getattr(ps, col)
found = self.api.get_ps(val).execute(check_error=True)
self.assertEqual(ps, found)
def test_get_ps_uuid(self):
self._test_get_ps('uuid')
def test_get_ps_name(self):
self._test_get_ps('name')
def test_add_ps_name(self):
name = utils.get_rand_device_name()
sw = self._add_ps(name)
self.assertEqual(name, sw.name)
def test_add_ps_exists(self):
name = utils.get_rand_device_name()
self._add_ps(name)
cmd = self.api.add_ps(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_add_ps_may_exist(self):
name = utils.get_rand_device_name()
sw = self._add_ps(name)
sw2 = self.api.add_ps(name, may_exist=True).execute(check_error=True)
self.assertEqual(sw, sw2)
def test_del_ps(self):
name = utils.get_rand_device_name()
sw = self._add_ps(name)
self.api.del_ps(sw.uuid).execute(check_error=True)
self.assertNotIn(sw.uuid, self.table.rows)
self.assertNotIn(sw, self.config.switches)
def test_del_ps_by_name(self):
name = utils.get_rand_device_name()
sw = self._add_ps(name)
self.api.del_ps(name).execute(check_error=True)
self.assertNotIn(sw.uuid, self.table.rows)
self.assertNotIn(sw, self.config.switches)
def test_del_ps_no_exist(self):
name = utils.get_rand_device_name()
cmd = self.api.del_ps(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_del_ps_if_exists(self):
name = utils.get_rand_device_name()
self.api.del_ps(name, if_exists=True).execute(check_error=True)
def test_list_ps(self):
switches = {self._add_ps(str(i)) for i in range(3)}
switch_set = set(self.api.list_ps().execute(check_error=True))
self.assertTrue(switches.issubset(switch_set))
class TestPhysicalPortOps(HardwareVtepTest):
def setUp(self):
super().setUp()
self.table = self.api.tables['Physical_Port']
self.ps = self.useFixture(fixtures.PhysicalSwitchFixture(
self.api, utils.get_rand_device_name())).obj
def _add_port(self, *args, name=None, **kwargs):
port = self.api.add_port(self.ps.uuid,
name or utils.get_rand_device_name(),
*args, **kwargs).execute()
self.assertIn(port.uuid, self.table.rows)
self.assertIn(port, self.ps.ports)
return port
def _test_get_port(self, col):
port = self._add_port(name=utils.get_rand_device_name())
val = getattr(port, col)
found = self.api.get_port(val).execute(check_error=True)
self.assertEqual(port, found)
def test_get_port_uuid(self):
self._test_get_port('uuid')
def test_get_port_name(self):
self._test_get_port('name')
def test_add_port_name(self):
name = utils.get_rand_device_name()
port = self._add_port(name=name)
self.assertEqual(name, port.name)
def test_add_port_exists(self):
name = utils.get_rand_device_name()
self._add_port(name=name)
cmd = self.api.add_port(self.ps.uuid, name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_add_port_may_exist(self):
name = utils.get_rand_device_name()
port = self._add_port(name=name)
port2 = self.api.add_port(
self.ps.uuid, name, may_exist=True).execute(check_error=True)
self.assertEqual(port, port2)
def test_del_port(self):
port = self._add_port()
self.api.del_port(self.ps.name, port.name).execute(check_error=True)
self.assertNotIn(port.uuid, self.table.rows)
self.assertNotIn(port, self.ps.ports)
def test_del_port_by_name(self):
name = utils.get_rand_device_name()
port = self._add_port(name=name)
self.api.del_port(self.ps.uuid, name).execute(check_error=True)
self.assertNotIn(port.uuid, self.table.rows)
self.assertNotIn(port, self.ps.ports)
def test_del_port_no_exist(self):
name = utils.get_rand_device_name()
cmd = self.api.del_port(self.ps.uuid, name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_del_ps_if_exists(self):
name = utils.get_rand_device_name()
self.api.del_port(self.ps.uuid, name, if_exists=True).execute(
check_error=True)
def test_list_ps(self):
ports = {self._add_port(str(i)) for i in range(3)}
port_set = set(self.api.list_ports(
self.ps.uuid).execute(check_error=True))
self.assertTrue(ports.issubset(port_set))
class TestLogicalSwitchOps(HardwareVtepTest):
def setUp(self):
super().setUp()
self.table = self.api.tables['Logical_Switch']
self.vlan = 10
def _add_ls(self, *args, **kwargs):
ls = self.useFixture(fixtures.LogicalSwitchFixture(self.api, *args,
**kwargs)).obj
self.assertIn(ls.uuid, self.table.rows)
return ls
def _test_get_ls(self, col):
ls = self._add_ls(switch=utils.get_rand_device_name())
val = getattr(ls, col)
found = self.api.get_ls(val).execute(check_error=True)
self.assertEqual(ls, found)
def test_get_ls_uuid(self):
self._test_get_ls('uuid')
def test_get_ls_name(self):
self._test_get_ls('name')
def test_add_ls_name(self):
name = utils.get_rand_device_name()
sw = self._add_ls(name)
self.assertEqual(name, sw.name)
def test_add_ls_exists(self):
name = utils.get_rand_device_name()
self._add_ls(name)
cmd = self.api.add_ls(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_add_ls_may_exist(self):
name = utils.get_rand_device_name()
sw = self._add_ls(name)
sw2 = self.api.add_ls(name, may_exist=True).execute(check_error=True)
self.assertEqual(sw, sw2)
def test_del_ls(self):
name = utils.get_rand_device_name()
sw = self._add_ls(name)
self.api.del_ls(sw.uuid).execute(check_error=True)
self.assertNotIn(sw.uuid, self.table.rows)
def test_del_ls_by_name(self):
name = utils.get_rand_device_name()
self._add_ls(name)
self.api.del_ls(name).execute(check_error=True)
def test_del_ls_no_exist(self):
name = utils.get_rand_device_name()
cmd = self.api.del_ls(name)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_del_ls_if_exists(self):
name = utils.get_rand_device_name()
self.api.del_ls(name, if_exists=True).execute(check_error=True)
def test_list_ls(self):
switches = {self._add_ls(str(i)) for i in range(3)}
switch_set = set(self.api.list_ls().execute(check_error=True))
self.assertTrue(switches.issubset(switch_set))
def test_bind_unbind_ls(self):
name = utils.get_rand_device_name()
switch = self._add_ls(name)
ps = self.useFixture(fixtures.PhysicalSwitchFixture(
self.api, name)).obj
port = self.api.add_port(ps.uuid, name,
may_exist=True).execute(check_error=True)
self.api.bind_ls(ps.name, port.name,
self.vlan, switch.name).execute(check_error=True)
self.assertEqual(port.vlan_bindings, {self.vlan: switch})
self.api.unbind_ls(ps.name, port.name,
self.vlan).execute(check_error=True)
self.assertEqual(port.vlan_bindings, {})
def _test_bind_ls_no_exist(self,
pswitch_name=None,
port_name=None,
switch_name=None):
cmd = self.api.bind_ls(pswitch_name, port_name, self.vlan, switch_name)
self.assertRaises(idlutils.RowNotFound, cmd.execute, check_error=True)
def test_bind_ls_pswitch_no_exist(self):
name = utils.get_rand_device_name()
switch = self._add_ls(utils.get_rand_device_name())
self._test_bind_ls_no_exist(pswitch_name=name,
port_name=name,
switch_name=switch.name)
def test_bind_ls_port_no_exist(self):
name = utils.get_rand_device_name()
switch = self._add_ls(utils.get_rand_device_name())
ps = self.useFixture(fixtures.PhysicalSwitchFixture(
self.api, utils.get_rand_device_name())).obj
self._test_bind_ls_no_exist(pswitch_name=ps.name,
port_name=name,
switch_name=switch.name)
def test_bind_ls_switch_no_exist(self):
name = utils.get_rand_device_name()
ps = self.useFixture(fixtures.PhysicalSwitchFixture(
self.api, utils.get_rand_device_name())).obj
port = self.api.add_port(ps.uuid, utils.get_rand_device_name(),
may_exist=True).execute(check_error=True)
self._test_bind_ls_no_exist(pswitch_name=ps.name,
port_name=port.name,
switch_name=name)
def test_unbind_ls_no_exist(self):
name = utils.get_rand_device_name()
switch = self._add_ls(name)
ps = self.useFixture(fixtures.PhysicalSwitchFixture(
self.api, name)).obj
port = self.api.add_port(ps.uuid, name,
may_exist=True).execute(check_error=True)
self.api.bind_ls(ps.name, port.name,
self.vlan, switch.name).execute(check_error=True)
for pswitch, port in [('pswitch', port.name), (ps.name, 'port')]:
cmd = self.api.unbind_ls(pswitch, port, self.vlan)
self.assertRaises(idlutils.RowNotFound, cmd.execute,
check_error=True)
def test_unbind_ls_vlan_no_exists(self):
name = utils.get_rand_device_name()
switch = self._add_ls(name)
ps = self.useFixture(fixtures.PhysicalSwitchFixture(
self.api, name)).obj
port = self.api.add_port(ps.uuid, name,
may_exist=True).execute(check_error=True)
self.api.bind_ls(ps.name, port.name,
self.vlan, switch.name).execute(check_error=True)
vlan_bindings = port.vlan_bindings.copy()
self.api.unbind_ls(ps.name, port.name,
self.vlan + 1).execute(check_error=True)
self.assertEqual(port.vlan_bindings, vlan_bindings)
class TestMacBindingsOps(HardwareVtepTest):
def setUp(self):
super().setUp()
self.ls = self.useFixture(fixtures.LogicalSwitchFixture(
self.api, utils.get_rand_device_name())).obj
self.mac = '0a:00:d0:af:20:c0'
self.ip = '192.168.0.1'
self.ovsvenv.call(['vtep-ctl', 'add-ucast-local',
self.ls.name, self.mac, self.ip])
self.ovsvenv.call(['vtep-ctl', 'add-mcast-local',
self.ls.name, self.mac, self.ip])
self.ovsvenv.call(['vtep-ctl', 'add-ucast-remote',
self.ls.name, self.mac, self.ip])
self.ovsvenv.call(['vtep-ctl', 'add-mcast-remote',
self.ls.name, self.mac, self.ip])
for args in [
['vtep-ctl', 'del-ucast-local', self.ls.name, self.mac],
['vtep-ctl', 'del-mcast-local', self.ls.name, self.mac, self.ip],
['vtep-ctl', 'del-ucast-remote', self.ls.name, self.mac],
['vtep-ctl', 'del-mcast-remote', self.ls.name, self.mac, self.ip]
]:
self.addCleanup(self.ovsvenv.call, args)
def test_list_local_macs(self):
local_macs = self.api.list_local_macs(
self.ls.name).execute(check_error=True)
for macs in local_macs:
self.assertEqual(len(macs), 1)
self.assertEqual(macs[0].MAC, self.mac)
def test_list_remote_macs(self):
remote_macs = self.api.list_remote_macs(
self.ls.name).execute(check_error=True)
for macs in remote_macs:
self.assertEqual(len(macs), 1)
self.assertEqual(macs[0].MAC, self.mac)
def test_clear_local_macs(self):
ucast_table = self.api.tables['Ucast_Macs_Local']
mcast_table = self.api.tables['Mcast_Macs_Local']
for table in [ucast_table, mcast_table]:
self.assertEqual(len(table.rows), 1)
self.api.clear_local_macs(self.ls.name).execute(check_error=True)
for table in [ucast_table, mcast_table]:
self.assertEqual(len(table.rows), 0)
def test_clear_remote_macs(self):
ucast_table = self.api.tables['Ucast_Macs_Remote']
mcast_table = self.api.tables['Mcast_Macs_Remote']
for table in [ucast_table, mcast_table]:
self.assertEqual(len(table.rows), 1)
self.api.clear_remote_macs(self.ls.name).execute(check_error=True)
for table in [ucast_table, mcast_table]:
self.assertEqual(len(table.rows), 0)

View File

@ -32,6 +32,8 @@ class OvsVenvFixture(fixtures.Fixture):
os.path.join(os.path.sep, 'usr', 'local', 'share', 'openvswitch'),
os.path.join(os.path.sep, 'usr', 'share', 'openvswitch'))
OVS_SCHEMA = 'vswitch.ovsschema'
def __init__(self, venv, ovsdir=None, dummy=DUMMY_OVERRIDE_ALL,
remove=False):
"""Initialize fixture
@ -76,10 +78,10 @@ class OvsVenvFixture(fixtures.Fixture):
@property
def ovs_schema(self):
path = os.path.join(self.ovsdir, 'vswitchd', 'vswitch.ovsschema')
path = os.path.join(self.ovsdir, 'vswitchd', self.OVS_SCHEMA)
if os.path.isfile(path):
return path
return os.path.join(self.ovsdir, 'vswitch.ovsschema')
return os.path.join(self.ovsdir, self.OVS_SCHEMA)
@property
def dummy_arg(self):
@ -275,3 +277,37 @@ class OvsOvnIcVenvFixture(OvsOvnVenvFixture):
def init_processes(self):
super().init_processes()
self.call(['ovn-ic-nbctl', 'init'])
class OvsVtepVenvFixture(OvsOvnVenvFixture):
VTEP_SCHEMA = 'vtep.ovsschema'
def __init__(self, venv, vtepdir=None, **kwargs):
if vtepdir and os.path.isdir(vtepdir):
self.PATH_VAR_TEMPLATE += ":{0}".format(vtepdir)
self.vtepdir = self._share_path(self.OVS_PATHS, vtepdir)
super().__init__(venv, **kwargs)
def _setUp(self):
if self.has_vtep:
super()._setUp()
@property
def vtep_schema(self):
return os.path.join(self.vtepdir, self.VTEP_SCHEMA)
@property
def has_vtep(self):
return os.path.isfile(self.vtep_schema)
def setup_dbs(self):
db_filename = 'vtep.conf'
super().setup_dbs()
self.create_db(db_filename, self.vtep_schema)
self.ovsdb_server_dbs.append(db_filename)
def init_processes(self):
super().init_processes()
# there are no 'init' method in vtep-ctl,
# but record in 'Global' table is needed
self.call(['vtep-ctl', 'show'])

View File

@ -0,0 +1,7 @@
---
features:
- |
Added support for the "hardware_vtep" schema. The ``HwVtepApiIdlImpl`` class provides
interaction with "hw_vtep.db", adds support to add, delete and list registers rows of
"Physical_Switch", "Physical_Port" and "Logical_Switch" tables. Also added support for
listing and clearing the remote and local multicast and unicast MAC bindings.

View File

@ -66,6 +66,7 @@ setenv = {[testenv]setenv}
OS_TEST_PATH=./ovsdbapp/tests/functional
OVS_SRCDIR={envdir}/src/ovs
OVN_SRCDIR={envdir}/src/ovn
VTEP_SRCDIR={envdir}/src/ovs/vtep
OVS_BRANCH={env:OVS_BRANCH:}
OVN_BRANCH={env:OVN_BRANCH:}
passenv = KEEP_VENV