From 5195b15f605a3ab3150d1fb7c7099a1e2eb618ce Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Sat, 10 Sep 2022 07:56:45 +0200 Subject: [PATCH 1/2] ovsdb: Add support for retrieving single record At present the SimpleOVSDB allows you to find records. The `find` command does however not allow you to match on a records UUID. Add API for retrieval of a specific record by subscripting the SimpleOVSDB Table class. --- charmhelpers/contrib/network/ovs/ovsdb.py | 58 ++++++++++++++---- tests/contrib/network/ovs/test_ovsdb.py | 75 ++++++++++++++--------- 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/charmhelpers/contrib/network/ovs/ovsdb.py b/charmhelpers/contrib/network/ovs/ovsdb.py index 1f23b0d4a..0eb72c08d 100644 --- a/charmhelpers/contrib/network/ovs/ovsdb.py +++ b/charmhelpers/contrib/network/ovs/ovsdb.py @@ -213,20 +213,14 @@ def _deserialize_ovsdb(self, data): f = ovs_type_cb_map.get(data[0], str) return f(data[1]) - def _find_tbl(self, condition=None): - """Run and parse output of OVSDB `find` command. + def _cmd_deserialize_data_generator(self, cmd): + """Run command and provide generator with deserialized data. - :param condition: An optional RFC 7047 5.1 match condition - :type condition: Optional[str] - :returns: Dictionary with data - :rtype: Dict[str, any] + :param cmd: Command and arguments to run. + :type cmd: Iterable[str] + :returns: Deserialzed data. + :rtype: Generator[Dict[str,any], None, None] """ - cmd = [self._tool] - if self._args: - cmd.extend(self._args) - cmd.extend(['-f', 'json', 'find', self._table]) - if condition: - cmd.append(condition) output = utils._run(*cmd) data = json.loads(output) for row in data['data']: @@ -238,9 +232,49 @@ def _find_tbl(self, condition=None): values.append(col) yield dict(zip(data['headings'], values)) + def _get_command(self): + """Get base command. + + :rtype: List[str] + """ + cmd = [self._tool] + if self._args: + cmd.extend(self._args) + cmd += ['-f', 'json'] + return cmd + + def _find_tbl(self, condition=None): + """Run and parse output of OVSDB `find` command. + + :param condition: An optional RFC 7047 5.1 match condition + :type condition: Optional[str] + :returns: Dictionary with data + :rtype: Generator[Dict[str, any], None, None] + """ + cmd = self._get_command() + cmd.extend(['find', self._table]) + if condition: + cmd.append(condition) + return self._cmd_deserialize_data_generator(cmd) + + def _list_tbl_record(self, record): + """Run and parse output of OVSDB `list` command for record. + + :param record: The UUID of the record to list data for. + :type record: uuid.UUID + :returns: Dictionary with data + :rtype: Dict[str, any] + """ + cmd = self._get_command() + cmd.extend(['list', self._table, str(record)]) + return next(self._cmd_deserialize_data_generator(cmd)) + def __iter__(self): return self._find_tbl() + def __getitem__(self, key): + return self._list_tbl_record(key) + def clear(self, rec, col): utils._run(self._tool, 'clear', self._table, rec, col) diff --git a/tests/contrib/network/ovs/test_ovsdb.py b/tests/contrib/network/ovs/test_ovsdb.py index d610286f2..a6081e29b 100644 --- a/tests/contrib/network/ovs/test_ovsdb.py +++ b/tests/contrib/network/ovs/test_ovsdb.py @@ -33,6 +33,37 @@ """) +VSCTL_BRIDGE_TBL_DESERIALIZED = { + '_uuid': uuid.UUID('1e21ba48-61ff-4b32-b35e-cb80411da351'), + 'auto_attach': [], + 'controller': [], + 'datapath_id': '0000a0369fdd3890', + 'datapath_type': '', + 'datapath_version': '', + 'external_ids': { + 'charm-ovn-chassis': 'managed', + 'other': 'value', + }, + 'fail_mode': [], + 'flood_vlans': [], + 'flow_tables': {}, + 'ipfix': [], + 'mcast_snooping_enable': False, + 'mirrors': [], + 'name': 'br-test', + 'netflow': [], + 'other_config': {}, + 'ports': [uuid.UUID('617f9359-77e2-41be-8af6-4c44e7a6bcc3'), + uuid.UUID('da840476-8809-4107-8733-591f4696f056')], + 'protocols': ['OpenFlow10', 'OpenFlow13', 'OpenFlow14'], + 'rstp_enable': False, + 'rstp_status': {}, + 'sflow': [], + 'status': {}, + 'stp_enable': False, +} + + class TestSimpleOVSDB(test_utils.BaseTestCase): def patch_target(self, attr, return_value=None): @@ -55,37 +86,9 @@ def test__find_tbl(self): self.patch_object(ovsdb.utils, '_run') self._run.return_value = VSCTL_BRIDGE_TBL self.maxDiff = None - expect = { - '_uuid': uuid.UUID('1e21ba48-61ff-4b32-b35e-cb80411da351'), - 'auto_attach': [], - 'controller': [], - 'datapath_id': '0000a0369fdd3890', - 'datapath_type': '', - 'datapath_version': '', - 'external_ids': { - 'charm-ovn-chassis': 'managed', - 'other': 'value', - }, - 'fail_mode': [], - 'flood_vlans': [], - 'flow_tables': {}, - 'ipfix': [], - 'mcast_snooping_enable': False, - 'mirrors': [], - 'name': 'br-test', - 'netflow': [], - 'other_config': {}, - 'ports': [uuid.UUID('617f9359-77e2-41be-8af6-4c44e7a6bcc3'), - uuid.UUID('da840476-8809-4107-8733-591f4696f056')], - 'protocols': ['OpenFlow10', 'OpenFlow13', 'OpenFlow14'], - 'rstp_enable': False, - 'rstp_status': {}, - 'sflow': [], - 'status': {}, - 'stp_enable': False} # this in effect also tests the __iter__ front end method for el in self.target.bridge: - self.assertDictEqual(el, expect) + self.assertDictEqual(el, VSCTL_BRIDGE_TBL_DESERIALIZED) break self._run.assert_called_once_with( 'ovs-vsctl', '-f', 'json', 'find', 'bridge') @@ -104,6 +107,20 @@ def test__find_tbl(self): 'ovs-vsctl', 'extra', 'args', '-f', 'json', 'find', 'bridge', 'name=br-test') + def test__list_tbl_record(self): + self.target = ovsdb.SimpleOVSDB('ovs-vsctl') + self.patch_object(ovsdb.utils, '_run') + self._run.return_value = VSCTL_BRIDGE_TBL + self.maxDiff = None + # this in effect also tests the __getitem__ front end method + self.assertEqual( + VSCTL_BRIDGE_TBL_DESERIALIZED, + self.target.bridge[ + uuid.UUID('1e21ba48-61ff-4b32-b35e-cb80411da351')]) + self._run.assert_called_once_with( + 'ovs-vsctl', '-f', 'json', 'list', 'bridge', + '1e21ba48-61ff-4b32-b35e-cb80411da351') + def test_clear(self): self.target = ovsdb.SimpleOVSDB('ovs-vsctl') self.patch_object(ovsdb.utils, '_run') From 9732835dace4437e2d8bc0437b404a7d7e635481 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Mon, 12 Sep 2022 14:56:09 +0200 Subject: [PATCH 2/2] ovsdb: Consistency list manipulation, += vs extend/append --- charmhelpers/contrib/network/ovs/ovsdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charmhelpers/contrib/network/ovs/ovsdb.py b/charmhelpers/contrib/network/ovs/ovsdb.py index 0eb72c08d..04546c10f 100644 --- a/charmhelpers/contrib/network/ovs/ovsdb.py +++ b/charmhelpers/contrib/network/ovs/ovsdb.py @@ -240,7 +240,7 @@ def _get_command(self): cmd = [self._tool] if self._args: cmd.extend(self._args) - cmd += ['-f', 'json'] + cmd.extend(['-f', 'json']) return cmd def _find_tbl(self, condition=None):