Charmed-Kubernetes/nrpe/mod/charmhelpers/tests/contrib/openstack/test_openstack_utils.py

2527 lines
106 KiB
Python

import io
import os
import unittest
from copy import copy
from tests.helpers import patch_open, FakeRelation
from testtools import TestCase
from mock import MagicMock, patch, call
from charmhelpers.fetch import ubuntu as fetch
from charmhelpers.core.hookenv import WORKLOAD_STATES, flush
import charmhelpers.contrib.openstack.utils as openstack
import charmhelpers.contrib.openstack.deferred_events as deferred_events
from charmhelpers.contrib.openstack.exceptions import ServiceActionError
import contextlib
import six
if not six.PY3:
builtin_open = '__builtin__.open'
builtin_import = '__builtin__.__import__'
else:
builtin_open = 'builtins.open'
builtin_import = 'builtins.__import__'
FAKE_CODENAME = 'precise'
# mocked return of openstack.lsb_release()
FAKE_RELEASE = {
'DISTRIB_CODENAME': 'precise',
'DISTRIB_RELEASE': '12.04',
'DISTRIB_ID': 'Ubuntu',
'DISTRIB_DESCRIPTION': '"Ubuntu 12.04"'
}
FAKE_REPO = {
# liberty patch release
'neutron-common': {
'pkg_vers': '2:7.0.1-0ubuntu1',
'os_release': 'liberty',
'os_version': '2015.2'
},
# liberty release version
'nova-common': {
'pkg_vers': '2:12.0.0~b1-0ubuntu1',
'os_release': 'liberty',
'os_version': '2015.2'
},
'nova': {
'pkg_vers': '2012.2.3-0ubuntu2.1',
'os_release': 'folsom',
'os_version': '2012.2'
},
'glance-common': {
'pkg_vers': '2012.1.3+stable-20130423-74b067df-0ubuntu1',
'os_release': 'essex',
'os_version': '2012.1'
},
'keystone-common': {
'pkg_vers': '1:2013.1-0ubuntu1.1~cloud0',
'os_release': 'grizzly',
'os_version': '2013.1'
},
# Exercise swift version detection
'swift-storage': {
'pkg_vers': '1.8.0-0ubuntu1',
'os_release': 'grizzly',
'os_version': '1.8.0'
},
'swift-proxy': {
'pkg_vers': '1.13.1-0ubuntu1',
'os_release': 'icehouse',
'os_version': '1.13.1'
},
'swift-common': {
'pkg_vers': '1.10.0~rc1-0ubuntu1',
'os_release': 'havana',
'os_version': '1.10.0'
},
'swift-mitaka-dev': {
'pkg_vers': '2.7.1.dev8.201605111703.trusty-0ubuntu1',
'os_release': 'mitaka',
'os_version': '2.7.0'
},
# a package that's available in the cache but is not installed
'cinder-common': {
'os_release': 'havana',
'os_version': '2013.2'
},
# poorly formed openstack version
'bad-version': {
'pkg_vers': '1:2200.1-0ubuntu1.1~cloud0',
'os_release': None,
'os_version': None
}
}
MOUNTS = [
['/mnt', '/dev/vdb']
]
url = 'deb ' + openstack.CLOUD_ARCHIVE_URL
UCA_SOURCES = [
('cloud:precise-folsom/proposed', url + ' precise-proposed/folsom main'),
('cloud:precise-folsom', url + ' precise-updates/folsom main'),
('cloud:precise-folsom/updates', url + ' precise-updates/folsom main'),
('cloud:precise-grizzly/proposed', url + ' precise-proposed/grizzly main'),
('cloud:precise-grizzly', url + ' precise-updates/grizzly main'),
('cloud:precise-grizzly/updates', url + ' precise-updates/grizzly main'),
('cloud:precise-havana/proposed', url + ' precise-proposed/havana main'),
('cloud:precise-havana', url + ' precise-updates/havana main'),
('cloud:precise-havana/updates', url + ' precise-updates/havana main'),
('cloud:precise-icehouse/proposed',
url + ' precise-proposed/icehouse main'),
('cloud:precise-icehouse', url + ' precise-updates/icehouse main'),
('cloud:precise-icehouse/updates', url + ' precise-updates/icehouse main'),
]
# Mock python-dnspython resolver used by get_host_ip()
class FakeAnswer(object):
def __init__(self, ip):
self.ip = ip
def __str__(self):
return self.ip
class FakeResolver(object):
def __init__(self, ip):
self.ip = ip
def query(self, hostname, query_type):
if self.ip == '':
return []
else:
return [FakeAnswer(self.ip)]
class FakeReverse(object):
def from_address(self, address):
return '156.94.189.91.in-addr.arpa'
class FakeDNSName(object):
def __init__(self, dnsname):
pass
class FakeDNS(object):
def __init__(self, ip):
self.resolver = FakeResolver(ip)
self.reversename = FakeReverse()
self.name = MagicMock()
self.name.Name = FakeDNSName
class OpenStackHelpersTestCase(TestCase):
def setUp(self):
super(OpenStackHelpersTestCase, self).setUp()
self.patch(fetch, 'get_apt_dpkg_env', lambda: {})
# Make sleep() and log() into noops for testing
for funcname in ('charmhelpers.core.decorators.log', 'charmhelpers.core.decorators.time.sleep'):
patcher = patch(funcname, return_value=None)
patcher.start()
self.addCleanup(patcher.stop)
def _apt_cache(self):
# mocks out the apt cache
def cache_get(package):
pkg = MagicMock()
if package in FAKE_REPO and 'pkg_vers' in FAKE_REPO[package]:
pkg.name = package
pkg.current_ver.ver_str = FAKE_REPO[package]['pkg_vers']
elif (package in FAKE_REPO and
'pkg_vers' not in FAKE_REPO[package]):
pkg.name = package
pkg.current_ver = None
else:
raise KeyError
return pkg
cache = MagicMock()
cache.__getitem__.side_effect = cache_get
return cache
@patch.object(openstack, 'filter_missing_packages')
def test_get_installed_semantic_versioned_packages(self, mock_filter):
def _filter_missing_packages(pkgs):
return [x for x in pkgs if x in ['cinder-common']]
mock_filter.side_effect = _filter_missing_packages
self.assertEquals(
openstack.get_installed_semantic_versioned_packages(),
['cinder-common'])
@patch('charmhelpers.contrib.openstack.utils.lsb_release')
def test_os_codename_from_install_source(self, mocked_lsb):
"""Test mapping install source to OpenStack release name"""
mocked_lsb.return_value = FAKE_RELEASE
# the openstack release shipped with respective ubuntu/lsb release.
self.assertEquals(openstack.get_os_codename_install_source('distro'),
'essex')
# proposed pocket
self.assertEquals(openstack.get_os_codename_install_source(
'distro-proposed'),
'essex')
self.assertEquals(openstack.get_os_codename_install_source(
'proposed'),
'essex')
# various cloud archive pockets
src = 'cloud:precise-grizzly'
self.assertEquals(openstack.get_os_codename_install_source(src),
'grizzly')
src = 'cloud:precise-grizzly/proposed'
self.assertEquals(openstack.get_os_codename_install_source(src),
'grizzly')
# ppas and full repo urls.
src = 'ppa:openstack-ubuntu-testing/havana-trunk-testing'
self.assertEquals(openstack.get_os_codename_install_source(src),
'havana')
src = ('deb http://ubuntu-cloud.archive.canonical.com/ubuntu '
'precise-havana main')
self.assertEquals(openstack.get_os_codename_install_source(src),
'havana')
self.assertEquals(openstack.get_os_codename_install_source(None),
'')
@patch.object(openstack, 'get_os_version_codename')
@patch.object(openstack, 'get_os_codename_install_source')
def test_os_version_from_install_source(self, codename, version):
codename.return_value = 'grizzly'
openstack.get_os_version_install_source('cloud:precise-grizzly')
version.assert_called_with('grizzly')
@patch('charmhelpers.contrib.openstack.utils.lsb_release')
def test_os_codename_from_bad_install_source(self, mocked_lsb):
"""Test mapping install source to OpenStack release name"""
_fake_release = copy(FAKE_RELEASE)
_fake_release['DISTRIB_CODENAME'] = 'natty'
mocked_lsb.return_value = _fake_release
_e = 'charmhelpers.contrib.openstack.utils.error_out'
with patch(_e) as mocked_err:
openstack.get_os_codename_install_source('distro')
_er = ('Could not derive openstack release for this Ubuntu '
'release: natty')
mocked_err.assert_called_with(_er)
def test_os_codename_from_version(self):
"""Test mapping OpenStack numerical versions to code name"""
self.assertEquals(openstack.get_os_codename_version('2013.1'),
'grizzly')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_codename_from_bad_version(self, mocked_error):
"""Test mapping a bad OpenStack numerical versions to code name"""
openstack.get_os_codename_version('2014.5.5')
expected_err = ('Could not determine OpenStack codename for '
'version 2014.5.5')
mocked_error.assert_called_with(expected_err)
def test_os_version_from_codename(self):
"""Test mapping a OpenStack codename to numerical version"""
self.assertEquals(openstack.get_os_version_codename('folsom'),
'2012.2')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_version_from_bad_codename(self, mocked_error):
"""Test mapping a bad OpenStack codename to numerical version"""
openstack.get_os_version_codename('foo')
expected_err = 'Could not derive OpenStack version for codename: foo'
mocked_error.assert_called_with(expected_err)
def test_os_version_swift_from_codename(self):
"""Test mapping a swift codename to numerical version"""
self.assertEquals(openstack.get_os_version_codename_swift('liberty'),
'2.5.0')
def test_get_swift_codename_single_version_kilo(self):
self.assertEquals(openstack.get_swift_codename('2.2.2'), 'kilo')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_version_swift_from_bad_codename(self, mocked_error):
"""Test mapping a bad swift codename to numerical version"""
openstack.get_os_version_codename_swift('foo')
expected_err = 'Could not derive swift version for codename: foo'
mocked_error.assert_called_with(expected_err)
def test_get_swift_codename_multiple_versions_liberty(self):
with patch('subprocess.check_output') as _subp:
_subp.return_value = b"... trusty-updates/liberty/main ..."
self.assertEquals(openstack.get_swift_codename('2.5.0'), 'liberty')
def test_get_swift_codename_multiple_versions_mitaka(self):
with patch('subprocess.check_output') as _subp:
_subp.return_value = b"... trusty-updates/mitaka/main ..."
self.assertEquals(openstack.get_swift_codename('2.5.0'), 'mitaka')
def test_get_swift_codename_none(self):
self.assertEquals(openstack.get_swift_codename('1.2.3'), None)
@patch("charmhelpers.core.hookenv.cache", new={})
@patch.object(openstack, 'openstack_release')
@patch.object(openstack, 'filter_installed_packages')
@patch.object(openstack, 'apt_install')
def test_get_installed_os_version_no_package(self, mock_apt_install,
mock_filter_installed_packages,
mock_openstack_release):
mock_openstack_release.return_value = {}
self.assertEquals(
openstack.get_installed_os_version(), None)
@patch("charmhelpers.core.hookenv.cache", new={})
@patch.object(openstack, 'openstack_release')
@patch.object(openstack, 'filter_installed_packages')
@patch.object(openstack, 'apt_install')
def test_get_installed_os_version_with_package(self, mock_apt_install,
mock_filter_installed_packages,
mock_openstack_release):
mock_openstack_release.return_value = {'OPENSTACK_CODENAME': 'wallaby'}
self.assertEquals(
openstack.get_installed_os_version(), 'wallaby')
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
def test_os_codename_from_package(self, mock_snap_install_requested,
mock_get_installed_os_version):
"""Test deriving OpenStack codename from an installed package"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
for pkg, vers in six.iteritems(FAKE_REPO):
# test fake repo for all "installed" packages
if pkg.startswith('bad-'):
continue
if 'pkg_vers' not in vers:
continue
self.assertEquals(openstack.get_os_codename_package(pkg),
vers['os_release'])
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_codename_from_bad_package_version(self, mocked_error,
mock_snap_install_requested,
mock_get_installed_os_version):
"""Test deriving OpenStack codename for a poorly versioned package"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
openstack.get_os_codename_package('bad-version')
_e = ('Could not determine OpenStack codename for version 2200.1')
mocked_error.assert_called_with(_e)
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_codename_from_bad_package(self, mocked_error,
mock_snap_install_requested,
mock_get_installed_os_version):
"""Test deriving OpenStack codename from an unavailable package"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
try:
openstack.get_os_codename_package('foo')
except Exception:
# ignore exceptions that raise when error_out is mocked
# and doesn't sys.exit(1)
pass
e = 'Could not determine version of package with no installation '\
'candidate: foo'
mocked_error.assert_called_with(e)
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
def test_os_codename_from_bad_package_nonfatal(
self, mock_snap_install_requested,
mock_get_installed_os_version):
"""Test OpenStack codename from an unavailable package is non-fatal"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
self.assertEquals(
None,
openstack.get_os_codename_package('foo', fatal=False)
)
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_codename_from_uninstalled_package(self, mock_error,
mock_snap_install_requested,
mock_get_installed_os_version):
"""Test OpenStack codename from an available but uninstalled pkg"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
try:
openstack.get_os_codename_package('cinder-common', fatal=True)
except Exception:
pass
e = ('Could not determine version of uninstalled package: '
'cinder-common')
mock_error.assert_called_with(e)
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
def test_os_codename_from_uninstalled_package_nonfatal(
self, mock_snap_install_requested,
mock_get_installed_os_version):
"""Test OpenStack codename from avail uninstalled pkg is non fatal"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
self.assertEquals(
None,
openstack.get_os_codename_package('cinder-common', fatal=False)
)
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_version_from_package(self, mocked_error,
mock_snap_install_requested,
mock_get_installed_os_version):
"""Test deriving OpenStack version from an installed package"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
for pkg, vers in six.iteritems(FAKE_REPO):
if pkg.startswith('bad-'):
continue
if 'pkg_vers' not in vers:
continue
self.assertEquals(openstack.get_os_version_package(pkg),
vers['os_version'])
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_os_version_from_bad_package(self, mocked_error,
mock_snap_install_requested,
mock_get_installed_os_version):
"""Test deriving OpenStack version from an uninstalled package"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
try:
openstack.get_os_version_package('foo')
except Exception:
# ignore exceptions that raise when error_out is mocked
# and doesn't sys.exit(1)
pass
e = 'Could not determine version of package with no installation '\
'candidate: foo'
mocked_error.assert_called_with(e)
@patch.object(openstack, 'get_installed_os_version')
@patch.object(openstack, 'snap_install_requested')
def test_os_version_from_bad_package_nonfatal(
self, mock_snap_install_requested,
mock_get_installed_os_version):
"""Test OpenStack version from an uninstalled package is non-fatal"""
mock_snap_install_requested.return_value = False
mock_get_installed_os_version.return_value = None
with patch.object(openstack, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
self.assertEquals(
None,
openstack.get_os_version_package('foo', fatal=False)
)
@patch.object(openstack, 'lsb_release')
@patch.object(openstack, 'get_os_codename_package')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_os_release_uncached(self, config, get_cn, mock_lsb_release):
openstack._os_rel = None
get_cn.return_value = 'folsom'
mock_lsb_release.return_value = {
'DISTRIB_CODENAME': 'bionic',
}
self.assertEquals('folsom', openstack.os_release('nova-common'))
@patch.object(openstack, 'lsb_release')
def test_os_release_cached(self, mock_lsb_release):
openstack._os_rel = 'foo'
mock_lsb_release.return_value = {
'DISTRIB_CODENAME': 'bionic',
}
self.assertEquals('foo', openstack.os_release('nova-common'))
@patch.object(openstack, 'juju_log')
@patch('sys.exit')
def test_error_out(self, mocked_exit, juju_log):
"""Test erroring out"""
openstack.error_out('Everything broke.')
_log = 'FATAL ERROR: Everything broke.'
juju_log.assert_called_with(_log, level='ERROR')
mocked_exit.assert_called_with(1)
def test_get_source_and_pgp_key(self):
tests = {
"source|key": ('source', 'key'),
"source|": ('source', None),
"|key": ('', 'key'),
"source": ('source', None),
}
for k, v in six.iteritems(tests):
self.assertEqual(openstack.get_source_and_pgp_key(k), v)
# These should still work, even though the bulk of the functionality has
# moved to charmhelpers.fetch.add_source()
def test_configure_install_source_distro(self):
"""Test configuring installation from distro"""
self.assertIsNone(openstack.configure_installation_source('distro'))
def test_configure_install_source_ppa(self):
"""Test configuring installation source from PPA"""
with patch('subprocess.check_call') as mock:
src = 'ppa:gandelman-a/openstack'
openstack.configure_installation_source(src)
ex_cmd = [
'add-apt-repository', '--yes', 'ppa:gandelman-a/openstack']
mock.assert_called_with(ex_cmd, env={})
@patch('subprocess.check_call')
@patch.object(fetch, 'import_key')
def test_configure_install_source_deb_url(self, _import, _spcc):
"""Test configuring installation source from deb repo url"""
src = ('deb http://ubuntu-cloud.archive.canonical.com/ubuntu '
'precise-havana main|KEYID')
openstack.configure_installation_source(src)
_import.assert_called_with('KEYID')
_spcc.assert_called_once_with(
['add-apt-repository', '--yes',
'deb http://ubuntu-cloud.archive.canonical.com/ubuntu '
'precise-havana main'], env={})
@patch.object(fetch, 'get_distrib_codename')
@patch(builtin_open)
@patch('subprocess.check_call')
def test_configure_install_source_distro_proposed(
self, _spcc, _open, _lsb):
"""Test configuring installation source from deb repo url"""
_lsb.return_value = FAKE_CODENAME
_file = MagicMock(spec=io.FileIO)
_open.return_value = _file
openstack.configure_installation_source('distro-proposed')
_file.__enter__().write.assert_called_once_with(
'# Proposed\ndeb http://archive.ubuntu.com/ubuntu '
'precise-proposed main universe multiverse restricted\n')
src = ('deb http://archive.ubuntu.com/ubuntu/ precise-proposed '
'restricted main multiverse universe')
openstack.configure_installation_source(src)
_spcc.assert_called_once_with(
['add-apt-repository', '--yes',
'deb http://archive.ubuntu.com/ubuntu/ precise-proposed '
'restricted main multiverse universe'], env={})
@patch('charmhelpers.fetch.filter_installed_packages')
@patch('charmhelpers.fetch.apt_install')
@patch.object(openstack, 'error_out')
@patch.object(openstack, 'juju_log')
def test_add_source_cloud_invalid_pocket(self, _log, _out,
apt_install, filter_pkg):
openstack.configure_installation_source("cloud:havana-updates")
_e = ('Invalid Cloud Archive release specified: '
'havana-updates on this Ubuntuversion')
_s = _out.call_args[0][0]
self.assertTrue(_s.startswith(_e))
@patch.object(fetch, 'filter_installed_packages')
@patch.object(fetch, 'apt_install')
@patch.object(fetch, 'get_distrib_codename')
def test_add_source_cloud_pocket_style(self, get_distrib_codename,
apt_install, filter_pkg):
source = "cloud:precise-updates/havana"
get_distrib_codename.return_value = 'precise'
result = (
"# Ubuntu Cloud Archive\n"
"deb http://ubuntu-cloud.archive.canonical.com/ubuntu "
"precise-updates/havana main\n")
with patch_open() as (mock_open, mock_file):
openstack.configure_installation_source(source)
mock_file.write.assert_called_with(result)
filter_pkg.assert_called_with(['ubuntu-cloud-keyring'])
@patch.object(fetch, 'filter_installed_packages')
@patch.object(fetch, 'apt_install')
@patch.object(fetch, 'get_distrib_codename')
def test_add_source_cloud_os_style(self, get_distrib_codename,
apt_install, filter_pkg):
source = "cloud:precise-havana"
get_distrib_codename.return_value = 'precise'
result = (
"# Ubuntu Cloud Archive\n"
"deb http://ubuntu-cloud.archive.canonical.com/ubuntu "
"precise-updates/havana main\n")
with patch_open() as (mock_open, mock_file):
openstack.configure_installation_source(source)
mock_file.write.assert_called_with(result)
filter_pkg.assert_called_with(['ubuntu-cloud-keyring'])
@patch.object(fetch, 'filter_installed_packages')
@patch.object(fetch, 'apt_install')
def test_add_source_cloud_distroless_style(self, apt_install, filter_pkg):
source = "cloud:havana"
result = (
"# Ubuntu Cloud Archive\n"
"deb http://ubuntu-cloud.archive.canonical.com/ubuntu "
"precise-updates/havana main\n")
with patch_open() as (mock_open, mock_file):
openstack.configure_installation_source(source)
mock_file.write.assert_called_with(result)
filter_pkg.assert_called_with(['ubuntu-cloud-keyring'])
@patch('charmhelpers.fetch.ubuntu.log', lambda *args, **kwargs: None)
@patch('charmhelpers.contrib.openstack.utils.juju_log',
lambda *args, **kwargs: None)
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_configure_bad_install_source(self, _error):
openstack.configure_installation_source('foo')
_error.assert_called_with("Unknown source: 'foo'")
@patch.object(fetch, 'get_distrib_codename')
def test_configure_install_source_uca_staging(self, _lsb):
"""Test configuring installation source from UCA staging sources"""
_lsb.return_value = FAKE_CODENAME
# staging pockets are configured as PPAs
with patch('subprocess.check_call') as _subp:
src = 'cloud:precise-folsom/staging'
openstack.configure_installation_source(src)
cmd = ['add-apt-repository', '-y',
'ppa:ubuntu-cloud-archive/folsom-staging']
_subp.assert_called_with(cmd, env={})
@patch(builtin_open)
@patch.object(fetch, 'apt_install')
@patch.object(fetch, 'get_distrib_codename')
@patch.object(fetch, 'filter_installed_packages')
def test_configure_install_source_uca_repos(
self, _fip, _lsb, _install, _open):
"""Test configuring installation source from UCA sources"""
_lsb.return_value = FAKE_CODENAME
_file = MagicMock(spec=io.FileIO)
_open.return_value = _file
_fip.side_effect = lambda x: x
for src, url in UCA_SOURCES:
actual_url = "# Ubuntu Cloud Archive\n{}\n".format(url)
openstack.configure_installation_source(src)
_install.assert_called_with(['ubuntu-cloud-keyring'],
fatal=True)
_open.assert_called_with(
'/etc/apt/sources.list.d/cloud-archive.list',
'w'
)
_file.__enter__().write.assert_called_with(actual_url)
@patch('charmhelpers.contrib.openstack.utils.error_out')
def test_configure_install_source_bad_uca(self, mocked_error):
"""Test configuring installation source from bad UCA source"""
try:
openstack.configure_installation_source('cloud:foo-bar')
except Exception:
# ignore exceptions that raise when error_out is mocked
# and doesn't sys.exit(1)
pass
_e = ('Invalid Cloud Archive release specified: foo-bar'
' on this Ubuntuversion')
_s = mocked_error.call_args[0][0]
self.assertTrue(_s.startswith(_e))
@patch.object(openstack, 'fetch_import_key')
def test_import_key_calls_fetch_import_key(self, fetch_import_key):
openstack.import_key('random-string')
fetch_import_key.assert_called_once_with('random-string')
@patch.object(openstack, 'juju_log', lambda *args, **kwargs: None)
@patch.object(openstack, 'fetch_import_key')
@patch.object(openstack, 'sys')
def test_import_key_calls_sys_exit_on_error(self, mock_sys,
fetch_import_key):
def raiser(_):
raise openstack.GPGKeyError("an error occurred")
fetch_import_key.side_effect = raiser
openstack.import_key('random failure')
mock_sys.exit.assert_called_once_with(1)
@patch('os.mkdir')
@patch('os.path.exists')
@patch('charmhelpers.contrib.openstack.utils.charm_dir')
@patch(builtin_open)
def test_save_scriptrc(self, _open, _charm_dir, _exists, _mkdir):
"""Test generation of scriptrc from environment"""
scriptrc = ['#!/bin/bash\n',
'export setting1=foo\n',
'export setting2=bar\n']
_file = MagicMock(spec=io.FileIO)
_open.return_value = _file
_charm_dir.return_value = '/var/lib/juju/units/testing-foo-0/charm'
_exists.return_value = False
os.environ['JUJU_UNIT_NAME'] = 'testing-foo/0'
openstack.save_script_rc(setting1='foo', setting2='bar')
rcdir = '/var/lib/juju/units/testing-foo-0/charm/scripts'
_mkdir.assert_called_with(rcdir)
expected_f = '/var/lib/juju/units/testing-foo-0/charm/scripts/scriptrc'
_open.assert_called_with(expected_f, 'wt')
_mkdir.assert_called_with(os.path.dirname(expected_f))
_file.__enter__().write.assert_has_calls(
list(call(line) for line in scriptrc), any_order=True)
@patch.object(openstack, 'lsb_release')
@patch.object(openstack, 'get_os_version_package')
@patch.object(openstack, 'get_os_version_codename_swift')
@patch.object(openstack, 'config')
def test_openstack_upgrade_detection_true(self, config, vers_swift,
vers_pkg, lsb):
"""Test it detects when an openstack package has available upgrade"""
lsb.return_value = FAKE_RELEASE
config.return_value = 'cloud:precise-havana'
vers_pkg.return_value = '2013.1.1'
self.assertTrue(openstack.openstack_upgrade_available('nova-common'))
# milestone to major release detection
vers_pkg.return_value = '2013.2~b1'
self.assertTrue(openstack.openstack_upgrade_available('nova-common'))
vers_pkg.return_value = '1.9.0'
vers_swift.return_value = '2.5.0'
self.assertTrue(openstack.openstack_upgrade_available('swift-proxy'))
vers_pkg.return_value = '2.5.0'
vers_swift.return_value = '2.10.0'
self.assertTrue(openstack.openstack_upgrade_available('swift-proxy'))
@patch.object(openstack, 'lsb_release')
@patch.object(openstack, 'get_os_version_package')
@patch.object(openstack, 'config')
def test_openstack_upgrade_detection_false(self, config, vers_pkg, lsb):
"""Test it detects when an openstack upgrade is not necessary"""
lsb.return_value = FAKE_RELEASE
config.return_value = 'cloud:precise-folsom'
vers_pkg.return_value = '2013.1.1'
self.assertFalse(openstack.openstack_upgrade_available('nova-common'))
# milestone to majro release detection
vers_pkg.return_value = '2013.1~b1'
self.assertFalse(openstack.openstack_upgrade_available('nova-common'))
# ugly duckling testing
config.return_value = 'cloud:precise-havana'
vers_pkg.return_value = '1.10.0'
self.assertFalse(openstack.openstack_upgrade_available('swift-proxy'))
@patch.object(openstack, 'is_block_device')
@patch.object(openstack, 'error_out')
def test_ensure_block_device_bad_config(self, err, is_bd):
"""Test it doesn't prepare storage with bad config"""
openstack.ensure_block_device(block_device='none')
self.assertTrue(err.called)
@patch.object(openstack, 'is_block_device')
@patch.object(openstack, 'ensure_loopback_device')
def test_ensure_block_device_loopback(self, ensure_loopback, is_bd):
"""Test it ensures loopback device when checking block device"""
defsize = openstack.DEFAULT_LOOPBACK_SIZE
is_bd.return_value = True
ensure_loopback.return_value = '/tmp/cinder.img'
result = openstack.ensure_block_device('/tmp/cinder.img')
ensure_loopback.assert_called_with('/tmp/cinder.img', defsize)
self.assertEquals(result, '/tmp/cinder.img')
ensure_loopback.return_value = '/tmp/cinder-2.img'
result = openstack.ensure_block_device('/tmp/cinder-2.img|15G')
ensure_loopback.assert_called_with('/tmp/cinder-2.img', '15G')
self.assertEquals(result, '/tmp/cinder-2.img')
@patch.object(openstack, 'is_block_device')
def test_ensure_standard_block_device(self, is_bd):
"""Test it looks for storage at both relative and full device path"""
for dev in ['vdb', '/dev/vdb']:
openstack.ensure_block_device(dev)
is_bd.assert_called_with('/dev/vdb')
@patch.object(openstack, 'is_block_device')
@patch.object(openstack, 'error_out')
def test_ensure_nonexistent_block_device(self, error_out, is_bd):
"""Test it will not ensure a non-existent block device"""
is_bd.return_value = False
openstack.ensure_block_device(block_device='foo')
self.assertTrue(error_out.called)
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'umount')
@patch.object(openstack, 'mounts')
@patch.object(openstack, 'zap_disk')
@patch.object(openstack, 'is_lvm_physical_volume')
def test_clean_storage_unmount(self, is_pv, zap_disk, mounts, umount, log):
"""Test it unmounts block device when cleaning storage"""
is_pv.return_value = False
zap_disk.return_value = True
mounts.return_value = MOUNTS
openstack.clean_storage('/dev/vdb')
umount.called_with('/dev/vdb', True)
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'remove_lvm_physical_volume')
@patch.object(openstack, 'deactivate_lvm_volume_group')
@patch.object(openstack, 'mounts')
@patch.object(openstack, 'is_lvm_physical_volume')
def test_clean_storage_lvm_wipe(self, is_pv, mounts, rm_lv, rm_vg, log):
"""Test it removes traces of LVM when cleaning storage"""
mounts.return_value = []
is_pv.return_value = True
openstack.clean_storage('/dev/vdb')
rm_lv.assert_called_with('/dev/vdb')
rm_vg .assert_called_with('/dev/vdb')
@patch.object(openstack, 'zap_disk')
@patch.object(openstack, 'is_lvm_physical_volume')
@patch.object(openstack, 'mounts')
def test_clean_storage_zap_disk(self, mounts, is_pv, zap_disk):
"""It removes traces of LVM when cleaning storage"""
mounts.return_value = []
is_pv.return_value = False
openstack.clean_storage('/dev/vdb')
zap_disk.assert_called_with('/dev/vdb')
@patch('os.path.isfile')
@patch(builtin_open)
def test_get_matchmaker_map(self, _open, _isfile):
_isfile.return_value = True
mm_data = """
{
"cinder-scheduler": [
"juju-t-machine-4"
]
}
"""
fh = _open.return_value.__enter__.return_value
fh.read.return_value = mm_data
self.assertEqual(
openstack.get_matchmaker_map(),
{'cinder-scheduler': ['juju-t-machine-4']}
)
@patch('os.path.isfile')
@patch(builtin_open)
def test_get_matchmaker_map_nofile(self, _open, _isfile):
_isfile.return_value = False
self.assertEqual(
openstack.get_matchmaker_map(),
{}
)
def test_incomplete_relation_data(self):
configs = MagicMock()
configs.complete_contexts.return_value = ['pgsql-db', 'amqp']
required_interfaces = {
'database': ['shared-db', 'pgsql-db'],
'message': ['amqp', 'zeromq-configuration'],
'identity': ['identity-service']}
expected_result = 'identity'
result = openstack.incomplete_relation_data(
configs, required_interfaces)
self.assertTrue(expected_result in result.keys())
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_complete(
self, is_unit_paused_set, status_set, log):
configs = MagicMock()
configs.complete_contexts.return_value = ['shared-db',
'amqp',
'identity-service']
required_interfaces = {
'database': ['shared-db', 'pgsql-db'],
'message': ['amqp', 'zeromq-configuration'],
'identity': ['identity-service']}
openstack.set_os_workload_status(configs, required_interfaces)
status_set.assert_called_with('active', 'Unit is ready')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.incomplete_relation_data',
return_value={'identity': {'identity-service': {'related': True}}})
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_related_incomplete(
self, is_unit_paused_set, status_set,
incomplete_relation_data, log):
configs = MagicMock()
configs.complete_contexts.return_value = ['shared-db', 'amqp']
required_interfaces = {
'database': ['shared-db', 'pgsql-db'],
'message': ['amqp', 'zeromq-configuration'],
'identity': ['identity-service']}
openstack.set_os_workload_status(configs, required_interfaces)
status_set.assert_called_with('waiting',
"Incomplete relations: identity")
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.incomplete_relation_data',
return_value={'identity': {'identity-service': {'related': False}}})
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_absent(
self, is_unit_paused_set, status_set,
incomplete_relation_data, log):
configs = MagicMock()
configs.complete_contexts.return_value = ['shared-db', 'amqp']
required_interfaces = {
'database': ['shared-db', 'pgsql-db'],
'message': ['amqp', 'zeromq-configuration'],
'identity': ['identity-service']}
openstack.set_os_workload_status(configs, required_interfaces)
status_set.assert_called_with('blocked',
'Missing relations: identity')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.hook_name',
return_value='identity-service-relation-broken')
@patch('charmhelpers.contrib.openstack.utils.incomplete_relation_data',
return_value={'identity': {'identity-service': {'related': True}}})
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_related_broken(
self, is_unit_paused_set, status_set,
incomplete_relation_data, hook_name, log):
configs = MagicMock()
configs.complete_contexts.return_value = ['shared-db', 'amqp']
required_interfaces = {
'database': ['shared-db', 'pgsql-db'],
'message': ['amqp', 'zeromq-configuration'],
'identity': ['identity-service']}
openstack.set_os_workload_status(configs, required_interfaces)
status_set.assert_called_with('blocked',
"Missing relations: identity")
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.incomplete_relation_data',
return_value={'identity':
{'identity-service': {'related': True}},
'message':
{'amqp': {'missing_data': ['rabbitmq-password'],
'related': True}},
'database':
{'shared-db': {'related': False}}
})
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_mixed(
self, is_unit_paused_set, status_set,
incomplete_relation_data, log):
configs = MagicMock()
configs.complete_contexts.return_value = ['shared-db', 'amqp']
required_interfaces = {
'database': ['shared-db', 'pgsql-db'],
'message': ['amqp', 'zeromq-configuration'],
'identity': ['identity-service']}
openstack.set_os_workload_status(configs, required_interfaces)
args = status_set.call_args
actual_parm1 = args[0][0]
actual_parm2 = args[0][1]
expected1 = ("Missing relations: database; incomplete relations: "
"identity, message")
expected2 = ("Missing relations: database; incomplete relations: "
"message, identity")
self.assertTrue(actual_parm1 == 'blocked')
self.assertTrue(actual_parm2 == expected1 or actual_parm2 == expected2)
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_complete_with_services_list(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = ['database', 'identity']
# Assume that the service and ports are open.
port_has_listener.return_value = True
service_running.return_value = True
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with('active', 'Unit is ready')
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_complete_services_list_not_running(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = ['database', 'identity']
port_has_listener.return_value = True
# Fail the identity service
service_running.side_effect = [True, False]
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with(
'blocked',
'Services not running that should be: identity')
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_complete_with_services(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
# Assume that the service and ports are open.
port_has_listener.return_value = True
service_running.return_value = True
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with('active', 'Unit is ready')
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_complete_service_not_running(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
port_has_listener.return_value = True
# Fail the identity service
service_running.side_effect = [True, False]
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with(
'blocked',
'Services not running that should be: identity')
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_complete_port_not_open(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
port_has_listener.side_effect = [True, False, True]
# Fail the identity service
service_running.return_value = True
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with(
'blocked',
'Services with ports not open that should be:'
' database: [20]')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=False)
def test_set_os_workload_status_complete_ports_not_open(
self, is_unit_paused_set, status_set, log, port_has_listener):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
ports = [50, 60, 70]
port_has_listener.side_effect = [True, False, True]
openstack.set_os_workload_status(
configs, required_interfaces, ports=ports)
status_set.assert_called_with(
'blocked',
'Ports which should be open, but are not: 60')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=True)
def test_set_os_workload_status_paused_simple(
self, is_unit_paused_set, status_set, log):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
openstack.set_os_workload_status(configs, required_interfaces)
status_set.assert_called_with(
'maintenance',
"Paused. Use 'resume' action to resume normal service.")
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=True)
def test_set_os_workload_status_paused_services_check(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
port_has_listener.return_value = False
service_running.side_effect = [False, False]
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with(
'maintenance',
"Paused. Use 'resume' action to resume normal service.")
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=True)
def test_set_os_workload_status_paused_services_fail(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
port_has_listener.return_value = False
# Fail the identity service
service_running.side_effect = [False, True]
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with(
'blocked',
"Services should be paused but these services running: identity")
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=True)
def test_set_os_workload_status_paused_services_ports_fail(
self, is_unit_paused_set, status_set, log,
port_has_listener, service_running):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
# make the service 20 port be still listening.
port_has_listener.side_effect = [False, True, False]
service_running.return_value = False
openstack.set_os_workload_status(
configs, required_interfaces, services=services)
status_set.assert_called_with(
'blocked',
"Services should be paused but these service:ports are open:"
" database: [20]")
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=True)
def test_set_os_workload_status_paused_ports_check(
self, is_unit_paused_set, status_set, log,
port_has_listener):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
ports = [50, 60, 70]
port_has_listener.side_effect = [False, False, False]
openstack.set_os_workload_status(
configs, required_interfaces, ports=ports)
status_set.assert_called_with(
'maintenance',
"Paused. Use 'resume' action to resume normal service.")
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
@patch.object(openstack, 'juju_log')
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.is_unit_paused_set',
return_value=True)
def test_set_os_workload_status_paused_ports_fail(
self, is_unit_paused_set, status_set, log,
port_has_listener):
configs = MagicMock()
configs.complete_contexts.return_value = []
required_interfaces = {}
# fail port 70 to make it seem to be running
ports = [50, 60, 70]
port_has_listener.side_effect = [False, False, True]
openstack.set_os_workload_status(
configs, required_interfaces, ports=ports)
status_set.assert_called_with(
'blocked',
"Services should be paused but "
"these ports which should be closed, but are open: 70")
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_check_actually_paused_simple_services(
self, port_has_listener, service_running):
services = ['database', 'identity']
port_has_listener.return_value = False
service_running.return_value = False
state, message = openstack.check_actually_paused(
services)
self.assertEquals(state, None)
self.assertEquals(message, None)
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_check_actually_paused_simple_services_fail(
self, port_has_listener, service_running):
services = ['database', 'identity']
port_has_listener.return_value = False
service_running.side_effect = [False, True]
state, message = openstack.check_actually_paused(
services)
self.assertEquals(state, 'blocked')
self.assertEquals(
message,
"Services should be paused but these services running: identity")
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_check_actually_paused_services_dict(
self, port_has_listener, service_running):
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
# Assume that the service and ports are open.
port_has_listener.return_value = False
service_running.return_value = False
state, message = openstack.check_actually_paused(
services)
self.assertEquals(state, None)
self.assertEquals(message, None)
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_check_actually_paused_services_dict_fail(
self, port_has_listener, service_running):
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
# Assume that the service and ports are open.
port_has_listener.return_value = False
service_running.side_effect = [False, True]
state, message = openstack.check_actually_paused(
services)
self.assertEquals(state, 'blocked')
self.assertEquals(
message,
"Services should be paused but these services running: identity")
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_check_actually_paused_services_dict_ports_fail(
self, port_has_listener, service_running):
services = [
{'service': 'database', 'ports': [10, 20]},
{'service': 'identity', 'ports': [30]},
]
# Assume that the service and ports are open.
port_has_listener.side_effect = [False, True, False]
service_running.return_value = False
state, message = openstack.check_actually_paused(
services)
self.assertEquals(state, 'blocked')
self.assertEquals(message,
'Services should be paused but these service:ports'
' are open: database: [20]')
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_check_actually_paused_ports_okay(
self, port_has_listener, service_running):
port_has_listener.side_effect = [False, False, False]
service_running.return_value = False
ports = [50, 60, 70]
state, message = openstack.check_actually_paused(
ports=ports)
self.assertEquals(state, None)
self.assertEquals(state, None)
@patch('charmhelpers.contrib.openstack.utils.service_running')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_check_actually_paused_ports_fail(
self, port_has_listener, service_running):
port_has_listener.side_effect = [False, True, False]
service_running.return_value = False
ports = [50, 60, 70]
state, message = openstack.check_actually_paused(
ports=ports)
self.assertEquals(state, 'blocked')
self.assertEquals(message,
'Services should be paused but these ports '
'which should be closed, but are open: 60')
@staticmethod
def _unit_paused_helper(hook_data_mock):
# HookData()() returns a tuple (kv, delta_config, delta_relation)
# but we only want kv in the test.
kv = MagicMock()
@contextlib.contextmanager
def hook_data__call__():
yield (kv, True, False)
hook_data__call__.return_value = (kv, True, False)
hook_data_mock.return_value = hook_data__call__
return kv
@patch('charmhelpers.contrib.openstack.utils.unitdata.HookData')
def test_set_unit_paused(self, hook_data):
kv = self._unit_paused_helper(hook_data)
openstack.set_unit_paused()
kv.set.assert_called_once_with('unit-paused', True)
@patch('charmhelpers.contrib.openstack.utils.unitdata.HookData')
def test_set_unit_upgrading(self, hook_data):
kv = self._unit_paused_helper(hook_data)
openstack.set_unit_upgrading()
kv.set.assert_called_once_with('unit-upgrading', True)
@patch('charmhelpers.contrib.openstack.utils.unitdata.HookData')
def test_clear_unit_paused(self, hook_data):
kv = self._unit_paused_helper(hook_data)
openstack.clear_unit_paused()
kv.set.assert_called_once_with('unit-paused', False)
@patch('charmhelpers.contrib.openstack.utils.unitdata.HookData')
def test_clear_unit_upgrading(self, hook_data):
kv = self._unit_paused_helper(hook_data)
openstack.clear_unit_upgrading()
kv.set.assert_called_once_with('unit-upgrading', False)
@patch('charmhelpers.contrib.openstack.utils.unitdata.HookData')
def test_is_unit_paused_set(self, hook_data):
kv = self._unit_paused_helper(hook_data)
kv.get.return_value = True
r = openstack.is_unit_paused_set()
kv.get.assert_called_once_with('unit-paused')
self.assertEquals(r, True)
kv.get.return_value = False
r = openstack.is_unit_paused_set()
self.assertEquals(r, False)
@patch('charmhelpers.contrib.openstack.utils.unitdata.HookData')
def test_is_unit_upgrading_set(self, hook_data):
kv = self._unit_paused_helper(hook_data)
kv.get.return_value = True
r = openstack.is_unit_upgrading_set()
kv.get.assert_called_once_with('unit-upgrading')
self.assertEquals(r, True)
kv.get.return_value = False
r = openstack.is_unit_upgrading_set()
self.assertEquals(r, False)
@patch.object(openstack, 'config')
@patch.object(openstack, 'is_unit_paused_set')
@patch.object(openstack.deferred_events, 'is_restart_permitted')
@patch.object(openstack.deferred_events, 'set_deferred_hook')
@patch.object(openstack.deferred_events, 'clear_deferred_hook')
def test_is_hook_allowed(self, clear_deferred_hook, set_deferred_hook,
is_restart_permitted, is_unit_paused_set, config):
# Test unit not paused and not checking whether restarts are allowed
is_unit_paused_set.return_value = False
self.assertEqual(
openstack.is_hook_allowed(
'config-changed',
check_deferred_restarts=False),
(True, ''))
self.assertFalse(clear_deferred_hook.called)
# Test unit paused and not checking whether restarts are allowed
is_unit_paused_set.return_value = True
self.assertEqual(
openstack.is_hook_allowed(
'config-changed',
check_deferred_restarts=False),
(False, 'Unit is pause or upgrading. Skipping config-changed'))
# Test unit not paused and restarts allowed
clear_deferred_hook.reset_mock()
is_unit_paused_set.return_value = False
is_restart_permitted.return_value = True
self.assertEqual(
openstack.is_hook_allowed(
'config-changed',
check_deferred_restarts=True),
(True, ''))
clear_deferred_hook.assert_called_once_with('config-changed')
# Test unit not paused and restarts not allowed
# enable-auto-restarts not enabled as part of this hook
clear_deferred_hook.reset_mock()
set_deferred_hook.reset_mock()
is_unit_paused_set.return_value = False
is_restart_permitted.return_value = False
config().changed.return_value = False
self.assertEqual(
openstack.is_hook_allowed(
'config-changed',
check_deferred_restarts=True),
(False, 'auto restarts are disabled'))
self.assertFalse(clear_deferred_hook.called)
set_deferred_hook.assert_called_once_with('config-changed')
# Test unit not paused and restarts not allowed.
# enable-auto-restarts enabled as part of this hook
clear_deferred_hook.reset_mock()
set_deferred_hook.reset_mock()
is_unit_paused_set.return_value = False
is_restart_permitted.return_value = False
config().changed.return_value = True
self.assertEqual(
openstack.is_hook_allowed(
'config-changed',
check_deferred_restarts=True),
(False, 'auto restarts are disabled'))
self.assertFalse(clear_deferred_hook.called)
# Test unit paused and restarts not allowed
# enable-auto-restarts not enabled as part of this hook
clear_deferred_hook.reset_mock()
set_deferred_hook.reset_mock()
is_unit_paused_set.return_value = True
is_restart_permitted.return_value = False
config().changed.return_value = False
self.assertEqual(
openstack.is_hook_allowed(
'config-changed',
check_deferred_restarts=True),
(False, 'Unit is pause or upgrading. Skipping config-changed and '
'auto restarts are disabled'))
self.assertFalse(clear_deferred_hook.called)
set_deferred_hook.assert_called_once_with('config-changed')
@patch('charmhelpers.contrib.openstack.utils.service_stop')
def test_manage_payload_services_ok(self, service_stop):
services = ['service1', 'service2']
service_stop.side_effect = [True, True]
self.assertEqual(
openstack.manage_payload_services('stop', services=services),
(True, []))
@patch('charmhelpers.contrib.openstack.utils.service_stop')
def test_manage_payload_services_fails(self, service_stop):
services = ['service1', 'service2']
service_stop.side_effect = [True, False]
self.assertEqual(
openstack.manage_payload_services('stop', services=services),
(False, ["service2 didn't stop cleanly."]))
@patch('charmhelpers.contrib.openstack.utils.service_stop')
def test_manage_payload_services_charm_func(self, service_stop):
bespoke_func = MagicMock()
bespoke_func.return_value = None
services = ['service1', 'service2']
service_stop.side_effect = [True, True]
self.assertEqual(
openstack.manage_payload_services('stop', services=services,
charm_func=bespoke_func),
(True, []))
bespoke_func.assert_called_once_with()
@patch('charmhelpers.contrib.openstack.utils.service_stop')
def test_manage_payload_services_charm_func_msg(self, service_stop):
bespoke_func = MagicMock()
bespoke_func.return_value = 'it worked'
services = ['service1', 'service2']
service_stop.side_effect = [True, True]
self.assertEqual(
openstack.manage_payload_services('stop', services=services,
charm_func=bespoke_func),
(True, ['it worked']))
bespoke_func.assert_called_once_with()
@patch('charmhelpers.contrib.openstack.utils.service_stop')
def test_manage_payload_services_charm_func_fails(self, service_stop):
bespoke_func = MagicMock()
bespoke_func.side_effect = Exception('it failed')
services = ['service1', 'service2']
service_stop.side_effect = [True, True]
self.assertEqual(
openstack.manage_payload_services('stop', services=services,
charm_func=bespoke_func),
(False, ['it failed']))
bespoke_func.assert_called_once_with()
def test_manage_payload_services_wrong_action(self):
self.assertRaises(
RuntimeError,
openstack.manage_payload_services,
'mangle')
@patch('charmhelpers.contrib.openstack.utils.service_pause')
@patch('charmhelpers.contrib.openstack.utils.set_unit_paused')
def test_pause_unit_okay(self, set_unit_paused, service_pause):
services = ['service1', 'service2']
service_pause.side_effect = [True, True]
openstack.pause_unit(None, services=services)
set_unit_paused.assert_called_once_with()
self.assertEquals(service_pause.call_count, 2)
@patch('charmhelpers.contrib.openstack.utils.service_pause')
@patch('charmhelpers.contrib.openstack.utils.set_unit_paused')
def test_pause_unit_service_fails(self, set_unit_paused, service_pause):
services = ['service1', 'service2']
service_pause.side_effect = [True, True]
openstack.pause_unit(None, services=services)
set_unit_paused.assert_called_once_with()
self.assertEquals(service_pause.call_count, 2)
# Fail the 2nd service
service_pause.side_effect = [True, False]
try:
openstack.pause_unit(None, services=services)
raise Exception("pause_unit should have raised Exception")
except Exception as e:
self.assertEquals(e.args[0],
"Couldn't pause: service2 didn't pause cleanly.")
@patch('charmhelpers.contrib.openstack.utils.service_pause')
@patch('charmhelpers.contrib.openstack.utils.set_unit_paused')
def test_pause_unit_service_charm_func(
self, set_unit_paused, service_pause):
services = ['service1', 'service2']
service_pause.return_value = True
charm_func = MagicMock()
charm_func.return_value = None
openstack.pause_unit(None, services=services, charm_func=charm_func)
charm_func.assert_called_once_with()
# fail the charm_func
charm_func.return_value = "Custom charm failed"
try:
openstack.pause_unit(
None, services=services, charm_func=charm_func)
raise Exception("pause_unit should have raised Exception")
except Exception as e:
self.assertEquals(e.args[0],
"Couldn't pause: Custom charm failed")
@patch('charmhelpers.contrib.openstack.utils.service_pause')
@patch('charmhelpers.contrib.openstack.utils.set_unit_paused')
def test_pause_unit_assess_status_func(
self, set_unit_paused, service_pause):
services = ['service1', 'service2']
service_pause.return_value = True
assess_status_func = MagicMock()
assess_status_func.return_value = None
openstack.pause_unit(assess_status_func, services=services)
assess_status_func.assert_called_once_with()
# fail the assess_status_func
assess_status_func.return_value = "assess_status_func failed"
try:
openstack.pause_unit(assess_status_func, services=services)
raise Exception("pause_unit should have raised Exception")
except Exception as e:
self.assertEquals(e.args[0],
"Couldn't pause: assess_status_func failed")
@patch('charmhelpers.contrib.openstack.utils.service_pause')
@patch('charmhelpers.contrib.openstack.utils.set_unit_paused')
@patch('charmhelpers.contrib.openstack.utils.port_has_listener')
def test_pause_unit_retry_port_check_retries(
self, port_has_listener, set_unit_paused, service_pause):
service_pause.return_value = True
port_has_listener.side_effect = [True, False]
wait_for_ports_func = openstack.make_wait_for_ports_barrier([77])
openstack.pause_unit(None, services=['service1'], ports=[77], charm_func=wait_for_ports_func)
port_has_listener.assert_has_calls([call('0.0.0.0', 77), call('0.0.0.0', 77)])
@patch('charmhelpers.contrib.openstack.utils.service_resume')
@patch('charmhelpers.contrib.openstack.utils.clear_unit_paused')
def test_resume_unit_okay(self, clear_unit_paused, service_resume):
services = ['service1', 'service2']
service_resume.side_effect = [True, True]
openstack.resume_unit(None, services=services)
clear_unit_paused.assert_called_once_with()
self.assertEquals(service_resume.call_count, 2)
@patch('charmhelpers.contrib.openstack.utils.service_resume')
@patch('charmhelpers.contrib.openstack.utils.clear_unit_paused')
def test_resume_unit_service_fails(
self, clear_unit_paused, service_resume):
services = ['service1', 'service2']
service_resume.side_effect = [True, True]
openstack.resume_unit(None, services=services)
clear_unit_paused.assert_called_once_with()
self.assertEquals(service_resume.call_count, 2)
# Fail the 2nd service
service_resume.side_effect = [True, False]
try:
openstack.resume_unit(None, services=services)
raise Exception("resume_unit should have raised Exception")
except Exception as e:
self.assertEquals(
e.args[0], "Couldn't resume: service2 didn't resume cleanly.")
@patch('charmhelpers.contrib.openstack.utils.service_resume')
@patch('charmhelpers.contrib.openstack.utils.clear_unit_paused')
def test_resume_unit_service_charm_func(
self, clear_unit_paused, service_resume):
services = ['service1', 'service2']
service_resume.return_value = True
charm_func = MagicMock()
charm_func.return_value = None
openstack.resume_unit(None, services=services, charm_func=charm_func)
charm_func.assert_called_once_with()
# fail the charm_func
charm_func.return_value = "Custom charm failed"
try:
openstack.resume_unit(
None, services=services, charm_func=charm_func)
raise Exception("resume_unit should have raised Exception")
except Exception as e:
self.assertEquals(e.args[0],
"Couldn't resume: Custom charm failed")
@patch('charmhelpers.contrib.openstack.utils.service_resume')
@patch('charmhelpers.contrib.openstack.utils.clear_unit_paused')
def test_resume_unit_assess_status_func(
self, clear_unit_paused, service_resume):
services = ['service1', 'service2']
service_resume.return_value = True
assess_status_func = MagicMock()
assess_status_func.return_value = None
openstack.resume_unit(assess_status_func, services=services)
assess_status_func.assert_called_once_with()
# fail the assess_status_func
assess_status_func.return_value = "assess_status_func failed"
try:
openstack.resume_unit(assess_status_func, services=services)
raise Exception("resume_unit should have raised Exception")
except Exception as e:
self.assertEquals(e.args[0],
"Couldn't resume: assess_status_func failed")
@patch('charmhelpers.contrib.openstack.utils.status_set')
@patch('charmhelpers.contrib.openstack.utils.'
'_determine_os_workload_status')
def test_make_assess_status_func(self, _determine_os_workload_status,
status_set):
_determine_os_workload_status.return_value = ('active', 'fine')
f = openstack.make_assess_status_func('one', 'two', three='three')
r = f()
self.assertEquals(r, None)
_determine_os_workload_status.assert_called_once_with(
'one', 'two', three='three')
status_set.assert_called_once_with('active', 'fine')
# return something other than 'active' or 'maintenance'
_determine_os_workload_status.return_value = ('broken', 'damaged')
r = f()
self.assertEquals(r, 'damaged')
# TODO(ajkavanagh) -- there should be a test for
# _determine_os_workload_status() as the policyd override code has changed
# it, but there wasn't a test previously.
@patch.object(openstack, 'restart_on_change_helper')
@patch.object(openstack, 'is_unit_paused_set')
def test_pausable_restart_on_change(
self, is_unit_paused_set, restart_on_change_helper):
@openstack.pausable_restart_on_change({})
def test_func():
pass
# test with pause: restart_on_change_helper should not be called.
is_unit_paused_set.return_value = True
test_func()
self.assertEquals(restart_on_change_helper.call_count, 0)
# test without pause: restart_on_change_helper should be called.
is_unit_paused_set.return_value = False
test_func()
self.assertEquals(restart_on_change_helper.call_count, 1)
@patch.object(openstack, 'restart_on_change_helper')
@patch.object(openstack, 'is_unit_paused_set')
def test_pausable_restart_on_change_with_callable(
self, is_unit_paused_set, restart_on_change_helper):
mock_test = MagicMock()
mock_test.called_set = False
def _restart_map():
mock_test.called_set = True
return {"a": "b"}
@openstack.pausable_restart_on_change(_restart_map)
def test_func():
pass
self.assertFalse(mock_test.called_set)
is_unit_paused_set.return_value = False
test_func()
self.assertEquals(restart_on_change_helper.call_count, 1)
self.assertTrue(mock_test.called_set)
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'action_set')
@patch.object(openstack, 'action_fail')
@patch.object(openstack, 'openstack_upgrade_available')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_openstack_upgrade(self, config, openstack_upgrade_available,
action_fail, action_set, log):
def do_openstack_upgrade(configs):
pass
openstack_upgrade_available.return_value = True
# action-managed-upgrade=True
config.side_effect = [True]
openstack.do_action_openstack_upgrade('package-xyz',
do_openstack_upgrade,
None)
self.assertTrue(openstack_upgrade_available.called)
msg = ('success, upgrade completed.')
action_set.assert_called_with({'outcome': msg})
self.assertFalse(action_fail.called)
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'action_set')
@patch.object(openstack, 'action_fail')
@patch.object(openstack, 'openstack_upgrade_available')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_openstack_upgrade_not_avail(self, config,
openstack_upgrade_available,
action_fail, action_set, log):
def do_openstack_upgrade(configs):
pass
openstack_upgrade_available.return_value = False
openstack.do_action_openstack_upgrade('package-xyz',
do_openstack_upgrade,
None)
self.assertTrue(openstack_upgrade_available.called)
msg = ('no upgrade available.')
action_set.assert_called_with({'outcome': msg})
self.assertFalse(action_fail.called)
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'action_set')
@patch.object(openstack, 'action_fail')
@patch.object(openstack, 'openstack_upgrade_available')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_openstack_upgrade_config_false(self, config,
openstack_upgrade_available,
action_fail, action_set, log):
def do_openstack_upgrade(configs):
pass
openstack_upgrade_available.return_value = True
# action-managed-upgrade=False
config.side_effect = [False]
openstack.do_action_openstack_upgrade('package-xyz',
do_openstack_upgrade,
None)
self.assertTrue(openstack_upgrade_available.called)
msg = ('action-managed-upgrade config is False, skipped upgrade.')
action_set.assert_called_with({'outcome': msg})
self.assertFalse(action_fail.called)
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'action_set')
@patch.object(openstack, 'action_fail')
@patch.object(openstack, 'openstack_upgrade_available')
@patch('traceback.format_exc')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_openstack_upgrade_traceback(self, config, traceback,
openstack_upgrade_available,
action_fail, action_set, log):
def do_openstack_upgrade(configs):
oops() # noqa
openstack_upgrade_available.return_value = True
# action-managed-upgrade=False
config.side_effect = [True]
openstack.do_action_openstack_upgrade('package-xyz',
do_openstack_upgrade,
None)
self.assertTrue(openstack_upgrade_available.called)
msg = 'do_openstack_upgrade resulted in an unexpected error'
action_fail.assert_called_with(msg)
self.assertTrue(action_set.called)
self.assertTrue(traceback.called)
@patch.object(openstack, 'os_release')
@patch.object(openstack, 'application_version_set')
def test_os_application_version_set(self,
mock_application_version_set,
mock_os_release):
with patch.object(fetch, 'apt_cache') as cache:
cache.return_value = self._apt_cache()
mock_os_release.return_value = 'mitaka'
openstack.os_application_version_set('neutron-common')
mock_application_version_set.assert_called_with('7.0.1')
openstack.os_application_version_set('cinder-common')
mock_application_version_set.assert_called_with('mitaka')
@patch.object(openstack, 'valid_snap_channel')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_snap_install_requested(self, config, valid_snap_channel):
valid_snap_channel.return_value = True
# Expect True
flush('snap_install_requested')
config.return_value = 'snap:ocata/edge'
self.assertTrue(openstack.snap_install_requested())
valid_snap_channel.assert_called_with('edge')
flush('snap_install_requested')
config.return_value = 'snap:pike'
self.assertTrue(openstack.snap_install_requested())
valid_snap_channel.assert_called_with('stable')
flush('snap_install_requested')
config.return_value = 'snap:pike/stable/jamespage'
self.assertTrue(openstack.snap_install_requested())
valid_snap_channel.assert_called_with('stable')
# Expect False
flush('snap_install_requested')
config.return_value = 'cloud:xenial-ocata'
self.assertFalse(openstack.snap_install_requested())
def test_get_snaps_install_info_from_origin(self):
snaps = ['os_project']
mode = 'jailmode'
# snap:track/channel
src = 'snap:ocata/beta'
expected = {snaps[0]: {'mode': mode,
'channel': '--channel=ocata/beta'}}
self.assertEqual(
expected,
openstack.get_snaps_install_info_from_origin(snaps, src,
mode=mode))
# snap:track/channel/branch
src = 'snap:ocata/beta/jamespage'
expected = {snaps[0]: {'mode': mode,
'channel': '--channel=ocata/beta/jamespage'}}
self.assertEqual(
expected,
openstack.get_snaps_install_info_from_origin(snaps, src,
mode=mode))
# snap:track
src = 'snap:pike'
expected = {snaps[0]: {'mode': mode,
'channel': '--channel=pike'}}
self.assertEqual(
expected,
openstack.get_snaps_install_info_from_origin(snaps, src,
mode=mode))
@patch.object(openstack, 'snap_install')
def test_install_os_snaps(self, mock_snap_install):
snaps = ['os_project']
mode = 'jailmode'
# snap:track/channel
src = 'snap:ocata/beta'
openstack.install_os_snaps(
openstack.get_snaps_install_info_from_origin(
snaps, src, mode=mode))
mock_snap_install.assert_called_with(
'os_project', '--channel=ocata/beta', '--jailmode')
# snap:track
src = 'snap:pike'
openstack.install_os_snaps(
openstack.get_snaps_install_info_from_origin(
snaps, src, mode=mode))
mock_snap_install.assert_called_with(
'os_project', '--channel=pike', '--jailmode')
@patch.object(openstack, 'set_unit_upgrading')
@patch.object(openstack, 'is_unit_paused_set')
def test_series_upgrade_prepare(
self, is_unit_paused_set, set_unit_upgrading):
is_unit_paused_set.return_value = False
fake_pause_helper = MagicMock()
fake_configs = MagicMock()
openstack.series_upgrade_prepare(fake_pause_helper, fake_configs)
set_unit_upgrading.assert_called_once()
fake_pause_helper.assert_called_once_with(fake_configs)
@patch.object(openstack, 'set_unit_upgrading')
@patch.object(openstack, 'is_unit_paused_set')
def test_series_upgrade_prepare_no_pause(
self, is_unit_paused_set, set_unit_upgrading):
is_unit_paused_set.return_value = True
fake_pause_helper = MagicMock()
fake_configs = MagicMock()
openstack.series_upgrade_prepare(fake_pause_helper, fake_configs)
set_unit_upgrading.assert_called_once()
fake_pause_helper.assert_not_called()
@patch.object(openstack, 'clear_unit_upgrading')
@patch.object(openstack, 'clear_unit_paused')
def test_series_upgrade_complete(
self, clear_unit_paused, clear_unit_upgrading):
fake_resume_helper = MagicMock()
fake_configs = MagicMock()
openstack.series_upgrade_complete(fake_resume_helper, fake_configs)
clear_unit_upgrading.assert_called_once()
clear_unit_paused.assert_called_once()
fake_configs.write_all.assert_called_once()
fake_resume_helper.assert_called_once_with(fake_configs)
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'leader_get')
def test_is_db_initialised(self, leader_get, juju_log):
leader_get.return_value = 'True'
self.assertTrue(openstack.is_db_initialised())
leader_get.return_value = 'False'
self.assertFalse(openstack.is_db_initialised())
leader_get.return_value = None
self.assertFalse(openstack.is_db_initialised())
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'leader_set')
def test_set_db_initialised(self, leader_set, juju_log):
openstack.set_db_initialised()
leader_set.assert_called_once_with({'db-initialised': True})
@patch.object(openstack, 'juju_log')
@patch.object(openstack, 'relation_ids')
@patch.object(openstack, 'related_units')
@patch.object(openstack, 'relation_get')
def test_is_db_maintenance_mode(self, relation_get, related_units,
relation_ids, juju_log):
relation_ids.return_value = ['rid:1']
related_units.return_value = ['unit/0', 'unit/2']
rsettings = {
'rid:1': {
'unit/0': {
'private-ip': '1.2.3.4',
'cluster-series-upgrading': 'True'},
'unit/2': {
'private-ip': '1.2.3.5'}}}
relation_get.side_effect = lambda unit, rid: rsettings[rid][unit]
self.assertTrue(openstack.is_db_maintenance_mode())
rsettings = {
'rid:1': {
'unit/0': {
'private-ip': '1.2.3.4'},
'unit/2': {
'private-ip': '1.2.3.5'}}}
self.assertFalse(openstack.is_db_maintenance_mode())
rsettings = {
'rid:1': {
'unit/0': {
'private-ip': '1.2.3.4',
'cluster-series-upgrading': 'False'},
'unit/2': {
'private-ip': '1.2.3.5'}}}
self.assertFalse(openstack.is_db_maintenance_mode())
rsettings = {
'rid:1': {
'unit/0': {
'private-ip': '1.2.3.4',
'cluster-series-upgrading': 'lskjfsd'},
'unit/2': {
'private-ip': '1.2.3.5'}}}
self.assertFalse(openstack.is_db_maintenance_mode())
def test_get_endpoint_key(self):
self.assertEqual(
openstack.get_endpoint_key('placement', 'is:2', 'keystone/0'),
'placement-is_2-keystone_0')
@patch.object(openstack, 'relation_get')
@patch.object(openstack, 'related_units')
@patch.object(openstack, 'relation_ids')
def test_get_endpoint_notifications(self, relation_ids, related_units,
relation_get):
id_svc_rel_units = {
'identity-service:3': ['keystone/0', 'keystone/1', 'keystone/2']
}
def _related_units(relid):
return id_svc_rel_units[relid]
id_svc_rel_data = {
'keystone/0': {
'ep_changed': '{"placement": "d5c3"}'},
'keystone/1': {
'ep_changed': '{"nova": "4d06", "neutron": "2aa6"}'},
'keystone/2': {}}
def _relation_get(unit, rid, attribute):
return id_svc_rel_data[unit].get(attribute)
relation_ids.return_value = id_svc_rel_units.keys()
related_units.side_effect = _related_units
relation_get.side_effect = _relation_get
self.assertEqual(
openstack.get_endpoint_notifications(['neutron']),
{
'neutron-identity-service_3-keystone_1': '2aa6'})
self.assertEqual(
openstack.get_endpoint_notifications(['placement', 'neutron']),
{
'neutron-identity-service_3-keystone_1': '2aa6',
'placement-identity-service_3-keystone_0': 'd5c3'})
@patch.object(openstack, 'get_endpoint_notifications')
@patch.object(openstack.unitdata, 'HookData')
def test_endpoint_changed(self, HookData, get_endpoint_notifications):
self.kv_data = {}
def _kv_get(key):
return self.kv_data.get(key)
kv = self._unit_paused_helper(HookData)
kv.get.side_effect = _kv_get
# Check endpoint_changed returns True when there are new notifications.
get_endpoint_notifications.return_value = {
'neutron-identity-service_3-keystone_1': '2aa6',
'placement-identity-service_3-keystone_0': 'd5c3'}
self.assertTrue(openstack.endpoint_changed('placement'))
# Check endpoint_changed returns False when there are new
# notifications but they are not the ones being looked for.
self.assertTrue(openstack.endpoint_changed('nova'))
# Check endpoint_changed returns False if the notification
# has already been seen
get_endpoint_notifications.return_value = {
'placement-identity-service_3-keystone_0': 'd5c3'}
self.kv_data = {
'placement-identity-service_3-keystone_0': 'd5c3'}
self.assertFalse(openstack.endpoint_changed('placement'))
@patch.object(openstack, 'get_endpoint_notifications')
@patch.object(openstack.unitdata, 'HookData')
def test_save_endpoint_changed_triggers(self, HookData,
get_endpoint_notifications):
kv = self._unit_paused_helper(HookData)
get_endpoint_notifications.return_value = {
'neutron-identity-service_3-keystone_1': '2aa6',
'placement-identity-service_3-keystone_0': 'd5c3'}
openstack.save_endpoint_changed_triggers(['neutron', 'placement'])
kv_set_calls = [
call('neutron-identity-service_3-keystone_1', '2aa6'),
call('placement-identity-service_3-keystone_0', 'd5c3')]
kv.set.assert_has_calls(kv_set_calls, any_order=True)
class OpenStackUtilsAdditionalTests(TestCase):
SHARED_DB_RELATIONS = {
'shared-db:8': {
'mysql-svc1/0': {
'allowed_units': 'client/0',
},
'mysql-svc1/1': {},
'mysql-svc1/2': {
'allowed_units': 'client/0 client/1',
},
},
'shared-db:12': {
'mysql-svc2/0': {
'allowed_units': 'client/1',
},
'mysql-svc2/1': {
'allowed_units': 'client/3',
},
'mysql-svc2/2': {
'allowed_units': {},
},
}
}
SCALE_RELATIONS = {
'cluster:2': {
'keystone/1': {},
'keystone/2': {}},
'shared-db:12': {
'mysql-svc2/0': {
'allowed_units': 'client/1',
},
'mysql-svc2/1': {
'allowed_units': 'client/3',
},
'mysql-svc2/2': {
'allowed_units': {},
}},
}
SCALE_RELATIONS_HA = {
'cluster:2': {
'keystone/1': {'unit-state-keystone-1': 'READY'},
'keystone/2': {}},
'shared-db:12': {
'mysql-svc2/0': {
'allowed_units': 'client/1',
},
'mysql-svc2/1': {
'allowed_units': 'client/3',
},
'mysql-svc2/2': {
'allowed_units': {},
}},
'ha:32': {
'hacluster-keystone/1': {}}
}
All_PEERS_READY = {
'cluster:2': {
'keystone/1': {'unit-state-keystone-1': 'READY'},
'keystone/2': {'unit-state-keystone-2': 'READY'}}}
PEERS_NOT_READY = {
'cluster:2': {
'keystone/1': {'unit-state-keystone-1': 'READY'},
'keystone/2': {}}}
def setUp(self):
super(OpenStackUtilsAdditionalTests, self).setUp()
[self._patch(m) for m in [
'expect_ha',
'expected_peer_units',
'expected_related_units',
'juju_log',
'metadata',
'related_units',
'relation_get',
'relation_id',
'relation_ids',
'relation_set',
'local_unit',
]]
def _patch(self, method):
_m = patch.object(openstack, method)
mock = _m.start()
self.addCleanup(_m.stop)
setattr(self, method, mock)
def setup_relation(self, relation_map):
relation = FakeRelation(relation_map)
self.relation_id.side_effect = relation.relation_id
self.relation_get.side_effect = relation.get
self.relation_ids.side_effect = relation.relation_ids
self.related_units.side_effect = relation.related_units
return relation
def test_is_db_ready(self):
relation = self.setup_relation(self.SHARED_DB_RELATIONS)
# Check unit allowed in 1st relation
self.local_unit.return_value = 'client/0'
self.assertTrue(openstack.is_db_ready())
# Check unit allowed in 2nd relation
self.local_unit.return_value = 'client/3'
self.assertTrue(openstack.is_db_ready())
# Check unit not allowed in any list
self.local_unit.return_value = 'client/5'
self.assertFalse(openstack.is_db_ready())
# Check call with an invalid relation
self.local_unit.return_value = 'client/3'
# None returned if not in a relation context (eg update-status)
relation.clear_relation_context()
self.assertRaises(
Exception,
openstack.is_db_ready,
use_current_context=True)
# Check unit allowed using current relation context
relation.set_relation_context('mysql-svc2/0', 'shared-db:12')
self.local_unit.return_value = 'client/1'
self.assertTrue(openstack.is_db_ready(use_current_context=True))
# Check unit not allowed using current relation context
relation.set_relation_context('mysql-svc2/0', 'shared-db:12')
self.local_unit.return_value = 'client/0'
self.assertFalse(openstack.is_db_ready(use_current_context=True))
@patch.object(openstack, 'container_scoped_relations')
def test_is_expected_scale_noha(self, container_scoped_relations):
self.setup_relation(self.SCALE_RELATIONS)
self.expect_ha.return_value = False
eru = {
'shared-db': ['mysql/0', 'mysql/1', 'mysql/2']}
def _expected_related_units(reltype):
return eru[reltype]
self.expected_related_units.side_effect = _expected_related_units
container_scoped_relations.return_value = ['ha', 'domain-backend']
# All peer and db units are present
self.expected_peer_units.return_value = ['keystone/0', 'keystone/2']
self.assertTrue(openstack.is_expected_scale())
# db units are present but a peer is missing
self.expected_peer_units.return_value = ['keystone/0', 'keystone/2', 'keystone/3']
self.assertFalse(openstack.is_expected_scale())
# peer units are present but a db unit is missing
eru['shared-db'].append('mysql/3')
self.expected_peer_units.return_value = ['keystone/0', 'keystone/2']
self.assertFalse(openstack.is_expected_scale())
eru['shared-db'].remove('mysql/3')
# Expect ha but ha unit is missing
self.expect_ha.return_value = True
self.expected_peer_units.return_value = ['keystone/0', 'keystone/2']
self.assertFalse(openstack.is_expected_scale())
@patch.object(openstack, 'container_scoped_relations')
def test_is_expected_scale_ha(self, container_scoped_relations):
self.setup_relation(self.SCALE_RELATIONS_HA)
eru = {
'shared-db': ['mysql/0', 'mysql/1', 'mysql/2']}
def _expected_related_units(reltype):
return eru[reltype]
self.expected_related_units.side_effect = _expected_related_units
container_scoped_relations.return_value = ['ha', 'domain-backend']
self.expect_ha.return_value = True
self.expected_peer_units.return_value = ['keystone/0', 'keystone/2']
self.assertTrue(openstack.is_expected_scale())
def test_container_scoped_relations(self):
_metadata = {
'provides': {
'amqp': {'interface': 'rabbitmq'},
'identity-service': {'interface': 'keystone'},
'ha': {
'interface': 'hacluster',
'scope': 'container'}},
'peers': {
'cluster': {'interface': 'openstack-ha'}}}
self.metadata.return_value = _metadata
self.assertEqual(openstack.container_scoped_relations(), ['ha'])
def test_get_peer_key(self):
self.assertEqual(
openstack.get_peer_key('cinder/0'),
'unit-state-cinder-0')
def test_inform_peers_unit_state(self):
self.local_unit.return_value = 'client/0'
self.setup_relation(self.All_PEERS_READY)
openstack.inform_peers_unit_state('READY')
self.relation_set.assert_called_once_with(
relation_id='cluster:2',
relation_settings={'unit-state-client-0': 'READY'})
def test_get_peers_unit_state(self):
self.setup_relation(self.All_PEERS_READY)
self.assertEqual(
openstack.get_peers_unit_state(),
{'keystone/1': 'READY', 'keystone/2': 'READY'})
self.setup_relation(self.PEERS_NOT_READY)
self.assertEqual(
openstack.get_peers_unit_state(),
{'keystone/1': 'READY', 'keystone/2': 'UNKNOWN'})
def test_are_peers_ready(self):
self.setup_relation(self.All_PEERS_READY)
self.assertTrue(openstack.are_peers_ready())
self.setup_relation(self.PEERS_NOT_READY)
self.assertFalse(openstack.are_peers_ready())
@patch.object(openstack, 'inform_peers_unit_state')
def test_inform_peers_if_ready(self, inform_peers_unit_state):
self.setup_relation(self.All_PEERS_READY)
def _not_ready():
return False, "Its all gone wrong"
def _ready():
return True, "Hurray!"
openstack.inform_peers_if_ready(_not_ready)
inform_peers_unit_state.assert_called_once_with('NOTREADY', 'cluster')
inform_peers_unit_state.reset_mock()
openstack.inform_peers_if_ready(_ready)
inform_peers_unit_state.assert_called_once_with('READY', 'cluster')
@patch.object(openstack, 'is_expected_scale')
@patch.object(openstack, 'is_db_initialised')
@patch.object(openstack, 'is_db_ready')
@patch.object(openstack, 'is_unit_paused_set')
@patch.object(openstack, 'is_db_maintenance_mode')
def test_check_api_unit_ready(self, is_db_maintenance_mode,
is_unit_paused_set, is_db_ready,
is_db_initialised, is_expected_scale):
is_db_maintenance_mode.return_value = True
self.assertFalse(openstack.check_api_unit_ready()[0])
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = True
self.assertFalse(openstack.check_api_unit_ready()[0])
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = False
self.assertFalse(openstack.check_api_unit_ready()[0])
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = True
is_db_initialised.return_value = False
self.assertFalse(openstack.check_api_unit_ready()[0])
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = True
is_db_initialised.return_value = True
is_expected_scale.return_value = False
self.assertFalse(openstack.check_api_unit_ready()[0])
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = True
is_db_initialised.return_value = True
is_expected_scale.return_value = True
self.assertTrue(openstack.check_api_unit_ready()[0])
@patch.object(openstack, 'is_expected_scale')
@patch.object(openstack, 'is_db_initialised')
@patch.object(openstack, 'is_db_ready')
@patch.object(openstack, 'is_unit_paused_set')
@patch.object(openstack, 'is_db_maintenance_mode')
def test_get_api_unit_status(self, is_db_maintenance_mode,
is_unit_paused_set, is_db_ready,
is_db_initialised, is_expected_scale):
is_db_maintenance_mode.return_value = True
self.assertEqual(
openstack.get_api_unit_status()[0].value,
'maintenance')
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = True
self.assertEqual(
openstack.get_api_unit_status()[0].value,
'blocked')
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = False
self.assertEqual(
openstack.get_api_unit_status()[0].value,
'waiting')
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = True
is_db_initialised.return_value = False
self.assertEqual(
openstack.get_api_unit_status()[0].value,
'waiting')
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = True
is_db_initialised.return_value = True
is_expected_scale.return_value = False
self.assertEqual(
openstack.get_api_unit_status()[0].value,
'waiting')
is_db_maintenance_mode.return_value = False
is_unit_paused_set.return_value = False
is_db_ready.return_value = True
is_db_initialised.return_value = True
is_expected_scale.return_value = True
self.assertEqual(
openstack.get_api_unit_status()[0].value,
'active')
@patch.object(openstack, 'get_api_unit_status')
def test_check_api_application_ready(self, get_api_unit_status):
get_api_unit_status.return_value = (WORKLOAD_STATES.ACTIVE, 'Hurray')
self.assertTrue(openstack.check_api_application_ready()[0])
get_api_unit_status.return_value = (WORKLOAD_STATES.BLOCKED, ':-(')
self.assertFalse(openstack.check_api_application_ready()[0])
@patch.object(openstack, 'get_api_unit_status')
def test_get_api_application_status(self, get_api_unit_status):
get_api_unit_status.return_value = (WORKLOAD_STATES.ACTIVE, 'Hurray')
self.assertEqual(
openstack.get_api_application_status()[0].value,
'active')
get_api_unit_status.return_value = (WORKLOAD_STATES.BLOCKED, ':-(')
self.assertEqual(
openstack.get_api_application_status()[0].value,
'blocked')
@patch.object(openstack.deferred_events, 'clear_deferred_restarts')
@patch.object(openstack, 'manage_payload_services')
@patch.object(openstack.deferred_events, 'get_deferred_restarts')
def test_restart_services_action(self, get_deferred_restarts,
manage_payload_services,
clear_deferred_restarts):
deferred_restarts = [
deferred_events.ServiceEvent(
timestamp=123,
service='svcA',
reason='ReasonA',
action='restart')]
get_deferred_restarts.return_value = deferred_restarts
manage_payload_services.return_value = (None, None)
openstack.restart_services_action(deferred_only=True)
manage_payload_services.assert_has_calls([
call('stop', services=['svcA'], charm_func=None),
call('start', services=['svcA'])])
clear_deferred_restarts.assert_called_once_with(['svcA'])
self.assertRaises(
ValueError,
openstack.restart_services_action,
['svcA'],
deferred_only=True)
manage_payload_services.return_value = (None, 'something went wrong')
self.assertRaises(
ServiceActionError,
openstack.restart_services_action,
['svcA'])
if __name__ == '__main__':
unittest.main()