From 74b52bb3491a9c4f6ec40e15d74652fc415269e0 Mon Sep 17 00:00:00 2001 From: Lucas Alvares Gomes Date: Wed, 21 Aug 2019 13:31:59 +0100 Subject: [PATCH] Add HA Chassis Group related commands This patch is introducing new commands to ovsdbapp to work with the new HA_Chassis_Group and HA_Chassis tables in OVN Northbound database. These tables were introduced by the commit [0] in core OVN which created a new type of port called "external" that can be used for various things including but not limited to: SR-IOV and baremetal booting support. [0] https://github.com/ovn-org/ovn/commit/b31c76000bef314b68e776d318d1ce4cf152450b Change-Id: I77ad33223607bee63a96e548f308bbffc1c9de52 Signed-off-by: Lucas Alvares Gomes --- ovsdbapp/schema/ovn_northbound/api.py | 67 +++++++++++ ovsdbapp/schema/ovn_northbound/commands.py | 108 ++++++++++++++++++ ovsdbapp/schema/ovn_northbound/impl_idl.py | 19 +++ .../schema/ovn_northbound/test_impl_idl.py | 98 ++++++++++++++++ 4 files changed, 292 insertions(+) diff --git a/ovsdbapp/schema/ovn_northbound/api.py b/ovsdbapp/schema/ovn_northbound/api.py index 0b1f6bee..6b9c6a34 100644 --- a/ovsdbapp/schema/ovn_northbound/api.py +++ b/ovsdbapp/schema/ovn_northbound/api.py @@ -968,3 +968,70 @@ class API(api.API): :type pg_id: string or uuid.UUID :returns: :class:`Command` with RowView result """ + + @abc.abstractmethod + def ha_chassis_group_add(self, name, may_exist=False, **columns): + """Create a HA Chassis Group + + :param name: The name of the ha chassis group + :type name: string + :param may_exist: If True, don't fail if the ha chassis group + already exists + :type may_exist: bool + :param columns: Additional columns to directly set on the ha + chassis group (e.g external_ids) + :type columns: dictionary + :returns: :class:`Command` with RowView result + """ + + @abc.abstractmethod + def ha_chassis_group_del(self, name, if_exists=False): + """Delete a HA Chassis Group + + :param name: The name of the ha chassis group + :type name: string + :param if_exists: If True, don't fail if the ha chassis group + doesn't exist + :type if_exists: boolean + :returns: :class:`Command` with no result + """ + + @abc.abstractmethod + def ha_chassis_group_get(self, name): + """Get HA Chassis Group + + :param name: The name or uuid of the ha chassis group + :type name: string or uuid.UUID + :returns: :class:`Command` with RowView result + """ + + @abc.abstractmethod + def ha_chassis_group_add_chassis(self, hcg_id, chassis, priority, + **columns): + """Add a HA Chassis to a HA Chassis Group + + :param hcg_id: The name or uuid of the ha chassis group + :type hcg_id: string or uuid.UUID + :param chassis: The name of the ha chassis + :type chassis: string + :param priority: The priority of the ha chassis + :type priority: int + :param columns: Additional columns to directly set on the ha + chassis (e.g external_ids) + :type columns: dictionary + :returns: :class:`Command` with RowView result + """ + + @abc.abstractmethod + def ha_chassis_group_del_chassis(self, hcg_id, chassis, if_exists=False): + """Delete a HA Chassis from a HA Chassis Group + + :param hcg_id: The name or uuid of the ha chassis group + :type hcg_id: string or uuid.UUID + :param chassis: The name of the ha chassis + :type chassis: string + :param if_exists: If True, don't fail if the ha chassis + doesn't exist + :type if_exists: boolean + :returns: :class:`Command` with no result + """ diff --git a/ovsdbapp/schema/ovn_northbound/commands.py b/ovsdbapp/schema/ovn_northbound/commands.py index 1e9fb74c..bb06d55d 100644 --- a/ovsdbapp/schema/ovn_northbound/commands.py +++ b/ovsdbapp/schema/ovn_northbound/commands.py @@ -1344,3 +1344,111 @@ class GatewayChassisAddCommand(cmd.AddCommand): gwc.priority = self.priority self.set_columns(gwc, **self.columns) self.result = gwc + + +class HAChassisGroupAddCommand(cmd.AddCommand): + table_name = 'HA_Chassis_Group' + + def __init__(self, api, name, may_exist=False, **columns): + super(HAChassisGroupAddCommand, self).__init__(api) + self.name = name + self.may_exist = may_exist + self.columns = columns + + def run_idl(self, txn): + if self.may_exist: + try: + hcg = self.api.lookup(self.table_name, self.name) + self.result = rowview.RowView(hcg) + return + except idlutils.RowNotFound: + pass + + hcg = txn.insert(self.api._tables[self.table_name]) + hcg.name = self.name + self.set_columns(hcg, **self.columns) + self.result = hcg.uuid + + +class HAChassisGroupDelCommand(cmd.BaseCommand): + table_name = 'HA_Chassis_Group' + + def __init__(self, api, name, if_exists=False): + super(HAChassisGroupDelCommand, self).__init__(api) + self.name = name + self.if_exists = if_exists + + def run_idl(self, txn): + try: + hcg = self.api.lookup(self.table_name, self.name) + hcg.delete() + except idlutils.RowNotFound: + if self.if_exists: + return + raise RuntimeError( + 'HA Chassis Group %s does not exist' % self.name) + + +class HAChassisGroupGetCommand(cmd.BaseGetRowCommand): + table = 'HA_Chassis_Group' + + +class HAChassisGroupAddChassisCommand(cmd.AddCommand): + table_name = 'HA_Chassis' + + def __init__(self, api, hcg_id, chassis, priority, **columns): + super(HAChassisGroupAddChassisCommand, self).__init__(api) + self.hcg_id = hcg_id + self.chassis = chassis + self.priority = priority + self.columns = columns + + def run_idl(self, txn): + hc_group = self.api.lookup('HA_Chassis_Group', self.hcg_id) + found = False + hc = None + for hc in hc_group.ha_chassis: + if hc.chassis_name != self.chassis: + continue + found = True + break + else: + hc = txn.insert(self.api.tables[self.table_name]) + hc.chassis_name = self.chassis + + hc.priority = self.priority + self.set_columns(hc, **self.columns) + if not found: + hc_group.addvalue('ha_chassis', hc) + + self.result = hc.uuid + + +class HAChassisGroupDelChassisCommand(cmd.BaseCommand): + table_name = 'HA_Chassis' + + def __init__(self, api, hcg_id, chassis, if_exists=False): + super(HAChassisGroupDelChassisCommand, self).__init__(api) + self.hcg_id = hcg_id + self.chassis = chassis + self.if_exists = if_exists + + def run_idl(self, txn): + try: + hc_group = self.api.lookup('HA_Chassis_Group', self.hcg_id) + except idlutils.RowNotFound: + if self.if_exists: + return + + hc = None + for hc in hc_group.ha_chassis: + if hc.chassis_name == self.chassis: + break + else: + if self.if_exists: + return + raise RuntimeError( + 'HA Chassis %s does not exist' % self.hcg_id) + + hc_group.delvalue('ha_chassis', hc) + hc.delete() diff --git a/ovsdbapp/schema/ovn_northbound/impl_idl.py b/ovsdbapp/schema/ovn_northbound/impl_idl.py index 43e96502..483a8ec1 100644 --- a/ovsdbapp/schema/ovn_northbound/impl_idl.py +++ b/ovsdbapp/schema/ovn_northbound/impl_idl.py @@ -296,3 +296,22 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API): def pg_get(self, pg): return cmd.PgGetCommand(self, pg) + + def ha_chassis_group_add(self, name, may_exist=False, **columns): + return cmd.HAChassisGroupAddCommand( + self, name, may_exist=may_exist, **columns) + + def ha_chassis_group_del(self, name, if_exists=False): + return cmd.HAChassisGroupDelCommand(self, name, if_exists=if_exists) + + def ha_chassis_group_get(self, name): + return cmd.HAChassisGroupGetCommand(self, name) + + def ha_chassis_group_add_chassis(self, hcg_id, chassis, priority, + **columns): + return cmd.HAChassisGroupAddChassisCommand( + self, hcg_id, chassis, priority, **columns) + + def ha_chassis_group_del_chassis(self, hcg_id, chassis, if_exists=False): + return cmd.HAChassisGroupDelChassisCommand( + self, hcg_id, chassis, if_exists=if_exists) diff --git a/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py b/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py index b6cc5c79..7cc18437 100644 --- a/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py +++ b/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py @@ -1448,3 +1448,101 @@ class TestPortGroup(OvnNorthboundTest): # Assert that if if_exists is True it won't raise an error self.api.pg_del_ports(self.pg_name, non_existent_res, if_exists=True).execute(check_error=True) + + +class TestHAChassisGroup(OvnNorthboundTest): + + def setUp(self): + super(TestHAChassisGroup, self).setUp() + self.hcg_name = 'ha-group-%s' % ovsdb_utils.generate_uuid() + self.chassis = 'chassis-%s' % ovsdb_utils.generate_uuid() + + def test_ha_chassis_group(self): + # Assert the HA Chassis Group was added + self.api.ha_chassis_group_add(self.hcg_name).execute(check_error=True) + hcg = self.api.ha_chassis_group_get(self.hcg_name).execute( + check_error=True) + self.assertEqual(self.hcg_name, hcg.name) + + # Assert the HA Chassis Group was deleted + self.api.ha_chassis_group_del(self.hcg_name).execute(check_error=True) + cmd = self.api.ha_chassis_group_get(self.hcg_name) + self.assertRaises(idlutils.RowNotFound, cmd.execute, check_error=True) + + def test_ha_chassis_group_add_delete_chassis(self): + self.api.ha_chassis_group_add(self.hcg_name).execute(check_error=True) + priority = 20 + self.api.ha_chassis_group_add_chassis( + self.hcg_name, self.chassis, priority).execute(check_error=True) + + # Assert that the HA Chassis entry was created + row = self.api.db_find( + 'HA_Chassis', + ('chassis_name', '=', self.chassis)).execute(check_error=True) + self.assertEqual(priority, row[0]['priority']) + + # Assert that the HA Chassis entry was associated with + # the HA Chassis Group + hcg = self.api.ha_chassis_group_get(self.hcg_name).execute( + check_error=True) + self.assertEqual(self.chassis, hcg.ha_chassis[0].chassis_name) + + # Deletes the HA Chassis entry + self.api.ha_chassis_group_del_chassis( + self.hcg_name, self.chassis).execute(check_error=True) + row = self.api.db_find( + 'HA_Chassis', + ('chassis_name', '=', self.chassis)).execute(check_error=True) + self.assertEqual([], row) + + # Assert that the deleted HA Chassis entry was dissociated from + # the HA Chassis Group + hcg = self.api.ha_chassis_group_get(self.hcg_name).execute( + check_error=True) + self.assertEqual([], hcg.ha_chassis) + + def test_ha_chassis_group_if_exists(self): + self.api.ha_chassis_group_add(self.hcg_name).execute(check_error=True) + self.api.ha_chassis_group_add_chassis( + self.hcg_name, self.chassis, priority=10).execute(check_error=True) + + # Deletes the HA Chassis entry + self.api.ha_chassis_group_del_chassis( + self.hcg_name, self.chassis).execute(check_error=True) + row = self.api.db_find( + 'HA_Chassis', + ('chassis_name', '=', self.chassis)).execute(check_error=True) + self.assertEqual([], row) + + # Tries to delete it again, since if_exists=True it shouldn't raise + # any errors + self.api.ha_chassis_group_del_chassis( + self.hcg_name, self.chassis, if_exists=True).execute( + check_error=True) + + # Tries to delete it again with if_exists=False, now it should raise + # a RuntimeError + cmd = self.api.ha_chassis_group_del_chassis( + self.hcg_name, self.chassis, if_exists=False) + self.assertRaises(RuntimeError, cmd.execute, check_error=True) + + # Deletes the HA Chassis Group entry + self.api.ha_chassis_group_del(self.hcg_name).execute(check_error=True) + cmd = self.api.ha_chassis_group_get(self.hcg_name) + self.assertRaises(idlutils.RowNotFound, cmd.execute, check_error=True) + + # Tries to delete it again, since if_exists=True it shouldn't raise + # any errors + self.api.ha_chassis_group_del( + self.hcg_name, if_exists=True).execute(check_error=True) + + # Tries to delete it again with if_exists=False, now it should raise + # a RuntimeError + cmd = self.api.ha_chassis_group_del(self.hcg_name) + self.assertRaises(RuntimeError, cmd.execute, check_error=True) + + def test_ha_chassis_group_may_exist(self): + cmd = self.api.ha_chassis_group_add(self.hcg_name, may_exist=True) + hcg1 = cmd.execute(check_error=True) + hcg2 = cmd.execute(check_error=True) + self.assertEqual(hcg1, hcg2)