changes are as per new templates.
This commit is contained in:
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# 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
|
||||
simplejson
|
@ -1,15 +1,19 @@
|
||||
Pure Storage Backend for Cinder
|
||||
purestorage Storage Backend for Cinder
|
||||
-------------------------------
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
This charm provides a Pure Storage backend for use with the Cinder
|
||||
This charm provides a purestorage storage backend for use with the Cinder
|
||||
charm.
|
||||
|
||||
To use:
|
||||
|
||||
juju deploy cinder
|
||||
juju deploy -n 3 ceph
|
||||
juju deploy cinder-purestorage
|
||||
juju add-relation cinder-purestorage cinder
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
See config.yaml for details of configuration options.
|
||||
|
@ -1,15 +1,51 @@
|
||||
options:
|
||||
driver-source:
|
||||
type: string
|
||||
default:
|
||||
description: |
|
||||
Optional configuration to support use of additional sources such as:
|
||||
- ppa:myteam/ppa
|
||||
- cloud:trusty-proposed/kilo
|
||||
- http://my.archive.com/ubuntu main
|
||||
The last option should be used in conjunction with the key configuration
|
||||
option.
|
||||
driver-key:
|
||||
type: string
|
||||
default:
|
||||
description: |
|
||||
Key ID to import to the apt keyring to support use with arbitary source
|
||||
configuration from outside of Launchpad archives or PPA's.
|
||||
debug:
|
||||
default: !!bool false
|
||||
type: boolean
|
||||
description: Enable debug logging
|
||||
verbose:
|
||||
default: !!bool false
|
||||
type: boolean
|
||||
description: Enable verbose logging
|
||||
use-syslog:
|
||||
type: boolean
|
||||
default: !!bool false
|
||||
description: |
|
||||
Setting this to True will allow supporting services to log to syslog.
|
||||
use-internal-endpoints:
|
||||
type: boolean
|
||||
default: !!bool false
|
||||
description: |
|
||||
Openstack mostly defaults to using public endpoints for
|
||||
internal communication between services. If set to True this option
|
||||
will configure services to use internal endpoints where possible.
|
||||
san-ip:
|
||||
type: string
|
||||
description: "Management VIP address of the Pure Storage FlashArray"
|
||||
description: Management VIP address of the Pure Storage FlashArray
|
||||
pure-api-token:
|
||||
type: string
|
||||
description: "API token for FlashArray access"
|
||||
description: API token for FlashArray access
|
||||
protocol:
|
||||
type: string
|
||||
default: iscsi
|
||||
description: "SAN protocol to use. Choose between iscsi or fc."
|
||||
description: SAN protocol to use. Choose between iscsi or fc.
|
||||
volume-backend-name:
|
||||
type: string
|
||||
description: "Service name to present to Cinder"
|
||||
default:
|
||||
description: Service name to present to Cinder
|
||||
default: !!null ""
|
||||
|
@ -1,5 +1,8 @@
|
||||
includes:
|
||||
- 'layer:openstack'
|
||||
- 'interface:cinder-backend'
|
||||
|
||||
repo: https://github.com/mskalka/charm-cinder-purestorage
|
||||
includes: ['layer:openstack', 'interface:cinder-backend']
|
||||
config:
|
||||
deletes:
|
||||
- debug
|
||||
- verbose
|
||||
- use-syslog
|
||||
- use-internal-endpoints
|
||||
- ssl_ca
|
||||
|
@ -1,66 +1,37 @@
|
||||
import json
|
||||
import subprocess
|
||||
import charms_openstack.charm
|
||||
import charmhelpers.core.hookenv as ch_hookenv # noqa
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
config,
|
||||
log,
|
||||
relation_ids,
|
||||
relation_set,
|
||||
status_set,
|
||||
service_name
|
||||
)
|
||||
charms_openstack.charm.use_defaults('charm.default-select-release')
|
||||
|
||||
from charmhelpers.contrib.openstack.context import OSContextGenerator
|
||||
from charms_openstack.charm import OpenStackCharm
|
||||
class CinderpurestorageCharm(
|
||||
charms_openstack.charm.CinderStoragePluginCharm):
|
||||
|
||||
name = 'cinder_purestorage'
|
||||
version_package = 'python-purestorage'
|
||||
release = 'ocata'
|
||||
packages = [version_package]
|
||||
stateless = True
|
||||
# Specify any config that the user *must* set.
|
||||
mandatory_config = [
|
||||
('san_ip', self.config.get('san-ip')),
|
||||
('pure_api_token', self.config.get('pure-api-token'))]
|
||||
|
||||
class ProtocolNotImplimented(Exception):
|
||||
"""Unsupported protocol error."""
|
||||
|
||||
|
||||
class PureStorageCharm(OpenStackCharm):
|
||||
service_name = 'cinder-purestorage'
|
||||
name = 'purestorage'
|
||||
packages = ['']
|
||||
release = 'queens'
|
||||
|
||||
def install(self):
|
||||
subprocess.check_call(['pip', 'install', 'purestorage', '--no-deps'])
|
||||
|
||||
def get_purestorage_config(self):
|
||||
status_set('active', 'Unit is ready')
|
||||
name = config('volume-backend-name') or service_name()
|
||||
return name, PureStorageSubordinateContext()()
|
||||
|
||||
|
||||
class PureStorageSubordinateContext(OSContextGenerator):
|
||||
interfaces = ['storage-backend']
|
||||
|
||||
def __call__(self):
|
||||
log('Generating cinder.conf stanza')
|
||||
ctxt = []
|
||||
def cinder_configuration(self):
|
||||
drivers = {
|
||||
'iscsi': 'cinder.volume.drivers.pure.PureISCSIDriver',
|
||||
'fc': 'cinder.volume.drivers.pure.PureFCIDriver',
|
||||
'fc': 'cinder.volume.drivers.pure.PureFCDriver',
|
||||
}
|
||||
service = config('volume-backend-name') or service_name()
|
||||
service = self.config.get('volume-backend-name') or service_name()
|
||||
driver_options = [
|
||||
('san_ip', self.config.get('san-ip')),
|
||||
('pure_api_token', self.config.get('pure-api-token')),
|
||||
('volume_driver', drivers[self.config.get('protocol').lower()]),
|
||||
('volume_backend_name', service)]
|
||||
return driver_options
|
||||
|
||||
ctxt.append(('volume_backend_name', service))
|
||||
ctxt.append(('san_ip', config('san-ip')))
|
||||
ctxt.append(('pure_api_token', config('pure-api-token')))
|
||||
try:
|
||||
ctxt.append(
|
||||
('volume_driver', drivers[config('protocol').lower()]))
|
||||
except KeyError:
|
||||
raise ProtocolNotImplimented(
|
||||
config('protocol'), ' is not an implimented protocol driver, '
|
||||
'please choose between `iscsi` and `fc`.')
|
||||
return {
|
||||
"cinder": {
|
||||
"/etc/cinder/cinder.conf": {
|
||||
"sections": {
|
||||
service: ctxt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class CinderpurestorageCharmRocky(CinderpurestorageCharm):
|
||||
|
||||
# Rocky needs py3 packages.
|
||||
release = 'rocky'
|
||||
version_package = 'python3-purestorage'
|
||||
packages = [version_package]
|
||||
|
@ -1,19 +1,24 @@
|
||||
name: cinder-purestorage
|
||||
summary: Cinder PureStorage backend
|
||||
maintainer: Michael Skalka <Michael.Skalka@mskalka>
|
||||
summary: purestorage integration for OpenStack Block Storage
|
||||
maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com>
|
||||
description: |
|
||||
|
||||
Cinder is the block storage service for the Openstack project.
|
||||
.
|
||||
This charm provides a purestorage backend for Cinder
|
||||
tags:
|
||||
- openstack
|
||||
- storage
|
||||
- file-servers
|
||||
- misc
|
||||
subordinate: true
|
||||
series:
|
||||
- xenial
|
||||
- bionic
|
||||
subordinate: true
|
||||
provides:
|
||||
storage-backend:
|
||||
interface: cinder-backend
|
||||
scope: container
|
||||
requires:
|
||||
container:
|
||||
juju-info:
|
||||
interface: juju-info
|
||||
scope: container
|
||||
|
@ -1,26 +1,32 @@
|
||||
import charms_openstack.charm as charm
|
||||
import charms.reactive as reactive
|
||||
# Copyright 2019
|
||||
#
|
||||
# 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 charms_openstack.charm
|
||||
import charms.reactive
|
||||
|
||||
import charm.openstack.cinder_purestorage as cinder_pure
|
||||
assert cinder_pure
|
||||
# This charm's library contains all of the handler code associated with
|
||||
# this charm -- we need to import it to get the definitions for the charm.
|
||||
import charm.openstack.cinder_purestorage # noqa
|
||||
|
||||
charms_openstack.charm.use_defaults(
|
||||
'charm.installed',
|
||||
'update-status',
|
||||
'upgrade-charm',
|
||||
'storage-backend.connected',
|
||||
)
|
||||
|
||||
@reactive.when_any('install')
|
||||
def install_pure_driver():
|
||||
with charm.provide_charm_instance() as charm_class:
|
||||
charm_class.install()
|
||||
|
||||
|
||||
@reactive.when('storage-backend.available')
|
||||
@reactive.when_not('cinder.configured')
|
||||
def storage_backend(principle):
|
||||
with charm.provide_charm_instance() as charm_class:
|
||||
name, config = charm_class.get_purestorage_config()
|
||||
principle.configure_principal(name, config)
|
||||
reactive.set_state('cinder.configured')
|
||||
|
||||
|
||||
@reactive.hook('config-changed')
|
||||
def update_config():
|
||||
reactive.remove_state('cinder.configured')
|
||||
@charms.reactive.when('config.changed.driver-source')
|
||||
def reinstall():
|
||||
with charms_openstack.charm.provide_charm_instance() as charm:
|
||||
charm.install()
|
||||
|
2
src/test-requirements.txt
Normal file
2
src/test-requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
# zaza
|
||||
git+https://github.com/openstack-charmers/zaza.git#egg=zaza
|
55
src/tests/bundles/xenial-ocata.yaml
Normal file
55
src/tests/bundles/xenial-ocata.yaml
Normal file
@ -0,0 +1,55 @@
|
||||
series: xenial
|
||||
comment:
|
||||
- 'machines section to decide order of deployment. database sooner = faster'
|
||||
machines:
|
||||
'0':
|
||||
constraints: mem=3072M
|
||||
'1':
|
||||
'2':
|
||||
'3':
|
||||
relations:
|
||||
- - keystone:shared-db
|
||||
- mysql:shared-db
|
||||
- - cinder:shared-db
|
||||
- mysql:shared-db
|
||||
- - cinder:identity-service
|
||||
- keystone:identity-service
|
||||
- - cinder:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - cinder:storage-backend
|
||||
- cinder-purestorage:storage-backend
|
||||
applications:
|
||||
mysql:
|
||||
charm: cs:~openstack-charmers-next/percona-cluster
|
||||
num_units: 1
|
||||
to:
|
||||
- '0'
|
||||
keystone:
|
||||
charm: cs:~openstack-charmers-next/keystone
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: cloud:xenial-ocata
|
||||
to:
|
||||
- '1'
|
||||
cinder:
|
||||
charm: cs:~openstack-charmers-next/cinder
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: cloud:xenial-ocata
|
||||
to:
|
||||
- '2'
|
||||
cinder-purestorage:
|
||||
series: xenial
|
||||
charm: cinder-purestorage
|
||||
options:
|
||||
# Add config options here
|
||||
driver-source: ppa:narindergupta/purestorage
|
||||
san-ip: 10.243.19.250
|
||||
pure-api-token: ac0a534f-b682-777a-45a4-15a02e0d5c7e
|
||||
protocol: iscsi
|
||||
volume-backend-name: cinder-pure
|
||||
rabbitmq-server:
|
||||
charm: cs:~openstack-charmers-next/rabbitmq-server
|
||||
num_units: 1
|
||||
to:
|
||||
- '3'
|
9
src/tests/tests.yaml
Normal file
9
src/tests/tests.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
charm_name: cinder-purestorage
|
||||
tests:
|
||||
- tests.tests_cinder_purestorage.CinderpurestorageTest
|
||||
configure:
|
||||
- zaza.charm_tests.keystone.setup.add_demo_user
|
||||
gate_bundles:
|
||||
- xenial-ocata
|
||||
smoke_bundles:
|
||||
- xenial-ocata
|
72
src/tests/tests_cinder_purestorage.py
Normal file
72
src/tests/tests_cinder_purestorage.py
Normal file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2019 Canonical Ltd.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Encapsulate cinder-purestorage testing."""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
import zaza.model
|
||||
import zaza.charm_tests.test_utils as test_utils
|
||||
import zaza.utilities.openstack as openstack_utils
|
||||
|
||||
class CinderpurestorageTest(test_utils.OpenStackBaseTest):
|
||||
"""Encapsulate purestorage tests."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run class setup for running tests."""
|
||||
super(CinderpurestorageTest, cls).setUpClass()
|
||||
cls.keystone_session = openstack_utils.get_overcloud_keystone_session()
|
||||
cls.model_name = zaza.model.get_juju_model()
|
||||
cls.cinder_client = openstack_utils.get_cinder_session_client(
|
||||
cls.keystone_session)
|
||||
|
||||
def test_cinder_config(self):
|
||||
logging.info('purestorage')
|
||||
expected_contents = {
|
||||
'cinder-purestorage': {
|
||||
'san_ip': ['10.243.19.250'],
|
||||
'pure_api_token': ['ac0a534f-b682-777a-45a4-15a02e0d5c7e'],
|
||||
'volume_driver': ['cinder.volume.drivers.pure.PureISCSIDriver'],
|
||||
'volume_backend_name': ['cinder-pure']}}
|
||||
|
||||
zaza.model.run_on_leader(
|
||||
'cinder',
|
||||
'sudo cp /etc/cinder/cinder.conf /tmp/',
|
||||
model_name=self.model_name)
|
||||
zaza.model.block_until_oslo_config_entries_match(
|
||||
'cinder',
|
||||
'/tmp/cinder.conf',
|
||||
expected_contents,
|
||||
model_name=self.model_name,
|
||||
timeout=2)
|
||||
|
||||
def test_create_volume(self):
|
||||
test_vol_name = "zaza{}".format(uuid.uuid1().fields[0])
|
||||
vol_new = self.cinder_client.volumes.create(
|
||||
name=test_vol_name,
|
||||
size=2)
|
||||
openstack_utils.resource_reaches_status(
|
||||
self.cinder_client.volumes,
|
||||
vol_new.id,
|
||||
expected_status='available')
|
||||
test_vol = self.cinder_client.volumes.find(name=test_vol_name)
|
||||
self.assertEqual(
|
||||
getattr(test_vol, 'os-vol-host-attr:host').split('#')[0],
|
||||
'cinder@cinder-purestorage')
|
||||
self.cinder_client.volumes.delete(vol_new)
|
||||
|
35
src/tox.ini
Normal file
35
src/tox.ini
Normal file
@ -0,0 +1,35 @@
|
||||
[tox]
|
||||
envlist = pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
PYTHONHASHSEED=0
|
||||
whitelist_externals = juju
|
||||
passenv = HOME TERM CS_API_* OS_* AMULET_*
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps=charm-tools
|
||||
commands = charm-proof
|
||||
|
||||
[testenv:func-noop]
|
||||
basepython = python3
|
||||
commands =
|
||||
true
|
||||
|
||||
[testenv:func]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model
|
||||
|
||||
[testenv:func-smoke]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model --smoke
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
@ -1,7 +1,7 @@
|
||||
# Unit test requirements
|
||||
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
|
||||
git+https://github.com/openstack/charms.openstack#egg=charms.openstack
|
||||
git+https://github.com/openstack/charms.openstack.git#egg=charms-openstack
|
||||
|
9
tox.ini
9
tox.ini
@ -3,7 +3,7 @@
|
||||
# within individual charm repos.
|
||||
[tox]
|
||||
skipsdist = True
|
||||
envlist = pep8,py34,py35
|
||||
envlist = pep8,py34,py35,py36
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
@ -13,7 +13,7 @@ setenv = VIRTUAL_ENV={envdir}
|
||||
LAYER_PATH={toxinidir}/layers
|
||||
INTERFACE_PATH={toxinidir}/interfaces
|
||||
JUJU_REPOSITORY={toxinidir}/build
|
||||
passenv = http_proxy https_proxy USER
|
||||
passenv = http_proxy https_proxy
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
deps =
|
||||
@ -28,7 +28,7 @@ commands =
|
||||
basepython = python2.7
|
||||
# Reactive source charms are Python3-only, but a py27 unit test target
|
||||
# is required by OpenStack Governance. Remove this shim as soon as
|
||||
# permitted. http://governance.openstack.org/reference/cti/python_cti.html
|
||||
# permitted. https://governance.openstack.org/tc/reference/cti/python_cti.html
|
||||
whitelist_externals = true
|
||||
commands = true
|
||||
|
||||
@ -48,11 +48,12 @@ deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = ostestr {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3.5
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = flake8 {posargs} src unit_tests
|
||||
|
||||
[testenv:venv]
|
||||
basepython = python3
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
|
@ -0,0 +1,22 @@
|
||||
# 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()
|
||||
|
Binary file not shown.
49
unit_tests/test_lib_charm_openstack_cinder_purestorage.py
Normal file
49
unit_tests/test_lib_charm_openstack_cinder_purestorage.py
Normal file
@ -0,0 +1,49 @@
|
||||
# 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 charmhelpers
|
||||
|
||||
import charm.openstack.cinder_purestorage as cinder_purestorage
|
||||
|
||||
import charms_openstack.test_utils as test_utils
|
||||
|
||||
|
||||
class TestCinderpurestorageCharm(test_utils.PatchHelper):
|
||||
|
||||
def _patch_config_and_charm(self, config):
|
||||
self.patch_object(charmhelpers.core.hookenv, 'config')
|
||||
|
||||
def cf(key=None):
|
||||
if key is not None:
|
||||
return config[key]
|
||||
return config
|
||||
|
||||
self.config.side_effect = cf
|
||||
c = cinder_purestorage.CinderpurestorageCharm()
|
||||
return c
|
||||
|
||||
def test_cinder_base(self):
|
||||
charm = self._patch_config_and_charm({})
|
||||
self.assertEqual(charm.name, 'cinder_purestorage')
|
||||
self.assertEqual(charm.version_package, 'python-purestorage')
|
||||
self.assertEqual(charm.packages, ['python-purestorage'])
|
||||
|
||||
def test_cinder_configuration(self):
|
||||
charm = self._patch_config_and_charm({'a': 'b'})
|
||||
config = charm.cinder_configuration() # noqa
|
||||
# Add check here that configuration is as expected.
|
||||
# self.assertEqual(config, {})
|
@ -1,127 +0,0 @@
|
||||
# Copyright 2018 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 unittest
|
||||
|
||||
import mock
|
||||
|
||||
import reactive.cinder_purestorage_handlers as handlers
|
||||
|
||||
|
||||
_when_args = {}
|
||||
_when_not_args = {}
|
||||
|
||||
|
||||
def mock_hook_factory(d):
|
||||
|
||||
def mock_hook(*args, **kwargs):
|
||||
|
||||
def inner(f):
|
||||
# remember what we were passed. Note that we can't actually
|
||||
# determine the class we're attached to, as the decorator only gets
|
||||
# the function.
|
||||
try:
|
||||
d[f.__name__].append(dict(args=args, kwargs=kwargs))
|
||||
except KeyError:
|
||||
d[f.__name__] = [dict(args=args, kwargs=kwargs)]
|
||||
return f
|
||||
return inner
|
||||
return mock_hook
|
||||
|
||||
|
||||
class TestCinderPureStorageHandlers(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls._patched_when = mock.patch('charms.reactive.when',
|
||||
mock_hook_factory(_when_args))
|
||||
cls._patched_when_started = cls._patched_when.start()
|
||||
cls._patched_when_not = mock.patch('charms.reactive.when_not',
|
||||
mock_hook_factory(_when_not_args))
|
||||
cls._patched_when_not_started = cls._patched_when_not.start()
|
||||
# force requires to rerun the mock_hook decorator:
|
||||
# try except is Python2/Python3 compatibility as Python3 has moved
|
||||
# reload to importlib.
|
||||
try:
|
||||
reload(handlers)
|
||||
except NameError:
|
||||
import importlib
|
||||
importlib.reload(handlers)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls._patched_when.stop()
|
||||
cls._patched_when_started = None
|
||||
cls._patched_when = None
|
||||
cls._patched_when_not.stop()
|
||||
cls._patched_when_not_started = None
|
||||
cls._patched_when_not = None
|
||||
# and fix any breakage we did to the module
|
||||
try:
|
||||
reload(handlers)
|
||||
except NameError:
|
||||
import importlib
|
||||
importlib.reload(handlers)
|
||||
|
||||
def setUp(self):
|
||||
self._patches = {}
|
||||
self._patches_start = {}
|
||||
|
||||
def tearDown(self):
|
||||
for k, v in self._patches.items():
|
||||
v.stop()
|
||||
setattr(self, k, None)
|
||||
self._patches = None
|
||||
self._patches_start = None
|
||||
|
||||
def patch(self, obj, attr, return_value=None):
|
||||
mocked = mock.patch.object(obj, attr)
|
||||
self._patches[attr] = mocked
|
||||
started = mocked.start()
|
||||
started.return_value = return_value
|
||||
self._patches_start[attr] = started
|
||||
setattr(self, attr, started)
|
||||
|
||||
def test_registered_hooks(self):
|
||||
# test that the hooks actually registered the relation expressions that
|
||||
# are meaningful for this interface: this is to handle regressions.
|
||||
# The keys are the function names that the hook attaches to.
|
||||
when_any_patterns = {
|
||||
'storage_backend': [
|
||||
('storage-backend.joined', 'storage-backend.changed')],
|
||||
}
|
||||
when_patterns = {
|
||||
'update_config': [
|
||||
('config.changed', )],
|
||||
}
|
||||
when_not_patterns = {
|
||||
'install_packages': [('installed', )],
|
||||
'storage_backend': [('storage-backend.available', )],
|
||||
}
|
||||
# check the when hooks are attached to the expected functions
|
||||
for t, p in [(_when_args, when_any_patterns),
|
||||
(_when_not_args, when_not_patterns),
|
||||
(_when_args, when_patterns),
|
||||
]:
|
||||
for f, args in t.items():
|
||||
# check that function is in patterns
|
||||
print(f)
|
||||
self.assertTrue(f in p.keys())
|
||||
# check that the lists are equal
|
||||
l = [a['args'] for a in args]
|
||||
self.assertEqual(l, p[f])
|
Reference in New Issue
Block a user