From 0cf56a3bb274cfd4a2d255b1d04713bb765faf49 Mon Sep 17 00:00:00 2001 From: Luciano Lo Giudice Date: Wed, 1 Dec 2021 14:05:27 -0300 Subject: [PATCH] Create a base class for cinder plugins The purpose of this new class is to ease development for existing and upcoming cinder plugins, for instance, the cookiecutter and solidfire ones. This class makes development much more in line with the existing reactive libraries. Change-Id: I55a849dd11e83e5b2b23efcece6ff8dfa511c96f --- ops_openstack/plugins/classes.py | 72 ++++++++++++++++++++++++++++++ unit_tests/test_plugins_classes.py | 47 +++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/ops_openstack/plugins/classes.py b/ops_openstack/plugins/classes.py index a4ed5ab..09d9176 100644 --- a/ops_openstack/plugins/classes.py +++ b/ops_openstack/plugins/classes.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json + import ops_openstack.core # ch_context needed for bluestore validation import charmhelpers.contrib.openstack.context as ch_context @@ -53,3 +55,73 @@ class BaseCephClientCharm(ops_openstack.core.OSBaseCharm): bluestore_compression = None if bluestore_compression: return bluestore_compression.get_kwargs() + + +class CinderStoragePluginCharm(ops_openstack.core.OSBaseCharm): + + def __init__(self, framework): + super().__init__(framework) + self.framework.observe(self.on.config_changed, self.on_config) + self.framework.observe( + self.on.storage_backend_relation_changed, + self.on_storage_backend) + + def render_config(self, config, app_name): + return json.dumps({ + "cinder": { + "/etc/cinder/cinder.confg": { + "sections": {app_name: self.cinder_configuration(config)} + } + } + }) + + def set_data(self, data, config, app_name): + """Inform another charm of the backend name and configuration.""" + data['backend_name'] = config['volume-backend-name'] or app_name + data['stateless'] = str(self.stateless) + data['active_active'] = str(self.active_active) + data['subordinate_configuration'] = self.render_config( + config, app_name) + + def on_config(self, event): + config = dict(self.framework.model.config) + app_name = self.framework.model.app.name + for relation in self.framework.model.relations.get('storage-backend'): + self.set_data(relation.data[self.unit], config, app_name) + self.unit.status = ActiveStatus('Unit is ready') + + def on_storage_backend(self, event): + self.set_data( + event.relation.data[self.unit], + self.framework.model.config, + self.framework.model.app.name) + + def cinder_configuration(self, charm_config): + """Entry point for cinder subordinates. + + This method should return a list of 2-element tuples, where the + first element is the configuration key, and the second, its value.""" + + raise NotImplementedError() + + @property + def stateless(self): + """Indicate whether the charm is stateless. + + For more information, see: https://cinderlib.readthedocs.io/en/v0.2.1/topics/serialization.html + + :returns: A boolean value indicating statefulness. + :rtype: bool + """ # noqa + return False + + @property + def active_active(self): + """Indicate active-active support in the charm. + + For more information, see: https://specs.openstack.org/openstack/cinder-specs/specs/mitaka/cinder-volume-active-active-support.html + + :returns: A boolean indicating active-active support. + :rtype: bool + """ # noqa + return False diff --git a/unit_tests/test_plugins_classes.py b/unit_tests/test_plugins_classes.py index e6d5038..2541899 100644 --- a/unit_tests/test_plugins_classes.py +++ b/unit_tests/test_plugins_classes.py @@ -103,3 +103,50 @@ class TestBaseCephClientCharm(CharmTestCase): self.assertIsInstance( self.harness.charm.unit.status, BlockedStatus) + + +class CinderCharm(ops_openstack.plugins.classes.CinderStoragePluginCharm): + + def cinder_configuration(self, cinder_config): + return [('volume_driver', 'my-driver'), + ('some-config', 'some-value')] + + +class TestBaseCinderCharm(unittest.TestCase): + + def setUp(self): + self.harness = Harness( + CinderCharm, + meta=''' + name: cinder-test + provides: + storage-backend: + interface: cinder-backend + scope: container + requires: + juju-info: + interface: juju-info + scope: container + ''', + config=''' + options: + volume-backend-name: + default: "" + type: string + ''' + ) + self.addCleanup(self.harness.cleanup) + self.harness.begin() + self.harness.set_leader(True) + backend = self.harness.add_relation('storage-backend', 'cinder') + self.harness.update_config({'volume-backend-name': 'test'}) + self.harness.add_relation_unit(backend, 'cinder/0') + + def test_cinder_base(self): + self.assertEqual(self.harness.framework.model.app.name, 'cinder-test') + self.harness.update_config({}) + self.assertTrue(isinstance(self.harness.model.unit.status, + ActiveStatus)) + config = self.harness.charm.cinder_configuration({}) + self.assertTrue(config[0], ('volume_driver', 'my-driver')) + self.assertTrue(config[1], ('some-config', 'some-value'))