Merge "Add support for block device encryption"
This commit is contained in:
commit
b03a8bc5bd
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ tags
|
|||||||
*.pyc
|
*.pyc
|
||||||
tests/cirros-*
|
tests/cirros-*
|
||||||
func-results.json
|
func-results.json
|
||||||
|
.settings
|
||||||
|
**/__pycache__
|
||||||
|
@ -6,6 +6,7 @@ global
|
|||||||
group haproxy
|
group haproxy
|
||||||
spread-checks 0
|
spread-checks 0
|
||||||
stats socket /var/run/haproxy/admin.sock mode 600 level admin
|
stats socket /var/run/haproxy/admin.sock mode 600 level admin
|
||||||
|
stats socket /var/run/haproxy/operator.sock mode 600 level operator
|
||||||
stats timeout 2m
|
stats timeout 2m
|
||||||
|
|
||||||
defaults
|
defaults
|
||||||
|
17
config.yaml
17
config.yaml
@ -40,6 +40,17 @@ options:
|
|||||||
description: |
|
description: |
|
||||||
If true, charm will attempt to unmount and overwrite existing and in-use
|
If true, charm will attempt to unmount and overwrite existing and in-use
|
||||||
block-devices (WARNING).
|
block-devices (WARNING).
|
||||||
|
ephemeral-unmount:
|
||||||
|
type: string
|
||||||
|
default:
|
||||||
|
description: |
|
||||||
|
Cloud instances provide ephermeral storage which is normally mounted
|
||||||
|
on /mnt.
|
||||||
|
.
|
||||||
|
Setting this option to the path of the ephemeral mountpoint will force
|
||||||
|
an unmount of the corresponding device so that it can be used as a swift
|
||||||
|
storage device. This is useful for testing purposes (cloud deployment
|
||||||
|
is not a typical use case).
|
||||||
zone:
|
zone:
|
||||||
default: 1
|
default: 1
|
||||||
type: int
|
type: int
|
||||||
@ -189,3 +200,9 @@ options:
|
|||||||
be loaded, the charm will fail to install.
|
be loaded, the charm will fail to install.
|
||||||
type: boolean
|
type: boolean
|
||||||
default: False
|
default: False
|
||||||
|
encrypt:
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Encrypt block devices used by swift using dm-crypt, making use of
|
||||||
|
vault for encryption key management; requires a relation to vault.
|
||||||
|
1
hooks/block-devices-storage-attached
Symbolic link
1
hooks/block-devices-storage-attached
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
storage.bootstrap
|
1
hooks/block-devices-storage-detached
Symbolic link
1
hooks/block-devices-storage-detached
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
storage.bootstrap
|
1
hooks/secrets-storage-relation-broken
Symbolic link
1
hooks/secrets-storage-relation-broken
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
swift_storage_hooks.py
|
1
hooks/secrets-storage-relation-changed
Symbolic link
1
hooks/secrets-storage-relation-changed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
swift_storage_hooks.py
|
1
hooks/secrets-storage-relation-departed
Symbolic link
1
hooks/secrets-storage-relation-departed
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
swift_storage_hooks.py
|
1
hooks/secrets-storage-relation-joined
Symbolic link
1
hooks/secrets-storage-relation-joined
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
swift_storage_hooks.py
|
8
hooks/storage.bootstrap
Executable file
8
hooks/storage.bootstrap
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! dpkg -s swift > /dev/null 2>&1; then
|
||||||
|
juju-log "Swift not yet installed."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
./hooks/storage.real
|
1
hooks/storage.real
Symbolic link
1
hooks/storage.real
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
swift_storage_hooks.py
|
@ -14,16 +14,20 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from lib.swift_storage_utils import (
|
from lib.swift_storage_utils import (
|
||||||
PACKAGES,
|
PACKAGES,
|
||||||
RESTART_MAP,
|
RESTART_MAP,
|
||||||
SWIFT_SVCS,
|
SWIFT_SVCS,
|
||||||
determine_block_devices,
|
|
||||||
do_openstack_upgrade,
|
do_openstack_upgrade,
|
||||||
ensure_swift_directories,
|
ensure_swift_directories,
|
||||||
fetch_swift_rings,
|
fetch_swift_rings,
|
||||||
@ -53,16 +57,20 @@ from charmhelpers.core.hookenv import (
|
|||||||
relations_of_type,
|
relations_of_type,
|
||||||
status_set,
|
status_set,
|
||||||
ingress_address,
|
ingress_address,
|
||||||
|
DEBUG,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.fetch import (
|
from charmhelpers.fetch import (
|
||||||
apt_install,
|
apt_install,
|
||||||
apt_update,
|
apt_update,
|
||||||
|
add_source,
|
||||||
filter_installed_packages
|
filter_installed_packages
|
||||||
)
|
)
|
||||||
from charmhelpers.core.host import (
|
from charmhelpers.core.host import (
|
||||||
add_to_updatedb_prunepath,
|
add_to_updatedb_prunepath,
|
||||||
rsync,
|
rsync,
|
||||||
|
write_file,
|
||||||
|
umount,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.core.sysctl import create as create_sysctl
|
from charmhelpers.core.sysctl import create as create_sysctl
|
||||||
@ -81,9 +89,12 @@ from charmhelpers.contrib.network.ip import (
|
|||||||
from charmhelpers.contrib.network import ufw
|
from charmhelpers.contrib.network import ufw
|
||||||
from charmhelpers.contrib.charmsupport import nrpe
|
from charmhelpers.contrib.charmsupport import nrpe
|
||||||
from charmhelpers.contrib.hardening.harden import harden
|
from charmhelpers.contrib.hardening.harden import harden
|
||||||
|
from charmhelpers.core.unitdata import kv
|
||||||
|
|
||||||
from distutils.dir_util import mkpath
|
from distutils.dir_util import mkpath
|
||||||
|
|
||||||
|
import charmhelpers.contrib.openstack.vaultlocker as vaultlocker
|
||||||
|
|
||||||
hooks = Hooks()
|
hooks = Hooks()
|
||||||
CONFIGS = register_configs()
|
CONFIGS = register_configs()
|
||||||
NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins'
|
NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins'
|
||||||
@ -173,8 +184,6 @@ def install():
|
|||||||
apt_update()
|
apt_update()
|
||||||
apt_install(PACKAGES, fatal=True)
|
apt_install(PACKAGES, fatal=True)
|
||||||
initialize_ufw()
|
initialize_ufw()
|
||||||
status_set('maintenance', 'Setting up storage')
|
|
||||||
setup_storage()
|
|
||||||
ensure_swift_directories()
|
ensure_swift_directories()
|
||||||
|
|
||||||
|
|
||||||
@ -186,6 +195,10 @@ def config_changed():
|
|||||||
initialize_ufw()
|
initialize_ufw()
|
||||||
else:
|
else:
|
||||||
ufw.disable()
|
ufw.disable()
|
||||||
|
|
||||||
|
if config('ephemeral-unmount'):
|
||||||
|
umount(config('ephemeral-unmount'), persist=True)
|
||||||
|
|
||||||
if config('prefer-ipv6'):
|
if config('prefer-ipv6'):
|
||||||
status_set('maintenance', 'Configuring ipv6')
|
status_set('maintenance', 'Configuring ipv6')
|
||||||
assert_charm_supports_ipv6()
|
assert_charm_supports_ipv6()
|
||||||
@ -198,10 +211,9 @@ def config_changed():
|
|||||||
status_set('maintenance', 'Running openstack upgrade')
|
status_set('maintenance', 'Running openstack upgrade')
|
||||||
do_openstack_upgrade(configs=CONFIGS)
|
do_openstack_upgrade(configs=CONFIGS)
|
||||||
|
|
||||||
setup_storage()
|
install_vaultlocker()
|
||||||
|
|
||||||
for rid in relation_ids('swift-storage'):
|
configure_storage()
|
||||||
swift_storage_relation_joined(rid=rid)
|
|
||||||
|
|
||||||
CONFIGS.write_all()
|
CONFIGS.write_all()
|
||||||
|
|
||||||
@ -216,6 +228,17 @@ def config_changed():
|
|||||||
add_to_updatedb_prunepath(STORAGE_MOUNT_PATH)
|
add_to_updatedb_prunepath(STORAGE_MOUNT_PATH)
|
||||||
|
|
||||||
|
|
||||||
|
def install_vaultlocker():
|
||||||
|
"""Determine whether vaultlocker is required and install"""
|
||||||
|
if config('encrypt'):
|
||||||
|
pkgs = ['vaultlocker', 'python-hvac']
|
||||||
|
installed = len(filter_installed_packages(pkgs)) == 0
|
||||||
|
if not installed:
|
||||||
|
add_source('ppa:openstack-charmers/vaultlocker')
|
||||||
|
apt_update(fatal=True)
|
||||||
|
apt_install(pkgs, fatal=True)
|
||||||
|
|
||||||
|
|
||||||
@hooks.hook('upgrade-charm')
|
@hooks.hook('upgrade-charm')
|
||||||
@harden()
|
@harden()
|
||||||
def upgrade_charm():
|
def upgrade_charm():
|
||||||
@ -227,6 +250,10 @@ def upgrade_charm():
|
|||||||
|
|
||||||
@hooks.hook()
|
@hooks.hook()
|
||||||
def swift_storage_relation_joined(rid=None):
|
def swift_storage_relation_joined(rid=None):
|
||||||
|
if config('encrypt') and not vaultlocker.vault_relation_complete():
|
||||||
|
log('Encryption configured and vault not ready, deferring',
|
||||||
|
level=DEBUG)
|
||||||
|
return
|
||||||
rel_settings = {
|
rel_settings = {
|
||||||
'zone': config('zone'),
|
'zone': config('zone'),
|
||||||
'object_port': config('object-server-port'),
|
'object_port': config('object-server-port'),
|
||||||
@ -234,7 +261,8 @@ def swift_storage_relation_joined(rid=None):
|
|||||||
'account_port': config('account-server-port'),
|
'account_port': config('account-server-port'),
|
||||||
}
|
}
|
||||||
|
|
||||||
devs = determine_block_devices() or []
|
db = kv()
|
||||||
|
devs = db.get('prepared-devices', [])
|
||||||
devs = [os.path.basename(d) for d in devs]
|
devs = [os.path.basename(d) for d in devs]
|
||||||
rel_settings['device'] = ':'.join(devs)
|
rel_settings['device'] = ':'.join(devs)
|
||||||
# Keep a reference of devices we are adding to the ring
|
# Keep a reference of devices we are adding to the ring
|
||||||
@ -272,6 +300,34 @@ def swift_storage_relation_departed():
|
|||||||
revoke_access(removed_client, port)
|
revoke_access(removed_client, port)
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('secrets-storage-relation-joined')
|
||||||
|
def secrets_storage_joined(relation_id=None):
|
||||||
|
relation_set(relation_id=relation_id,
|
||||||
|
secret_backend='charm-vaultlocker',
|
||||||
|
isolated=True,
|
||||||
|
access_address=get_relation_ip('secrets-storage'),
|
||||||
|
hostname=socket.gethostname())
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('secrets-storage-relation-changed')
|
||||||
|
def secrets_storage_changed():
|
||||||
|
vault_ca = relation_get('vault_ca')
|
||||||
|
if vault_ca:
|
||||||
|
vault_ca = base64.decodestring(json.loads(vault_ca).encode())
|
||||||
|
write_file('/usr/local/share/ca-certificates/vault-ca.crt',
|
||||||
|
vault_ca, perms=0o644)
|
||||||
|
subprocess.check_call(['update-ca-certificates', '--fresh'])
|
||||||
|
configure_storage()
|
||||||
|
|
||||||
|
|
||||||
|
@hooks.hook('storage.real')
|
||||||
|
def configure_storage():
|
||||||
|
setup_storage(config('encrypt'))
|
||||||
|
|
||||||
|
for rid in relation_ids('swift-storage'):
|
||||||
|
swift_storage_relation_joined(rid=rid)
|
||||||
|
|
||||||
|
|
||||||
@hooks.hook('nrpe-external-master-relation-joined')
|
@hooks.hook('nrpe-external-master-relation-joined')
|
||||||
@hooks.hook('nrpe-external-master-relation-changed')
|
@hooks.hook('nrpe-external-master-relation-changed')
|
||||||
def update_nrpe_config():
|
def update_nrpe_config():
|
||||||
@ -318,7 +374,10 @@ def main():
|
|||||||
hooks.execute(sys.argv)
|
hooks.execute(sys.argv)
|
||||||
except UnregisteredHookError as e:
|
except UnregisteredHookError as e:
|
||||||
log('Unknown hook {} - skipping.'.format(e))
|
log('Unknown hook {} - skipping.'.format(e))
|
||||||
set_os_workload_status(CONFIGS, REQUIRED_INTERFACES,
|
required_interfaces = copy.deepcopy(REQUIRED_INTERFACES)
|
||||||
|
if config('encrypt'):
|
||||||
|
required_interfaces['vault'] = ['secrets-storage']
|
||||||
|
set_os_workload_status(CONFIGS, required_interfaces,
|
||||||
charm_func=assess_status)
|
charm_func=assess_status)
|
||||||
os_application_version_set(VERSION_PACKAGE)
|
os_application_version_set(VERSION_PACKAGE)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import re
|
|||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import uuid
|
||||||
|
|
||||||
from subprocess import check_call, call, CalledProcessError, check_output
|
from subprocess import check_call, call, CalledProcessError, check_output
|
||||||
|
|
||||||
@ -54,6 +55,8 @@ from charmhelpers.core.hookenv import (
|
|||||||
relation_ids,
|
relation_ids,
|
||||||
iter_units_for_relation_name,
|
iter_units_for_relation_name,
|
||||||
ingress_address,
|
ingress_address,
|
||||||
|
storage_list,
|
||||||
|
storage_get,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.network import ufw
|
from charmhelpers.contrib.network import ufw
|
||||||
@ -62,6 +65,7 @@ from charmhelpers.contrib.network.ip import get_host_ip
|
|||||||
from charmhelpers.contrib.storage.linux.utils import (
|
from charmhelpers.contrib.storage.linux.utils import (
|
||||||
is_block_device,
|
is_block_device,
|
||||||
is_device_mounted,
|
is_device_mounted,
|
||||||
|
mkfs_xfs,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.storage.linux.loopback import (
|
from charmhelpers.contrib.storage.linux.loopback import (
|
||||||
@ -84,6 +88,10 @@ from charmhelpers.core.decorators import (
|
|||||||
retry_on_exception,
|
retry_on_exception,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import charmhelpers.contrib.openstack.vaultlocker as vaultlocker
|
||||||
|
|
||||||
|
from charmhelpers.core.unitdata import kv
|
||||||
|
|
||||||
PACKAGES = [
|
PACKAGES = [
|
||||||
'swift', 'swift-account', 'swift-container', 'swift-object',
|
'swift', 'swift-account', 'swift-container', 'swift-object',
|
||||||
'xfsprogs', 'gdisk', 'lvm2', 'python-jinja2', 'python-psutil',
|
'xfsprogs', 'gdisk', 'lvm2', 'python-jinja2', 'python-psutil',
|
||||||
@ -162,11 +170,14 @@ def register_configs():
|
|||||||
[SwiftStorageContext()])
|
[SwiftStorageContext()])
|
||||||
configs.register('/etc/rsync-juju.d/050-swift-storage.conf',
|
configs.register('/etc/rsync-juju.d/050-swift-storage.conf',
|
||||||
[RsyncContext(), SwiftStorageServerContext()])
|
[RsyncContext(), SwiftStorageServerContext()])
|
||||||
|
# NOTE: add VaultKVContext so interface status can be assessed
|
||||||
for server in ['account', 'object', 'container']:
|
for server in ['account', 'object', 'container']:
|
||||||
configs.register('/etc/swift/%s-server.conf' % server,
|
configs.register('/etc/swift/%s-server.conf' % server,
|
||||||
[SwiftStorageServerContext(),
|
[SwiftStorageServerContext(),
|
||||||
context.BindHostContext(),
|
context.BindHostContext(),
|
||||||
context.WorkerConfigContext()]),
|
context.WorkerConfigContext(),
|
||||||
|
vaultlocker.VaultKVContext(
|
||||||
|
vaultlocker.VAULTLOCKER_BACKEND)]),
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
|
|
||||||
@ -269,6 +280,12 @@ def determine_block_devices():
|
|||||||
else:
|
else:
|
||||||
bdevs = block_device.split(' ')
|
bdevs = block_device.split(' ')
|
||||||
|
|
||||||
|
# List storage instances for the 'block-devices'
|
||||||
|
# store declared for this charm too, and add
|
||||||
|
# their block device paths to the list.
|
||||||
|
storage_ids = storage_list('block-devices')
|
||||||
|
bdevs.extend((storage_get('location', s) for s in storage_ids))
|
||||||
|
|
||||||
bdevs = list(set(bdevs))
|
bdevs = list(set(bdevs))
|
||||||
# attempt to ensure block devices, but filter out missing devs
|
# attempt to ensure block devices, but filter out missing devs
|
||||||
_none = ['None', 'none']
|
_none = ['None', 'none']
|
||||||
@ -279,19 +296,6 @@ def determine_block_devices():
|
|||||||
return valid_bdevs
|
return valid_bdevs
|
||||||
|
|
||||||
|
|
||||||
def mkfs_xfs(bdev, force=False):
|
|
||||||
"""Format device with XFS filesystem.
|
|
||||||
|
|
||||||
By default this should fail if the device already has a filesystem on it.
|
|
||||||
"""
|
|
||||||
cmd = ['mkfs.xfs']
|
|
||||||
if force:
|
|
||||||
cmd.append("-f")
|
|
||||||
|
|
||||||
cmd += ['-i', 'size=1024', bdev]
|
|
||||||
check_call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def devstore_safe_load(devstore):
|
def devstore_safe_load(devstore):
|
||||||
"""Attempt to decode json data and return None if an error occurs while
|
"""Attempt to decode json data and return None if an error occurs while
|
||||||
also printing a log.
|
also printing a log.
|
||||||
@ -446,21 +450,73 @@ def ensure_devs_tracked():
|
|||||||
is_device_in_ring(dev, skip_rel_check=True)
|
is_device_in_ring(dev, skip_rel_check=True)
|
||||||
|
|
||||||
|
|
||||||
def setup_storage():
|
def setup_storage(encrypt=False):
|
||||||
|
# Preflight check vault relation if encryption is enabled
|
||||||
|
vault_kv = vaultlocker.VaultKVContext(vaultlocker.VAULTLOCKER_BACKEND)
|
||||||
|
context = vault_kv()
|
||||||
|
if encrypt and not vault_kv.complete:
|
||||||
|
log("Encryption requested but vault relation not complete",
|
||||||
|
level=DEBUG)
|
||||||
|
return
|
||||||
|
elif encrypt and vault_kv.complete:
|
||||||
|
# NOTE: only write vaultlocker configuration once relation is complete
|
||||||
|
# otherwise we run the chance of an empty configuration file
|
||||||
|
# being installed on a machine with other vaultlocker based
|
||||||
|
# services
|
||||||
|
vaultlocker.write_vaultlocker_conf(context, priority=90)
|
||||||
|
|
||||||
# Ensure /srv/node exists just in case no disks
|
# Ensure /srv/node exists just in case no disks
|
||||||
# are detected and used.
|
# are detected and used.
|
||||||
mkdir(os.path.join('/srv', 'node'),
|
mkdir(os.path.join('/srv', 'node'),
|
||||||
owner='swift', group='swift',
|
owner='swift', group='swift',
|
||||||
perms=0o755)
|
perms=0o755)
|
||||||
reformat = str(config('overwrite')).lower() == "true"
|
reformat = str(config('overwrite')).lower() == "true"
|
||||||
|
|
||||||
|
db = kv()
|
||||||
|
prepared_devices = db.get('prepared-devices', [])
|
||||||
|
|
||||||
for dev in determine_block_devices():
|
for dev in determine_block_devices():
|
||||||
|
if dev in prepared_devices:
|
||||||
|
log('Device {} already processed by charm,'
|
||||||
|
' skipping'.format(dev))
|
||||||
|
continue
|
||||||
|
|
||||||
if is_device_in_ring(os.path.basename(dev)):
|
if is_device_in_ring(os.path.basename(dev)):
|
||||||
log("Device '%s' already in the ring - ignoring" % (dev))
|
log("Device '%s' already in the ring - ignoring" % (dev))
|
||||||
|
# NOTE: record existing use of device dealing with
|
||||||
|
# upgrades from older versions of charms without
|
||||||
|
# this feature
|
||||||
|
prepared_devices.append(dev)
|
||||||
|
db.set('prepared-devices', prepared_devices)
|
||||||
|
db.flush()
|
||||||
|
continue
|
||||||
|
|
||||||
|
# NOTE: this deals with a dm-crypt'ed block device already in
|
||||||
|
# use
|
||||||
|
if is_device_mounted(dev):
|
||||||
|
log("Device '{}' is already mounted, ignoring".format(dev))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if reformat:
|
if reformat:
|
||||||
clean_storage(dev)
|
clean_storage(dev)
|
||||||
|
|
||||||
|
loopback_device = is_mapped_loopback_device(dev)
|
||||||
|
options = None
|
||||||
|
|
||||||
|
if encrypt and not loopback_device:
|
||||||
|
dev_uuid = str(uuid.uuid4())
|
||||||
|
check_call(['vaultlocker', 'encrypt',
|
||||||
|
'--uuid', dev_uuid,
|
||||||
|
dev])
|
||||||
|
dev = '/dev/mapper/crypt-{}'.format(dev_uuid)
|
||||||
|
options = ','.join([
|
||||||
|
"defaults",
|
||||||
|
"nofail",
|
||||||
|
("x-systemd.requires="
|
||||||
|
"vaultlocker-decrypt@{uuid}.service".format(uuid=dev_uuid)),
|
||||||
|
"comment=vaultlocker",
|
||||||
|
])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# If not cleaned and in use, mkfs should fail.
|
# If not cleaned and in use, mkfs should fail.
|
||||||
mkfs_xfs(dev, force=reformat)
|
mkfs_xfs(dev, force=reformat)
|
||||||
@ -475,8 +531,6 @@ def setup_storage():
|
|||||||
_mp = os.path.join('/srv', 'node', basename)
|
_mp = os.path.join('/srv', 'node', basename)
|
||||||
mkdir(_mp, owner='swift', group='swift')
|
mkdir(_mp, owner='swift', group='swift')
|
||||||
|
|
||||||
options = None
|
|
||||||
loopback_device = is_mapped_loopback_device(dev)
|
|
||||||
mountpoint = '/srv/node/%s' % basename
|
mountpoint = '/srv/node/%s' % basename
|
||||||
if loopback_device:
|
if loopback_device:
|
||||||
# If an exiting fstab entry exists using the image file as the
|
# If an exiting fstab entry exists using the image file as the
|
||||||
@ -497,6 +551,12 @@ def setup_storage():
|
|||||||
check_call(['chown', '-R', 'swift:swift', mountpoint])
|
check_call(['chown', '-R', 'swift:swift', mountpoint])
|
||||||
check_call(['chmod', '-R', '0755', mountpoint])
|
check_call(['chmod', '-R', '0755', mountpoint])
|
||||||
|
|
||||||
|
# NOTE: record preparation of device - this will be used when
|
||||||
|
# providing block device configuration for ring builders.
|
||||||
|
prepared_devices.append(dev)
|
||||||
|
db.set('prepared-devices', prepared_devices)
|
||||||
|
db.flush()
|
||||||
|
|
||||||
|
|
||||||
@retry_on_exception(3, base_delay=2, exc_type=CalledProcessError)
|
@retry_on_exception(3, base_delay=2, exc_type=CalledProcessError)
|
||||||
def fetch_swift_rings(rings_url):
|
def fetch_swift_rings(rings_url):
|
||||||
|
@ -28,3 +28,12 @@ provides:
|
|||||||
scope: container
|
scope: container
|
||||||
swift-storage:
|
swift-storage:
|
||||||
interface: swift
|
interface: swift
|
||||||
|
requires:
|
||||||
|
secrets-storage:
|
||||||
|
interface: vault-kv
|
||||||
|
storage:
|
||||||
|
block-devices:
|
||||||
|
type: block
|
||||||
|
multiple:
|
||||||
|
range: 0-
|
||||||
|
minimum-size: 1G
|
||||||
|
6
templates/vaultlocker.conf.j2
Normal file
6
templates/vaultlocker.conf.j2
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# vaultlocker configuration from swift-proxy charm
|
||||||
|
[vault]
|
||||||
|
url = {{ vault_url }}
|
||||||
|
approle = {{ role_id }}
|
||||||
|
backend = {{ secret_backend }}
|
||||||
|
secret_id = {{ secret_id }}
|
@ -5,12 +5,12 @@ coverage>=3.6
|
|||||||
mock>=1.2
|
mock>=1.2
|
||||||
flake8>=2.2.4,<=2.4.1
|
flake8>=2.2.4,<=2.4.1
|
||||||
os-testr>=0.4.1
|
os-testr>=0.4.1
|
||||||
charm-tools>=2.0.0
|
charm-tools>=2.0.0;python_version=='2.7' # cheetah templates aren't availble in Python 3+
|
||||||
requests==2.6.0
|
requests==2.6.0
|
||||||
# BEGIN: Amulet OpenStack Charm Helper Requirements
|
# BEGIN: Amulet OpenStack Charm Helper Requirements
|
||||||
# Liberty client lower constraints
|
# Liberty client lower constraints
|
||||||
amulet>=1.14.3,<2.0
|
amulet>=1.14.3,<2.0
|
||||||
bundletester>=0.6.1,<1.0
|
bundletester>=0.6.1,<1.0;python_version=='2.7' # cheetah templates aren't availble in Python 3+
|
||||||
python-ceilometerclient>=1.5.0
|
python-ceilometerclient>=1.5.0
|
||||||
python-cinderclient>=1.4.0
|
python-cinderclient>=1.4.0
|
||||||
python-glanceclient>=1.1.0
|
python-glanceclient>=1.1.0
|
||||||
|
@ -96,6 +96,7 @@ class SwiftStorageBasicDeployment(OpenStackAmuletDeployment):
|
|||||||
'zone': '1',
|
'zone': '1',
|
||||||
'block-device': 'vdb',
|
'block-device': 'vdb',
|
||||||
'overwrite': 'true',
|
'overwrite': 'true',
|
||||||
|
'ephemeral-unmount': '/mnt',
|
||||||
}
|
}
|
||||||
pxc_config = {
|
pxc_config = {
|
||||||
'innodb-buffer-pool-size': '256M',
|
'innodb-buffer-pool-size': '256M',
|
||||||
|
5
tox.ini
5
tox.ini
@ -26,6 +26,11 @@ basepython = python3.5
|
|||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
|
[testenv:py36]
|
||||||
|
basepython = python3.6
|
||||||
|
deps = -r{toxinidir}/requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
[testenv:pep8]
|
[testenv:pep8]
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
|
@ -18,7 +18,7 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from test_utils import CharmTestCase, patch_open
|
from test_utils import CharmTestCase, TestKV, patch_open
|
||||||
|
|
||||||
with patch('hooks.charmhelpers.contrib.hardening.harden.harden') as mock_dec:
|
with patch('hooks.charmhelpers.contrib.hardening.harden.harden') as mock_dec:
|
||||||
mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f:
|
mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f:
|
||||||
@ -47,7 +47,6 @@ TO_PATCH = [
|
|||||||
'configure_installation_source',
|
'configure_installation_source',
|
||||||
'openstack_upgrade_available',
|
'openstack_upgrade_available',
|
||||||
# swift_storage_utils
|
# swift_storage_utils
|
||||||
'determine_block_devices',
|
|
||||||
'do_openstack_upgrade',
|
'do_openstack_upgrade',
|
||||||
'ensure_swift_directories',
|
'ensure_swift_directories',
|
||||||
'execd_preinstall',
|
'execd_preinstall',
|
||||||
@ -66,6 +65,7 @@ TO_PATCH = [
|
|||||||
'ufw',
|
'ufw',
|
||||||
'setup_ufw',
|
'setup_ufw',
|
||||||
'revoke_access',
|
'revoke_access',
|
||||||
|
'kv',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -93,6 +93,8 @@ class SwiftStorageRelationsTests(CharmTestCase):
|
|||||||
self.config.side_effect = self.test_config.get
|
self.config.side_effect = self.test_config.get
|
||||||
self.relation_get.side_effect = self.test_relation.get
|
self.relation_get.side_effect = self.test_relation.get
|
||||||
self.get_relation_ip.return_value = '10.10.10.2'
|
self.get_relation_ip.return_value = '10.10.10.2'
|
||||||
|
self.test_kv = TestKV()
|
||||||
|
self.kv.return_value = self.test_kv
|
||||||
|
|
||||||
@patch.object(hooks, 'add_ufw_gre_rule', lambda *args: None)
|
@patch.object(hooks, 'add_ufw_gre_rule', lambda *args: None)
|
||||||
def test_prunepath(self):
|
def test_prunepath(self):
|
||||||
@ -108,8 +110,6 @@ class SwiftStorageRelationsTests(CharmTestCase):
|
|||||||
)
|
)
|
||||||
self.assertTrue(self.apt_update.called)
|
self.assertTrue(self.apt_update.called)
|
||||||
self.apt_install.assert_called_with(PACKAGES, fatal=True)
|
self.apt_install.assert_called_with(PACKAGES, fatal=True)
|
||||||
|
|
||||||
self.assertTrue(self.setup_storage.called)
|
|
||||||
self.assertTrue(self.execd_preinstall.called)
|
self.assertTrue(self.execd_preinstall.called)
|
||||||
|
|
||||||
@patch.object(hooks, 'add_ufw_gre_rule', lambda *args: None)
|
@patch.object(hooks, 'add_ufw_gre_rule', lambda *args: None)
|
||||||
@ -197,7 +197,7 @@ class SwiftStorageRelationsTests(CharmTestCase):
|
|||||||
kvstore = mock_kvstore.return_value
|
kvstore = mock_kvstore.return_value
|
||||||
kvstore.__enter__.return_value = kvstore
|
kvstore.__enter__.return_value = kvstore
|
||||||
kvstore.get.return_value = None
|
kvstore.get.return_value = None
|
||||||
self.determine_block_devices.return_value = ['/dev/vdb']
|
self.test_kv.set('prepared-devices', ['/dev/vdb'])
|
||||||
|
|
||||||
hooks.swift_storage_relation_joined()
|
hooks.swift_storage_relation_joined()
|
||||||
|
|
||||||
@ -254,8 +254,8 @@ class SwiftStorageRelationsTests(CharmTestCase):
|
|||||||
test_uuid = uuid.uuid4()
|
test_uuid = uuid.uuid4()
|
||||||
test_environ = {'JUJU_ENV_UUID': test_uuid}
|
test_environ = {'JUJU_ENV_UUID': test_uuid}
|
||||||
mock_environ.get.side_effect = test_environ.get
|
mock_environ.get.side_effect = test_environ.get
|
||||||
self.determine_block_devices.return_value = ['/dev/vdb', '/dev/vdc',
|
self.test_kv.set('prepared-devices', ['/dev/vdb', '/dev/vdc',
|
||||||
'/dev/vdd']
|
'/dev/vdd'])
|
||||||
mock_local_unit.return_value = 'test/0'
|
mock_local_unit.return_value = 'test/0'
|
||||||
kvstore = mock_kvstore.return_value
|
kvstore = mock_kvstore.return_value
|
||||||
kvstore.__enter__.return_value = kvstore
|
kvstore.__enter__.return_value = kvstore
|
||||||
@ -298,8 +298,8 @@ class SwiftStorageRelationsTests(CharmTestCase):
|
|||||||
test_uuid = uuid.uuid4()
|
test_uuid = uuid.uuid4()
|
||||||
test_environ = {'JUJU_ENV_UUID': test_uuid}
|
test_environ = {'JUJU_ENV_UUID': test_uuid}
|
||||||
mock_environ.get.side_effect = test_environ.get
|
mock_environ.get.side_effect = test_environ.get
|
||||||
self.determine_block_devices.return_value = ['/dev/vdb', '/dev/vdc',
|
self.test_kv.set('prepared-devices', ['/dev/vdb', '/dev/vdc',
|
||||||
'/dev/vdd']
|
'/dev/vdd'])
|
||||||
mock_local_unit.return_value = 'test/0'
|
mock_local_unit.return_value = 'test/0'
|
||||||
kvstore = mock_kvstore.return_value
|
kvstore = mock_kvstore.return_value
|
||||||
kvstore.__enter__.return_value = kvstore
|
kvstore.__enter__.return_value = kvstore
|
||||||
|
@ -17,7 +17,7 @@ import tempfile
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from mock import call, patch, MagicMock
|
from mock import call, patch, MagicMock
|
||||||
from test_utils import CharmTestCase, patch_open
|
from test_utils import CharmTestCase, TestKV, patch_open
|
||||||
|
|
||||||
import lib.swift_storage_utils as swift_utils
|
import lib.swift_storage_utils as swift_utils
|
||||||
|
|
||||||
@ -50,6 +50,8 @@ TO_PATCH = [
|
|||||||
'iter_units_for_relation_name',
|
'iter_units_for_relation_name',
|
||||||
'ingress_address',
|
'ingress_address',
|
||||||
'relation_ids',
|
'relation_ids',
|
||||||
|
'vaultlocker',
|
||||||
|
'kv',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -104,11 +106,14 @@ TARGET SOURCE FSTYPE OPTIONS
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SwiftStorageUtilsTests(CharmTestCase):
|
class SwiftStorageUtilsTests(CharmTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(SwiftStorageUtilsTests, self).setUp(swift_utils, TO_PATCH)
|
super(SwiftStorageUtilsTests, self).setUp(swift_utils, TO_PATCH)
|
||||||
self.config.side_effect = self.test_config.get
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.test_kv = TestKV()
|
||||||
|
self.kv.return_value = self.test_kv
|
||||||
|
|
||||||
def test_ensure_swift_directories(self):
|
def test_ensure_swift_directories(self):
|
||||||
with patch('os.path.isdir') as isdir:
|
with patch('os.path.isdir') as isdir:
|
||||||
@ -229,18 +234,6 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
self.assertTrue(_find.called)
|
self.assertTrue(_find.called)
|
||||||
self.assertEqual(result, [])
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
def test_mkfs_xfs(self):
|
|
||||||
swift_utils.mkfs_xfs('/dev/sdb')
|
|
||||||
self.check_call.assert_called_with(
|
|
||||||
['mkfs.xfs', '-i', 'size=1024', '/dev/sdb']
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_mkfs_xfs_force(self):
|
|
||||||
swift_utils.mkfs_xfs('/dev/sdb', force=True)
|
|
||||||
self.check_call.assert_called_with(
|
|
||||||
['mkfs.xfs', '-f', '-i', 'size=1024', '/dev/sdb']
|
|
||||||
)
|
|
||||||
|
|
||||||
@patch.object(swift_utils.charmhelpers.core.fstab, "Fstab")
|
@patch.object(swift_utils.charmhelpers.core.fstab, "Fstab")
|
||||||
@patch.object(swift_utils, 'is_device_in_ring')
|
@patch.object(swift_utils, 'is_device_in_ring')
|
||||||
@patch.object(swift_utils, 'clean_storage')
|
@patch.object(swift_utils, 'clean_storage')
|
||||||
@ -249,6 +242,7 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
def test_setup_storage_no_overwrite(self, determine, mkfs, clean,
|
def test_setup_storage_no_overwrite(self, determine, mkfs, clean,
|
||||||
mock_is_device_in_ring, mock_Fstab):
|
mock_is_device_in_ring, mock_Fstab):
|
||||||
mock_is_device_in_ring.return_value = False
|
mock_is_device_in_ring.return_value = False
|
||||||
|
self.is_device_mounted.return_value = False
|
||||||
determine.return_value = ['/dev/vdb']
|
determine.return_value = ['/dev/vdb']
|
||||||
swift_utils.setup_storage()
|
swift_utils.setup_storage()
|
||||||
self.assertFalse(clean.called)
|
self.assertFalse(clean.called)
|
||||||
@ -260,6 +254,8 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
perms=0o755),
|
perms=0o755),
|
||||||
call('/srv/node/vdb', group='swift', owner='swift')
|
call('/srv/node/vdb', group='swift', owner='swift')
|
||||||
])
|
])
|
||||||
|
self.assertEqual(self.test_kv.get('prepared-devices'),
|
||||||
|
['/dev/vdb'])
|
||||||
|
|
||||||
@patch.object(swift_utils, 'is_device_in_ring')
|
@patch.object(swift_utils, 'is_device_in_ring')
|
||||||
@patch.object(swift_utils, 'clean_storage')
|
@patch.object(swift_utils, 'clean_storage')
|
||||||
@ -270,6 +266,7 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
self.test_config.set('overwrite', True)
|
self.test_config.set('overwrite', True)
|
||||||
mock_is_device_in_ring.return_value = False
|
mock_is_device_in_ring.return_value = False
|
||||||
self.is_mapped_loopback_device.return_value = None
|
self.is_mapped_loopback_device.return_value = None
|
||||||
|
self.is_device_mounted.return_value = False
|
||||||
determine.return_value = ['/dev/vdb']
|
determine.return_value = ['/dev/vdb']
|
||||||
swift_utils.setup_storage()
|
swift_utils.setup_storage()
|
||||||
clean.assert_called_with('/dev/vdb')
|
clean.assert_called_with('/dev/vdb')
|
||||||
@ -288,6 +285,8 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
perms=0o755),
|
perms=0o755),
|
||||||
call('/srv/node/vdb', group='swift', owner='swift')
|
call('/srv/node/vdb', group='swift', owner='swift')
|
||||||
])
|
])
|
||||||
|
self.assertEqual(self.test_kv.get('prepared-devices'),
|
||||||
|
['/dev/vdb'])
|
||||||
|
|
||||||
@patch.object(swift_utils, 'is_device_in_ring')
|
@patch.object(swift_utils, 'is_device_in_ring')
|
||||||
@patch.object(swift_utils, 'determine_block_devices')
|
@patch.object(swift_utils, 'determine_block_devices')
|
||||||
@ -304,6 +303,71 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
swift_utils.setup_storage()
|
swift_utils.setup_storage()
|
||||||
self.assertEqual(self.check_call.call_count, 0)
|
self.assertEqual(self.check_call.call_count, 0)
|
||||||
|
|
||||||
|
@patch.object(swift_utils, "uuid")
|
||||||
|
@patch.object(swift_utils, "vaultlocker")
|
||||||
|
@patch.object(swift_utils.charmhelpers.core.fstab, "Fstab")
|
||||||
|
@patch.object(swift_utils, 'is_device_in_ring')
|
||||||
|
@patch.object(swift_utils, 'clean_storage')
|
||||||
|
@patch.object(swift_utils, 'mkfs_xfs')
|
||||||
|
@patch.object(swift_utils, 'determine_block_devices')
|
||||||
|
def test_setup_storage_encrypt(self, determine, mkfs, clean,
|
||||||
|
mock_is_device_in_ring, mock_Fstab,
|
||||||
|
mock_vaultlocker, mock_uuid):
|
||||||
|
mock_context = MagicMock()
|
||||||
|
mock_context.complete = True
|
||||||
|
mock_context.return_value = 'test_context'
|
||||||
|
mock_vaultlocker.VaultKVContext.return_value = mock_context
|
||||||
|
mock_uuid.uuid4.return_value = '7c3ff7c8-fd20-4dca-9be6-6f44f213d3fe'
|
||||||
|
mock_is_device_in_ring.return_value = False
|
||||||
|
self.is_device_mounted.return_value = False
|
||||||
|
self.is_mapped_loopback_device.return_value = None
|
||||||
|
determine.return_value = ['/dev/vdb']
|
||||||
|
swift_utils.setup_storage(encrypt=True)
|
||||||
|
self.assertFalse(clean.called)
|
||||||
|
calls = [
|
||||||
|
call(['vaultlocker', 'encrypt',
|
||||||
|
'--uuid', '7c3ff7c8-fd20-4dca-9be6-6f44f213d3fe',
|
||||||
|
'/dev/vdb']),
|
||||||
|
call(['chown', '-R', 'swift:swift',
|
||||||
|
'/srv/node/crypt-7c3ff7c8-fd20-4dca-9be6-6f44f213d3fe']),
|
||||||
|
call(['chmod', '-R', '0755',
|
||||||
|
'/srv/node/crypt-7c3ff7c8-fd20-4dca-9be6-6f44f213d3fe'])
|
||||||
|
]
|
||||||
|
self.check_call.assert_has_calls(calls)
|
||||||
|
self.mkdir.assert_has_calls([
|
||||||
|
call('/srv/node', owner='swift', group='swift',
|
||||||
|
perms=0o755),
|
||||||
|
call('/srv/node/crypt-7c3ff7c8-fd20-4dca-9be6-6f44f213d3fe',
|
||||||
|
group='swift', owner='swift')
|
||||||
|
])
|
||||||
|
self.assertEqual(self.test_kv.get('prepared-devices'),
|
||||||
|
['/dev/mapper/crypt-7c3ff7c8-fd20-4dca-9be6-6f44f213d3fe'])
|
||||||
|
mock_vaultlocker.write_vaultlocker_conf.assert_called_with(
|
||||||
|
'test_context',
|
||||||
|
priority=90
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch.object(swift_utils, "uuid")
|
||||||
|
@patch.object(swift_utils, "vaultlocker")
|
||||||
|
@patch.object(swift_utils.charmhelpers.core.fstab, "Fstab")
|
||||||
|
@patch.object(swift_utils, 'is_device_in_ring')
|
||||||
|
@patch.object(swift_utils, 'clean_storage')
|
||||||
|
@patch.object(swift_utils, 'mkfs_xfs')
|
||||||
|
@patch.object(swift_utils, 'determine_block_devices')
|
||||||
|
def test_setup_storage_encrypt_noready(self, determine, mkfs, clean,
|
||||||
|
mock_is_device_in_ring, mock_Fstab,
|
||||||
|
mock_vaultlocker, mock_uuid):
|
||||||
|
mock_context = MagicMock()
|
||||||
|
mock_context.complete = False
|
||||||
|
mock_context.return_value = {}
|
||||||
|
mock_vaultlocker.VaultKVContext.return_value = mock_context
|
||||||
|
swift_utils.setup_storage(encrypt=True)
|
||||||
|
mock_vaultlocker.write_vaultlocker_conf.assert_not_called()
|
||||||
|
clean.assert_not_called()
|
||||||
|
self.check_call.assert_not_called()
|
||||||
|
self.mkdir.assert_not_called()
|
||||||
|
self.assertEqual(self.test_kv.get('prepared-devices'), None)
|
||||||
|
|
||||||
def _fake_is_device_mounted(self, device):
|
def _fake_is_device_mounted(self, device):
|
||||||
if device in ["/dev/sda", "/dev/vda", "/dev/cciss/c0d0"]:
|
if device in ["/dev/sda", "/dev/vda", "/dev/cciss/c0d0"]:
|
||||||
return True
|
return True
|
||||||
@ -373,6 +437,7 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
server.return_value = 'swift_server_context'
|
server.return_value = 'swift_server_context'
|
||||||
bind_context.return_value = 'bind_host_context'
|
bind_context.return_value = 'bind_host_context'
|
||||||
worker_context.return_value = 'worker_context'
|
worker_context.return_value = 'worker_context'
|
||||||
|
self.vaultlocker.VaultKVContext.return_value = 'vl_context'
|
||||||
self.get_os_codename_package.return_value = 'grizzly'
|
self.get_os_codename_package.return_value = 'grizzly'
|
||||||
configs = MagicMock()
|
configs = MagicMock()
|
||||||
configs.register = MagicMock()
|
configs.register = MagicMock()
|
||||||
@ -386,13 +451,16 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
['rsync_context', 'swift_context']),
|
['rsync_context', 'swift_context']),
|
||||||
call('/etc/swift/account-server.conf', ['swift_context',
|
call('/etc/swift/account-server.conf', ['swift_context',
|
||||||
'bind_host_context',
|
'bind_host_context',
|
||||||
'worker_context']),
|
'worker_context',
|
||||||
|
'vl_context']),
|
||||||
call('/etc/swift/object-server.conf', ['swift_context',
|
call('/etc/swift/object-server.conf', ['swift_context',
|
||||||
'bind_host_context',
|
'bind_host_context',
|
||||||
'worker_context']),
|
'worker_context',
|
||||||
|
'vl_context']),
|
||||||
call('/etc/swift/container-server.conf', ['swift_context',
|
call('/etc/swift/container-server.conf', ['swift_context',
|
||||||
'bind_host_context',
|
'bind_host_context',
|
||||||
'worker_context'])
|
'worker_context',
|
||||||
|
'vl_context'])
|
||||||
]
|
]
|
||||||
self.assertEqual(ex, configs.register.call_args_list)
|
self.assertEqual(ex, configs.register.call_args_list)
|
||||||
|
|
||||||
@ -434,6 +502,7 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
mock_is_device_in_ring.return_value = False
|
mock_is_device_in_ring.return_value = False
|
||||||
determine.return_value = ["/dev/loop0", ]
|
determine.return_value = ["/dev/loop0", ]
|
||||||
self.is_mapped_loopback_device.return_value = "/srv/test.img"
|
self.is_mapped_loopback_device.return_value = "/srv/test.img"
|
||||||
|
self.is_device_mounted.return_value = False
|
||||||
swift_utils.setup_storage()
|
swift_utils.setup_storage()
|
||||||
self.mount.assert_called_with(
|
self.mount.assert_called_with(
|
||||||
"/dev/loop0",
|
"/dev/loop0",
|
||||||
@ -476,6 +545,7 @@ class SwiftStorageUtilsTests(CharmTestCase):
|
|||||||
mock_is_device_in_ring.return_value = False
|
mock_is_device_in_ring.return_value = False
|
||||||
determine.return_value = ["/dev/loop0", ]
|
determine.return_value = ["/dev/loop0", ]
|
||||||
self.is_mapped_loopback_device.return_value = "/srv/test.img"
|
self.is_mapped_loopback_device.return_value = "/srv/test.img"
|
||||||
|
self.is_device_mounted.return_value = False
|
||||||
swift_utils.setup_storage()
|
swift_utils.setup_storage()
|
||||||
self.mount.assert_called_with(
|
self.mount.assert_called_with(
|
||||||
"/srv/test.img",
|
"/srv/test.img",
|
||||||
|
@ -117,6 +117,23 @@ class TestRelation(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class TestKV(dict):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(TestKV, self).__init__()
|
||||||
|
self.flushed = False
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
|
def get(self, attribute, default=None):
|
||||||
|
return self.data.get(attribute, default)
|
||||||
|
|
||||||
|
def set(self, attribute, value):
|
||||||
|
self.data[attribute] = value
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
self.flushed = True
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def patch_open():
|
def patch_open():
|
||||||
'''Patch open() to allow mocking both open() itself and the file that is
|
'''Patch open() to allow mocking both open() itself and the file that is
|
||||||
|
Loading…
Reference in New Issue
Block a user