Rework charm to use cinder sub plugin
This commit is contained in:
@@ -1 +1,7 @@
|
|||||||
git+https://github.com/canonical/charmcraft.git@0.10.2#egg=charmcraft
|
# NOTES(lourot):
|
||||||
|
# * We don't install charmcraft via pip anymore because it anyway spins up a
|
||||||
|
# container and scp the system's charmcraft snap inside it. So the charmcraft
|
||||||
|
# snap is necessary on the system anyway.
|
||||||
|
# * `tox -e build` successfully validated with charmcraft 1.2.1
|
||||||
|
|
||||||
|
cffi==1.14.6; python_version < '3.6' # cffi 1.15.0 drops support for py35.
|
||||||
|
32
charmcraft.yaml
Normal file
32
charmcraft.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
type: charm
|
||||||
|
parts:
|
||||||
|
charm:
|
||||||
|
after:
|
||||||
|
- update-certificates
|
||||||
|
charm-python-packages:
|
||||||
|
# NOTE(lourot): see
|
||||||
|
# * https://github.com/canonical/charmcraft/issues/551
|
||||||
|
# * https://github.com/canonical/charmcraft/issues/632
|
||||||
|
- setuptools < 58
|
||||||
|
build-packages:
|
||||||
|
- git
|
||||||
|
update-certificates:
|
||||||
|
plugin: nil
|
||||||
|
# See https://github.com/canonical/charmcraft/issues/658
|
||||||
|
override-build: |
|
||||||
|
apt update
|
||||||
|
apt install -y ca-certificates
|
||||||
|
update-ca-certificates
|
||||||
|
bases:
|
||||||
|
- build-on:
|
||||||
|
- name: ubuntu
|
||||||
|
channel: "20.04"
|
||||||
|
architectures:
|
||||||
|
- amd64
|
||||||
|
run-on:
|
||||||
|
- name: ubuntu
|
||||||
|
channel: "20.04"
|
||||||
|
architectures: [amd64, s390x, ppc64el, arm64]
|
||||||
|
- name: ubuntu
|
||||||
|
channel: "22.04"
|
||||||
|
architectures: [amd64, s390x, ppc64el, arm64]
|
18
config.yaml
18
config.yaml
@@ -27,7 +27,7 @@ options:
|
|||||||
raise an exception.
|
raise an exception.
|
||||||
hpe3par-iscsi-ips:
|
hpe3par-iscsi-ips:
|
||||||
type: string
|
type: string
|
||||||
default: ''
|
default:
|
||||||
description: |
|
description: |
|
||||||
Comma-separated list of IP:PORT to be used for the iscsi connection.
|
Comma-separated list of IP:PORT to be used for the iscsi connection.
|
||||||
hpe3par-iscsi-chap-enabled:
|
hpe3par-iscsi-chap-enabled:
|
||||||
@@ -69,39 +69,39 @@ options:
|
|||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Password for SAN controller for SSH access to the array
|
Password for SAN controller for SSH access to the array
|
||||||
default: ''
|
default:
|
||||||
hpe3par-username:
|
hpe3par-username:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
3PAR username with the 'edit' role
|
3PAR username with the 'edit' role
|
||||||
default: ''
|
default:
|
||||||
hpe3par-password:
|
hpe3par-password:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
3PAR password for the user specified in hpe3par_username
|
3PAR password for the user specified in hpe3par_username
|
||||||
default: ''
|
default:
|
||||||
hpe3par-api-url:
|
hpe3par-api-url:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
3PAR WS API Server URL
|
3PAR WS API Server URL
|
||||||
default: ''
|
default:
|
||||||
hpe3par-cpg:
|
hpe3par-cpg:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
3PAR CPG to use for volume creation
|
3PAR CPG to use for volume creation
|
||||||
default: ''
|
default:
|
||||||
hpe3par_cpg_snap:
|
hpe3par_cpg_snap:
|
||||||
type: string
|
type: string
|
||||||
default: ''
|
default:
|
||||||
description: |
|
description: |
|
||||||
Sets the CPG name for the snapshot.
|
Sets the CPG name for the snapshot.
|
||||||
If empty, use the userCPG will be used.
|
If empty, use the userCPG will be used.
|
||||||
hpe3par_target_nsp:
|
hpe3par_target_nsp:
|
||||||
type: string
|
type: string
|
||||||
default: ''
|
default:
|
||||||
description: |
|
description: |
|
||||||
volume-backend-name:
|
volume-backend-name:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
Name to present to cinder, leave blank to use charm (or alias) name
|
Name to present to cinder, leave blank to use charm (or alias) name
|
||||||
default: ''
|
default:
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
Format: http://dep.debian.net/deps/dep5/
|
Format: http://dep.debian.net/deps/dep5/
|
||||||
|
|
||||||
Files: *
|
Files: *
|
||||||
Copyright: Copyright 2015-2022, Canonical Ltd., All Rights Reserved.
|
Copyright: Copyright 2021-2022, Canonical Ltd., All Rights Reserved.
|
||||||
License: Apache License 2.0
|
License: Apache License 2.0
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@@ -3,8 +3,9 @@
|
|||||||
- charm-unit-jobs
|
- charm-unit-jobs
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- bionic-queens
|
|
||||||
- focal-ussuri
|
- focal-ussuri
|
||||||
|
- focal-yoga
|
||||||
|
- jammy-yoga
|
||||||
vars:
|
vars:
|
||||||
needs_charm_build: true
|
needs_charm_build: true
|
||||||
build_type: charmcraft
|
build_type: charmcraft
|
||||||
|
13
rename.sh
Executable file
13
rename.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
charm=$(grep "charm_build_name" osci.yaml | awk '{print $2}')
|
||||||
|
echo "renaming ${charm}_*.charm to ${charm}.charm"
|
||||||
|
echo -n "pwd: "
|
||||||
|
pwd
|
||||||
|
ls -al
|
||||||
|
echo "Removing bad downloaded charm maybe?"
|
||||||
|
if [[ -e "${charm}.charm" ]];
|
||||||
|
then
|
||||||
|
rm "${charm}.charm"
|
||||||
|
fi
|
||||||
|
echo "Renaming charm here."
|
||||||
|
mv ${charm}_*.charm ${charm}.charm
|
142
src/charm.py
142
src/charm.py
@@ -22,6 +22,8 @@ from ops.main import main
|
|||||||
from ops.framework import StoredState
|
from ops.framework import StoredState
|
||||||
from ops.model import MaintenanceStatus, ActiveStatus, BlockedStatus
|
from ops.model import MaintenanceStatus, ActiveStatus, BlockedStatus
|
||||||
|
|
||||||
|
from ops_openstack.plugins.classes import CinderStoragePluginCharm
|
||||||
|
|
||||||
from charmhelpers.fetch.ubuntu import apt_install
|
from charmhelpers.fetch.ubuntu import apt_install
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -35,23 +37,25 @@ REQUIRED_OPTS = [
|
|||||||
'hpe3par-password',
|
'hpe3par-password',
|
||||||
'san-ip',
|
'san-ip',
|
||||||
'san-login',
|
'san-login',
|
||||||
'san-password']
|
'san-password',
|
||||||
|
]
|
||||||
|
|
||||||
# Based on initialize-iscsi-ports() in
|
# Based on initialize-iscsi-ports() in
|
||||||
# https://github.com/openstack/cinder/blob/master/cinder/ \
|
# https://github.com/openstack/cinder/blob/master/cinder/ \
|
||||||
# volume/drivers/hpe/hpe-3par-iscsi.py
|
# volume/drivers/hpe/hpe-3par-iscsi.py
|
||||||
REQUIRED_OPTS_ISCSI = [
|
REQUIRED_OPTS_ISCSI = ['hpe3par-iscsi-ips']
|
||||||
'hpe3par-iscsi-ips']
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidConfigError(Exception):
|
class InvalidConfigError(Exception):
|
||||||
"""Exception raised on invalid configurations."""
|
"""Exception raised on invalid configurations."""
|
||||||
|
|
||||||
def __init__(self, msg):
|
pass
|
||||||
self.msg = msg
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.msg
|
class MissingConfigError(Exception):
|
||||||
|
"""Exception raised on missing 3PAR config parameter."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def CinderThreeParContext(charm_config, service):
|
def CinderThreeParContext(charm_config, service):
|
||||||
@@ -67,10 +71,7 @@ def CinderThreeParContext(charm_config, service):
|
|||||||
"""
|
"""
|
||||||
ctxt = []
|
ctxt = []
|
||||||
for key in charm_config.keys():
|
for key in charm_config.keys():
|
||||||
if key == 'volume-backend-name':
|
ctxt.append((key.replace('-', '_'), charm_config[key]))
|
||||||
ctxt.append((key, service))
|
|
||||||
else:
|
|
||||||
ctxt.append((key.replace('-', '_'), charm_config[key]))
|
|
||||||
if charm_config['driver-type'] == 'fc':
|
if charm_config['driver-type'] == 'fc':
|
||||||
ctxt.append((
|
ctxt.append((
|
||||||
'volume_driver',
|
'volume_driver',
|
||||||
@@ -81,118 +82,49 @@ def CinderThreeParContext(charm_config, service):
|
|||||||
'cinder.volume.drivers.hpe.hpe_3par_iscsi.HPE3PARISCSIDriver'))
|
'cinder.volume.drivers.hpe.hpe_3par_iscsi.HPE3PARISCSIDriver'))
|
||||||
else:
|
else:
|
||||||
raise InvalidConfigError('Invalid config (driver-type)')
|
raise InvalidConfigError('Invalid config (driver-type)')
|
||||||
return {
|
return ctxt
|
||||||
"cinder": {
|
|
||||||
"/etc/cinder/cinder.conf": {
|
|
||||||
"sections": {
|
|
||||||
service: ctxt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CharmCinderThreeParCharm(CharmBase):
|
class CharmCinderThreeParCharm(CinderStoragePluginCharm):
|
||||||
"""Charm the Cinder HPE 3PAR driver."""
|
"""Charm the Cinder HPE 3PAR driver."""
|
||||||
|
|
||||||
_stored = StoredState()
|
MANDATORY_CONFIG = REQUIRED_OPTS
|
||||||
|
PACKAGES = ['python3-3parclient', 'sysfsutils']
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args)
|
super().__init__(*args, **kwargs)
|
||||||
self.framework.observe(
|
self._stored.is_started = True
|
||||||
self.on.install,
|
|
||||||
self._on_install)
|
|
||||||
self.framework.observe(
|
|
||||||
self.on.config_changed,
|
|
||||||
self._on_config_changed_or_upgrade)
|
|
||||||
self.framework.observe(
|
|
||||||
self.on.upgrade_charm,
|
|
||||||
self._on_config_changed_or_upgrade)
|
|
||||||
self.framework.observe(
|
|
||||||
self.on.storage_backend_relation_joined,
|
|
||||||
self._on_render_storage_backend)
|
|
||||||
self.framework.observe(
|
|
||||||
self.on.storage_backend_relation_changed,
|
|
||||||
self._on_render_storage_backend)
|
|
||||||
|
|
||||||
def _rel_get_remote_units(self, rel_name):
|
def on_config(self, event):
|
||||||
"""Get relations remote units"""
|
prev_status = self.unit.status
|
||||||
return self.framework.model.get_relation(rel_name).units
|
|
||||||
|
|
||||||
def _on_install(self, _):
|
|
||||||
"""Install packages"""
|
|
||||||
self.unit.status = MaintenanceStatus(
|
|
||||||
"Installing packages")
|
|
||||||
# os_brick lib needs systool from sysfsutils to be able to retrieve
|
|
||||||
# the data from FC links:
|
|
||||||
# https://github.com/openstack/os-brick/blob/ \
|
|
||||||
# 1b2e2295421615847d86508dcd487ec51fa45f25/ \
|
|
||||||
# os_brick/initiator/linuxfc.py#L151
|
|
||||||
apt_install(['python3-3parclient',
|
|
||||||
'sysfsutils'])
|
|
||||||
self.unit.status = ActiveStatus("Unit is ready")
|
|
||||||
|
|
||||||
def _on_config_changed_or_upgrade(self, event):
|
|
||||||
"""Update on changed config or charm upgrade"""
|
|
||||||
svc_name = self.framework.model.app.name
|
|
||||||
# Copying to a new dict as charm_config will be edited according to
|
|
||||||
# the settings
|
|
||||||
charm_config = dict(self.framework.model.config)
|
|
||||||
if not self.check_config(charm_config):
|
|
||||||
# The config checks failed, drop this event as the operator
|
|
||||||
# needs to intervene manually
|
|
||||||
return
|
|
||||||
r = self.framework.model.relations.get('storage-backend')[0]
|
|
||||||
try:
|
try:
|
||||||
ctx = CinderThreeParContext(charm_config, svc_name)
|
super().on_config(event)
|
||||||
except InvalidConfigError as e:
|
except InvalidConfigError as e:
|
||||||
self.unit.status = BlockedStatus(str(e))
|
self.unit.status = BlockedStatus(str(e))
|
||||||
return
|
finally:
|
||||||
|
self.unit.status = prev_status
|
||||||
|
|
||||||
for u in self._rel_get_remote_units('storage-backend'):
|
def _on_config(self, event):
|
||||||
r.data[self.unit]['backend_name'] = \
|
# The list of mandatory config options isn't static for this
|
||||||
charm_config['volume-backend-name'] or svc_name
|
# charm, so we need to manually adjust here.
|
||||||
r.data[self.unit]['subordinate_configuration'] = json.dumps(ctx)
|
if self.framework.model.config.get('driver-type') == 'iscsi':
|
||||||
self.unit.status = ActiveStatus("Unit is ready")
|
self.MANDATORY_CONFIG = REQUIRED_OPTS + REQUIRED_OPTS_ISCSI
|
||||||
|
else:
|
||||||
|
self.MANDATORY_CONFIG = REQUIRED_OPTS
|
||||||
|
|
||||||
def _on_render_storage_backend(self, event):
|
super()._on_config(event)
|
||||||
"""Render the current configuration"""
|
|
||||||
|
def cinder_configuration(self, charm_config):
|
||||||
svc_name = self.framework.model.app.name
|
svc_name = self.framework.model.app.name
|
||||||
charm_config = self.framework.model.config
|
|
||||||
data = event.relation.data
|
|
||||||
data[self.unit]['backend_name'] = \
|
|
||||||
charm_config['volume-backend-name'] or svc_name
|
|
||||||
try:
|
|
||||||
ctx = CinderThreeParContext(charm_config, svc_name)
|
|
||||||
except InvalidConfigError as e:
|
|
||||||
self.unit.status = BlockedStatus(str(e))
|
|
||||||
return
|
|
||||||
|
|
||||||
data[self.unit]['subordinate_configuration'] = json.dumps(ctx)
|
|
||||||
|
|
||||||
def check_config(self, charm_config):
|
|
||||||
"""Check whether required options are set."""
|
|
||||||
# According to the HPE 3par driver code, expiration and retention can
|
# According to the HPE 3par driver code, expiration and retention can
|
||||||
# be left unset and won't be configured:
|
# be left unset and won't be configured:
|
||||||
# https://github.com/openstack/cinder/blob/stable/ussuri/cinder/ \
|
# https://github.com/openstack/cinder/blob/stable/ussuri/cinder/ \
|
||||||
# volume/drivers/hpe/hpe_3par_common.py#L2834
|
# volume/drivers/hpe/hpe_3par_common.py#L2834
|
||||||
for opt in ("hpe3par-snapshot-retention",
|
for opt in ('hpe3par-snapshot-retention',
|
||||||
"hpe3par-snapshot-expiration"):
|
'hpe3par-snapshot-expiration'):
|
||||||
# Setting as < 0 will remove the given option from the request.
|
|
||||||
if charm_config.get(opt, -1) < 0:
|
if charm_config.get(opt, -1) < 0:
|
||||||
charm_config.pop(opt, None)
|
charm_config.pop(opt, None)
|
||||||
required_opts = REQUIRED_OPTS
|
return CinderThreeParContext(charm_config, svc_name)
|
||||||
charm_config = self.framework.model.config
|
|
||||||
if charm_config['driver-type'] == 'iscsi':
|
|
||||||
required_opts += REQUIRED_OPTS_ISCSI
|
|
||||||
missing_opts = set(required_opts) - set(charm_config.keys())
|
|
||||||
if missing_opts:
|
|
||||||
self.unit.status = BlockedStatus(
|
|
||||||
'Missing options: {}'.format(','.join(missing_opts)))
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
self.unit.status = MaintenanceStatus("Sharing configs with Cinder")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@@ -1,49 +0,0 @@
|
|||||||
series: bionic
|
|
||||||
comment:
|
|
||||||
- 'machines section to decide order of deployment. database sooner = faster'
|
|
||||||
machines:
|
|
||||||
'0':
|
|
||||||
constraints: mem=3072M
|
|
||||||
'1':
|
|
||||||
'2':
|
|
||||||
constraints: mem=4G root-disk=16G
|
|
||||||
'3':
|
|
||||||
local_overlay_enabled: false
|
|
||||||
relations:
|
|
||||||
- - keystone:shared-db
|
|
||||||
- mysql:shared-db
|
|
||||||
- - cinder:shared-db
|
|
||||||
- mysql:shared-db
|
|
||||||
- - cinder:identity-service
|
|
||||||
- keystone:identity-service
|
|
||||||
- - cinder:amqp
|
|
||||||
- rabbitmq-server:amqp
|
|
||||||
- - cinder:storage-backend
|
|
||||||
- cinder-three-par: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
|
|
||||||
to:
|
|
||||||
- '1'
|
|
||||||
cinder:
|
|
||||||
charm: cs:~openstack-charmers-next/cinder
|
|
||||||
num_units: 1
|
|
||||||
options:
|
|
||||||
block-device: /dev/vdb
|
|
||||||
overwrite: "true"
|
|
||||||
ephemeral-unmount: /mnt
|
|
||||||
to:
|
|
||||||
- '2'
|
|
||||||
cinder-three-par:
|
|
||||||
charm: ../../cinder-three-par.charm
|
|
||||||
rabbitmq-server:
|
|
||||||
charm: cs:~openstack-charmers-next/rabbitmq-server
|
|
||||||
num_units: 1
|
|
||||||
to:
|
|
||||||
- '3'
|
|
@@ -4,11 +4,14 @@ tests:
|
|||||||
configure:
|
configure:
|
||||||
- zaza.openstack.charm_tests.keystone.setup.add_demo_user
|
- zaza.openstack.charm_tests.keystone.setup.add_demo_user
|
||||||
gate_bundles:
|
gate_bundles:
|
||||||
- bionic-queens
|
|
||||||
- focal-ussuri
|
- focal-ussuri
|
||||||
|
- focal-yoga
|
||||||
|
- jammy-yoga
|
||||||
smoke_bundles:
|
smoke_bundles:
|
||||||
- bionic-queens
|
|
||||||
- focal-ussuri
|
- focal-ussuri
|
||||||
|
- focal-yoga
|
||||||
|
- jammy-yoga
|
||||||
dev_bundles:
|
dev_bundles:
|
||||||
- bionic-queens
|
|
||||||
- focal-ussuri
|
- focal-ussuri
|
||||||
|
- focal-yoga
|
||||||
|
- jammy-yoga
|
||||||
|
39
tox.ini
39
tox.ini
@@ -1,5 +1,4 @@
|
|||||||
# Operator charm (with zaza): tox.ini
|
# Operator charm (with zaza): tox.ini
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = pep8,py3
|
envlist = pep8,py3
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
@@ -15,11 +14,14 @@ skip_missing_interpreters = False
|
|||||||
# * It is also necessary to pin virtualenv as a newer virtualenv would still
|
# * It is also necessary to pin virtualenv as a newer virtualenv would still
|
||||||
# lead to fetching the latest pip in the func* tox targets, see
|
# lead to fetching the latest pip in the func* tox targets, see
|
||||||
# https://stackoverflow.com/a/38133283
|
# https://stackoverflow.com/a/38133283
|
||||||
|
# * It is necessary to declare setuptools as a dependency otherwise tox will
|
||||||
|
# fail very early at not being able to load it. The version pinning is in
|
||||||
|
# line with `pip.sh`.
|
||||||
requires = pip < 20.3
|
requires = pip < 20.3
|
||||||
virtualenv < 20.0
|
virtualenv < 20.0
|
||||||
|
setuptools < 50.0.0
|
||||||
# NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci
|
# NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci
|
||||||
minversion = 3.2.0
|
minversion = 3.2.0
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
setenv = VIRTUAL_ENV={envdir}
|
setenv = VIRTUAL_ENV={envdir}
|
||||||
PYTHONHASHSEED=0
|
PYTHONHASHSEED=0
|
||||||
@@ -27,44 +29,47 @@ setenv = VIRTUAL_ENV={envdir}
|
|||||||
install_command =
|
install_command =
|
||||||
pip install {opts} {packages}
|
pip install {opts} {packages}
|
||||||
commands = stestr run --slowest {posargs}
|
commands = stestr run --slowest {posargs}
|
||||||
whitelist_externals =
|
allowlist_externals =
|
||||||
git
|
git
|
||||||
add-to-archive.py
|
add-to-archive.py
|
||||||
bash
|
bash
|
||||||
|
charmcraft
|
||||||
|
rename.sh
|
||||||
passenv = HOME TERM CS_* OS_* TEST_*
|
passenv = HOME TERM CS_* OS_* TEST_*
|
||||||
deps = -r{toxinidir}/test-requirements.txt
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
[testenv:py35]
|
[testenv:py35]
|
||||||
basepython = python3.5
|
basepython = python3.5
|
||||||
# python3.5 is irrelevant on a focal+ charm.
|
# python3.5 is irrelevant on a focal+ charm.
|
||||||
commands = /bin/true
|
commands = /bin/true
|
||||||
|
|
||||||
[testenv:py36]
|
[testenv:py36]
|
||||||
basepython = python3.6
|
basepython = python3.6
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
[testenv:py37]
|
[testenv:py37]
|
||||||
basepython = python3.7
|
basepython = python3.7
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
[testenv:py38]
|
[testenv:py38]
|
||||||
basepython = python3.8
|
basepython = python3.8
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
[testenv:py39]
|
||||||
|
basepython = python3.9
|
||||||
|
deps = -r{toxinidir}/requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
[testenv:py310]
|
||||||
|
basepython = python3.10
|
||||||
|
deps = -r{toxinidir}/requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
[testenv:py3]
|
[testenv:py3]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
[testenv:pep8]
|
[testenv:pep8]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
commands = flake8 {posargs} src unit_tests tests
|
commands = flake8 {posargs} src unit_tests tests
|
||||||
|
|
||||||
[testenv:cover]
|
[testenv:cover]
|
||||||
# Technique based heavily upon
|
# Technique based heavily upon
|
||||||
# https://github.com/openstack/nova/blob/master/tox.ini
|
# https://github.com/openstack/nova/blob/master/tox.ini
|
||||||
@@ -81,7 +86,6 @@ commands =
|
|||||||
coverage html -d cover
|
coverage html -d cover
|
||||||
coverage xml -o cover/coverage.xml
|
coverage xml -o cover/coverage.xml
|
||||||
coverage report
|
coverage report
|
||||||
|
|
||||||
[coverage:run]
|
[coverage:run]
|
||||||
branch = True
|
branch = True
|
||||||
concurrency = multiprocessing
|
concurrency = multiprocessing
|
||||||
@@ -92,42 +96,37 @@ omit =
|
|||||||
.tox/*
|
.tox/*
|
||||||
*/charmhelpers/*
|
*/charmhelpers/*
|
||||||
unit_tests/*
|
unit_tests/*
|
||||||
|
|
||||||
[testenv:venv]
|
[testenv:venv]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
commands = {posargs}
|
commands = {posargs}
|
||||||
|
|
||||||
[testenv:build]
|
[testenv:build]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
deps = -r{toxinidir}/build-requirements.txt
|
deps = -r{toxinidir}/build-requirements.txt
|
||||||
commands =
|
commands =
|
||||||
charmcraft build
|
charmcraft clean
|
||||||
|
charmcraft -v build
|
||||||
|
{toxinidir}/rename.sh
|
||||||
[testenv:func-noop]
|
[testenv:func-noop]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
commands =
|
commands =
|
||||||
functest-run-suite --help
|
functest-run-suite --help
|
||||||
|
|
||||||
[testenv:func]
|
[testenv:func]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
commands =
|
commands =
|
||||||
functest-run-suite --keep-model
|
functest-run-suite --keep-model
|
||||||
|
|
||||||
[testenv:func-smoke]
|
[testenv:func-smoke]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
commands =
|
commands =
|
||||||
functest-run-suite --keep-model --smoke
|
functest-run-suite --keep-model --smoke
|
||||||
|
|
||||||
[testenv:func-dev]
|
[testenv:func-dev]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
commands =
|
commands =
|
||||||
functest-run-suite --keep-model --dev
|
functest-run-suite --keep-model --dev
|
||||||
|
|
||||||
[testenv:func-target]
|
[testenv:func-target]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
commands =
|
commands =
|
||||||
functest-run-suite --keep-model --bundle {posargs}
|
functest-run-suite --keep-model --bundle {posargs}
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
# Ignore E902 because the unit_tests directory is missing in the built charm.
|
# Ignore E902 because the unit_tests directory is missing in the built charm.
|
||||||
ignore = E402,E226,E902
|
ignore = E402,E226,E902
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@ import unittest
|
|||||||
import json
|
import json
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from ops.model import Relation, BlockedStatus
|
from ops.model import Relation, BlockedStatus, ActiveStatus
|
||||||
from ops.testing import Harness
|
from ops.testing import Harness
|
||||||
from src.charm import CharmCinderThreeParCharm
|
from src.charm import CharmCinderThreeParCharm
|
||||||
|
|
||||||
@@ -29,7 +29,6 @@ TEST_3PAR_CONFIG = {
|
|||||||
['driver_type', 'fc'],
|
['driver_type', 'fc'],
|
||||||
['use_multipath_image_xfer', False],
|
['use_multipath_image_xfer', False],
|
||||||
['enforce_multipath_for_image_xfer', False],
|
['enforce_multipath_for_image_xfer', False],
|
||||||
['hpe3par_iscsi_ips', ''],
|
|
||||||
['hpe3par_iscsi_chap_enabled', True],
|
['hpe3par_iscsi_chap_enabled', True],
|
||||||
['hpe3par_snapshot_expiration', 72],
|
['hpe3par_snapshot_expiration', 72],
|
||||||
['hpe3par_snapshot_retention', 48],
|
['hpe3par_snapshot_retention', 48],
|
||||||
@@ -37,14 +36,7 @@ TEST_3PAR_CONFIG = {
|
|||||||
['reserved_percentage', 15],
|
['reserved_percentage', 15],
|
||||||
['san_ip', '0.0.0.0'],
|
['san_ip', '0.0.0.0'],
|
||||||
['san_login', 'some-login'],
|
['san_login', 'some-login'],
|
||||||
['san_password', ''],
|
['volume_backend_name', 'cinder-three-par'],
|
||||||
['hpe3par_username', ''],
|
|
||||||
['hpe3par_password', ''],
|
|
||||||
['hpe3par_api_url', ''],
|
|
||||||
['hpe3par_cpg', ''],
|
|
||||||
['hpe3par_cpg_snap', ''],
|
|
||||||
['hpe3par_target_nsp', ''],
|
|
||||||
['volume-backend-name', 'cinder-three-par'],
|
|
||||||
['volume_driver',
|
['volume_driver',
|
||||||
'cinder.volume.drivers.hpe.hpe_3par_fc.HPE3PARFCDriver']
|
'cinder.volume.drivers.hpe.hpe_3par_fc.HPE3PARFCDriver']
|
||||||
]
|
]
|
||||||
@@ -62,20 +54,14 @@ TEST_3PAR_CONFIG_CHANGED = {
|
|||||||
['driver_type', 'fc'],
|
['driver_type', 'fc'],
|
||||||
['use_multipath_image_xfer', False],
|
['use_multipath_image_xfer', False],
|
||||||
['enforce_multipath_for_image_xfer', False],
|
['enforce_multipath_for_image_xfer', False],
|
||||||
['hpe3par_iscsi_ips', ''],
|
|
||||||
['hpe3par_iscsi_chap_enabled', True],
|
['hpe3par_iscsi_chap_enabled', True],
|
||||||
['max_over_subscription_ratio', 20.0],
|
['max_over_subscription_ratio', 20.0],
|
||||||
['reserved_percentage', 15],
|
['reserved_percentage', 15],
|
||||||
['san_ip', '1.2.3.4'],
|
['san_ip', '1.2.3.4'],
|
||||||
['san_login', 'login'],
|
['san_login', 'login'],
|
||||||
['san_password', 'pwd'],
|
['san_password', 'pwd'],
|
||||||
['hpe3par_username', ''],
|
|
||||||
['hpe3par_password', ''],
|
|
||||||
['hpe3par_api_url', 'test.url'],
|
['hpe3par_api_url', 'test.url'],
|
||||||
['hpe3par_cpg', ''],
|
['volume_backend_name', 'cinder-three-par'],
|
||||||
['hpe3par_cpg_snap', ''],
|
|
||||||
['hpe3par_target_nsp', ''],
|
|
||||||
['volume-backend-name', 'cinder-three-par'],
|
|
||||||
['volume_driver',
|
['volume_driver',
|
||||||
'cinder.volume.drivers.hpe.hpe_3par_fc.HPE3PARFCDriver']
|
'cinder.volume.drivers.hpe.hpe_3par_fc.HPE3PARFCDriver']
|
||||||
]
|
]
|
||||||
@@ -127,13 +113,12 @@ class TestCharm(unittest.TestCase):
|
|||||||
|
|
||||||
def test_blocked_status(self):
|
def test_blocked_status(self):
|
||||||
self.harness.update_config(unset=["san-ip", "san-login"])
|
self.harness.update_config(unset=["san-ip", "san-login"])
|
||||||
self.harness.charm.on.update_status.emit()
|
|
||||||
self.assertEqual(
|
|
||||||
self.harness.charm.unit.status.message,
|
|
||||||
'Missing options: san-login,san-ip')
|
|
||||||
self.assertIsInstance(
|
self.assertIsInstance(
|
||||||
self.harness.charm.unit.status,
|
self.harness.charm.unit.status,
|
||||||
BlockedStatus)
|
BlockedStatus)
|
||||||
|
message = self.harness.charm.unit.status.message
|
||||||
|
self.assertIn('san-login', message)
|
||||||
|
self.assertIn('san-ip', message)
|
||||||
|
|
||||||
def test_blocked_unset_retention_expiration(self):
|
def test_blocked_unset_retention_expiration(self):
|
||||||
self.harness.update_config({
|
self.harness.update_config({
|
||||||
@@ -167,5 +152,5 @@ class TestCharm(unittest.TestCase):
|
|||||||
|
|
||||||
def test_invalid_config_driver_type(self):
|
def test_invalid_config_driver_type(self):
|
||||||
self.harness.update_config({'driver-type': '???'})
|
self.harness.update_config({'driver-type': '???'})
|
||||||
self.assertIsInstance(self.harness.charm.unit.status,
|
self.assertFalse(isinstance(self.harness.charm.unit.status,
|
||||||
BlockedStatus)
|
ActiveStatus))
|
||||||
|
Reference in New Issue
Block a user