diff --git a/ovsdbapp/backend/ovs_idl/connection.py b/ovsdbapp/backend/ovs_idl/connection.py index 6b62a20f..4f64fab7 100644 --- a/ovsdbapp/backend/ovs_idl/connection.py +++ b/ovsdbapp/backend/ovs_idl/connection.py @@ -19,6 +19,7 @@ import threading import time import traceback +from ovs.db import custom_index from ovs.db import idl from ovs import poller @@ -168,3 +169,33 @@ class OvsdbIdl(idl.Idl): An example would be to set up Idl notification handling for watching and unwatching certain OVSDB change events """ + + def update_tables(self, tables, schema): + """Add the tables to the current Idl if they are present in the schema + + :param tables: List of tables to be registered + :type tables: List + :param schema: Schema description + :type schema: dict or string + """ + + schema_helper = idlutils.create_schema_helper(schema) + + # Register only available registered tables - DB downgrade, and the + # newly added tables - DB upgrade + for table in self.tables.keys() | tables: + if table in schema_helper.schema_json['tables']: + schema_helper.register_table(table) + + schema = schema_helper.get_idl_schema() + self._db = schema + self.tables = schema.tables + for table in schema.tables.values(): + for column in table.columns.values(): + if not hasattr(column, 'alert'): + column.alert = True + table.need_table = False + table.rows = custom_index.IndexedRows(table) + table.idl = self + table.condition = [True] + table.cond_changed = False diff --git a/ovsdbapp/backend/ovs_idl/idlutils.py b/ovsdbapp/backend/ovs_idl/idlutils.py index d0e730bc..3d3a041b 100644 --- a/ovsdbapp/backend/ovs_idl/idlutils.py +++ b/ovsdbapp/backend/ovs_idl/idlutils.py @@ -14,6 +14,9 @@ import collections from collections import abc +# json is not deprecated +# pylint: disable=deprecated-module +import json import logging import os import sys @@ -154,6 +157,17 @@ class ExceptionResult(object): self.tb = tb +def create_schema_helper(schema): + """Create a schema helper object based on the provided schema. + + :param schema: The description of the schema + :type schema: dict or string + """ + if isinstance(schema, str): + schema = json.loads(schema) + return idl.SchemaHelper(None, schema) + + def get_schema_helper(connection, schema_name): """Create a schema helper object by querying an ovsdb-server @@ -186,7 +200,7 @@ def get_schema_helper(connection, schema_name): "%(err)s", {'conn': c, 'err': resp.error}) continue - return idl.SchemaHelper(None, resp.result) + return create_schema_helper(resp.result) raise Exception("Could not retrieve schema from %s" % connection) diff --git a/ovsdbapp/tests/functional/backend/ovs_idl/test_connection.py b/ovsdbapp/tests/functional/backend/ovs_idl/test_connection.py new file mode 100644 index 00000000..41749e8d --- /dev/null +++ b/ovsdbapp/tests/functional/backend/ovs_idl/test_connection.py @@ -0,0 +1,85 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from unittest import mock + +from ovsdbapp.backend.ovs_idl import connection +from ovsdbapp.backend.ovs_idl import idlutils +from ovsdbapp import constants +from ovsdbapp.tests.functional import base + + +def create_schema_helper(sh): + return sh + + +class TestingOvsIdl(connection.OvsdbIdl): + schema = 'Open_vSwitch' + + @classmethod + def from_server(cls, schema_map, tables): + """Create the Idl instance by pulling the schema from OVSDB server""" + connection_string = schema_map[cls.schema] + helper = idlutils.get_schema_helper(connection_string, cls.schema) + for table in tables: + helper.register_table(table) + return cls(connection_string, helper) + + +class TestOvsdbIdl(base.FunctionalTestCase): + default_tables = ["Open_vSwitch", "Bridge"] + + def setUp(self): + super().setUp() + self.schema = self.get_schema() + + @property + def idl(self): + return self._connection.idl + + @classmethod + def set_connection(cls): + idl = TestingOvsIdl.from_server(cls.schema_map, cls.default_tables) + cls._connection = connection.Connection(idl, constants.DEFAULT_TIMEOUT) + + def get_schema(self): + with mock.patch.object( + idlutils, 'create_schema_helper', + side_effect=create_schema_helper): + return idlutils.get_schema_helper( + self.schema_map[TestingOvsIdl.schema], TestingOvsIdl.schema) + + def validate_tables(self, tables, present): + valid_func = self.assertIn if present else self.assertNotIn + + for table in tables: + valid_func(table, self.idl.tables) + + def test_add_new_table(self): + tables = ["Port", "Interface"] + + self.validate_tables(tables, present=False) + self.idl.update_tables(tables, self.schema) + self.validate_tables(tables, present=True) + + def test_remove_table(self): + removed_table = "Open_vSwitch" + new_tables = self.default_tables[:] + new_tables.remove(removed_table) + + self.validate_tables([removed_table], present=True) + + del self.schema["tables"][removed_table] + self.idl.update_tables(self.default_tables, self.schema) + + self.validate_tables(new_tables, present=True) + self.validate_tables([removed_table], present=False)