From abffaa2578b701a72fece33c51cb2cf02d54916d Mon Sep 17 00:00:00 2001 From: attardi <giuseppe.attardi@garr.it> Date: Tue, 2 Jul 2019 15:15:41 +0000 Subject: [PATCH] Upgraded to charm Gnocchi #23 --- LICENSE | 26 +++ Makefile | 40 ++--- bin/layer_option | 6 +- config.yaml | 5 - hooks/amqp-relation-broken | 9 +- hooks/amqp-relation-changed | 9 +- hooks/amqp-relation-departed | 9 +- hooks/amqp-relation-joined | 9 +- hooks/cluster-relation-broken | 9 +- hooks/cluster-relation-changed | 9 +- hooks/cluster-relation-departed | 9 +- hooks/cluster-relation-joined | 9 +- hooks/config-changed | 9 +- hooks/coordinator-memcached-relation-broken | 9 +- hooks/coordinator-memcached-relation-changed | 9 +- hooks/coordinator-memcached-relation-departed | 9 +- hooks/coordinator-memcached-relation-joined | 9 +- hooks/ha-relation-broken | 9 +- hooks/ha-relation-changed | 9 +- hooks/ha-relation-departed | 9 +- hooks/ha-relation-joined | 9 +- hooks/hook.template | 9 +- hooks/identity-service-relation-broken | 9 +- hooks/identity-service-relation-changed | 9 +- hooks/identity-service-relation-departed | 9 +- hooks/identity-service-relation-joined | 9 +- hooks/install | 9 +- hooks/leader-elected | 9 +- hooks/leader-settings-changed | 9 +- hooks/metric-service-relation-broken | 9 +- hooks/metric-service-relation-changed | 9 +- hooks/metric-service-relation-departed | 9 +- hooks/metric-service-relation-joined | 9 +- hooks/relations/ceph-client/interface.yaml | 12 +- hooks/relations/ceph-client/requires.py | 59 ++++++- .../ceph-client/test-requirements.txt | 2 - hooks/relations/ceph-client/tox.ini | 43 ----- hooks/relations/gnocchi/tox.ini | 7 +- hooks/relations/hacluster/common.py | 67 +++++++- hooks/relations/hacluster/interface.yaml | 8 + hooks/relations/hacluster/requires.py | 97 ++++++++++- .../relations/hacluster/test-requirements.txt | 7 +- hooks/relations/hacluster/tox.ini | 39 ----- hooks/relations/keystone/interface.yaml | 11 +- hooks/relations/keystone/requires.py | 7 +- .../relations/keystone/test-requirements.txt | 2 - hooks/relations/keystone/tox.ini | 40 ----- hooks/relations/mysql-shared/.gitreview | 2 +- hooks/relations/mysql-shared/requires.py | 8 + hooks/relations/mysql-shared/tox.ini | 7 +- hooks/relations/openstack-ha/.gitreview | 2 +- hooks/relations/openstack-ha/tox.ini | 5 +- hooks/relations/rabbitmq/.gitreview | 2 +- hooks/relations/rabbitmq/tox.ini | 5 +- hooks/shared-db-relation-broken | 9 +- hooks/shared-db-relation-changed | 9 +- hooks/shared-db-relation-departed | 9 +- hooks/shared-db-relation-joined | 9 +- hooks/start | 9 +- hooks/stop | 9 +- hooks/storage-ceph-relation-broken | 9 +- hooks/storage-ceph-relation-changed | 9 +- hooks/storage-ceph-relation-departed | 9 +- hooks/storage-ceph-relation-joined | 9 +- hooks/update-status | 9 +- hooks/upgrade-charm | 18 +- layer.yaml | 30 +++- lib/charm/openstack/gnocchi.py | 56 +------ lib/charms/layer/__init__.py | 69 ++++++-- lib/charms/layer/basic.py | 70 +++++++- metadata.yaml | 7 +- reactive/gnocchi_handlers.py | 20 ++- reactive/layer_openstack.py | 27 ++- reactive/layer_openstack_api.py | 22 +++ rebuild | 5 - repo-info | 6 +- requirements.txt | 10 +- run-func-tests.sh | 72 -------- templates/gnocchi.conf | 2 + templates/parts/section-database | 2 +- templates/parts/section-keystone-authtoken | 5 + tests/basic_deployment.py | 2 +- tox.ini | 139 +++++----------- unit_tests/__init__.py | 42 ----- unit_tests/test_gnocchi_handlers.py | 154 ------------------ 85 files changed, 827 insertions(+), 800 deletions(-) delete mode 100644 hooks/relations/ceph-client/test-requirements.txt delete mode 100644 hooks/relations/ceph-client/tox.ini delete mode 100644 hooks/relations/hacluster/tox.ini delete mode 100644 hooks/relations/keystone/test-requirements.txt delete mode 100644 hooks/relations/keystone/tox.ini delete mode 100644 rebuild delete mode 100755 run-func-tests.sh delete mode 100644 unit_tests/__init__.py delete mode 100644 unit_tests/test_gnocchi_handlers.py diff --git a/LICENSE b/LICENSE index 68c771a0..d6456956 100644 --- a/LICENSE +++ b/LICENSE @@ -174,3 +174,29 @@ incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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/Makefile b/Makefile index 3b02e946..ddfef427 100644 --- a/Makefile +++ b/Makefile @@ -4,34 +4,30 @@ PYTHON := /usr/bin/env python USER = csd-garr NAME = gnocchi -all: lint test +all: lint unit_test -lint: - @tox -e pep8 - -test: - @# Bundletester expects unit tests here. - @tox -e py27 +.PHONY: clean +clean: +clean: + @rm -rf .coverage .tox .testrepository trusty .unit-state.db + find . -iname '*.pyc' -delete -functional_test: - @echo Starting functional tests... - @tox -e func27 +.PHONY: apt_prereqs +apt_prereqs: + @# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip) + @which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox) -bin/charm_helpers_sync.py: - @mkdir -p bin - @curl -o bin/charm_helpers_sync.py https://raw.githubusercontent.com/juju/charm-helpers/master/tools/charm_helpers_sync/charm_helpers_sync.py +.PHONY: lint +lint: apt_prereqs + @tox --notest -sync: bin/charm_helpers_sync.py - @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml - @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-tests.yaml +.PHONY: unit_test +unit_test: apt_prereqs + @echo Starting tests... + tox -publish: lint test +publish: lint unit_test export OUTPUT=`charm push . cs:~$(USER)/$(NAME)`; echo $$OUTPUT export REV=`echo $$OUTPUT | sed 's/.*$(NAME)-\([0-9]*\).*/\1/'` charm release cs:~$(USER)/$(NAME)-$(REV) --channel stable charm grant cs:~$(USER)/$(NAME)-$(REV) everyone --channel stable - -.PHONY: clean -clean: - @rm -rf .coverage .tox .testrepository trusty .unit-state.db - find . -iname '*.pyc' -delete diff --git a/bin/layer_option b/bin/layer_option index 90dc400e..3253ef8a 100755 --- a/bin/layer_option +++ b/bin/layer_option @@ -1,10 +1,8 @@ #!/usr/bin/env python3 import sys -sys.path.append('lib') - import argparse -from charms.layer import options +from charms import layer parser = argparse.ArgumentParser(description='Access layer options.') @@ -14,7 +12,7 @@ parser.add_argument('option', help='the option to access') args = parser.parse_args() -value = options(args.section).get(args.option, '') +value = layer.options.get(args.section, args.option) if isinstance(value, bool): sys.exit(0 if value else 1) elif isinstance(value, list): diff --git a/config.yaml b/config.yaml index 903d418c..df0615d9 100644 --- a/config.yaml +++ b/config.yaml @@ -13,11 +13,6 @@ "description": | Setting this to True will allow supporting services to log to syslog. "incomingtofile": - "type": "boolean" - "default": !!bool "false" - "description": | - Use file driver for incoming measures. - "use-internal-endpoints": "type": "boolean" "default": !!bool "false" "description": | diff --git a/hooks/amqp-relation-broken b/hooks/amqp-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/amqp-relation-broken +++ b/hooks/amqp-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/amqp-relation-changed b/hooks/amqp-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/amqp-relation-changed +++ b/hooks/amqp-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/amqp-relation-departed b/hooks/amqp-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/amqp-relation-departed +++ b/hooks/amqp-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/amqp-relation-joined b/hooks/amqp-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/amqp-relation-joined +++ b/hooks/amqp-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/cluster-relation-broken b/hooks/cluster-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/cluster-relation-broken +++ b/hooks/cluster-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/cluster-relation-changed b/hooks/cluster-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/cluster-relation-changed +++ b/hooks/cluster-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/cluster-relation-departed b/hooks/cluster-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/cluster-relation-departed +++ b/hooks/cluster-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/cluster-relation-joined b/hooks/cluster-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/cluster-relation-joined +++ b/hooks/cluster-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/config-changed b/hooks/config-changed index 64c84897..9858c6be 100755 --- a/hooks/config-changed +++ b/hooks/config-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/coordinator-memcached-relation-broken b/hooks/coordinator-memcached-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/coordinator-memcached-relation-broken +++ b/hooks/coordinator-memcached-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/coordinator-memcached-relation-changed b/hooks/coordinator-memcached-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/coordinator-memcached-relation-changed +++ b/hooks/coordinator-memcached-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/coordinator-memcached-relation-departed b/hooks/coordinator-memcached-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/coordinator-memcached-relation-departed +++ b/hooks/coordinator-memcached-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/coordinator-memcached-relation-joined b/hooks/coordinator-memcached-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/coordinator-memcached-relation-joined +++ b/hooks/coordinator-memcached-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/ha-relation-broken b/hooks/ha-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/ha-relation-broken +++ b/hooks/ha-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/ha-relation-changed b/hooks/ha-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/ha-relation-changed +++ b/hooks/ha-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/ha-relation-departed b/hooks/ha-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/ha-relation-departed +++ b/hooks/ha-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/ha-relation-joined b/hooks/ha-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/ha-relation-joined +++ b/hooks/ha-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/hook.template b/hooks/hook.template index 64c84897..9858c6be 100644 --- a/hooks/hook.template +++ b/hooks/hook.template @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/identity-service-relation-broken b/hooks/identity-service-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/identity-service-relation-broken +++ b/hooks/identity-service-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/identity-service-relation-changed b/hooks/identity-service-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/identity-service-relation-changed +++ b/hooks/identity-service-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/identity-service-relation-departed b/hooks/identity-service-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/identity-service-relation-departed +++ b/hooks/identity-service-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/identity-service-relation-joined b/hooks/identity-service-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/identity-service-relation-joined +++ b/hooks/identity-service-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/install b/hooks/install index 64c84897..9858c6be 100755 --- a/hooks/install +++ b/hooks/install @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/leader-elected b/hooks/leader-elected index 64c84897..9858c6be 100755 --- a/hooks/leader-elected +++ b/hooks/leader-elected @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/leader-settings-changed b/hooks/leader-settings-changed index 64c84897..9858c6be 100755 --- a/hooks/leader-settings-changed +++ b/hooks/leader-settings-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/metric-service-relation-broken b/hooks/metric-service-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/metric-service-relation-broken +++ b/hooks/metric-service-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/metric-service-relation-changed b/hooks/metric-service-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/metric-service-relation-changed +++ b/hooks/metric-service-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/metric-service-relation-departed b/hooks/metric-service-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/metric-service-relation-departed +++ b/hooks/metric-service-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/metric-service-relation-joined b/hooks/metric-service-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/metric-service-relation-joined +++ b/hooks/metric-service-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/relations/ceph-client/interface.yaml b/hooks/relations/ceph-client/interface.yaml index 3adb29b8..85788591 100644 --- a/hooks/relations/ceph-client/interface.yaml +++ b/hooks/relations/ceph-client/interface.yaml @@ -1,3 +1,13 @@ name: ceph-client summary: Ceph Client Interface -version: 1 \ No newline at end of file +version: 1 +maintainer: OpenStack Charmers <openstack-discuss@lists.openstack.org> +ignore: + - 'unit_tests' + - 'Makefile' + - '.testr.conf' + - 'test-requirements.txt' + - 'tox.ini' + - '.gitignore' + - '.gitreview' + - '.unit-state.db' \ No newline at end of file diff --git a/hooks/relations/ceph-client/requires.py b/hooks/relations/ceph-client/requires.py index ddffd371..fc9a0562 100644 --- a/hooks/relations/ceph-client/requires.py +++ b/hooks/relations/ceph-client/requires.py @@ -67,13 +67,23 @@ class CephClientRequires(RelationBase): self.remove_state('{relation_name}.connected') self.remove_state('{relation_name}.pools.available') - def create_pool(self, name, replicas=3): + def create_pool(self, name, replicas=3, weight=None, pg_num=None, + group=None, namespace=None): """ Request pool setup - @param name: name of pool to create - @param replicas: number of replicas for supporting pools + @param name: Name of pool to create + @param replicas: Number of replicas for supporting pools + @param weight: The percentage of data the pool makes up + @param pg_num: If not provided, this value will be calculated by the + broker based on how many OSDs are in the cluster at the + time of creation. Note that, if provided, this value + will be capped at the current available maximum. + @param group: Group to add pool to. + @param namespace: A group can optionally have a namespace defined that + will be used to further restrict pool access. """ + # json.dumps of the CephBrokerRq() json_rq = self.get_local(key='broker_req') @@ -81,7 +91,10 @@ class CephClientRequires(RelationBase): rq = CephBrokerRq() rq.add_op_create_pool(name="{}".format(name), replica_count=replicas, - weight=None) + pg_num=pg_num, + weight=weight, + group=group, + namespace=namespace) self.set_local(key='broker_req', value=rq.request) send_request_if_needed(rq, relation=self.relation_name) else: @@ -95,6 +108,35 @@ class CephClientRequires(RelationBase): log("Unable to decode broker_req: {}. Error: {}".format( json_rq, err)) + def request_access_to_group(self, name, namespace=None, permission=None, + key_name=None, object_prefix_permissions=None): + """ + Adds the requested permissions to service's Ceph key + + Adds the requested permissions to the current service's Ceph key, + allowing the key to access only the specified pools or + object prefixes. object_prefix_permissions should be a dictionary + keyed on the permission with the corresponding value being a list + of prefixes to apply that permission to. + { + 'rwx': ['prefix1', 'prefix2'], + 'class-read': ['prefix3']} + @param name: Target group name for permissions request. + @param namespace: namespace to further restrict pool access. + @param permission: Permission to be requested against pool + @param key_name: userid to grant permission to + @param object_prefix_permissions: Add object_prefix permissions. + """ + current_request = self.get_current_request() + current_request.add_op_request_access_to_group( + name, + namespace=namespace, + permission=permission, + key_name=key_name, + object_prefix_permissions=object_prefix_permissions) + self.set_local(key='broker_req', value=current_request.request) + send_request_if_needed(current_request, relation=self.relation_name) + def get_remote_all(self, key, default=None): """Return a list of all values presented by remote units for key""" # TODO: might be a nicer way todo this - written a while back! @@ -113,7 +155,12 @@ class CephClientRequires(RelationBase): """List of all monitor host public addresses""" hosts = [] addrs = self.get_remote_all('ceph-public-address') - for addr in addrs: - hosts.append(format_ipv6_addr(addr) or addr) + for ceph_addrs in addrs: + # NOTE(jamespage): This looks odd but deals with + # use with ceph-proxy which + # presents all monitors in + # a single space delimited field. + for addr in ceph_addrs.split(' '): + hosts.append(format_ipv6_addr(addr) or addr) hosts.sort() return hosts diff --git a/hooks/relations/ceph-client/test-requirements.txt b/hooks/relations/ceph-client/test-requirements.txt deleted file mode 100644 index 1bae0c7d..00000000 --- a/hooks/relations/ceph-client/test-requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -flake8>=2.2.4,<=2.4.1 -os-testr>=0.4.1 \ No newline at end of file diff --git a/hooks/relations/ceph-client/tox.ini b/hooks/relations/ceph-client/tox.ini deleted file mode 100644 index 0facda63..00000000 --- a/hooks/relations/ceph-client/tox.ini +++ /dev/null @@ -1,43 +0,0 @@ -[tox] -envlist = pep8,py27,py34,py35 -skipsdist = True -skip_missing_interpreters = True - -[testenv] -setenv = VIRTUAL_ENV={envdir} - PYTHONHASHSEED=0 -install_command = - pip install --allow-unverified python-apt {opts} {packages} -commands = ostestr {posargs} - -[testenv:py27] -basepython = python2.7 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -# https://github.com/juju/charm-tools/issues/249 -commands = /bin/true - -[testenv:py34] -basepython = python3.4 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -# https://github.com/juju/charm-tools/issues/249 -commands = /bin/true - -[testenv:py35] -basepython = python3.5 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -# https://github.com/juju/charm-tools/issues/249 -commands = /bin/true - -[testenv:pep8] -basepython = python2.7 -deps = -r{toxinidir}/test-requirements.txt -commands = flake8 {posargs} - -[testenv:venv] -commands = {posargs} - -[flake8] -ignore = E402,E226 diff --git a/hooks/relations/gnocchi/tox.ini b/hooks/relations/gnocchi/tox.ini index 0facda63..603dfe25 100644 --- a/hooks/relations/gnocchi/tox.ini +++ b/hooks/relations/gnocchi/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = pep8,py27,py34,py35 +envlist = pep8,py27,py34,py35,py36 skipsdist = True skip_missing_interpreters = True @@ -7,7 +7,7 @@ skip_missing_interpreters = True setenv = VIRTUAL_ENV={envdir} PYTHONHASHSEED=0 install_command = - pip install --allow-unverified python-apt {opts} {packages} + pip install {opts} {packages} commands = ostestr {posargs} [testenv:py27] @@ -32,11 +32,12 @@ deps = -r{toxinidir}/test-requirements.txt commands = /bin/true [testenv:pep8] -basepython = python2.7 +basepython = python3 deps = -r{toxinidir}/test-requirements.txt commands = flake8 {posargs} [testenv:venv] +basepython = python3 commands = {posargs} [flake8] diff --git a/hooks/relations/hacluster/common.py b/hooks/relations/hacluster/common.py index 3d637558..29bae5b4 100644 --- a/hooks/relations/hacluster/common.py +++ b/hooks/relations/hacluster/common.py @@ -11,6 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import hashlib import ipaddress from six import string_types @@ -138,8 +139,8 @@ class CRM(dict): if first: results = results + ' ' first = False - results = results + ('%s %s' % (prefix, d)) - + results = results + ('%s %s ' % (prefix, d)) + results = results.rstrip() return results def clone(self, name, resource, description=None, **kwargs): @@ -274,7 +275,7 @@ class CRM(dict): """ specs = ' '.join(resources) if 'description' in kwargs: - specs = specs + (' description="' % kwargs['description']) + specs = specs + (' description=%s"' % kwargs['description']) for key in 'meta', 'params': if key not in kwargs: @@ -284,8 +285,24 @@ class CRM(dict): self['groups'][name] = specs + def remove_deleted_resources(self): + """Work through the existing resources and remove any mention of ones + which have been marked for deletion.""" + for res in self['delete_resources']: + for key in self.keys(): + if key == 'delete_resources': + continue + if isinstance(self[key], dict) and res in self[key].keys(): + del self[key][res] + elif isinstance(self[key], list) and res in self[key]: + self[key].remove(res) + elif isinstance(self[key], tuple) and res in self[key]: + self[key] = tuple(x for x in self[key] if x != res) + def delete_resource(self, *resources): - """Deletes one or more objects/resources within Pacemaker. + """Specify objects/resources to be deleted from within Pacemaker. This + is not additive, the list of resources is set to exaclty what was + passed in. Parameters ---------- @@ -300,7 +317,34 @@ class CRM(dict): -------- http://crmsh.github.io/man/#cmdhelp_configure_delete """ - self['delete_resources'].extend(resources) + self['delete_resources'] = resources + self.remove_deleted_resources() + + def add_delete_resource(self, resource): + """Specify an object/resource to delete from within Pacemaker. It can + be called multiple times to add additional resources to the deletion + list. + + Parameters + ---------- + resources: str + the name or id of the specific resource to delete. + + Returns + ------- + None + + See Also + -------- + http://crmsh.github.io/man/#cmdhelp_configure_delete + """ + if resource not in self['delete_resources']: + # NOTE(fnordahl): this unpleasant piece of code is regrettably + # necessary for Python3.4 (and trusty) compability see LP: #1814218 + # and LP: #1813982 + self['delete_resources'] = tuple( + self['delete_resources'] or ()) + (resource,) + self.remove_deleted_resources() def init_services(self, *resources): """Specifies that the service(s) is an init or upstart service. @@ -317,7 +361,7 @@ class CRM(dict): ------- None """ - self['init_services'].extend(resources) + self['init_services'] = resources def ms(self, name, resource, description=None, **kwargs): """Create a master/slave resource type. @@ -565,7 +609,12 @@ class VirtualIP(ResourceDescriptor): :param crm: CRM() instance - Config object for Pacemaker resources :returns: None """ - vip_key = 'res_{}_{}_vip'.format(self.service_name, self.nic) + if self.nic: + vip_key = 'res_{}_{}_vip'.format(self.service_name, self.nic) + else: + vip_key = 'res_{}_{}_vip'.format( + self.service_name, + hashlib.sha1(self.vip.encode('UTF-8')).hexdigest()[:7]) ipaddr = ipaddress.ip_address(self.vip) if isinstance(ipaddr, ipaddress.IPv4Address): res_type = 'ocf:heartbeat:IPaddr2' @@ -581,7 +630,9 @@ class VirtualIP(ResourceDescriptor): res_params = '{} nic="{}"'.format(res_params, self.nic) if self.cidr: res_params = '{} cidr_netmask="{}"'.format(res_params, self.cidr) - crm.primitive(vip_key, res_type, params=res_params) + # Monitor the VIP + _op_monitor = 'monitor depth="0" timeout="20s" interval="10s"' + crm.primitive(vip_key, res_type, params=res_params, op=_op_monitor) class DNSEntry(ResourceDescriptor): diff --git a/hooks/relations/hacluster/interface.yaml b/hooks/relations/hacluster/interface.yaml index 833da97f..edd0c902 100644 --- a/hooks/relations/hacluster/interface.yaml +++ b/hooks/relations/hacluster/interface.yaml @@ -3,3 +3,11 @@ summary: | Provides the hacluster interface used for configuring Corosync and Pacemaker services. maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com> +ignore: + - '.gitignore' + - '.gitreview' + - '.testr.conf' + - 'test-requirements' + - 'tox.ini' + - 'unit_tests' + - '.zuul.yaml' diff --git a/hooks/relations/hacluster/requires.py b/hooks/relations/hacluster/requires.py index ce0bf7ad..c3795a69 100644 --- a/hooks/relations/hacluster/requires.py +++ b/hooks/relations/hacluster/requires.py @@ -12,12 +12,14 @@ # limitations under the License. import json +import hashlib import relations.hacluster.common from charms.reactive import hook from charms.reactive import RelationBase from charms.reactive import scopes from charms.reactive.helpers import data_changed +from charmhelpers.core import hookenv class HAClusterRequires(RelationBase): @@ -32,13 +34,38 @@ class HAClusterRequires(RelationBase): @hook('{requires:hacluster}-relation-changed') def changed(self): - self.set_state('{relation_name}.available') + if self.is_clustered(): + self.set_state('{relation_name}.available') + else: + self.remove_state('{relation_name}.available') @hook('{requires:hacluster}-relation-{broken,departed}') def departed(self): self.remove_state('{relation_name}.available') self.remove_state('{relation_name}.connected') + def is_clustered(self): + """Has the hacluster charm set clustered? + + The hacluster charm sets cluster=True when it determines it is ready. + Check the relation data for clustered and force a boolean return. + + :returns: boolean + """ + clustered_values = self.get_remote_all('clustered') + if clustered_values: + # There is only ever one subordinate hacluster unit + clustered = clustered_values[0] + # Future versions of hacluster will return a bool + # Current versions return a string + if type(clustered) is bool: + return clustered + elif (clustered is not None and + (clustered.lower() == 'true' or + clustered.lower() == 'yes')): + return True + return False + def bind_on(self, iface=None, mcastport=None): relation_data = {} if iface: @@ -74,7 +101,7 @@ class HAClusterRequires(RelationBase): self.set_local(**relation_data) self.set_remote(**relation_data) - def bind_resources(self, iface, mcastport=None): + def bind_resources(self, iface=None, mcastport=None): """Inform the ha subordinate about each service it should manage. The child class specifies the services via self.ha_resources @@ -88,7 +115,16 @@ class HAClusterRequires(RelationBase): self.bind_on(iface=iface, mcastport=mcastport) self.manage_resources(resources) - def add_vip(self, name, vip, iface, netmask): + def delete_resource(self, resource_name): + resource_dict = self.get_local('resources') + if resource_dict: + resources = relations.hacluster.common.CRM(**resource_dict) + else: + resources = relations.hacluster.common.CRM() + resources.add_delete_resource(resource_name) + self.set_local(resources=resources) + + def add_vip(self, name, vip, iface=None, netmask=None): """Add a VirtualIP object for each user specified vip to self.resources :param name: string - Name of service @@ -118,10 +154,24 @@ class HAClusterRequires(RelationBase): for vip_res in vip_resources: if 'vip' in vip_res: vip_res_group_members.append(vip_res) - resources.group(group, *vip_res_group_members) + resources.group(group, + *sorted(vip_res_group_members)) self.set_local(resources=resources) + def remove_vip(self, name, vip, iface=None): + """Remove a virtual IP + + :param name: string - Name of service + :param vip: string - Virtual IP + :param iface: string - Network interface vip bound to + """ + if iface: + nic_name = iface + else: + nic_name = hashlib.sha1(vip.encode('UTF-8')).hexdigest()[:7] + self.delete_resource('res_{}_{}_vip'.format(name, nic_name)) + def add_init_service(self, name, service, clone=True): """Add a InitService object for haproxy to self.resources @@ -138,6 +188,17 @@ class HAClusterRequires(RelationBase): relations.hacluster.common.InitService(name, service, clone)) self.set_local(resources=resources) + def remove_init_service(self, name, service): + """Remove an init service + + :param name: string - Name of service + :param service: string - Name of service used in init system + """ + res_key = 'res_{}_{}'.format( + name.replace('-', '_'), + service.replace('-', '_')) + self.delete_resource(res_key) + def add_dnsha(self, name, ip, fqdn, endpoint_type): """Add a DNS entry to self.resources @@ -164,6 +225,32 @@ class HAClusterRequires(RelationBase): for dns_res in dns_resources: if 'hostname' in dns_res: dns_res_group_members.append(dns_res) - resources.group(group, *dns_res_group_members) + resources.group(group, + *sorted(dns_res_group_members)) self.set_local(resources=resources) + + def remove_dnsha(self, name, endpoint_type): + """Remove a DNS entry + + :param name: string - Name of service + :param endpoint_type: string - Public, private, internal etc + :returns: None + """ + res_key = 'res_{}_{}_hostname'.format( + self.service_name.replace('-', '_'), + self.endpoint_type) + self.delete_resource(res_key) + + def get_remote_all(self, key, default=None): + """Return a list of all values presented by remote units for key""" + values = [] + for conversation in self.conversations(): + for relation_id in conversation.relation_ids: + for unit in hookenv.related_units(relation_id): + value = hookenv.relation_get(key, + unit, + relation_id) or default + if value: + values.append(value) + return list(set(values)) diff --git a/hooks/relations/hacluster/test-requirements.txt b/hooks/relations/hacluster/test-requirements.txt index 095ec9c9..4d72d9a8 100644 --- a/hooks/relations/hacluster/test-requirements.txt +++ b/hooks/relations/hacluster/test-requirements.txt @@ -1,2 +1,7 @@ -flake8>=2.2.4,<=2.4.1 +# Lint and unit test requirements +flake8 os-testr>=0.4.1 +charms.reactive +mock>=1.2 +coverage>=3.6 +netifaces diff --git a/hooks/relations/hacluster/tox.ini b/hooks/relations/hacluster/tox.ini deleted file mode 100644 index 8ae4d782..00000000 --- a/hooks/relations/hacluster/tox.ini +++ /dev/null @@ -1,39 +0,0 @@ -[tox] -envlist = pep8,py27 -skipsdist = True - -[testenv] -setenv = VIRTUAL_ENV={envdir} - PYTHONHASHSEED=0 -install_command = - pip install --allow-unverified python-apt {opts} {packages} -commands = ostestr {posargs} - -[testenv:py27] -basepython = python2.7 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -commands = /bin/true - -[testenv:py34] -basepython = python3.4 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -commands = /bin/true - -[testenv:py35] -basepython = python3.5 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -commands = /bin/true - -[testenv:pep8] -basepython = python2.7 -deps = -r{toxinidir}/test-requirements.txt -commands = flake8 {posargs} - -[testenv:venv] -commands = {posargs} - -[flake8] -ignore = E402,E226 diff --git a/hooks/relations/keystone/interface.yaml b/hooks/relations/keystone/interface.yaml index f01f858d..23a355d1 100644 --- a/hooks/relations/keystone/interface.yaml +++ b/hooks/relations/keystone/interface.yaml @@ -1,3 +1,12 @@ name: keystone summary: Interface for integrating with Keystone identity service -maintainer: OpenStack Charmers <openstack-dev@lists.openstack.org> +maintainer: OpenStack Charmers <openstack-discuss@lists.openstack.org> +ignore: + - 'unit_tests' + - 'Makefile' + - '.testr.conf' + - 'test-requirements.txt' + - 'tox.ini' + - '.gitignore' + - '.gitreview' + - '.unit-state.db' diff --git a/hooks/relations/keystone/requires.py b/hooks/relations/keystone/requires.py index 67906520..876e075e 100644 --- a/hooks/relations/keystone/requires.py +++ b/hooks/relations/keystone/requires.py @@ -31,7 +31,7 @@ class KeystoneRequires(RelationBase): 'ca_cert', 'ssl_cert', 'https_keystone', 'ssl_cert_admin', 'ssl_cert_internal', 'ssl_cert_public', 'ssl_key_admin', 'ssl_key_internal', - 'ssl_key_public', 'api_version'] + 'ssl_key_public', 'api_version', 'service_domain'] @hook('{requires:keystone}-relation-joined') def joined(self): @@ -119,7 +119,7 @@ class KeystoneRequires(RelationBase): return True def register_endpoints(self, service, region, public_url, internal_url, - admin_url): + admin_url, requested_roles=None): """ Register this service with keystone """ @@ -130,6 +130,9 @@ class KeystoneRequires(RelationBase): 'admin_url': admin_url, 'region': region, } + if requested_roles: + relation_info.update( + {'requested_roles': ','.join(requested_roles)}) self.set_local(**relation_info) self.set_remote(**relation_info) diff --git a/hooks/relations/keystone/test-requirements.txt b/hooks/relations/keystone/test-requirements.txt deleted file mode 100644 index 095ec9c9..00000000 --- a/hooks/relations/keystone/test-requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -flake8>=2.2.4,<=2.4.1 -os-testr>=0.4.1 diff --git a/hooks/relations/keystone/tox.ini b/hooks/relations/keystone/tox.ini deleted file mode 100644 index c3951382..00000000 --- a/hooks/relations/keystone/tox.ini +++ /dev/null @@ -1,40 +0,0 @@ -[tox] -envlist = pep8,py27,py34,py35 -skipsdist = True -skip_missing_interpreters = True - -[testenv] -setenv = VIRTUAL_ENV={envdir} - PYTHONHASHSEED=0 -install_command = - pip install --allow-unverified python-apt {opts} {packages} -commands = ostestr {posargs} - -[testenv:py27] -basepython = python2.7 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -commands = /bin/true - -[testenv:py34] -basepython = python3.4 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -commands = /bin/true - -[testenv:py35] -basepython = python3.5 -deps = -r{toxinidir}/test-requirements.txt -# TODO: Need to write unit tests then remove the following command. -commands = /bin/true - -[testenv:pep8] -basepython = python2.7 -deps = -r{toxinidir}/test-requirements.txt -commands = flake8 {posargs} - -[testenv:venv] -commands = {posargs} - -[flake8] -ignore = E402,E226 diff --git a/hooks/relations/mysql-shared/.gitreview b/hooks/relations/mysql-shared/.gitreview index 057f8613..4c583ab4 100644 --- a/hooks/relations/mysql-shared/.gitreview +++ b/hooks/relations/mysql-shared/.gitreview @@ -1,4 +1,4 @@ [gerrit] -host=review.openstack.org +host=review.opendev.org port=29418 project=openstack/charm-interface-mysql-shared diff --git a/hooks/relations/mysql-shared/requires.py b/hooks/relations/mysql-shared/requires.py index 0b2ea8c9..8c61b1e7 100644 --- a/hooks/relations/mysql-shared/requires.py +++ b/hooks/relations/mysql-shared/requires.py @@ -27,10 +27,18 @@ class MySQLSharedRequires(RelationBase): @hook('{requires:mysql-shared}-relation-{broken,departed}') def departed(self): + # Clear state self.remove_state('{relation_name}.connected') self.remove_state('{relation_name}.available') self.remove_state('{relation_name}.available.access_network') self.remove_state('{relation_name}.available.ssl') + # Check if this is the last unit + for conversation in self.conversations(): + for rel_id in conversation.relation_ids: + if len(hookenv.related_units(rel_id)) > 0: + # This is not the last unit so reevaluate state + self.joined() + self.changed() def configure(self, database, username, hostname=None, prefix=None): """ diff --git a/hooks/relations/mysql-shared/tox.ini b/hooks/relations/mysql-shared/tox.ini index c3951382..f877d2cd 100644 --- a/hooks/relations/mysql-shared/tox.ini +++ b/hooks/relations/mysql-shared/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = pep8,py27,py34,py35 +envlist = pep8,py27,py34,py35,py36 skipsdist = True skip_missing_interpreters = True @@ -7,7 +7,7 @@ skip_missing_interpreters = True setenv = VIRTUAL_ENV={envdir} PYTHONHASHSEED=0 install_command = - pip install --allow-unverified python-apt {opts} {packages} + pip install {opts} {packages} commands = ostestr {posargs} [testenv:py27] @@ -29,11 +29,12 @@ deps = -r{toxinidir}/test-requirements.txt commands = /bin/true [testenv:pep8] -basepython = python2.7 +basepython = python3 deps = -r{toxinidir}/test-requirements.txt commands = flake8 {posargs} [testenv:venv] +basepython = python3 commands = {posargs} [flake8] diff --git a/hooks/relations/openstack-ha/.gitreview b/hooks/relations/openstack-ha/.gitreview index 687b9fcb..c168b71f 100644 --- a/hooks/relations/openstack-ha/.gitreview +++ b/hooks/relations/openstack-ha/.gitreview @@ -1,4 +1,4 @@ [gerrit] -host=review.openstack.org +host=review.opendev.org port=29418 project=openstack/charm-interface-openstack-ha.git diff --git a/hooks/relations/openstack-ha/tox.ini b/hooks/relations/openstack-ha/tox.ini index c3951382..da2a1f12 100644 --- a/hooks/relations/openstack-ha/tox.ini +++ b/hooks/relations/openstack-ha/tox.ini @@ -7,7 +7,7 @@ skip_missing_interpreters = True setenv = VIRTUAL_ENV={envdir} PYTHONHASHSEED=0 install_command = - pip install --allow-unverified python-apt {opts} {packages} + pip install {opts} {packages} commands = ostestr {posargs} [testenv:py27] @@ -29,11 +29,12 @@ deps = -r{toxinidir}/test-requirements.txt commands = /bin/true [testenv:pep8] -basepython = python2.7 +basepython = python3 deps = -r{toxinidir}/test-requirements.txt commands = flake8 {posargs} [testenv:venv] +basepython = python3 commands = {posargs} [flake8] diff --git a/hooks/relations/rabbitmq/.gitreview b/hooks/relations/rabbitmq/.gitreview index 6d7ea5fc..487a7c09 100644 --- a/hooks/relations/rabbitmq/.gitreview +++ b/hooks/relations/rabbitmq/.gitreview @@ -1,4 +1,4 @@ [gerrit] -host=review.openstack.org +host=review.opendev.org port=29418 project=openstack/charm-interface-rabbitmq.git diff --git a/hooks/relations/rabbitmq/tox.ini b/hooks/relations/rabbitmq/tox.ini index a1247d16..c0f14f6d 100644 --- a/hooks/relations/rabbitmq/tox.ini +++ b/hooks/relations/rabbitmq/tox.ini @@ -6,7 +6,7 @@ skipsdist = True setenv = VIRTUAL_ENV={envdir} PYTHONHASHSEED=0 install_command = - pip install --allow-unverified python-apt {opts} {packages} + pip install {opts} {packages} commands = ostestr {posargs} [testenv:py27] @@ -22,11 +22,12 @@ deps = -r{toxinidir}/test-requirements.txt commands = /bin/true [testenv:pep8] -basepython = python2.7 +basepython = python3 deps = -r{toxinidir}/test-requirements.txt commands = flake8 {posargs} [testenv:venv] +basepython = python3 commands = {posargs} [flake8] diff --git a/hooks/shared-db-relation-broken b/hooks/shared-db-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/shared-db-relation-broken +++ b/hooks/shared-db-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/shared-db-relation-changed b/hooks/shared-db-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/shared-db-relation-changed +++ b/hooks/shared-db-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/shared-db-relation-departed b/hooks/shared-db-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/shared-db-relation-departed +++ b/hooks/shared-db-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/shared-db-relation-joined b/hooks/shared-db-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/shared-db-relation-joined +++ b/hooks/shared-db-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/start b/hooks/start index 64c84897..9858c6be 100755 --- a/hooks/start +++ b/hooks/start @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/stop b/hooks/stop index 64c84897..9858c6be 100755 --- a/hooks/stop +++ b/hooks/stop @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/storage-ceph-relation-broken b/hooks/storage-ceph-relation-broken index 64c84897..9858c6be 100755 --- a/hooks/storage-ceph-relation-broken +++ b/hooks/storage-ceph-relation-broken @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/storage-ceph-relation-changed b/hooks/storage-ceph-relation-changed index 64c84897..9858c6be 100755 --- a/hooks/storage-ceph-relation-changed +++ b/hooks/storage-ceph-relation-changed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/storage-ceph-relation-departed b/hooks/storage-ceph-relation-departed index 64c84897..9858c6be 100755 --- a/hooks/storage-ceph-relation-departed +++ b/hooks/storage-ceph-relation-departed @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/storage-ceph-relation-joined b/hooks/storage-ceph-relation-joined index 64c84897..9858c6be 100755 --- a/hooks/storage-ceph-relation-joined +++ b/hooks/storage-ceph-relation-joined @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/update-status b/hooks/update-status index 64c84897..9858c6be 100755 --- a/hooks/update-status +++ b/hooks/update-status @@ -4,9 +4,12 @@ import sys sys.path.append('lib') -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -15,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/hooks/upgrade-charm b/hooks/upgrade-charm index 85817d8f..9858c6be 100755 --- a/hooks/upgrade-charm +++ b/hooks/upgrade-charm @@ -1,21 +1,15 @@ #!/usr/bin/env python3 # Load modules from $JUJU_CHARM_DIR/lib -import os import sys sys.path.append('lib') -# This is an upgrade-charm context, make sure we install latest deps -if not os.path.exists('wheelhouse/.upgrade'): - open('wheelhouse/.upgrade', 'w').close() - if os.path.exists('wheelhouse/.bootstrapped'): - os.unlink('wheelhouse/.bootstrapped') -else: - os.unlink('wheelhouse/.upgrade') - -from charms.layer import basic +from charms.layer import basic # noqa basic.bootstrap_charm_deps() -basic.init_config_states() + +from charmhelpers.core import hookenv # noqa +hookenv.atstart(basic.init_config_states) +hookenv.atexit(basic.clear_config_states) # This will load and run the appropriate @hook and other decorated @@ -24,5 +18,5 @@ basic.init_config_states() # # See https://jujucharms.com/docs/stable/authors-charm-building # for more information on this pattern. -from charms.reactive import main +from charms.reactive import main # noqa main() diff --git a/layer.yaml b/layer.yaml index 2e2c3e2a..d265da56 100644 --- a/layer.yaml +++ b/layer.yaml @@ -1,24 +1,36 @@ -"options": - "basic": - "use_venv": !!bool "true" - "include_system_packages": !!bool "true" - "packages": [] - "openstack-principle": {} - "openstack": {} - "openstack-api": {} - "gnocchi": {} "includes": +- "layer:options" - "layer:basic" - "layer:openstack" +- "layer:debug" +- "interface:tls-certificates" - "layer:openstack-principle" - "interface:mysql-shared" - "interface:rabbitmq" - "interface:keystone" - "interface:hacluster" - "interface:openstack-ha" +- "layer:tls-client" - "layer:openstack-api" - "interface:ceph-client" - "interface:gnocchi" - "interface:memcache" +"options": + "basic": + "use_venv": !!bool "true" + "include_system_packages": !!bool "true" + "packages": [] + "python_packages": [] + "tls-client": + "ca_certificate_path": "" + "client_certificate_path": "" + "client_key_path": "" + "server_key_path": "" + "server_certificate_path": "" + "openstack-principle": {} + "openstack-api": {} + "debug": {} + "openstack": {} + "gnocchi": {} "repo": "https://github.com/openstack/charm-gnocchi" "is": "gnocchi" diff --git a/lib/charm/openstack/gnocchi.py b/lib/charm/openstack/gnocchi.py index 2ca377b3..be1d34e2 100644 --- a/lib/charm/openstack/gnocchi.py +++ b/lib/charm/openstack/gnocchi.py @@ -12,10 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import errno import os -import pwd -import grp import collections import subprocess @@ -26,7 +23,6 @@ import charmhelpers.core.host as host import charms_openstack.charm import charms_openstack.adapters as adapters import charms_openstack.ip as os_ip -import charms_openstack.plugins GNOCCHI_DIR = '/etc/gnocchi' @@ -65,18 +61,10 @@ charms_openstack.charm.use_defaults('charm.default-select-release') @charms_openstack.adapters.config_property def log_config(config): if ch_utils.snap_install_requested(): - log_config_file = os.path.join(SNAP_PREFIX, - 'log/gnocchi-api.log') + return os.path.join(SNAP_PREFIX, + 'log/gnocchi-api.log') else: - log_config_file = '/var/log/gnocchi/gnocchi-api.log' - usr = GnocchiCharm.user - gru = GnocchiCharm.group - uid = pwd.getpwnam(usr).pw_uid - gid = grp.getgrnam(gru).gr_gid - # touch file - open(log_config_file, 'a').close() - os.chown(log_config_file, uid, gid) - return log_config_file + return '/var/log/gnocchi/gnocchi-api.log' @charms_openstack.adapters.config_property @@ -87,37 +75,6 @@ def ceph_config(config): return CEPH_CONF -@charms_openstack.adapters.config_property -def file_pathbase(config): - file_pathbasedir = '/var/lib/gnocchi/measures' - # create path if it does not exist - try: - os.makedirs(file_pathbasedir) - except OSError as exc: - if exc.errno == errno.EEXIST and os.path.isdir(file_pathbasedir): - pass - else: - raise - usr = GnocchiCharm.user - gru = GnocchiCharm.group - uid = pwd.getpwnam(usr).pw_uid - gid = grp.getgrnam(gru).gr_gid - os.chown(file_pathbasedir, uid, gid) - # mount tmpfs unless already mounted - do_mount = 1 - cmd_out = subprocess.check_output(['cat', '/proc/mounts']).decode('utf-8') - lines = cmd_out.split('\n') - for line in lines: - if file_pathbasedir in line: - do_mount = 0 - break - tmpfs_size = 'size=128m' - if do_mount == 1: - subprocess.check_call(['mount', '-t', 'tmpfs', '-o', tmpfs_size, - 'tmpfs', file_pathbasedir]) - return file_pathbasedir - - # TODO(jamespage): charms.openstack class StorageCephRelationAdapter(adapters.OpenStackRelationAdapter): @@ -147,15 +104,14 @@ class GnocchiCharmRelationAdapaters(adapters.OpenStackAPIRelationAdapters): """ relation_adapters = { - 'storage_ceph': charms_openstack.plugins.CephRelationAdapter, + 'storage_ceph': StorageCephRelationAdapter, 'shared_db': adapters.DatabaseRelationAdapter, 'cluster': adapters.PeerHARelationAdapter, 'coordinator_memcached': adapters.MemcacheRelationAdapter, } -class GnochiCharmBase(charms_openstack.charm.HAOpenStackCharm, - charms_openstack.plugins.BaseOpenStackCephCharm): +class GnochiCharmBase(charms_openstack.charm.HAOpenStackCharm): """ Base class for shared charm functions for all package types @@ -273,7 +229,6 @@ class GnocchiCharm(GnochiCharmBase): } sync_cmd = ['gnocchi-upgrade', - '--config-file=' + GNOCCHI_CONF, '--log-file=/var/log/gnocchi/gnocchi-upgrade.log'] # User and group for permissions management @@ -351,7 +306,6 @@ class GnocchiSnapCharm(GnochiCharmBase): sync_cmd = [ '/snap/bin/gnocchi.upgrade', - '--config-file=' + GNOCCHI_CONF, '--log-file=/var/snap/gnocchi/common/log/gnocchi-upgrade.log' ] diff --git a/lib/charms/layer/__init__.py b/lib/charms/layer/__init__.py index 9d1048d0..a8e0c640 100644 --- a/lib/charms/layer/__init__.py +++ b/lib/charms/layer/__init__.py @@ -1,21 +1,60 @@ -import os +import sys +from importlib import import_module +from pathlib import Path -class LayerOptions(dict): - def __init__(self, layer_file, section=None): - import yaml # defer, might not be available until bootstrap - with open(layer_file) as f: - layer = yaml.safe_load(f.read()) - opts = layer.get('options', {}) - if section and section in opts: - super(LayerOptions, self).__init__(opts.get(section)) +def import_layer_libs(): + """ + Ensure that all layer libraries are imported. + + This makes it possible to do the following: + + from charms import layer + + layer.foo.do_foo_thing() + + Note: This function must be called after bootstrap. + """ + for module_file in Path('lib/charms/layer').glob('*'): + module_name = module_file.stem + if module_name in ('__init__', 'basic', 'execd') or not ( + module_file.suffix == '.py' or module_file.is_dir() + ): + continue + import_module('charms.layer.{}'.format(module_name)) + + +# Terrible hack to support the old terrible interface. +# Try to get people to call layer.options.get() instead so +# that we can remove this garbage. +# Cribbed from https://stackoverfLow.com/a/48100440/4941864 +class OptionsBackwardsCompatibilityHack(sys.modules[__name__].__class__): + def __call__(self, section=None, layer_file=None): + if layer_file is None: + return self.get(section=section) else: - super(LayerOptions, self).__init__(opts) + return self.get(section=section, + layer_file=Path(layer_file)) + +def patch_options_interface(): + from charms.layer import options + if sys.version_info.minor >= 5: + options.__class__ = OptionsBackwardsCompatibilityHack + else: + # Py 3.4 doesn't support changing the __class__, so we have to do it + # another way. The last line is needed because we already have a + # reference that doesn't get updated with sys.modules. + name = options.__name__ + hack = OptionsBackwardsCompatibilityHack(name) + hack.get = options.get + sys.modules[name] = hack + sys.modules[__name__].options = hack -def options(section=None, layer_file=None): - if not layer_file: - base_dir = os.environ.get('JUJU_CHARM_DIR', os.getcwd()) - layer_file = os.path.join(base_dir, 'layer.yaml') - return LayerOptions(layer_file, section) +try: + patch_options_interface() +except ImportError: + # This may fail if pyyaml hasn't been installed yet. But in that + # case, the bootstrap logic will try it again once it has. + pass diff --git a/lib/charms/layer/basic.py b/lib/charms/layer/basic.py index f1ec007b..1a6ea9fc 100644 --- a/lib/charms/layer/basic.py +++ b/lib/charms/layer/basic.py @@ -5,6 +5,7 @@ from glob import glob from subprocess import check_call, CalledProcessError from time import sleep +from charms import layer from charms.layer.execd import execd_preinstall @@ -35,9 +36,28 @@ def bootstrap_charm_deps(): vbin = os.path.join(venv, 'bin') vpip = os.path.join(vbin, 'pip') vpy = os.path.join(vbin, 'python') - if os.path.exists('wheelhouse/.bootstrapped'): + hook_name = os.path.basename(sys.argv[0]) + is_bootstrapped = os.path.exists('wheelhouse/.bootstrapped') + is_charm_upgrade = hook_name == 'upgrade-charm' + is_series_upgrade = hook_name == 'post-series-upgrade' + post_upgrade = os.path.exists('wheelhouse/.upgrade') + is_upgrade = not post_upgrade and (is_charm_upgrade or is_series_upgrade) + if is_bootstrapped and not is_upgrade: activate_venv() + # the .upgrade file prevents us from getting stuck in a loop + # when re-execing to activate the venv; at this point, we've + # activated the venv, so it's safe to clear it + if post_upgrade: + os.unlink('wheelhouse/.upgrade') return + if is_series_upgrade and os.path.exists(venv): + # series upgrade should do a full clear of the venv, rather than just + # updating it, to bring in updates to Python itself + shutil.rmtree(venv) + if is_upgrade: + if os.path.exists('wheelhouse/.bootstrapped'): + os.unlink('wheelhouse/.bootstrapped') + open('wheelhouse/.upgrade', 'w').close() # bootstrap wheelhouse if os.path.exists('wheelhouse'): with open('/root/.pydistutils.cfg', 'w') as fp: @@ -53,9 +73,11 @@ def bootstrap_charm_deps(): 'python3-setuptools', 'python3-yaml', 'python3-dev', + 'python3-wheel', + 'build-essential', ]) - from charms import layer - cfg = layer.options('basic') + from charms.layer import options + cfg = options.get('basic') # include packages defined in layer.yaml apt_install(cfg.get('packages', [])) # if we're using a venv, set it up @@ -82,16 +104,41 @@ def bootstrap_charm_deps(): # https://github.com/pypa/pip/issues/56 check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip']) + # per https://github.com/juju-solutions/layer-basic/issues/110 + # this replaces the setuptools that was copied over from the system on + # venv create with latest setuptools and adds setuptools_scm + check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', + 'setuptools', 'setuptools-scm']) # install the rest of the wheelhouse deps check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + glob('wheelhouse/*')) + # re-enable installation from pypi + os.remove('/root/.pydistutils.cfg') + # install python packages from layer options + if cfg.get('python_packages'): + check_call([pip, 'install', '-U'] + cfg.get('python_packages')) if not cfg.get('use_venv'): # restore system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip.save'): shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') os.remove('/usr/bin/pip.save') - os.remove('/root/.pydistutils.cfg') + # setup wrappers to ensure envs are used for scripts + shutil.copy2('bin/charm-env', '/usr/local/sbin/') + for wrapper in ('charms.reactive', 'charms.reactive.sh', + 'chlp', 'layer_option'): + src = os.path.join('/usr/local/sbin', 'charm-env') + dst = os.path.join('/usr/local/sbin', wrapper) + if not os.path.exists(dst): + os.symlink(src, dst) + if cfg.get('use_venv'): + shutil.copy2('bin/layer_option', vbin) + else: + shutil.copy2('bin/layer_option', '/usr/local/bin/') + # re-link the charm copy to the wrapper in case charms + # call bin/layer_option directly (as was the old pattern) + os.remove('bin/layer_option') + os.symlink('/usr/local/sbin/layer_option', 'bin/layer_option') # flag us as having already bootstrapped so we don't do it again open('wheelhouse/.bootstrapped', 'w').close() # Ensure that the newly bootstrapped libs are available. @@ -118,15 +165,17 @@ def activate_venv(): This will ensure that modules installed in the charm's virtual environment are available to the action. """ + from charms.layer import options venv = os.path.abspath('../.venv') vbin = os.path.join(venv, 'bin') vpy = os.path.join(vbin, 'python') - from charms import layer - cfg = layer.options('basic') - if cfg.get('use_venv') and '.venv' not in sys.executable: + use_venv = options.get('basic', 'use_venv') + if use_venv and '.venv' not in sys.executable: # activate the venv os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) reload_interpreter(vpy) + layer.patch_options_interface() + layer.import_layer_libs() def reload_interpreter(python): @@ -164,6 +213,12 @@ def apt_install(packages): except CalledProcessError: if attempt == 2: # third attempt raise + try: + # sometimes apt-get update needs to be run + check_call(['apt-get', 'update']) + except CalledProcessError: + # sometimes it's a dpkg lock issue + pass sleep(5) else: break @@ -190,7 +245,6 @@ def init_config_states(): toggle_state('config.set.{}'.format(opt), config.get(opt)) toggle_state('config.default.{}'.format(opt), config.get(opt) == config_defaults[opt]) - hookenv.atexit(clear_config_states) def clear_config_states(): diff --git a/metadata.yaml b/metadata.yaml index 243ff372..81c89b27 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -9,12 +9,15 @@ metrics and resources information and history. "tags": - "openstack" +- "misc" "series": - "xenial" - "bionic" -- "artful" -- "trusty" +- "cosmic" +- "disco" "requires": + "certificates": + "interface": "tls-certificates" "shared-db": "interface": "mysql-shared" "amqp": diff --git a/reactive/gnocchi_handlers.py b/reactive/gnocchi_handlers.py index 2c9d9773..d118d114 100644 --- a/reactive/gnocchi_handlers.py +++ b/reactive/gnocchi_handlers.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os + import charms_openstack.charm as charm import charms.reactive as reactive import charm.openstack.gnocchi as gnocchi # noqa +import charmhelpers.contrib.storage.linux.ceph as ceph_helper import charmhelpers.core.hookenv as hookenv +import charmhelpers.core.host as host charm.use_defaults( 'charm.installed', @@ -81,14 +85,22 @@ def storage_ceph_connected(ceph): @reactive.when('storage-ceph.available') def configure_ceph(ceph): - with charm.provide_charm_instance() as charm_instance: - charm_instance.configure_ceph_keyring(ceph.key()) + with charm.provide_charm_instance() as charm_class: + # TODO(jamespage): refactor to avoid massaging helper + ceph_helper.KEYRING = charm_class.ceph_keyring + host.mkdir(os.path.dirname(charm_class.ceph_keyring)) + ceph_helper.ensure_ceph_keyring(service=hookenv.service_name(), + key=ceph.key(), + user=charm_class.gnocchi_user, + group=charm_class.gnocchi_group) @reactive.when_not('storage-ceph.connected') def storage_ceph_disconnected(): - with charm.provide_charm_instance() as charm_instance: - charm_instance.delete_ceph_keyring() + with charm.provide_charm_instance() as charm_class: + # TODO(jamespage): refactor to avoid massaging helper + ceph_helper.KEYRING = charm_class.ceph_keyring + ceph_helper.delete_keyring(hookenv.service_name()) @reactive.when('metric-service.connected') diff --git a/reactive/layer_openstack.py b/reactive/layer_openstack.py index 65337bb2..21f02206 100644 --- a/reactive/layer_openstack.py +++ b/reactive/layer_openstack.py @@ -13,7 +13,7 @@ def default_install(): The instance automagically becomes the derived OpenStackCharm instance. The kv() key charmers.openstack-release-version' is used to cache the release being used for this charm. It is determined by the - default_select_release() function below, unless this is overriden by + default_select_release() function below, unless this is overridden by the charm author """ unitdata.kv().unset(defaults.OPENSTACK_RELEASE_KEY) @@ -64,3 +64,28 @@ def run_default_update_status(): with charm.provide_charm_instance() as instance: instance.assess_status() reactive.remove_state('run-default-update-status') + + +@reactive.when('storage-backend.connected', + 'charms.openstack.do-default-storage-backend.connected') +def run_storage_backend(): + with charm.provide_charm_instance() as instance: + instance.send_storage_backend_data() + + +# Series upgrade hooks are a special case and reacting to the hook directly +# makes sense as we may not want other charm code to run +@reactive.hook('pre-series-upgrade') +def default_pre_series_upgrade(): + """Default handler for pre-series-upgrade. + """ + with charm.provide_charm_instance() as instance: + instance.series_upgrade_prepare() + + +@reactive.hook('post-series-upgrade') +def default_post_series_upgrade(): + """Default handler for post-series-upgrade. + """ + with charm.provide_charm_instance() as instance: + instance.series_upgrade_complete() diff --git a/reactive/layer_openstack_api.py b/reactive/layer_openstack_api.py index 8111bb9c..13d1247e 100644 --- a/reactive/layer_openstack_api.py +++ b/reactive/layer_openstack_api.py @@ -62,3 +62,25 @@ def default_setup_endpoint_available(keystone): with charm.provide_charm_instance() as instance: instance.configure_ssl(keystone) instance.assess_status() + + +@reactive.when('certificates.available') +def default_setup_certificates(tls): + """When the identity-service interface is available, this default + handler switches on the SSL support. + """ + with charm.provide_charm_instance() as instance: + for cn, req in instance.get_certificate_requests().items(): + tls.add_request_server_cert(cn, req['sans']) + tls.request_server_certs() + instance.assess_status() + + +@reactive.when('certificates.batch.cert.available') +def default_setup_endpoint_available(tls): + """When the identity-service interface is available, this default + handler switches on the SSL support. + """ + with charm.provide_charm_instance() as instance: + instance.configure_ssl(tls) + instance.assess_status() diff --git a/rebuild b/rebuild deleted file mode 100644 index 551ab478..00000000 --- a/rebuild +++ /dev/null @@ -1,5 +0,0 @@ -# This file is used to trigger rebuilds -# when dependencies of the charm change, -# but nothing in the charm needs to. -# simply change the uuid to something new -120650ec-5aab-11e9-a87e-fbc92e9be59b diff --git a/repo-info b/repo-info index 11c2b433..69367159 100644 --- a/repo-info +++ b/repo-info @@ -1,6 +1,6 @@ -commit-sha-1: 1eb98df671de730c199ef42b432d5229ca33a0c1 -commit-short: 1eb98df +commit-sha-1: 6d429c05f327d3ef46358d573a105fc022f23a56 +commit-short: 6d429c0 branch: HEAD remote: https://github.com/openstack/charm-gnocchi -info-generated: Fri Feb 9 14:49:01 UTC 2018 +info-generated: Tue May 7 12:28:07 UTC 2019 note: This file should exist only in a built or released charm artifact (not in the charm source code tree). diff --git a/requirements.txt b/requirements.txt index 20f335d2..56253896 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,5 @@ -# 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. -# -# Build requirements -charm-tools>=2.4.4 +# Requirements to build the layer +charm-tools +ruamel.yaml==0.10.12 simplejson +flake8 diff --git a/run-func-tests.sh b/run-func-tests.sh deleted file mode 100755 index da261380..00000000 --- a/run-func-tests.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -x -set -e - -cleanup(){ - type -t indexer_stop >/dev/null && indexer_stop || true - type -t storage_stop >/dev/null && storage_stop || true -} -trap cleanup EXIT - -check_empty_var() { - local x=$(eval echo `echo \\$${1}`) - if [ -z "$x" ]; then - echo "Variable \$${1} is unset" - exit 15 - fi -} - -PYTHON_VERSION_MAJOR=$(python -c 'import sys; print(sys.version_info.major)') - -GNOCCHI_TEST_STORAGE_DRIVERS=${GNOCCHI_TEST_STORAGE_DRIVERS:-file} -GNOCCHI_TEST_INDEXER_DRIVERS=${GNOCCHI_TEST_INDEXER_DRIVERS:-postgresql} -for storage in ${GNOCCHI_TEST_STORAGE_DRIVERS}; do - if [ "$storage" == "swift" ] && [ "$PYTHON_VERSION_MAJOR" == "3" ]; then - echo "WARNING: swift does not support python 3 skipping" - continue - fi - for indexer in ${GNOCCHI_TEST_INDEXER_DRIVERS}; do - unset STORAGE_URL - unset INDEXER_URL - case $storage in - ceph) - eval $(pifpaf -e STORAGE run ceph) - check_empty_var STORAGE_URL - rados -c $STORAGE_CEPH_CONF mkpool gnocchi - STORAGE_URL=ceph://$STORAGE_CEPH_CONF - ;; - s3) - if ! which s3rver >/dev/null 2>&1 - then - mkdir -p npm-s3rver - export NPM_CONFIG_PREFIX=npm-s3rver - npm install s3rver --global - export PATH=$PWD/npm-s3rver/bin:$PATH - fi - eval $(pifpaf -e STORAGE run s3rver) - ;; - file) - STORAGE_URL=file:// - ;; - - swift|redis) - eval $(pifpaf -e STORAGE run $storage) - ;; - *) - echo "Unsupported storage backend by functional tests: $storage" - exit 1 - ;; - esac - - check_empty_var STORAGE_URL - - eval $(pifpaf -e INDEXER run $indexer) - check_empty_var INDEXER_URL - - export GNOCCHI_SERVICE_TOKEN="" # Just make gabbi happy - export GNOCCHI_AUTHORIZATION="basic YWRtaW46" # admin in base64 - export GNOCCHI_TEST_PATH=gnocchi/tests/functional_live - pifpaf -e GNOCCHI run gnocchi --indexer-url $INDEXER_URL --storage-url $STORAGE_URL --coordination-driver redis -- ./tools/pretty_tox.sh $* - - cleanup - done -done diff --git a/templates/gnocchi.conf b/templates/gnocchi.conf index 66f94fd8..1734545c 100644 --- a/templates/gnocchi.conf +++ b/templates/gnocchi.conf @@ -41,3 +41,5 @@ ceph_conffile = {{ options.ceph_config }} {%- endif %} {% include "parts/section-keystone-authtoken" %} + +{% include "parts/section-oslo-middleware" %} diff --git a/templates/parts/section-database b/templates/parts/section-database index 7046295d..a4fa7e71 100644 --- a/templates/parts/section-database +++ b/templates/parts/section-database @@ -1,5 +1,5 @@ {% if shared_db.host -%} [database] -connection = {{ shared_db.type }}://{{ shared_db.username }}:{{ shared_db.password }}@{{ shared_db.host }}/{{ shared_db.database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} +connection = {{ shared_db.uri }} {% endif -%} diff --git a/templates/parts/section-keystone-authtoken b/templates/parts/section-keystone-authtoken index 40354c62..dffe478c 100644 --- a/templates/parts/section-keystone-authtoken +++ b/templates/parts/section-keystone-authtoken @@ -3,8 +3,13 @@ auth_uri = {{ identity_service.service_protocol }}://{{ identity_service.service_host }}:{{ identity_service.service_port }} auth_url = {{ identity_service.auth_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }} auth_type = password +{% if identity_service.service_domain -%} +project_domain_name = {{ identity_service.service_domain }} +user_domain_name = {{ identity_service.service_domain }} +{% else %} project_domain_name = default user_domain_name = default +{% endif -%} project_name = services username = {{ identity_service.service_username }} password = {{ identity_service.service_password }} diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 6dcd0881..1e876524 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -36,7 +36,7 @@ class GnocchiCharmDeployment(amulet_deployment.OpenStackAmuletDeployment): no_origin = ['memcached', 'percona-cluster', 'rabbitmq-server', 'ceph-mon', 'ceph-osd'] - def __init__(self, series, openstack=None, source=None, stable=False, + def __init__(self, series, openstack=None, source=None, stable=True, snap_source=None): """Deploy the entire test environment.""" super(GnocchiCharmDeployment, self).__init__(series, openstack, diff --git a/tox.ini b/tox.ini index 7e54929b..0e36e84b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,114 +1,53 @@ +# Source charm: ./src/tox.ini +# This file is managed centrally by release-tools and should not be modified +# within individual charm repos. [tox] -minversion = 2.4 -envlist = py{37,27}-{postgresql,mysql}{,-file,-swift,-ceph,-s3},pep8 +envlist = pep8 skipsdist = True [testenv] -skip_install = True -sitepackages = False -passenv = LANG GNOCCHI_TEST_* AWS_* -setenv = - GNOCCHI_TEST_STORAGE_DRIVER=file - GNOCCHI_TEST_INDEXER_DRIVER=postgresql - GNOCCHI_TEST_STORAGE_DRIVERS=file swift ceph s3 redis - GNOCCHI_TEST_INDEXER_DRIVERS=postgresql mysql - file: GNOCCHI_TEST_STORAGE_DRIVERS=file - swift: GNOCCHI_TEST_STORAGE_DRIVERS=swift - ceph: GNOCCHI_TEST_STORAGE_DRIVERS=ceph - redis: GNOCCHI_TEST_STORAGE_DRIVERS=redis - s3: GNOCCHI_TEST_STORAGE_DRIVERS=s3 - postgresql: GNOCCHI_TEST_INDEXER_DRIVERS=postgresql - mysql: GNOCCHI_TEST_INDEXER_DRIVERS=mysql +setenv = VIRTUAL_ENV={envdir} + PYTHONHASHSEED=0 + AMULET_SETUP_TIMEOUT=5400 +whitelist_externals = juju +passenv = HOME TERM AMULET_* CS_* +deps = -r{toxinidir}/test-requirements.txt +install_command = + pip install {opts} {packages} - GNOCCHI_STORAGE_DEPS=file,swift,test-swift,s3,ceph,redis - ceph: GNOCCHI_STORAGE_DEPS=ceph - swift: GNOCCHI_STORAGE_DEPS=swift,test-swift - file: GNOCCHI_STORAGE_DEPS=file - redis: GNOCCHI_STORAGE_DEPS=redis - s3: GNOCCHI_STORAGE_DEPS=s3 - - GNOCCHI_INDEXER_DEPS=mysql,postgresql - mysql: GNOCCHI_INDEXER_DEPS=mysql - postgresql: GNOCCHI_INDEXER_DEPS=postgresql +[testenv:pep8] +basepython = python2.7 +commands = charm-proof - # FIXME(sileht): pbr doesn't support url in setup.cfg extras, so we do this crap - GNOCCHI_TEST_TARBALLS=http://tarballs.openstack.org/swift/swift-master.tar.gz#egg=swift - ceph: GNOCCHI_TEST_TARBALLS= - swift: GNOCCHI_TEST_TARBALLS=http://tarballs.openstack.org/swift/swift-master.tar.gz#egg=swift - s3: GNOCCHI_TEST_TARBALLS= - redis: GNOCCHI_TEST_TARBALLS= - file: GNOCCHI_TEST_TARBALLS= -# NOTE(jd) Install redis as a test dependency since it is used as a -# coordination driver in functional tests (--coordination-driver is passed to -# pifpaf) -deps = - -e - .[test,redis,prometheus,amqp1,{env:GNOCCHI_STORAGE_DEPS:},{env:GNOCCHI_INDEXER_DEPS:}] - {env:GNOCCHI_TEST_TARBALLS:} - cliff!=2.9.0 +[testenv:func27-noop] +# DRY RUN - For Debug +basepython = python2.7 commands = - {toxinidir}/run-tests.sh {posargs} - {toxinidir}/run-func-tests.sh {posargs} - -[testenv:py37-postgresql-file-upgrade-from-4.3] -# We should always recreate since the script upgrade -# Gnocchi we can't reuse the virtualenv -recreate = True -setenv = GNOCCHI_VARIANT=test,postgresql,file -deps = gnocchi[{env:GNOCCHI_VARIANT}]>=4.3,<4.4 - pifpaf[gnocchi]>=0.13 - gnocchiclient>=2.8.0 - xattr!=0.9.4 -commands = pifpaf --env-prefix INDEXER run postgresql {toxinidir}/run-upgrade-tests.sh {posargs} - -[testenv:py27-mysql-ceph-upgrade-from-4.3] -# We should always recreate since the script upgrade -# Gnocchi we can't reuse the virtualenv -recreate = True -setenv = GNOCCHI_VARIANT=test,mysql,ceph,ceph_recommended_lib -deps = gnocchi[{env:GNOCCHI_VARIANT}]>=4.3,<4.4 - gnocchiclient>=2.8.0 - pifpaf[ceph,gnocchi]>=0.13 - xattr!=0.9.4 -commands = pifpaf --env-prefix INDEXER run mysql -- pifpaf --env-prefix STORAGE run ceph {toxinidir}/run-upgrade-tests.sh {posargs} - -[testenv:pep8] -deps = hacking>=0.12,<0.13 -commands = flake8 + bundletester -vl DEBUG -r json -o func-results.json --test-pattern "gate-*" -n --no-destroy -[testenv:py27-cover] -commands = pifpaf -g GNOCCHI_INDEXER_URL run postgresql -- python setup.py testr --coverage --testr-args="{posargs}" +[testenv:func27] +# Run all gate tests which are +x (expected to always pass) +basepython = python2.7 +commands = + bundletester -vl DEBUG -r json -o func-results.json --test-pattern "gate-*" --no-destroy -[flake8] -exclude = .tox,.eggs,doc,gnocchi/rest/prometheus/remote_pb2.py -show-source = true -enable-extensions = H904 +[testenv:func27-smoke] +# Run a specific test as an Amulet smoke test (expected to always pass) +basepython = python2.7 +commands = + bundletester -vl DEBUG -r json -o func-results.json gate-basic-bionic-queens --no-destroy -[testenv:docs] -basepython = python3 -## This does not work, see: https://github.com/tox-dev/tox/issues/509 -# deps = {[testenv]deps} -# .[postgresql,doc] -# setenv = GNOCCHI_STORAGE_DEPS=file -deps = - -e - .[test,file,postgresql,doc] - doc8 -setenv = GNOCCHI_TEST_DEBUG=1 -commands = doc8 --ignore-path doc/source/rest.rst,doc/source/comparison-table.rst doc/source - pifpaf -g GNOCCHI_INDEXER_URL run postgresql -- python setup.py build_sphinx -W +[testenv:func27-dfs] +# Run all deploy-from-source tests which are +x (may not always pass!) +basepython = python2.7 +commands = + bundletester -vl DEBUG -r json -o func-results.json --test-pattern "dfs-*" --no-destroy -[testenv:docs-gnocchi.xyz] +[testenv:func27-dev] +# Run all development test targets which are +x (may not always pass!) basepython = python2.7 -whitelist_externals = bash rm -setenv = GNOCCHI_STORAGE_DEPS=file - GNOCCHI_TEST_DEBUG=1 -install_command = pip install -U {opts} {packages} -deps = {[testenv:docs]deps} - setuptools commands = - rm -rf doc/build/html - pifpaf -g GNOCCHI_INDEXER_URL run postgresql -- python setup.py build_sphinx + bundletester -vl DEBUG -r json -o func-results.json --test-pattern "dev-*" --no-destroy -[doc8] -ignore-path = doc/source/rest.rst,doc/source/comparison-table.rst +[testenv:venv] +commands = {posargs} diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py deleted file mode 100644 index d7887757..00000000 --- a/unit_tests/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 sys - -sys.path.append('src') -sys.path.append('src/lib') - -# Mock out charmhelpers so that we can test without it. -import charms_openstack.test_mocks # noqa -charms_openstack.test_mocks.mock_charmhelpers() - - -def mock_more_stuff(): - charmhelpers = charms_openstack.test_mocks.charmhelpers - sys.modules['charmhelpers.core.hookenv.charm_dir'] = ( - charmhelpers.core.hookenv.charm_dir - ) - charmhelpers.core.hookenv.charm_dir.return_value = "/tmp" - sys.modules['charmhelpers.contrib.storage'] = ( - charmhelpers.contrib.storage - ) - sys.modules['charmhelpers.contrib.storage.linux'] = ( - charmhelpers.contrib.storage.linux - ) - sys.modules['charmhelpers.contrib.storage.linux.ceph'] = ( - charmhelpers.contrib.storage.linux.ceph - ) - - -mock_more_stuff() diff --git a/unit_tests/test_gnocchi_handlers.py b/unit_tests/test_gnocchi_handlers.py deleted file mode 100644 index 99000e2c..00000000 --- a/unit_tests/test_gnocchi_handlers.py +++ /dev/null @@ -1,154 +0,0 @@ -# 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. - -from __future__ import absolute_import -from __future__ import print_function - -import mock - -import charms_openstack.test_utils as test_utils - -import reactive.gnocchi_handlers as handlers - - -class TestRegisteredHooks(test_utils.TestRegisteredHooks): - - def test_hooks(self): - defaults = [ - 'charm.installed', - 'shared-db.connected', - 'ha.connected', - 'identity-service.connected', - 'identity-service.available', - 'config.changed', - 'update-status', - 'charm.default-select-release'] - hook_set = { - 'when': { - 'render_config': ( - 'coordinator-memcached.available', - 'shared-db.available', - 'identity-service.available', - 'storage-ceph.pools.available', - ), - 'init_db': ( - 'config.rendered', - ), - 'cluster_connected': ( - 'ha.connected', - ), - 'provide_gnocchi_url': ( - 'metric-service.connected', - 'config.rendered', - 'db.synced', - ), - 'configure_ceph': ( - 'storage-ceph.available', - ), - 'storage_ceph_connected': ( - 'storage-ceph.connected', - ), - }, - 'when_not': { - 'storage_ceph_disconnected': ( - 'storage-ceph.connected', - ), - 'disable_services': ( - 'config.rendered', - ), - 'cluster_connected': ( - 'ha.available', - ), - 'init_db': ( - 'db.synced', - ), - }, - } - # test that the hooks were registered via the - # reactive.gnocchi_handlers - self.registered_hooks_test_helper(handlers, hook_set, defaults) - - -class TestHandlers(test_utils.PatchHelper): - - def setUp(self): - super(TestHandlers, self).setUp() - self.gnocchi_charm = mock.MagicMock() - self.gnocchi_charm.gnocchi_user = 'gnocchi' - self.gnocchi_charm.gnocchi_group = 'gnocchi' - self.patch_object(handlers.charm, 'provide_charm_instance', - new=mock.MagicMock()) - self.provide_charm_instance().__enter__.return_value = \ - self.gnocchi_charm - self.provide_charm_instance().__exit__.return_value = None - - def test_render_stuff(self): - handlers.render_config('arg1', 'arg2') - self.gnocchi_charm.render_with_interfaces.assert_called_once_with( - ('arg1', 'arg2') - ) - self.gnocchi_charm.assess_status.assert_called_once_with() - self.gnocchi_charm.enable_webserver_site.assert_called_once_with() - - def test_init_db(self): - handlers.init_db() - self.gnocchi_charm.db_sync.assert_called_once_with() - - @mock.patch.object(handlers, 'hookenv') - def test_storage_ceph_connected(self, hookenv): - mock_ceph = mock.MagicMock() - hookenv.service_name.return_value = 'mygnocchi' - handlers.storage_ceph_connected(mock_ceph) - mock_ceph.create_pool.assert_called_once_with( - 'mygnocchi', - ) - - def test_configure_ceph(self): - mock_ceph = mock.MagicMock() - mock_ceph.key.return_value = 'testkey' - handlers.configure_ceph(mock_ceph) - self.gnocchi_charm.configure_ceph_keyring.assert_called_once_with( - 'testkey') - mock_ceph.key.assert_called_once_with() - - def test_storage_ceph_disconnected(self): - handlers.storage_ceph_disconnected() - self.gnocchi_charm.delete_ceph_keyring.assert_called_once_with() - - @mock.patch.object(handlers.reactive.flags, 'is_flag_set') - def test_provide_gnocchi_url(self, mock_is_flag_set): - mock_is_flag_set.return_value = False - mock_gnocchi = mock.MagicMock() - self.gnocchi_charm.public_url = "http://gnocchi:8041" - handlers.provide_gnocchi_url(mock_gnocchi) - mock_gnocchi.set_gnocchi_url.assert_called_once_with( - "http://gnocchi:8041" - ) - - @mock.patch.object(handlers.reactive.flags, 'is_flag_set') - def test_provide_gnocchi_url_ha_connected(self, mock_is_flag_set): - mock_is_flag_set.side_effect = [True, False] - mock_gnocchi = mock.MagicMock() - handlers.provide_gnocchi_url(mock_gnocchi) - mock_gnocchi.set_gnocchi_url.assert_not_called() - - @mock.patch.object(handlers.reactive.flags, 'is_flag_set') - def test_provide_gnocchi_url_ha_available(self, mock_is_flag_set): - mock_is_flag_set.side_effect = [True, True] - mock_gnocchi = mock.MagicMock() - self.gnocchi_charm.public_url = "http://gnocchi:8041" - handlers.provide_gnocchi_url(mock_gnocchi) - mock_gnocchi.set_gnocchi_url.assert_called_once_with( - "http://gnocchi:8041" - ) -- GitLab