From b9a5c54da809c090ccca195ebbeb5ed032094fd5 Mon Sep 17 00:00:00 2001 From: "camille.rodriguez" Date: Tue, 9 Jun 2020 14:22:43 -0500 Subject: [PATCH] Enable S3 storage backend for Gnocchi This feature enables the use of a S3 storage backend for Gnocchi. Charm specs PR: https://review.opendev.org/#/c/738631/ func-test-pr: https://github.com/openstack-charmers/zaza-openstack-tests/pull/334 Change-Id: I959ed69e08b178cddf72535f22499a66b24f9567 --- .gitignore | 2 + requirements.txt | 1 + src/README.md | 11 ++ src/config.yaml | 43 +++++++ src/lib/charm/openstack/gnocchi.py | 108 ++++++++++++++++-- src/metadata.yaml | 5 +- src/reactive/gnocchi_handlers.py | 82 +++++++++++++- src/templates/ceph.conf | 2 +- src/templates/gnocchi.conf | 11 +- src/templates/queens/gnocchi.conf | 12 +- src/tests/bundles/bionic-stein-s3.yaml | 124 ++++++++++++++++++++ src/tests/bundles/bionic-train-s3.yaml | 123 ++++++++++++++++++++ src/tests/bundles/bionic-ussuri-s3.yaml | 123 ++++++++++++++++++++ src/tests/bundles/eoan-train-s3.yaml | 122 ++++++++++++++++++++ src/tests/bundles/focal-ussuri-s3.yaml | 145 ++++++++++++++++++++++++ src/tests/tests.yaml | 26 ++++- src/wheelhouse.txt | 1 + unit_tests/__init__.py | 5 + unit_tests/test_gnocchi_handlers.py | 13 ++- 19 files changed, 935 insertions(+), 24 deletions(-) create mode 100644 src/config.yaml create mode 100644 src/tests/bundles/bionic-stein-s3.yaml create mode 100644 src/tests/bundles/bionic-train-s3.yaml create mode 100644 src/tests/bundles/bionic-ussuri-s3.yaml create mode 100644 src/tests/bundles/eoan-train-s3.yaml create mode 100644 src/tests/bundles/focal-ussuri-s3.yaml create mode 100644 src/wheelhouse.txt diff --git a/.gitignore b/.gitignore index 48489cc..0801879 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ test-charm/ **/__pycache__ interfaces layers +.idea +.history diff --git a/requirements.txt b/requirements.txt index 5f2fff3..63bb96e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ charm-tools>=2.4.4 # importlib-resources 1.1.0 removed Python 3.5 support importlib-resources<1.1.0 simplejson +boto3 diff --git a/src/README.md b/src/README.md index 4b263ce..1b25cc6 100644 --- a/src/README.md +++ b/src/README.md @@ -26,3 +26,14 @@ measures. Gnocchi then needs to be initialized with the current ceilometer data: juju run-action ceilometer-upgrade + +# Usage with S3 storage backend + +> **Note**: S3 storage support for Gnocchi is available starting with OpenStack + Stein. + +Gnocchi is configured to be deployed by default with Ceph, however, +it can also connect to an S3 storage backend. To configure Gnocchi with S3, +configuration options (`storage-backend`, `s3-region-name`, `s3-endpoint-url`, +`s3-access-key-id` and `s3-secret-access-key`) must be provided. +Please take a look at `config.yaml` for more details. \ No newline at end of file diff --git a/src/config.yaml b/src/config.yaml new file mode 100644 index 0000000..0a37a44 --- /dev/null +++ b/src/config.yaml @@ -0,0 +1,43 @@ +options: + storage-backend: + type: string + default: ceph + description: | + Storage backend to use for Gnocchi. Valid options are ceph or s3. + s3-endpoint-url: + type: string + default: + description: | + When using a S3 storage backend, user must provide the endpoint URL. + s3-region-name: + type: string + default: + description: | + When using a S3 storage backend, user must provide the S3 region name. + s3-access-key-id: + type: string + default: + description: | + When using a S3 storage backend, user must provide the S3 access key ID. + s3-secret-access-key: + type: string + default: + description: | + When using a S3 storage backend, user must provide the S3 secret access key. + s3-bucket-prefix: + type: string + default: "gnocchi" + description: | + When using a S3 storage backend, user must provide the prefix to namespace metric bucket. + s3-check-consistency-timeout: + type: int + default: 60 + description: | + Maximum time to wait checking data consistency when writing to S3. Set to 0 to + disable data consistency validation. (floating point value). Minimum value: 0 + s3-max-pool-connections: + type: int + default: 50 + description: | + The maximum number of connections to keep in a connection pool. (integer value). + Minimum value: 1 \ No newline at end of file diff --git a/src/lib/charm/openstack/gnocchi.py b/src/lib/charm/openstack/gnocchi.py index 0ba04da..11c15c3 100644 --- a/src/lib/charm/openstack/gnocchi.py +++ b/src/lib/charm/openstack/gnocchi.py @@ -19,6 +19,7 @@ import subprocess import charmhelpers.contrib.openstack.utils as ch_utils import charmhelpers.contrib.network.ip as ch_ip import charmhelpers.core.host as host +import charmhelpers.core.hookenv as hookenv import charms_openstack.charm import charms_openstack.adapters as adapters @@ -95,12 +96,11 @@ class GnocchiCharmDatabaseRelationAdapter(adapters.DatabaseRelationAdapter): return uri -class GnocchiCharmRelationAdapaters(adapters.OpenStackAPIRelationAdapters): +class GnocchiCharmRelationAdapters(adapters.OpenStackAPIRelationAdapters): """ Adapters collection to append specific adapters for Gnocchi """ - relation_adapters = { 'storage_ceph': charms_openstack.plugins.CephRelationAdapter, 'shared_db': GnocchiCharmDatabaseRelationAdapter, @@ -109,8 +109,8 @@ class GnocchiCharmRelationAdapaters(adapters.OpenStackAPIRelationAdapters): } -class GnochiCharmBase(charms_openstack.charm.HAOpenStackCharm, - charms_openstack.plugins.BaseOpenStackCephCharm): +class GnocchiCharmBase(charms_openstack.charm.HAOpenStackCharm, + charms_openstack.plugins.BaseOpenStackCephCharm): """ Base class for shared charm functions for all package types @@ -132,12 +132,9 @@ class GnochiCharmBase(charms_openstack.charm.HAOpenStackCharm, } } - required_relations = ['shared-db', 'identity-service', - 'storage-ceph', 'coordinator-memcached'] - ha_resources = ['vips', 'haproxy', 'dnsha'] - adapters_class = GnocchiCharmRelationAdapaters + adapters_class = GnocchiCharmRelationAdapters def enable_webserver_site(self): """Enable Gnocchi Webserver sites if rendered or installed""" @@ -149,6 +146,26 @@ class GnochiCharmBase(charms_openstack.charm.HAOpenStackCharm, 'username': 'gnocchi', 'hostname': ch_ip.get_relation_ip(DB_INTERFACE)}, ] + @property + def required_relations(self): + _required_relations = ['shared-db', + 'identity-service', + 'coordinator-memcached'] + if self.options.storage_backend == 'ceph': + _required_relations.append('storage-ceph') + return _required_relations + + @property + def mandatory_config(self): + _mandatory_config = [] + if self.options.storage_backend == 's3': + s3_config = ['s3-region-name', + 's3-endpoint-url', + 's3-access-key-id', + 's3-secret-access-key'] + _mandatory_config.extend(s3_config) + return _mandatory_config + @property def gnocchi_user(self): '''Determine user gnocchi processes will run as @@ -180,8 +197,75 @@ class GnochiCharmBase(charms_openstack.charm.HAOpenStackCharm, except KeyError: return CEPH_KEYRING + def db_sync(self): + """Override db_sync to catch exceptions for the s3 backend. + Perform a database sync using the command defined in the + self.sync_cmd attribute. The services defined in self.services are + restarted after the database sync. + """ + if not self.db_sync_done() and hookenv.is_leader(): + try: + f = open("/var/log/gnocchi/gnocchi-upgrade.log", "w+") + subprocess.check_call(self.sync_cmd, + stdout=f, + stderr=subprocess.STDOUT) + hookenv.leader_set({'db-sync-done': True}) + # Restart services immediately after db sync as + # render_domain_config needs a working system + self.restart_all() + except subprocess.CalledProcessError as e: + hookenv.status_set('blocked', 'An error occured while ' + + 'running gnocchi-upgrade. Logs available ' + + 'in /var/log/gnocchi/gnocchi-upgrade.log') + hookenv.log(e, hookenv.DEBUG) + raise e -class GnocchiCharm(GnochiCharmBase): + def do_openstack_upgrade_db_migration(self): + """Overrides do_openstack_upgrade_db_migration for Openstack + upgrades. This function's purpose is to run a database migration + after the Openstack upgrade. A check of the S3 connection is + required first to avoid a failed migration, in the case of an S3 + storage backend. + :returns: None + """ + self.db_sync() + + def states_to_check(self, required_relations=None): + """Custom states to check function. + + Construct a custom set of connected and available states for each + of the relations passed, along with error messages and new status + conditions. + + :param self: Self + :type self: GnocchiCharm instance + :param required_relations: List of relations which overrides + self.relations + :type required_relations: list of strings + :returns: {relation: [(state, err_status, err_msg), (...),]} + :rtype: dict + """ + states_to_check = super().states_to_check(required_relations) + states_to_check["gnocchi-upgrade"] = [ + ("gnocchi-storage-configuration.ready", + "blocked", + "Mandatory S3 configuration parameters missing."), + ("gnocchi-storage-authentication.ready", + "blocked", + "Authentication to the storage backend failed. " + + "Please verify your S3 credentials."), + ("gnocchi-storage-network.ready", + "blocked", + "Could not connect to the storage backend endpoint URL. " + + "Please verify your network and your s3 endpoint URL."), + ("gnocchi-upgrade.ready", + "error", + "Storage backend not ready. Check logs for troubleshooting.") + ] + return states_to_check + + +class GnocchiCharm(GnocchiCharmBase): """ Charm for Juju deployment of Gnocchi @@ -196,7 +280,7 @@ class GnocchiCharm(GnochiCharmBase): # List of packages to install for this charm packages = ['gnocchi-api', 'gnocchi-metricd', 'python-apt', 'ceph-common', 'python-rados', 'python-keystonemiddleware', - 'apache2', 'libapache2-mod-wsgi'] + 'apache2', 'libapache2-mod-wsgi', 'python-boto3'] services = ['gnocchi-metricd', 'apache2'] @@ -255,10 +339,10 @@ class GnocchiQueensCharm(GnocchiCharm): packages = ['gnocchi-api', 'gnocchi-metricd', 'python3-apt', 'ceph-common', 'python3-rados', 'python3-keystonemiddleware', - 'python3-memcache'] + 'python3-memcache', 'python3-boto3'] -class GnocchiSnapCharm(GnochiCharmBase): +class GnocchiSnapCharm(GnocchiCharmBase): """ Charm for Juju deployment of Gnocchi via Snap diff --git a/src/metadata.yaml b/src/metadata.yaml index 152244e..e236c49 100644 --- a/src/metadata.yaml +++ b/src/metadata.yaml @@ -19,7 +19,8 @@ provides: metric-service: interface: gnocchi requires: - storage-ceph: - interface: ceph-client coordinator-memcached: interface: memcache + storage-ceph: + interface: ceph-client + optional: True diff --git a/src/reactive/gnocchi_handlers.py b/src/reactive/gnocchi_handlers.py index 5380d57..c469f3d 100644 --- a/src/reactive/gnocchi_handlers.py +++ b/src/reactive/gnocchi_handlers.py @@ -15,6 +15,9 @@ import charms_openstack.charm as charm import charms.reactive as reactive +import boto3 +import botocore + import charm.openstack.gnocchi as gnocchi # noqa import charmhelpers.core.hookenv as hookenv @@ -32,15 +35,85 @@ charm.use_defaults( required_interfaces = ['coordinator-memcached.available', 'shared-db.available', - 'identity-service.available', - 'storage-ceph.pools.available'] + 'identity-service.available'] + +if hookenv.config('storage-backend').lower() == 'ceph': + required_interfaces.append('storage-ceph.pools.available') +storage_config = ['config.changed.storage-backend', + 'config.changed.s3-endpoint-url', + 'config.changed.s3-region-name', + 'config.changed.s3-access-key-id', + 'config.changed.s3-secret-access-key'] + + +@reactive.when_any(*storage_config) +def storage_backend_connection(): + """Test the connection to the S3 backend provided.""" + reactive.clear_flag('gnocchi-upgrade.ready') + reactive.clear_flag('storage-ceph.needed') + reactive.set_flag('gnocchi-storage-configuration.ready') + reactive.set_flag('gnocchi-storage-authentication.ready') + reactive.set_flag('gnocchi-storage-network.ready') + + with charm.provide_charm_instance() as charm_class: + if charm_class.options.storage_backend == 's3': + kwargs = { + 'region_name': charm_class.options.s3_region_name, + 'aws_access_key_id': charm_class.options.s3_access_key_id, + 'aws_secret_access_key': + charm_class.options.s3_secret_access_key, + 'endpoint_url': charm_class.options.s3_endpoint_url, + } + for value in kwargs.values(): + if not value: + hookenv.log('Mandatory S3 configuration parameters ' + + 'missing.', hookenv.DEBUG) + reactive.clear_flag('gnocchi-storage-configuration.ready') + return + + try: + boto3.client('s3', **kwargs) + hookenv.log('S3 successfully reachable.', hookenv.DEBUG) + reactive.set_flag('gnocchi-upgrade.ready') + + except botocore.exceptions.ClientError as e: + hookenv.log("Authentication to S3 backend failed. Error : " + + "{}".format(e), hookenv.DEBUG) + reactive.clear_flag('gnocchi-storage-authentication.ready') + return + except botocore.exceptions.EndpointConnectionError as e: + hookenv.log("Could not connect to the endpoint URL: Error : " + + "{}".format(e), hookenv.DEBUG) + reactive.clear_flag('gnocchi-storage-network.ready') + return + except botocore.exceptions.SSLError as e: + # this status check does not check for ssl validation + reactive.set_flag('gnocchi-upgrade.ready') + return + except Exception as e: + hookenv.log("An error occured when trying to reach the S3 " + + "backend: {}".format(e), hookenv.DEBUG) + return + elif charm_class.options.storage_backend == 'ceph': + reactive.set_flag('storage-ceph.needed') + reactive.set_flag('gnocchi-upgrade.ready') + return + else: + reactive.set_flag('gnocchi-upgrade.ready') + + +@reactive.when('gnocchi-upgrade.ready') @reactive.when(*required_interfaces) def render_config(*args): """Render the configuration for charm when all the interfaces are available. + + Note that the storage-ceph interface is optional and thus is only + used if it is available. """ + with charm.provide_charm_instance() as charm_class: charm_class.upgrade_if_available(args) charm_class.configure_ssl() @@ -50,12 +123,13 @@ def render_config(*args): reactive.set_state('config.rendered') -# db_sync checks if sync has been done so rerunning is a noop +# db_sync checks if sync has been done so rerunning is a noop. @reactive.when('config.rendered') @reactive.when_not('db.synced') def init_db(): with charm.provide_charm_instance() as charm_class: charm_class.db_sync() + charm_class.assess_status() hookenv.log("Database synced", hookenv.DEBUG) reactive.set_state('db.synced') @@ -92,6 +166,7 @@ def check_ceph_request_status(ceph): ceph.changed() +@reactive.when('storage-ceph.needed') @reactive.when_not('storage-ceph.connected') def storage_ceph_disconnected(): with charm.provide_charm_instance() as charm_instance: @@ -118,6 +193,7 @@ def provide_gnocchi_url(metric_service): metric_service.set_gnocchi_url(charm_class.public_url) +@reactive.when('storage-ceph.needed') @reactive.when_not('storage-ceph.connected') @reactive.when_not('storage-ceph.pools.available') def reset_state_create_pool_req_sent(): diff --git a/src/templates/ceph.conf b/src/templates/ceph.conf index 13e5b0a..b57d424 100644 --- a/src/templates/ceph.conf +++ b/src/templates/ceph.conf @@ -4,7 +4,7 @@ # local changes will be overwritten. ############################################################################### [global] -{% if storage_ceph.auth -%} +{% if options.storage_backend == 'ceph' and storage_ceph.auth -%} auth_supported = {{ storage_ceph.auth }} mon host = {{ storage_ceph.monitors }} {% endif -%} diff --git a/src/templates/gnocchi.conf b/src/templates/gnocchi.conf index 5eee35a..52a7129 100644 --- a/src/templates/gnocchi.conf +++ b/src/templates/gnocchi.conf @@ -26,12 +26,21 @@ workers = {{ options.workers }} coordination_url = {{ coordinator_memcached.url }} {%- endif %} -{% if storage_ceph.key -%} +{% if options.storage_backend == 'ceph' and storage_ceph.key -%} driver = ceph ceph_pool = {{ options.application_name }} ceph_username = {{ options.application_name }} ceph_secret = {{ storage_ceph.key }} ceph_conffile = {{ options.ceph_config }} +{% elif options.storage_backend == 's3' -%} +driver = s3 +s3_endpoint_url = {{ options.s3_endpoint_url }} +s3_region_name = {{ options.s3_region_name }} +s3_access_key_id = {{ options.s3_access_key_id }} +s3_secret_access_key = {{ options.s3_secret_access_key }} +s3_bucket_prefix = {{ options.s3_bucket_prefix }} +s3_check_consistency_timeout = {{ options.s3_check_consistency_timeout }} +s3_max_pool_connections = {{ options.s3_max_pool_connections }} {%- endif %} {% include "parts/section-keystone-authtoken" %} diff --git a/src/templates/queens/gnocchi.conf b/src/templates/queens/gnocchi.conf index 29000f5..b3bfa07 100644 --- a/src/templates/queens/gnocchi.conf +++ b/src/templates/queens/gnocchi.conf @@ -26,14 +26,24 @@ url = {{ shared_db.uri }} workers = {{ options.workers }} [storage] -{% if storage_ceph.key -%} +{% if options.storage_backend == 'ceph' and storage_ceph.key -%} driver = ceph ceph_pool = {{ options.application_name }} ceph_username = {{ options.application_name }} ceph_secret = {{ storage_ceph.key }} ceph_conffile = {{ options.ceph_config }} +{% elif options.storage_backend == 's3' -%} +driver = s3 +s3_endpoint_url = {{ options.s3_endpoint_url }} +s3_region_name = {{ options.s3_region_name }} +s3_access_key_id = {{ options.s3_access_key_id }} +s3_secret_access_key = {{ options.s3_secret_access_key }} +s3_bucket_prefix = {{ options.s3_bucket_prefix }} +s3_check_consistency_timeout = {{ options.s3_check_consistency_timeout }} +s3_max_pool_connections = {{ options.s3_max_pool_connections }} {%- endif %} + {% include "parts/section-keystone-authtoken" %} {% include "parts/section-oslo-middleware" %} diff --git a/src/tests/bundles/bionic-stein-s3.yaml b/src/tests/bundles/bionic-stein-s3.yaml new file mode 100644 index 0000000..47d9273 --- /dev/null +++ b/src/tests/bundles/bionic-stein-s3.yaml @@ -0,0 +1,124 @@ +variables: + openstack-origin: &openstack-origin cloud:bionic-stein + +series: &series bionic + +machines: + 0: + constraints: "mem=3072M" + 1: {} + 2: {} + 3: {} + 4: {} + 5: {} + 6: {} + 7: {} + 8: {} + +# We specify machine placements for these to improve iteration +# time, given that machine "0" comes up way before machine "6" +applications: + percona-cluster: + charm: cs:~openstack-charmers-next/percona-cluster + num_units: 1 + options: + source: *openstack-origin + to: + - '0' + rabbitmq-server: + charm: cs:~openstack-charmers-next/rabbitmq-server + num_units: 1 + options: + source: *openstack-origin + to: + - '1' + keystone: + charm: cs:~openstack-charmers-next/keystone + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '2' + ceilometer: + charm: cs:~openstack-charmers-next/ceilometer + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '3' + gnocchi: + series: *series + charm: ../../../gnocchi + num_units: 1 + options: + openstack-origin: *openstack-origin + storage-backend: s3 + to: + - '4' + memcached: + charm: cs:~memcached-team/memcached + num_units: 1 + to: + - '5' + #swift-proxy is used to test gnocchi against an S3 storage backend + swift-proxy: + charm: swift-proxy + series: bionic + num_units: 1 + options: + zone-assignment: manual + replicas: 1 + swift-hash: fdfef9d4-8b06-11e2-8ac0-531c923c8fae + openstack-origin: *openstack-origin + to: + - '6' + swift-storage: + charm: cs:~openstack-charmers-next/swift-storage + num_units: 1 + storage: + block-devices: 'cinder,10G' + options: + openstack-origin: *openstack-origin + zone: 1 + to: + - '7' + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + series: *series + to: + - '8' +relations: + - - keystone:shared-db + - percona-cluster:shared-db + - - ceilometer:amqp + - rabbitmq-server:amqp + - - ceilometer:identity-credentials + - keystone:identity-credentials + - - ceilometer:identity-notifications + - keystone:identity-notifications + - - ceilometer:metric-service + - gnocchi:metric-service + - - gnocchi:identity-service + - keystone:identity-service + - - gnocchi:shared-db + - percona-cluster:shared-db + - - gnocchi:coordinator-memcached + - memcached:cache + - - swift-proxy:identity-service + - keystone:identity-service + - - swift-storage:swift-storage + - swift-proxy:swift-storage + - - vault:shared-db + - percona-cluster:shared-db + - - vault:certificates + - swift-proxy:certificates + - - vault:certificates + - gnocchi:certificates + - - vault:certificates + - ceilometer:certificates + - - vault:certificates + - keystone:certificates + - - vault:certificates + - rabbitmq-server:certificates + \ No newline at end of file diff --git a/src/tests/bundles/bionic-train-s3.yaml b/src/tests/bundles/bionic-train-s3.yaml new file mode 100644 index 0000000..1b153a1 --- /dev/null +++ b/src/tests/bundles/bionic-train-s3.yaml @@ -0,0 +1,123 @@ +variables: + openstack-origin: &openstack-origin cloud:bionic-train + +series: &series bionic + +machines: + 0: + constraints: "mem=3072M" + 1: {} + 2: {} + 3: {} + 4: {} + 5: {} + 6: {} + 7: {} + 8: {} + +# We specify machine placements for these to improve iteration +# time, given that machine "0" comes up way before machine "6" +applications: + percona-cluster: + charm: cs:~openstack-charmers-next/percona-cluster + num_units: 1 + options: + source: *openstack-origin + to: + - '0' + rabbitmq-server: + charm: cs:~openstack-charmers-next/rabbitmq-server + num_units: 1 + options: + source: *openstack-origin + to: + - '1' + keystone: + charm: cs:~openstack-charmers-next/keystone + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '2' + ceilometer: + charm: cs:~openstack-charmers-next/ceilometer + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '3' + gnocchi: + series: *series + charm: ../../../gnocchi + num_units: 1 + options: + openstack-origin: *openstack-origin + storage-backend: s3 + to: + - '4' + memcached: + charm: cs:~memcached-team/memcached + num_units: 1 + to: + - '5' + #swift-proxy is used to test gnocchi against an S3 storage backend + swift-proxy: + charm: swift-proxy + series: bionic + num_units: 1 + options: + zone-assignment: manual + replicas: 1 + swift-hash: fdfef9d4-8b06-11e2-8ac0-531c923c8fae + openstack-origin: *openstack-origin + to: + - '6' + swift-storage: + charm: cs:~openstack-charmers-next/swift-storage + num_units: 1 + storage: + block-devices: 'cinder,10G' + options: + openstack-origin: *openstack-origin + zone: 1 + to: + - '7' + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + series: *series + to: + - '8' +relations: + - - keystone:shared-db + - percona-cluster:shared-db + - - ceilometer:amqp + - rabbitmq-server:amqp + - - ceilometer:identity-credentials + - keystone:identity-credentials + - - ceilometer:identity-notifications + - keystone:identity-notifications + - - ceilometer:metric-service + - gnocchi:metric-service + - - gnocchi:identity-service + - keystone:identity-service + - - gnocchi:shared-db + - percona-cluster:shared-db + - - gnocchi:coordinator-memcached + - memcached:cache + - - swift-proxy:identity-service + - keystone:identity-service + - - swift-storage:swift-storage + - swift-proxy:swift-storage + - - vault:shared-db + - percona-cluster:shared-db + - - vault:certificates + - swift-proxy:certificates + - - vault:certificates + - gnocchi:certificates + - - vault:certificates + - ceilometer:certificates + - - vault:certificates + - keystone:certificates + - - vault:certificates + - rabbitmq-server:certificates diff --git a/src/tests/bundles/bionic-ussuri-s3.yaml b/src/tests/bundles/bionic-ussuri-s3.yaml new file mode 100644 index 0000000..f250dd8 --- /dev/null +++ b/src/tests/bundles/bionic-ussuri-s3.yaml @@ -0,0 +1,123 @@ +variables: + openstack-origin: &openstack-origin cloud:bionic-ussuri + +series: &series bionic + +machines: + 0: + constraints: "mem=3072M" + 1: {} + 2: {} + 3: {} + 4: {} + 5: {} + 6: {} + 7: {} + 8: {} + +# We specify machine placements for these to improve iteration +# time, given that machine "0" comes up way before machine "6" +applications: + percona-cluster: + charm: cs:~openstack-charmers-next/percona-cluster + num_units: 1 + options: + source: *openstack-origin + to: + - '0' + rabbitmq-server: + charm: cs:~openstack-charmers-next/rabbitmq-server + num_units: 1 + options: + source: *openstack-origin + to: + - '1' + keystone: + charm: cs:~openstack-charmers-next/keystone + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '2' + ceilometer: + charm: cs:~openstack-charmers-next/ceilometer + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '3' + gnocchi: + series: *series + charm: ../../../gnocchi + num_units: 1 + options: + openstack-origin: *openstack-origin + storage-backend: s3 + to: + - '4' + memcached: + charm: cs:~memcached-team/memcached + num_units: 1 + to: + - '5' + #swift-proxy is used to test gnocchi against an S3 storage backend + swift-proxy: + charm: swift-proxy + series: bionic + num_units: 1 + options: + zone-assignment: manual + replicas: 1 + swift-hash: fdfef9d4-8b06-11e2-8ac0-531c923c8fae + openstack-origin: *openstack-origin + to: + - '6' + swift-storage: + charm: cs:~openstack-charmers-next/swift-storage + num_units: 1 + storage: + block-devices: 'cinder,10G' + options: + openstack-origin: *openstack-origin + zone: 1 + to: + - '7' + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + series: *series + to: + - '8' +relations: + - - keystone:shared-db + - percona-cluster:shared-db + - - ceilometer:amqp + - rabbitmq-server:amqp + - - ceilometer:identity-credentials + - keystone:identity-credentials + - - ceilometer:identity-notifications + - keystone:identity-notifications + - - ceilometer:metric-service + - gnocchi:metric-service + - - gnocchi:identity-service + - keystone:identity-service + - - gnocchi:shared-db + - percona-cluster:shared-db + - - gnocchi:coordinator-memcached + - memcached:cache + - - swift-proxy:identity-service + - keystone:identity-service + - - swift-storage:swift-storage + - swift-proxy:swift-storage + - - vault:shared-db + - percona-cluster:shared-db + - - vault:certificates + - swift-proxy:certificates + - - vault:certificates + - gnocchi:certificates + - - vault:certificates + - ceilometer:certificates + - - vault:certificates + - keystone:certificates + - - vault:certificates + - rabbitmq-server:certificates diff --git a/src/tests/bundles/eoan-train-s3.yaml b/src/tests/bundles/eoan-train-s3.yaml new file mode 100644 index 0000000..484cd89 --- /dev/null +++ b/src/tests/bundles/eoan-train-s3.yaml @@ -0,0 +1,122 @@ +variables: + openstack-origin: &openstack-origin distro + +series: &series eoan + +machines: + 0: + constraints: "mem=3072M" + 1: {} + 2: {} + 3: {} + 4: {} + 5: {} + 6: {} + 7: {} + 8: {} + +# We specify machine placements for these to improve iteration +# time, given that machine "0" comes up way before machine "6" +applications: + percona-cluster: + charm: cs:~openstack-charmers-next/percona-cluster + num_units: 1 + options: + source: *openstack-origin + to: + - '0' + rabbitmq-server: + charm: cs:~openstack-charmers-next/rabbitmq-server + num_units: 1 + options: + source: *openstack-origin + to: + - '1' + keystone: + charm: cs:~openstack-charmers-next/keystone + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '2' + ceilometer: + charm: cs:~openstack-charmers-next/ceilometer + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '3' + gnocchi: + series: *series + charm: ../../../gnocchi + num_units: 1 + options: + openstack-origin: *openstack-origin + storage-backend: s3 + to: + - '4' + memcached: + charm: cs:~memcached-team/memcached + num_units: 1 + to: + - '5' + #swift-proxy is used to test gnocchi against an S3 storage backend + swift-proxy: + charm: swift-proxy + num_units: 1 + options: + zone-assignment: manual + replicas: 1 + swift-hash: fdfef9d4-8b06-11e2-8ac0-531c923c8fae + openstack-origin: *openstack-origin + to: + - '6' + swift-storage: + charm: cs:~openstack-charmers-next/swift-storage + num_units: 1 + storage: + block-devices: 'cinder,10G' + options: + openstack-origin: *openstack-origin + zone: 1 + to: + - '7' + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + series: *series + to: + - '8' +relations: + - - keystone:shared-db + - percona-cluster:shared-db + - - ceilometer:amqp + - rabbitmq-server:amqp + - - ceilometer:identity-credentials + - keystone:identity-credentials + - - ceilometer:identity-notifications + - keystone:identity-notifications + - - ceilometer:metric-service + - gnocchi:metric-service + - - gnocchi:identity-service + - keystone:identity-service + - - gnocchi:shared-db + - percona-cluster:shared-db + - - gnocchi:coordinator-memcached + - memcached:cache + - - swift-proxy:identity-service + - keystone:identity-service + - - swift-storage:swift-storage + - swift-proxy:swift-storage + - - vault:shared-db + - percona-cluster:shared-db + - - vault:certificates + - swift-proxy:certificates + - - vault:certificates + - gnocchi:certificates + - - vault:certificates + - ceilometer:certificates + - - vault:certificates + - keystone:certificates + - - vault:certificates + - rabbitmq-server:certificates diff --git a/src/tests/bundles/focal-ussuri-s3.yaml b/src/tests/bundles/focal-ussuri-s3.yaml new file mode 100644 index 0000000..9c2dd1d --- /dev/null +++ b/src/tests/bundles/focal-ussuri-s3.yaml @@ -0,0 +1,145 @@ +variables: + openstack-origin: &openstack-origin distro + +series: &series focal + +machines: + 0: + constraints: "mem=3072M" + 1: + constraints: "mem=3072M" + 2: + constraints: "mem=3072M" + 3: {} + 4: {} + 5: {} + 6: {} + 7: {} + 8: {} + 9: {} + 10: + series: bionic + +# We specify machine placements for these to improve iteration +# time, given that machine "0" comes up way before machine "6" +applications: + + keystone-mysql-router: + charm: cs:~openstack-charmers-next/mysql-router + gnocchi-mysql-router: + charm: cs:~openstack-charmers-next/mysql-router + vault-mysql-router: + charm: cs:~openstack-charmers-next/mysql-router + + mysql-innodb-cluster: + charm: cs:~openstack-charmers-next/mysql-innodb-cluster + num_units: 3 + options: + source: *openstack-origin + to: + - '0' + - '1' + - '2' + rabbitmq-server: + charm: cs:~openstack-charmers-next/rabbitmq-server + num_units: 1 + options: + source: *openstack-origin + to: + - '3' + keystone: + charm: cs:~openstack-charmers-next/keystone + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '4' + ceilometer: + charm: cs:~openstack-charmers-next/ceilometer + num_units: 1 + options: + openstack-origin: *openstack-origin + to: + - '5' + gnocchi: + series: *series + charm: ../../../gnocchi + num_units: 1 + options: + openstack-origin: *openstack-origin + storage-backend: s3 + to: + - '6' + memcached: + charm: cs:~memcached-team/memcached + num_units: 1 + # holding at bionic as memcached doesn't support focal yet + series: bionic + to: + - '10' + #swift-proxy is used to test gnocchi against an S3 storage backend + swift-proxy: + charm: swift-proxy + num_units: 1 + options: + zone-assignment: manual + replicas: 1 + swift-hash: fdfef9d4-8b06-11e2-8ac0-531c923c8fae + openstack-origin: *openstack-origin + to: + - '7' + swift-storage: + charm: cs:~openstack-charmers-next/swift-storage + num_units: 1 + storage: + block-devices: 'cinder,10G' + options: + openstack-origin: *openstack-origin + zone: 1 + to: + - '8' + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + series: *series + to: + - '9' +relations: + - - keystone:shared-db + - keystone-mysql-router:shared-db + - - keystone-mysql-router:db-router + - mysql-innodb-cluster:db-router + - - gnocchi:shared-db + - gnocchi-mysql-router:shared-db + - - gnocchi-mysql-router:db-router + - mysql-innodb-cluster:db-router + - - ceilometer:amqp + - rabbitmq-server:amqp + - - ceilometer:identity-credentials + - keystone:identity-credentials + - - ceilometer:identity-notifications + - keystone:identity-notifications + - - ceilometer:metric-service + - gnocchi:metric-service + - - gnocchi:identity-service + - keystone:identity-service + - - gnocchi:coordinator-memcached + - memcached:cache + - - swift-proxy:identity-service + - keystone:identity-service + - - swift-storage:swift-storage + - swift-proxy:swift-storage + - - vault:shared-db + - vault-mysql-router:shared-db + - - vault-mysql-router:db-router + - mysql-innodb-cluster:db-router + - - vault:certificates + - swift-proxy:certificates + - - vault:certificates + - gnocchi:certificates + - - vault:certificates + - ceilometer:certificates + - - vault:certificates + - keystone:certificates + - - vault:certificates + - rabbitmq-server:certificates diff --git a/src/tests/tests.yaml b/src/tests/tests.yaml index 14137dd..5da412c 100644 --- a/src/tests/tests.yaml +++ b/src/tests/tests.yaml @@ -10,21 +10,41 @@ gate_bundles: - bionic-train - bionic-ussuri - focal-ussuri + - test-s3: bionic-stein-s3 + - test-s3: bionic-train-s3 + - test-s3: bionic-ussuri-s3 + - test-s3: focal-ussuri-s3 smoke_bundles: - bionic-train + - test-s3: bionic-train-s3 dev_bundles: - eoan-train -tests: - - zaza.openstack.charm_tests.gnocchi.tests.GnocchiTest + - test-s3: eoan-train-s3 configure: - zaza.openstack.charm_tests.ceilometer.setup.basic_setup + - test-s3: + - zaza.openstack.charm_tests.vault.setup.auto_initialize + - zaza.openstack.charm_tests.gnocchi.setup.configure_s3_backend + - zaza.openstack.charm_tests.ceilometer.setup.basic_setup +tests: + - zaza.openstack.charm_tests.gnocchi.tests.GnocchiTest + - test-s3: + - zaza.openstack.charm_tests.gnocchi.tests.GnocchiS3Test + - zaza.openstack.charm_tests.gnocchi.tests.GnocchiTest target_deploy_status: + vault: + workload-status: blocked + workload-status-message: Vault needs to be initialized ceilometer: workload-status: blocked - workload-status-message: Run the ceilometer-upgrade action on the leader to initialize ceilometer and gnocchi + workload-status-message: '' mongodb: workload-status: unknown workload-status-message: '' + gnocchi: + workload-status: blocked + workload-status-message: Mandatory S3 configuration parameters missing. tests_options: force_deploy: - focal-ussuri + - focal-ussuri-s3 diff --git a/src/wheelhouse.txt b/src/wheelhouse.txt new file mode 100644 index 0000000..30ddf82 --- /dev/null +++ b/src/wheelhouse.txt @@ -0,0 +1 @@ +boto3 diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py index d788775..b55ca32 100644 --- a/unit_tests/__init__.py +++ b/unit_tests/__init__.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import mock import sys sys.path.append('src') @@ -38,5 +39,9 @@ def mock_more_stuff(): charmhelpers.contrib.storage.linux.ceph ) +boto3 = mock.MagicMock() +botocore = mock.MagicMock() +sys.modules['boto3'] = boto3 +sys.modules['botocore'] = botocore mock_more_stuff() diff --git a/unit_tests/test_gnocchi_handlers.py b/unit_tests/test_gnocchi_handlers.py index 5be6efe..3a79759 100644 --- a/unit_tests/test_gnocchi_handlers.py +++ b/unit_tests/test_gnocchi_handlers.py @@ -37,11 +37,14 @@ class TestRegisteredHooks(test_utils.TestRegisteredHooks): 'certificates.available'] hook_set = { 'when': { + 'define_storage_states': ( + 'config.changed.storage-backend', + ), 'render_config': ( 'coordinator-memcached.available', 'shared-db.available', 'identity-service.available', - 'storage-ceph.pools.available', + 'gnocchi-upgrade.ready', ), 'init_db': ( 'config.rendered', @@ -63,6 +66,12 @@ class TestRegisteredHooks(test_utils.TestRegisteredHooks): 'check_ceph_request_status': ( 'storage-ceph.connected', ), + 'storage_ceph_disconnected': ( + 'storage-ceph.needed', + ), + 'reset_state_create_pool_req_sent': ( + 'storage-ceph.needed', + ) }, 'when_not': { 'storage_ceph_disconnected': ( @@ -106,6 +115,8 @@ class TestHandlers(test_utils.PatchHelper): def test_render_stuff(self): self.patch_object(handlers.reactive, 'set_state') + self.patch_object(handlers.charm, 'optional_interfaces') + handlers.render_config('arg1', 'arg2') self.gnocchi_charm.render_with_interfaces.assert_called_once_with( ('arg1', 'arg2')