From cb09203a677895bd8d130af473a101e8f9f2f730 Mon Sep 17 00:00:00 2001 From: Gustavo Sanchez Date: Tue, 23 Nov 2021 10:42:36 -0400 Subject: [PATCH] Initial Cookiecutter Commit. --- .gitignore | 9 ++ .stestr.conf | 3 + README.md | 19 ++++ build-requirements.txt | 1 + config.yaml | 5 + metadata.yaml | 24 ++++ pip.sh | 18 +++ requirements.txt | 2 + src/charm.py | 89 +++++++++++++++ src/test-requirements.txt | 9 ++ src/tox.ini | 61 ++++++++++ test-requirements.txt | 16 +++ tests/bundles/bionic-queens.yaml | 50 ++++++++ tests/tests.yaml | 9 ++ tests/tests_cinder_solidfire.py | 70 ++++++++++++ tox.ini | 133 ++++++++++++++++++++++ unit_tests/__init__.py | 13 +++ unit_tests/test_cinder_solidfire_charm.py | 43 +++++++ 18 files changed, 574 insertions(+) create mode 100644 .gitignore create mode 100644 .stestr.conf create mode 100644 README.md create mode 100644 build-requirements.txt create mode 100644 config.yaml create mode 100644 metadata.yaml create mode 100755 pip.sh create mode 100644 requirements.txt create mode 100755 src/charm.py create mode 100644 src/test-requirements.txt create mode 100644 src/tox.ini create mode 100644 test-requirements.txt create mode 100644 tests/bundles/bionic-queens.yaml create mode 100644 tests/tests.yaml create mode 100644 tests/tests_cinder_solidfire.py create mode 100644 tox.ini create mode 100644 unit_tests/__init__.py create mode 100644 unit_tests/test_cinder_solidfire_charm.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6425e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +build +layers +.tox +interfaces +.testrepository +.stestr +*__pycache__* +*.pyc +/*.charm diff --git a/.stestr.conf b/.stestr.conf new file mode 100644 index 0000000..5fcccac --- /dev/null +++ b/.stestr.conf @@ -0,0 +1,3 @@ +[DEFAULT] +test_path=./unit_tests +top_dir=./ diff --git a/README.md b/README.md new file mode 100644 index 0000000..4fa1d18 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +Solidfire Storage Backend for Cinder +------------------------------- + +Overview +======== + +This charm provides a Solidfire storage backend for use with the Cinder +charm. + +To use: + + juju deploy cinder + juju deploy cinder-solidfire + juju add-relation cinder-solidfire cinder + +Configuration +============= + +See config.yaml for details of configuration options. diff --git a/build-requirements.txt b/build-requirements.txt new file mode 100644 index 0000000..271d895 --- /dev/null +++ b/build-requirements.txt @@ -0,0 +1 @@ +git+https://github.com/canonical/charmcraft.git@0.10.2#egg=charmcraft diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..87076c5 --- /dev/null +++ b/config.yaml @@ -0,0 +1,5 @@ +options: + volume-dd-blocksize: + default: 1024 + description: Disk block size for the volume + type: int diff --git a/metadata.yaml b/metadata.yaml new file mode 100644 index 0000000..c0cec4e --- /dev/null +++ b/metadata.yaml @@ -0,0 +1,24 @@ +name: cinder-solidfire +summary: Solidfire integration for OpenStack Block Storage +maintainer: OpenStack Charmers +description: | + Cinder is the block storage service for the Openstack project. + . + This charm provides a Solidfire backend for Cinder +tags: + - openstack + - storage + - file-servers + - misc +series: + - focal + - bionic +subordinate: true +provides: + storage-backend: + interface: cinder-backend + scope: container +requires: + juju-info: + interface: juju-info + scope: container diff --git a/pip.sh b/pip.sh new file mode 100755 index 0000000..9a7e6b0 --- /dev/null +++ b/pip.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# +# This file is managed centrally by release-tools and should not be modified +# within individual charm repos. See the 'global' dir contents for available +# choices of tox.ini for OpenStack Charms: +# https://github.com/openstack-charmers/release-tools +# +# setuptools 58.0 dropped the support for use_2to3=true which is needed to +# install blessings (an indirect dependency of charm-tools). +# +# More details on the beahvior of tox and virtualenv creation can be found at +# https://github.com/tox-dev/tox/issues/448 +# +# This script is wrapper to force the use of the pinned versions early in the +# process when the virtualenv was created and upgraded before installing the +# depedencies declared in the target. +pip install 'pip<20.3' 'setuptools<50.0.0' +pip "$@" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..86315ca --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +ops +git+https://opendev.org/openstack/charm-ops-openstack#egg=ops_openstack diff --git a/src/charm.py b/src/charm.py new file mode 100755 index 0000000..2847ea8 --- /dev/null +++ b/src/charm.py @@ -0,0 +1,89 @@ +#! /usr/bin/env python3 + +# Copyright 2021 Canonical Ltd +# +# 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 json + + +from ops_openstack.core import OSBaseCharm +from ops.framework import StoredState +from ops.main import main +from ops.model import ActiveStatus + + +class CinderSolidfireCharm(OSBaseCharm): + + _stored = StoredState() + PACKAGES = ['cinder-common'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # We listen to the following events: + # - Installation: Just to set the charm status + # - Configuration changes: Rewrite the cinder.conf file and inform + # other charms of the changes + # - Backend storage join/change: Inform the other charm of what the + # configuration is and the backend name. + self.framework.observe(self.on.install, self._on_install) + self.framework.observe(self.on.config_changed, self._on_config) + self.framework.observe( + self.on.storage_backend_relation_joined, + self._on_storage_backend) + self.framework.observe( + self.on.storage_backend_relation_changed, + self._on_storage_backend) + + def _on_install(self, _): + self.install_pkgs() + self.unit.status = ActiveStatus('Unit is ready') + + def _render_config(self, config, app_name): + # Generate the JSON with the updated configuration. + volume_driver = '' + options = [ + ('volume_driver', volume_driver), + ] + return json.dumps({ + "cinder": { + "/etc/cinder/cinder.confg": { + "sections": {app_name: options} + } + } + }) + + def _set_data(self, data, config, app_name): + # Inform another charm of the backend name and our configuration. + data['backend-name'] = config['volume-backend-name'] or app_name + data['subordinate_configuration'] = self._render_config( + config, app_name) + + def _on_config(self, event): + config = dict(self.framework.model.config) + rel = self.framework.model.relations.get('storage-backend')[0] + app_name = self.framework.model.app.name + for unit in self.framework.model.get_relation('storage-backend').units: + self._set_data(rel.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) + + +if __name__ == '__main__': + main(CinderSolidfireCharm) diff --git a/src/test-requirements.txt b/src/test-requirements.txt new file mode 100644 index 0000000..e771023 --- /dev/null +++ b/src/test-requirements.txt @@ -0,0 +1,9 @@ +# This file is managed centrally by release-tools and should not be modified +# within individual charm repos. See the 'global' dir contents for available +# choices of *requirements.txt files for OpenStack Charms: +# https://github.com/openstack-charmers/release-tools +# + +# Functional Test Requirements (let Zaza's dependencies solve all dependencies here!) +git+https://github.com/openstack-charmers/zaza.git#egg=zaza +git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack diff --git a/src/tox.ini b/src/tox.ini new file mode 100644 index 0000000..b40d295 --- /dev/null +++ b/src/tox.ini @@ -0,0 +1,61 @@ +# Source charm (with zaza): ./src/tox.ini +# This file is managed centrally by release-tools and should not be modified +# within individual charm repos. See the 'global' dir contents for available +# choices of tox.ini for OpenStack Charms: +# https://github.com/openstack-charmers/release-tools + +[tox] +envlist = pep8 +skipsdist = True +# NOTE: Avoid build/test env pollution by not enabling sitepackages. +sitepackages = False +# NOTE: Avoid false positives by not skipping missing interpreters. +skip_missing_interpreters = False +# NOTES: +# * We avoid the new dependency resolver by pinning pip < 20.3, see +# https://github.com/pypa/pip/issues/9187 +# * Pinning dependencies requires tox >= 3.2.0, see +# https://tox.readthedocs.io/en/latest/config.html#conf-requires +# * It is also necessary to pin virtualenv as a newer virtualenv would still +# lead to fetching the latest pip in the func* tox targets, see +# https://stackoverflow.com/a/38133283 +requires = pip < 20.3 + virtualenv < 20.0 +# NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci +minversion = 3.18.0 + +[testenv] +setenv = VIRTUAL_ENV={envdir} + PYTHONHASHSEED=0 +allowlist_externals = juju +passenv = HOME TERM CS_* OS_* TEST_* +deps = -r{toxinidir}/test-requirements.txt +install_command = + pip install {opts} {packages} + +[testenv:pep8] +basepython = python3 +commands = charm-proof + +[testenv:func-noop] +basepython = python3 +commands = + functest-run-suite --help + +[testenv:func] +basepython = python3 +commands = + functest-run-suite --keep-model + +[testenv:func-smoke] +basepython = python3 +commands = + functest-run-suite --keep-model --smoke + +[testenv:func-target] +basepython = python3 +commands = + functest-run-suite --keep-model --bundle {posargs} + +[testenv:venv] +commands = {posargs} diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..170df5e --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,16 @@ +# This file is managed centrally. If you find the need to modify this as a +# one-off, please don't. Intead, consult #openstack-charms and ask about +# requirements management in charms via bot-control. Thank you. +charm-tools>=2.4.4 +coverage>=3.6 +mock>=1.2 +flake8>=4.0.1 +stestr>=2.2.0 +requests>=2.18.4 +psutil +# oslo.i18n dropped py35 support +oslo.i18n<4.0.0 +git+https://github.com/openstack-charmers/zaza.git#egg=zaza +git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack +pytz # workaround for 14.04 pip/tox +pyudev # for ceph-* charm unit tests (not mocked?) diff --git a/tests/bundles/bionic-queens.yaml b/tests/bundles/bionic-queens.yaml new file mode 100644 index 0000000..163a10b --- /dev/null +++ b/tests/bundles/bionic-queens.yaml @@ -0,0 +1,50 @@ +series: bionic +comment: +- 'machines section to decide order of deployment. database sooner = faster' +machines: + '0': + constraints: mem=3072M + '1': + '2': + '3': +local_overlay_enabled: false +relations: +- - keystone:shared-db + - mysql:shared-db +- - cinder:shared-db + - mysql:shared-db +- - cinder:identity-service + - keystone:identity-service +- - cinder:amqp + - rabbitmq-server:amqp +- - cinder:storage-backend + - cinder-solidfire:storage-backend +applications: + mysql: + charm: cs:~openstack-charmers-next/percona-cluster + num_units: 1 + to: + - '0' + keystone: + charm: cs:~openstack-charmers-next/keystone + num_units: 1 + options: + openstack-origin: cloud:bionic-queens + to: + - '1' + cinder: + charm: cs:~openstack-charmers-next/cinder + num_units: 1 + options: + openstack-origin: cloud:bionic-queens + to: + - '2' + cinder-solidfire: + charm: ../../cinder-solidfire.charm + options: +# Add config options here + rabbitmq-server: + charm: cs:~openstack-charmers-next/rabbitmq-server + num_units: 1 + to: + - '3' diff --git a/tests/tests.yaml b/tests/tests.yaml new file mode 100644 index 0000000..26ad9f5 --- /dev/null +++ b/tests/tests.yaml @@ -0,0 +1,9 @@ +charm_name: cinder-solidfire +tests: + - tests.tests_cinder_solidfire.CinderSolidfireTest +configure: + - zaza.openstack.charm_tests.keystone.setup.add_demo_user +gate_bundles: + - bionic-queens +smoke_bundles: + - bionic-queens diff --git a/tests/tests_cinder_solidfire.py b/tests/tests_cinder_solidfire.py new file mode 100644 index 0000000..a42e771 --- /dev/null +++ b/tests/tests_cinder_solidfire.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +# Copyright 2019 Canonical Ltd. +# +# 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. + +"""Encapsulate cinder-solidfire testing.""" + +import logging +import uuid + +import zaza.model +import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.openstack.utilities.openstack as openstack_utils + + +class CinderSolidfireTest(test_utils.OpenStackBaseTest): + """Encapsulate Solidfire tests.""" + + @classmethod + def setUpClass(cls): + """Run class setup for running tests.""" + super(CinderSolidfireTest, cls).setUpClass() + cls.keystone_session = openstack_utils.get_overcloud_keystone_session() + cls.model_name = zaza.model.get_juju_model() + cls.cinder_client = openstack_utils.get_cinder_session_client( + cls.keystone_session) + + def test_cinder_config(self): + logging.info('solidfire') + expected_contents = { + 'cinder-solidfire': { + 'iscsi_helper': ['tgtadm'], + 'volume_dd_blocksize': ['512']}} + + zaza.model.run_on_leader( + 'cinder', + 'sudo cp /etc/cinder/cinder.conf /tmp/', + model_name=self.model_name) + zaza.model.block_until_oslo_config_entries_match( + 'cinder', + '/tmp/cinder.conf', + expected_contents, + model_name=self.model_name, + timeout=2) + + def test_create_volume(self): + test_vol_name = "zaza{}".format(uuid.uuid1().fields[0]) + vol_new = self.cinder_client.volumes.create( + name=test_vol_name, + size=2) + openstack_utils.resource_reaches_status( + self.cinder_client.volumes, + vol_new.id, + expected_status='available') + test_vol = self.cinder_client.volumes.find(name=test_vol_name) + self.assertEqual( + getattr(test_vol, 'os-vol-host-attr:host').split('#')[0], + 'cinder@cinder-solidfire') + self.cinder_client.volumes.delete(vol_new) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..99cf840 --- /dev/null +++ b/tox.ini @@ -0,0 +1,133 @@ +# Operator charm (with zaza): tox.ini + +[tox] +envlist = pep8,py3 +skipsdist = True +# NOTE: Avoid build/test env pollution by not enabling sitepackages. +sitepackages = False +# NOTE: Avoid false positives by not skipping missing interpreters. +skip_missing_interpreters = False +# NOTES: +# * We avoid the new dependency resolver by pinning pip < 20.3, see +# https://github.com/pypa/pip/issues/9187 +# * Pinning dependencies requires tox >= 3.2.0, see +# https://tox.readthedocs.io/en/latest/config.html#conf-requires +# * It is also necessary to pin virtualenv as a newer virtualenv would still +# lead to fetching the latest pip in the func* tox targets, see +# https://stackoverflow.com/a/38133283 +requires = pip < 20.3 + virtualenv < 20.0 +# NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci +minversion = 3.2.0 + +[testenv] +setenv = VIRTUAL_ENV={envdir} + PYTHONHASHSEED=0 + CHARM_DIR={envdir} +install_command = + pip install {opts} {packages} +commands = stestr run --slowest {posargs} +whitelist_externals = + git + add-to-archive.py + bash +passenv = HOME TERM CS_* OS_* TEST_* +deps = -r{toxinidir}/test-requirements.txt + +[testenv:py35] +basepython = python3.5 +# python3.5 is irrelevant on a focal+ charm. +commands = /bin/true + +[testenv:py36] +basepython = python3.6 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:py37] +basepython = python3.7 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:py38] +basepython = python3.8 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:py3] +basepython = python3 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:pep8] +basepython = python3 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = flake8 {posargs} src unit_tests tests + +[testenv:cover] +# Technique based heavily upon +# https://github.com/openstack/nova/blob/master/tox.ini +basepython = python3 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +setenv = + {[testenv]setenv} + PYTHON=coverage run +commands = + coverage erase + stestr run --slowest {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml + coverage report + +[coverage:run] +branch = True +concurrency = multiprocessing +parallel = True +source = + . +omit = + .tox/* + */charmhelpers/* + unit_tests/* + +[testenv:venv] +basepython = python3 +commands = {posargs} + +[testenv:build] +basepython = python3 +deps = -r{toxinidir}/build-requirements.txt +commands = + charmcraft build + +[testenv:func-noop] +basepython = python3 +commands = + functest-run-suite --help + +[testenv:func] +basepython = python3 +commands = + functest-run-suite --keep-model + +[testenv:func-smoke] +basepython = python3 +commands = + functest-run-suite --keep-model --smoke + +[testenv:func-dev] +basepython = python3 +commands = + functest-run-suite --keep-model --dev + +[testenv:func-target] +basepython = python3 +commands = + functest-run-suite --keep-model --bundle {posargs} + +[flake8] +# Ignore E902 because the unit_tests directory is missing in the built charm. +ignore = E402,E226,W503,W504,E902 diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py new file mode 100644 index 0000000..8381d13 --- /dev/null +++ b/unit_tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2021 Canonical Ltd +# +# 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. diff --git a/unit_tests/test_cinder_solidfire_charm.py b/unit_tests/test_cinder_solidfire_charm.py new file mode 100644 index 0000000..af7f2cc --- /dev/null +++ b/unit_tests/test_cinder_solidfire_charm.py @@ -0,0 +1,43 @@ +# Copyright 2016 Canonical Ltd +# +# 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 unittest +from src.charm import CinderSolidfireCharm +from ops.model import ActiveStatus +from ops.testing import Harness + + +class TestCinderSolidfireCharm(unittest.TestCase): + + def setUp(self): + self.harness = Harness(CinderSolidfireCharm) + 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-solidfire') + # Test that charm is active upon installation. + self.harness.update_config({}) + self.assertTrue(isinstance( + self.harness.model.unit.status, ActiveStatus)) + + def test_cinder_configuration(self): + # Add check here that configuration is as expected. + pass