[coreycb,r=james-page] Add deploy from source support.

This commit is contained in:
James Page 2015-04-16 20:49:27 +01:00
commit 9962b569e0
29 changed files with 878 additions and 70 deletions

View File

@ -2,7 +2,7 @@
PYTHON := /usr/bin/env python
lint:
@flake8 --exclude hooks/charmhelpers hooks unit_tests tests
@flake8 --exclude hooks/charmhelpers actions hooks unit_tests tests
@charm proof
unit_test:
@ -14,7 +14,9 @@ test:
#NOTE(beisner): can remove -v after bug 1320357 is fixed
# https://bugs.launchpad.net/amulet/+bug/1320357
@juju test -v -p AMULET_HTTP_PROXY --timeout 900 \
00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse
00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse \
16-basic-trusty-icehouse-git 17-basic-trusty-juno \
18-basic-trusty-juno-git
bin/charm_helpers_sync.py:
@mkdir -p bin

View File

@ -117,3 +117,94 @@ overwrite: Whether or not to wipe local storage that of data that may prevent
enabled-services: Can be used to separate cinder services between service
service units (see previous section)
Deploying from source
---------------------
The minimum openstack-origin-git config required to deploy from source is:
openstack-origin-git:
"repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: stable/juno}
- {name: cinder,
repository: 'git://git.openstack.org/openstack/cinder',
branch: stable/juno}"
Note that there are only two 'name' values the charm knows about: 'requirements'
and 'cinder'. These repositories must correspond to these 'name' values.
Additionally, the requirements repository must be specified first and the
cinder repository must be specified last. All other repostories are installed
in the order in which they are specified.
The following is a full list of current tip repos (may not be up-to-date):
openstack-origin-git:
"repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: master}
- {name: oslo-concurrency,
repository: 'git://git.openstack.org/openstack/oslo.concurrency',
branch: master}
- {name: oslo-config,
repository: 'git://git.openstack.org/openstack/oslo.config',
branch: master}
- {name: oslo-context,
repository': 'git://git.openstack.org/openstack/oslo.context.git',
branch: master}
- {name: oslo-db,
repository: 'git://git.openstack.org/openstack/oslo.db',
branch: master}
- {name: oslo-i18n,
repository: 'git://git.openstack.org/openstack/oslo.i18n',
branch: master}
- {name: oslo-messaging,
repository: 'git://git.openstack.org/openstack/oslo.messaging.git',
branch: master}
- {name: oslo-serialization,
repository: 'git://git.openstack.org/openstack/oslo.serialization',
branch: master}
- {name: oslo-utils,
repository: 'git://git.openstack.org/openstack/oslo.utils',
branch: master}
- {name: oslo-rootwrap,
repository: 'git://git.openstack.org/openstack/oslo.rootwrap.git',
branch: master}
- {name: oslo-vmware,
repository: 'git://git.openstack.org/openstack/oslo.vmware.git',
branch: master}
- {name: osprofiler,
repository: 'git://git.openstack.org/stackforge/osprofiler.git',
branch: master}
- {name: pbr,
repository: 'git://git.openstack.org/openstack-dev/pbr',
branch: master}
- {name: python-barbicanclient,
repository: 'git://git.openstack.org/openstack/python-barbicanclient.git',
branch: master}
- {name: python-glanceclient,
repository: 'git://git.openstack.org/openstack/python-glanceclient.git',
branch: master}
- {name: python-novaclient,
repository: 'git://git.openstack.org/openstack/python-novaclient.git',
branch: master}
- {name: python-swiftclient:
repository: 'git://git.openstack.org/openstack/python-swiftclient.git',
branch: master}
- {name: sqlalchemy-migrate,
repository: 'git://git.openstack.org/stackforge/sqlalchemy-migrate',
branch: master}
- {name: stevedore,
repository: 'git://git.openstack.org/openstack/stevedore.git',
branch: master}
- {name: taskflow,
repository: 'git://git.openstack.org/openstack/taskflow.git',
branch: master}
- {name: keystonemiddleware,
repository: 'git://git.openstack.org/openstack/keystonemiddleware',
branch: master}
- {name: cinder,
repository: 'git://git.openstack.org/openstack/cinder',
branch: master}"

2
actions.yaml Normal file
View File

@ -0,0 +1,2 @@
git-reinstall:
description: Reinstall cinder from the openstack-origin-git repositories.

1
actions/git-reinstall Symbolic link
View File

@ -0,0 +1 @@
git_reinstall.py

45
actions/git_reinstall.py Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/python
import sys
import traceback
sys.path.append('hooks/')
from charmhelpers.contrib.openstack.utils import (
git_install_requested,
)
from charmhelpers.core.hookenv import (
action_set,
action_fail,
config,
)
from cinder_utils import (
git_install,
)
from cinder_hooks import (
config_changed,
)
def git_reinstall():
"""Reinstall from source and restart services.
If the openstack-origin-git config option was used to install openstack
from source git repositories, then this action can be used to reinstall
from updated git repositories, followed by a restart of services."""
if not git_install_requested():
action_fail('openstack-origin-git is not configured')
return
try:
git_install(config('openstack-origin-git'))
config_changed()
except:
action_set({'traceback': traceback.format_exc()})
action_fail('git-reinstall resulted in an unexpected error')
if __name__ == '__main__':
git_reinstall()

View File

@ -1,4 +1,4 @@
branch: lp:charm-helpers
branch: lp:charm-helpers
destination: hooks/charmhelpers
include:
- core

View File

@ -15,6 +15,22 @@ options:
the cloud:precise-folsom/updates repository instead, since Cinder
was not available in the Ubuntu archive for Precise and is only
available via the Ubuntu Cloud Archive.
Note that when openstack-origin-git is specified, openstack
specific packages will be installed from source rather than
from the openstack-origin repository.
openstack-origin-git:
default:
type: string
description: |
Specifies a YAML-formatted dictionary listing the git
repositories and branches from which to install OpenStack and
its dependencies.
Note that the installed config files will be determined based on
the OpenStack release of the openstack-origin option.
For more details see README.md.
enabled-services:
default: all
type: string

View File

@ -524,9 +524,10 @@ def git_clone_and_install(projects_yaml, core_project):
projects = yaml.load(projects_yaml)
_git_validate_projects_yaml(projects, core_project)
old_environ = dict(os.environ)
if 'http_proxy' in projects.keys():
os.environ['http_proxy'] = projects['http_proxy']
if 'https_proxy' in projects.keys():
os.environ['https_proxy'] = projects['https_proxy']
@ -544,6 +545,8 @@ def git_clone_and_install(projects_yaml, core_project):
repo_dir = _git_clone_and_install_single(repo, branch, parent_dir,
update_requirements=True)
os.environ = old_environ
def _git_validate_projects_yaml(projects, core_project):
"""

View File

@ -10,6 +10,7 @@ from subprocess import (
from cinder_utils import (
determine_packages,
do_openstack_upgrade,
git_install,
juju_log,
migrate_database,
configure_lvm_storage,
@ -53,10 +54,12 @@ from charmhelpers.core.host import (
)
from charmhelpers.contrib.openstack.utils import (
config_value_changed,
configure_installation_source,
git_install_requested,
openstack_upgrade_available,
sync_db_with_multi_ipv6_addresses,
get_os_codename_package
os_release,
)
from charmhelpers.contrib.storage.linux.ceph import (
@ -101,9 +104,12 @@ def install():
src == 'distro'):
src = 'cloud:precise-folsom'
configure_installation_source(src)
apt_update()
apt_install(determine_packages(), fatal=True)
git_install(config('openstack-origin-git'))
@hooks.hook('config-changed')
@restart_on_change(restart_map(), stopstart=True)
@ -123,12 +129,16 @@ def config_changed():
conf['overwrite'] in ['true', 'True', True],
conf['remove-missing'])
if openstack_upgrade_available('cinder-common'):
do_openstack_upgrade(configs=CONFIGS)
# NOTE(jamespage) tell any storage-backends we just upgraded
for rid in relation_ids('storage-backend'):
relation_set(relation_id=rid,
upgrade_nonce=uuid.uuid4())
if git_install_requested():
if config_value_changed('openstack-origin-git'):
git_install(config('openstack-origin-git'))
else:
if openstack_upgrade_available('cinder-common'):
do_openstack_upgrade(configs=CONFIGS)
# NOTE(jamespage) tell any storage-backends we just upgraded
for rid in relation_ids('storage-backend'):
relation_set(relation_id=rid,
upgrade_nonce=uuid.uuid4())
CONFIGS.write_all()
configure_https()
@ -255,7 +265,7 @@ def identity_joined(rid=None):
'cinder_internal_url': internal_url,
'cinder_admin_url': admin_url,
}
if get_os_codename_package('cinder-common') >= 'icehouse':
if os_release('cinder-common') >= 'icehouse':
# NOTE(jamespage) register v2 endpoint as well
public_url = '{}:{}/v2/$(tenant_id)s'.format(
canonical_url(CONFIGS, PUBLIC),

View File

@ -1,10 +1,12 @@
import os
import shutil
import subprocess
from collections import OrderedDict
from copy import copy
from charmhelpers.core.hookenv import (
charm_dir,
config,
relation_ids,
log,
@ -19,12 +21,17 @@ from charmhelpers.fetch import (
)
from charmhelpers.core.host import (
adduser,
add_group,
add_user_to_group,
lsb_release,
mkdir,
mounts,
umount,
service_restart,
service_stop,
service_start,
mkdir,
lsb_release
write_file,
)
from charmhelpers.contrib.openstack.alternatives import install_alternative
@ -58,10 +65,15 @@ from charmhelpers.contrib.openstack import (
from charmhelpers.contrib.openstack.utils import (
configure_installation_source,
get_os_codename_package,
get_os_codename_install_source,
git_install_requested,
git_clone_and_install,
git_src_dir,
os_release,
)
from charmhelpers.core.templating import render
import cinder_contexts
COMMON_PACKAGES = [
@ -81,6 +93,25 @@ API_PACKAGES = ['cinder-api']
VOLUME_PACKAGES = ['cinder-volume']
SCHEDULER_PACKAGES = ['cinder-scheduler']
BASE_GIT_PACKAGES = [
'libxml2-dev',
'libxslt1-dev',
'lvm2',
'python-dev',
'python-pip',
'python-setuptools',
'zlib1g-dev',
]
# ubuntu packages that should not be installed when deploying from source
GIT_PACKAGE_BLACKLIST = [
'cinder-api',
'cinder-common',
'cinder-scheduler',
'cinder-volume',
'python-keystoneclient',
]
DEFAULT_LOOPBACK_SIZE = '5G'
# Cluster resource used to determine leadership when hacluster'd
@ -166,7 +197,7 @@ def register_configs():
# if called without anything installed (eg during install hook)
# just default to earliest supported release. configs dont get touched
# till post-install, anyway.
release = get_os_codename_package('cinder-common', fatal=False) or 'folsom'
release = os_release('cinder-common', base='folsom')
configs = templating.OSConfigRenderer(templates_dir=TEMPLATES,
openstack_release=release)
@ -218,6 +249,13 @@ def determine_packages():
('scheduler', SCHEDULER_PACKAGES)]:
if service_enabled(s):
pkgs += p
if git_install_requested():
pkgs.extend(BASE_GIT_PACKAGES)
# don't include packages that will be installed from git
for p in GIT_PACKAGE_BLACKLIST:
pkgs.remove(p)
return pkgs
@ -467,3 +505,142 @@ def setup_ipv6():
' main')
apt_update()
apt_install('haproxy/trusty-backports', fatal=True)
def git_install(projects_yaml):
"""Perform setup, and install git repos specified in yaml parameter."""
if git_install_requested():
git_pre_install()
git_clone_and_install(projects_yaml, core_project='cinder')
git_post_install(projects_yaml)
def git_pre_install():
"""Perform cinder pre-install setup."""
dirs = [{'path': '/etc/tgt',
'owner': 'cinder',
'group': 'cinder',
'perms': 0750,
},
{'path': '/var/lib/cinder',
'owner': 'cinder',
'group': 'cinder',
'perms': 0755,
},
{'path': '/var/lib/cinder/volumes',
'owner': 'cinder',
'group': 'cinder',
'perms': 0750,
},
{'path': '/var/lock/cinder',
'owner': 'cinder',
'group': 'root',
'perms': 0750,
},
{'path': '/var/log/cinder',
'owner': 'cinder',
'group': 'cinder',
'perms': 0750,
}]
logs = [
'/var/log/cinder/cinder-api.log',
'/var/log/cinder/cinder-backup.log',
'/var/log/cinder/cinder-scheduler.log',
'/var/log/cinder/cinder-volume.log',
]
adduser('cinder', shell='/bin/bash', system_user=True)
add_group('cinder', system_group=True)
add_user_to_group('cinder', 'cinder')
for d in dirs:
mkdir(d['path'], owner=d['owner'], group=d['group'], perms=d['perms'],
force=False)
for l in logs:
write_file(l, '', owner='cinder', group='cinder', perms=0600)
def git_post_install(projects_yaml):
"""Perform cinder post-install setup."""
src_etc = os.path.join(git_src_dir(projects_yaml, 'cinder'), 'etc/cinder')
configs = {
'src': src_etc,
'dest': '/etc/cinder',
}
if os.path.exists(configs['dest']):
shutil.rmtree(configs['dest'])
shutil.copytree(configs['src'], configs['dest'])
render('cinder.conf', '/etc/cinder/cinder.conf', {}, owner='cinder',
group='cinder', perms=0o644)
render('git/cinder_tgt.conf', '/etc/tgt/conf.d', {}, owner='cinder',
group='cinder', perms=0o644)
render('git/logging.conf', '/etc/cinder/logging.conf', {}, owner='cinder',
group='cinder', perms=0o644)
render('git/cinder_sudoers', '/etc/sudoers.d/cinder_sudoers', {},
owner='root', group='root', perms=0o440)
os.chmod('/etc/sudoers.d', 0o750)
cinder_api_context = {
'service_description': 'Cinder API server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-api',
'executable_name': '/usr/local/bin/cinder-api',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-api.log',
}
cinder_backup_context = {
'service_description': 'Cinder backup server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-backup',
'executable_name': '/usr/local/bin/cinder-backup',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-backup.log',
}
cinder_scheduler_context = {
'service_description': 'Cinder scheduler server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-scheduler',
'executable_name': '/usr/local/bin/cinder-scheduler',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-scheduler.log',
}
cinder_volume_context = {
'service_description': 'Cinder volume server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-volume',
'executable_name': '/usr/local/bin/cinder-volume',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-volume.log',
}
# NOTE(coreycb): Needs systemd support
templates_dir = 'hooks/charmhelpers/contrib/openstack/templates'
templates_dir = os.path.join(charm_dir(), templates_dir)
render('git.upstart', '/etc/init/cinder-api.conf',
cinder_api_context, perms=0o644, templates_dir=templates_dir)
render('git.upstart', '/etc/init/cinder-backup.conf',
cinder_backup_context, perms=0o644, templates_dir=templates_dir)
render('git.upstart', '/etc/init/cinder-scheduler.conf',
cinder_scheduler_context, perms=0o644, templates_dir=templates_dir)
render('git.upstart', '/etc/init/cinder-volume.conf',
cinder_volume_context, perms=0o644, templates_dir=templates_dir)
service_restart('tgtd')
[service_restart(s) for s in services()]

View File

@ -0,0 +1,4 @@
Defaults:cinder !requiretty
cinder ALL = (root) NOPASSWD: /usr/local/bin/cinder-rootwrap /etc/cinder/rootwrap.conf *

View File

@ -0,0 +1 @@
include /var/lib/cinder/volumes/*

View File

@ -0,0 +1,76 @@
[loggers]
keys = root, cinder
[handlers]
keys = stderr, stdout, watchedfile, syslog, null
[formatters]
keys = legacycinder, default
[logger_root]
level = WARNING
handlers = null
[logger_cinder]
level = INFO
handlers = stderr
qualname = cinder
[logger_amqplib]
level = WARNING
handlers = stderr
qualname = amqplib
[logger_sqlalchemy]
level = WARNING
handlers = stderr
qualname = sqlalchemy
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARNING" logs neither. (Recommended for production systems.)
[logger_boto]
level = WARNING
handlers = stderr
qualname = boto
[logger_suds]
level = INFO
handlers = stderr
qualname = suds
[logger_eventletwsgi]
level = WARNING
handlers = stderr
qualname = eventlet.wsgi.server
[handler_stderr]
class = StreamHandler
args = (sys.stderr,)
formatter = legacycinder
[handler_stdout]
class = StreamHandler
args = (sys.stdout,)
formatter = legacycinder
[handler_watchedfile]
class = handlers.WatchedFileHandler
args = ('cinder.log',)
formatter = legacycinder
[handler_syslog]
class = handlers.SysLogHandler
args = ('/dev/log', handlers.SysLogHandler.LOG_USER)
formatter = legacycinder
[handler_null]
class = cinder.log.NullHandler
formatter = default
args = ()
[formatter_legacycinder]
class = cinder.log.LegacyCinderFormatter
[formatter_default]
format = %(message)s

View File

@ -5,6 +5,7 @@ set -ex
sudo add-apt-repository --yes ppa:juju/stable
sudo apt-get update --yes
sudo apt-get install --yes python-amulet \
python-keystoneclient \
python-cinderclient \
python-glanceclient
python-glanceclient \
python-keystoneclient \
python-novaclient

View File

@ -1,11 +0,0 @@
#!/usr/bin/python
"""Amulet tests on a basic cinder deployment on precise-folsom."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='precise',
openstack='cloud:precise-folsom',
source='cloud:precise-updates/folsom')
deployment.run_tests()

View File

@ -1,11 +0,0 @@
#!/usr/bin/python
"""Amulet tests on a basic cinder deployment on precise-grizzly."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='precise',
openstack='cloud:precise-grizzly',
source='cloud:precise-updates/grizzly')
deployment.run_tests()

View File

@ -1,11 +0,0 @@
#!/usr/bin/python
"""Amulet tests on a basic cinder deployment on precise-havana."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='precise',
openstack='cloud:precise-havana',
source='cloud:precise-updates/havana')
deployment.run_tests()

View File

@ -0,0 +1,9 @@
#!/usr/bin/python
"""Amulet tests on a basic Cinder git deployment on trusty-icehouse."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='trusty', git=True)
deployment.run_tests()

11
tests/17-basic-trusty-juno Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/python
"""Amulet tests on a basic Cinder deployment on trusty-juno."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='trusty',
openstack='cloud:trusty-juno',
source='cloud:trusty-updates/juno')
deployment.run_tests()

12
tests/18-basic-trusty-juno-git Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/python
"""Amulet tests on a basic Cinder git deployment on trusty-juno."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='trusty',
openstack='cloud:trusty-juno',
source='cloud:trusty-updates/juno',
git=True)
deployment.run_tests()

12
tests/20-basic-trusty-kilo-git Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/python
"""Amulet tests on a basic cinder git deployment on trusty-kilo."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='trusty',
openstack='cloud:trusty-kilo',
source='cloud:trusty-updates/kilo',
git=True)
deployment.run_tests()

9
tests/22-basic-vivid-kilo-git Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/python
"""Amulet tests on a basic Cinder git deployment on vivid-kilo."""
from basic_deployment import CinderBasicDeployment
if __name__ == '__main__':
deployment = CinderBasicDeployment(series='vivid', git=True)
deployment.run_tests()

View File

@ -1,8 +1,10 @@
#!/usr/bin/python
import amulet
import os
import types
from time import sleep
import yaml
import cinderclient.v1.client as cinder_client
from charmhelpers.contrib.openstack.amulet.deployment import (
@ -28,10 +30,12 @@ class CinderBasicDeployment(OpenStackAmuletDeployment):
# NOTE(beisner): Features and tests vary across Openstack releases.
# https://wiki.openstack.org/wiki/CinderSupportMatrix
def __init__(self, series=None, openstack=None, source=None, stable=False):
def __init__(self, series=None, openstack=None, source=None, git=False,
stable=False):
'''Deploy the entire test environment.'''
super(CinderBasicDeployment, self).__init__(series, openstack, source,
stable)
self.git = git
self._add_services()
self._add_relations()
self._configure_services()
@ -67,11 +71,31 @@ class CinderBasicDeployment(OpenStackAmuletDeployment):
def _configure_services(self):
'''Configure all of the services.'''
keystone_config = {'admin-password': 'openstack',
'admin-token': 'ubuntutesting'}
cinder_config = {'block-device': 'vdb',
'glance-api-version': '2',
'overwrite': 'true'}
if self.git:
branch = 'stable/' + self._get_openstack_release_string()
amulet_http_proxy = os.environ.get('AMULET_HTTP_PROXY')
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository':
'git://git.openstack.org/openstack/requirements',
'branch': branch},
{'name': 'cinder',
'repository': 'git://git.openstack.org/openstack/cinder',
'branch': branch},
],
'directory': '/mnt/openstack-git',
'http_proxy': amulet_http_proxy,
'https_proxy': amulet_http_proxy,
}
cinder_config['openstack-origin-git'] = \
yaml.dump(openstack_origin_git)
keystone_config = {'admin-password': 'openstack',
'admin-token': 'ubuntutesting'}
mysql_config = {'dataset-size': '50%'}
configs = {'cinder': cinder_config,
'keystone': keystone_config,

View File

@ -1,2 +1,4 @@
import sys
sys.path.append('actions')
sys.path.append('hooks')

View File

@ -0,0 +1,110 @@
from mock import patch, MagicMock
import os
os.environ['JUJU_UNIT_NAME'] = 'cinder'
from test_utils import RESTART_MAP
import cinder_utils as utils
# Need to do some early patching to get the module loaded.
_restart_map = utils.restart_map
_register_configs = utils.register_configs
utils.restart_map = MagicMock()
utils.restart_map.return_value = RESTART_MAP
utils.register_configs = MagicMock()
import git_reinstall
# Unpatch it now that its loaded.
utils.restart_map = _restart_map
utils.register_configs = _register_configs
from test_utils import (
CharmTestCase
)
TO_PATCH = [
'config',
]
openstack_origin_git = \
"""repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: stable/juno}
- {name: cinder,
repository: 'git://git.openstack.org/openstack/cinder',
branch: stable/juno}"""
class TestCinderActions(CharmTestCase):
def setUp(self):
super(TestCinderActions, self).setUp(git_reinstall, TO_PATCH)
self.config.side_effect = self.test_config.get
@patch.object(git_reinstall, 'action_set')
@patch.object(git_reinstall, 'action_fail')
@patch.object(git_reinstall, 'git_install')
@patch.object(git_reinstall, 'config_changed')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_git_reinstall(self, _config, config_changed, git_install,
action_fail, action_set):
_config.return_value = openstack_origin_git
self.test_config.set('openstack-origin-git', openstack_origin_git)
git_reinstall.git_reinstall()
git_install.assert_called_with(openstack_origin_git)
self.assertTrue(git_install.called)
self.assertTrue(config_changed.called)
self.assertFalse(action_set.called)
self.assertFalse(action_fail.called)
@patch.object(git_reinstall, 'action_set')
@patch.object(git_reinstall, 'action_fail')
@patch.object(git_reinstall, 'git_install')
@patch.object(git_reinstall, 'config_changed')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_git_reinstall_not_configured(self, _config, config_changed,
git_install, action_fail,
action_set):
_config.return_value = None
git_reinstall.git_reinstall()
msg = 'openstack-origin-git is not configured'
action_fail.assert_called_with(msg)
self.assertFalse(git_install.called)
self.assertFalse(action_set.called)
@patch.object(git_reinstall, 'action_set')
@patch.object(git_reinstall, 'action_fail')
@patch.object(git_reinstall, 'git_install')
@patch.object(git_reinstall, 'config_changed')
@patch('traceback.format_exc')
@patch('charmhelpers.contrib.openstack.utils.config')
def test_git_reinstall_exception(self, _config, format_exc, config_changed,
git_install, action_fail, action_set):
_config.return_value = openstack_origin_git
e = OSError('something bad happened')
git_install.side_effect = e
traceback = (
"Traceback (most recent call last):\n"
" File \"actions/git_reinstall.py\", line 37, in git_reinstall\n"
" git_install(config(\'openstack-origin-git\'))\n"
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 964, in __call__\n" # noqa
" return _mock_self._mock_call(*args, **kwargs)\n"
" File \"/usr/lib/python2.7/dist-packages/mock.py\", line 1019, in _mock_call\n" # noqa
" raise effect\n"
"OSError: something bad happened\n")
format_exc.return_value = traceback
git_reinstall.git_reinstall()
msg = 'git-reinstall resulted in an unexpected error'
action_fail.assert_called_with(msg)
action_set.assert_called_with({'traceback': traceback})

View File

@ -4,6 +4,7 @@ from mock import (
patch,
call
)
import yaml
import cinder_utils as utils
from test_utils import (
@ -35,6 +36,7 @@ TO_PATCH = [
'determine_packages',
'do_openstack_upgrade',
'ensure_ceph_keyring',
'git_install',
'juju_log',
'log',
'lsb_release',
@ -63,7 +65,7 @@ TO_PATCH = [
# charmhelpers.contrib.openstack.openstack_utils
'configure_installation_source',
'openstack_upgrade_available',
'get_os_codename_package',
'os_release',
# charmhelpers.contrib.hahelpers.cluster_utils
'canonical_url',
'eligible_leader',
@ -79,17 +81,47 @@ class TestInstallHook(CharmTestCase):
def setUp(self):
super(TestInstallHook, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get_all
self.config.side_effect = self.test_config.get
def test_install_precise_distro(self):
@patch.object(utils, 'git_install_requested')
def test_install_precise_distro(self, git_requested):
'It redirects to cloud archive if setup to install precise+distro'
git_requested.return_value = False
self.lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'}
hooks.hooks.execute(['hooks/install'])
ca = 'cloud:precise-folsom'
self.configure_installation_source.assert_called_with(ca)
def test_correct_install_packages(self):
@patch.object(utils, 'git_install_requested')
def test_install_git(self, git_requested):
git_requested.return_value = True
self.determine_packages.return_value = ['foo', 'bar', 'baz']
repo = 'cloud:trusty-juno'
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
'branch': 'stable/juno'},
{'name': 'cinder',
'repository': 'git://git.openstack.org/openstack/cinder',
'branch': 'stable/juno'}
],
'directory': '/mnt/openstack-git',
}
projects_yaml = yaml.dump(openstack_origin_git)
self.test_config.set('openstack-origin', repo)
self.test_config.set('openstack-origin-git', projects_yaml)
hooks.hooks.execute(['hooks/install'])
self.assertTrue(self.execd_preinstall.called)
self.configure_installation_source.assert_called_with(repo)
self.apt_update.assert_called_with()
self.apt_install.assert_called_with(['foo', 'bar', 'baz'], fatal=True)
self.git_install.assert_called_with(projects_yaml)
@patch.object(utils, 'git_install_requested')
def test_correct_install_packages(self, git_requested):
'It installs the correct packages based on what is determined'
git_requested.return_value = False
self.determine_packages.return_value = ['foo', 'bar', 'baz']
hooks.hooks.execute(['hooks/install'])
self.apt_install.assert_called_with(['foo', 'bar', 'baz'], fatal=True)
@ -99,7 +131,7 @@ class TestChangedHooks(CharmTestCase):
def setUp(self):
super(TestChangedHooks, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get_all
self.config.side_effect = self.test_config.get
@patch.object(hooks, 'amqp_joined')
def test_upgrade_charm_no_amqp(self, _joined):
@ -114,8 +146,10 @@ class TestChangedHooks(CharmTestCase):
_joined.assert_called_with(relation_id='amqp:1')
@patch.object(hooks, 'configure_https')
def test_config_changed(self, conf_https):
@patch.object(hooks, 'git_install_requested')
def test_config_changed(self, git_requested, conf_https):
'It writes out all config'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = False
hooks.hooks.execute(['hooks/config-changed'])
self.assertTrue(self.CONFIGS.write_all.called)
@ -125,8 +159,10 @@ class TestChangedHooks(CharmTestCase):
False, False)
@patch.object(hooks, 'configure_https')
def test_config_changed_block_devices(self, conf_https):
@patch.object(hooks, 'git_install_requested')
def test_config_changed_block_devices(self, git_requested, conf_https):
'It writes out all config'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = False
self.test_config.set('block-device', 'sdb /dev/sdc sde')
self.test_config.set('volume-group', 'cinder-new')
@ -141,12 +177,40 @@ class TestChangedHooks(CharmTestCase):
True, True)
@patch.object(hooks, 'configure_https')
def test_config_changed_upgrade_available(self, conf_https):
@patch.object(hooks, 'git_install_requested')
def test_config_changed_upgrade_available(self, git_requested, conf_https):
'It writes out all config with an available OS upgrade'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = True
hooks.hooks.execute(['hooks/config-changed'])
self.do_openstack_upgrade.assert_called_with(configs=self.CONFIGS)
@patch.object(hooks, 'configure_https')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed_git_updated(self, config_val_changed,
git_requested, conf_https):
git_requested.return_value = True
repo = 'cloud:trusty-juno'
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
'branch': 'stable/juno'},
{'name': 'cinder',
'repository': 'git://git.openstack.org/openstack/',
'branch': 'stable/juno'}
],
'directory': '/mnt/openstack-git',
}
projects_yaml = yaml.dump(openstack_origin_git)
self.test_config.set('openstack-origin', repo)
self.test_config.set('openstack-origin-git', projects_yaml)
hooks.hooks.execute(['hooks/config-changed'])
self.git_install.assert_called_with(projects_yaml)
self.assertFalse(self.do_openstack_upgrade.called)
self.assertTrue(conf_https.called)
def test_db_changed(self):
'It writes out cinder.conf on db changed'
self.relation_get.return_value = 'cinder/0 cinder/1'
@ -341,7 +405,7 @@ class TestJoinedHooks(CharmTestCase):
def test_identity_service_joined(self):
'It properly requests unclustered endpoint via identity-service'
self.get_os_codename_package.return_value = 'havana'
self.os_release.return_value = 'havana'
self.unit_get.return_value = 'cindernode1'
self.config.side_effect = self.test_config.get
self.canonical_url.return_value = 'http://cindernode1'
@ -363,7 +427,7 @@ class TestJoinedHooks(CharmTestCase):
def test_identity_service_joined_icehouse(self):
'It properly requests unclustered endpoint via identity-service'
self.get_os_codename_package.return_value = 'icehouse'
self.os_release.return_value = 'icehouse'
self.unit_get.return_value = 'cindernode1'
self.config.side_effect = self.test_config.get
self.canonical_url.return_value = 'http://cindernode1'

View File

@ -30,7 +30,7 @@ TO_PATCH = [
'ensure_loopback_device',
'is_block_device',
'zap_disk',
'get_os_codename_package',
'os_release',
'get_os_codename_install_source',
'configure_installation_source',
'eligible_leader',
@ -68,6 +68,15 @@ FDISKDISPLAY = """
"""
openstack_origin_git = \
"""repositories:
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements',
branch: stable/juno}
- {name: cinder,
repository: 'git://git.openstack.org/openstack/cinder',
branch: stable/juno}"""
class TestCinderUtils(CharmTestCase):
@ -97,8 +106,10 @@ class TestCinderUtils(CharmTestCase):
self.assertFalse(cinder_utils.service_enabled('volume'))
@patch('cinder_utils.service_enabled')
def test_determine_packages_all(self, service_enabled):
@patch('cinder_utils.git_install_requested')
def test_determine_packages_all(self, git_requested, service_enabled):
'It determines all packages required when all services enabled'
git_requested.return_value = False
service_enabled.return_value = True
pkgs = cinder_utils.determine_packages()
self.assertEquals(sorted(pkgs),
@ -108,8 +119,10 @@ class TestCinderUtils(CharmTestCase):
cinder_utils.SCHEDULER_PACKAGES))
@patch('cinder_utils.service_enabled')
def test_determine_packages_subset(self, service_enabled):
@patch('cinder_utils.git_install_requested')
def test_determine_packages_subset(self, git_requested, service_enabled):
'It determines packages required for a subset of enabled services'
git_requested.return_value = False
service_enabled.side_effect = self.svc_enabled
self.test_config.set('enabled-services', 'api')
@ -402,7 +415,7 @@ class TestCinderUtils(CharmTestCase):
@patch('os.path.exists')
def test_register_configs_apache(self, exists):
exists.return_value = False
self.get_os_codename_package.return_value = 'grizzly'
self.os_release.return_value = 'grizzly'
self.relation_ids.return_value = False
configs = cinder_utils.register_configs()
calls = []
@ -419,7 +432,7 @@ class TestCinderUtils(CharmTestCase):
@patch('os.path.exists')
def test_register_configs_apache24(self, exists):
exists.return_value = True
self.get_os_codename_package.return_value = 'grizzly'
self.os_release.return_value = 'grizzly'
self.relation_ids.return_value = False
configs = cinder_utils.register_configs()
calls = []
@ -438,7 +451,7 @@ class TestCinderUtils(CharmTestCase):
def test_register_configs_ceph(self, exists, isdir):
exists.return_value = True
isdir.return_value = False
self.get_os_codename_package.return_value = 'grizzly'
self.os_release.return_value = 'grizzly'
self.relation_ids.return_value = ['ceph:0']
self.ceph_config_file.return_value = '/var/lib/charm/cinder/ceph.conf'
configs = cinder_utils.register_configs()
@ -504,3 +517,149 @@ class TestCinderUtils(CharmTestCase):
self.apt_install.assert_called_with(['mypackage'], fatal=True)
configs.set_release.assert_called_with(openstack_release='havana')
self.assertFalse(migrate.called)
@patch.object(cinder_utils, 'git_install_requested')
@patch.object(cinder_utils, 'git_clone_and_install')
@patch.object(cinder_utils, 'git_post_install')
@patch.object(cinder_utils, 'git_pre_install')
def test_git_install(self, git_pre, git_post, git_clone_and_install,
git_requested):
projects_yaml = openstack_origin_git
git_requested.return_value = True
cinder_utils.git_install(projects_yaml)
self.assertTrue(git_pre.called)
git_clone_and_install.assert_called_with(openstack_origin_git,
core_project='cinder')
self.assertTrue(git_post.called)
@patch.object(cinder_utils, 'mkdir')
@patch.object(cinder_utils, 'write_file')
@patch.object(cinder_utils, 'add_user_to_group')
@patch.object(cinder_utils, 'add_group')
@patch.object(cinder_utils, 'adduser')
def test_git_pre_install(self, adduser, add_group, add_user_to_group,
write_file, mkdir):
cinder_utils.git_pre_install()
adduser.assert_called_with('cinder', shell='/bin/bash',
system_user=True)
add_group.assert_called_with('cinder', system_group=True)
add_user_to_group.assert_called_with('cinder', 'cinder')
expected = [
call('/etc/tgt', owner='cinder', perms=488, force=False,
group='cinder'),
call('/var/lib/cinder', owner='cinder', perms=493, force=False,
group='cinder'),
call('/var/lib/cinder/volumes', owner='cinder', perms=488,
force=False, group='cinder'),
call('/var/lock/cinder', owner='cinder', perms=488, force=False,
group='root'),
call('/var/log/cinder', owner='cinder', perms=488, force=False,
group='cinder'),
]
self.assertEquals(mkdir.call_args_list, expected)
expected = [
call('/var/log/cinder/cinder-api.log', '', perms=0600,
owner='cinder', group='cinder'),
call('/var/log/cinder/cinder-backup.log', '', perms=0600,
owner='cinder', group='cinder'),
call('/var/log/cinder/cinder-scheduler.log', '', perms=0600,
owner='cinder', group='cinder'),
call('/var/log/cinder/cinder-volume.log', '', perms=0600,
owner='cinder', group='cinder'),
]
self.assertEquals(write_file.call_args_list, expected)
@patch.object(cinder_utils, 'git_src_dir')
@patch.object(cinder_utils, 'service_restart')
@patch.object(cinder_utils, 'render')
@patch('os.path.join')
@patch('os.path.exists')
@patch('shutil.copytree')
@patch('shutil.rmtree')
@patch('pwd.getpwnam')
@patch('grp.getgrnam')
@patch('os.chown')
@patch('os.chmod')
def test_git_post_install(self, chmod, chown, grp, pwd, rmtree, copytree,
exists, join, render, service_restart,
git_src_dir):
projects_yaml = openstack_origin_git
join.return_value = 'joined-string'
cinder_utils.git_post_install(projects_yaml)
expected = [
call('joined-string', '/etc/cinder'),
]
copytree.assert_has_calls(expected)
cinder_api_context = {
'service_description': 'Cinder API server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-api',
'executable_name': '/usr/local/bin/cinder-api',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-api.log',
}
cinder_backup_context = {
'service_description': 'Cinder backup server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-backup',
'executable_name': '/usr/local/bin/cinder-backup',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-backup.log',
}
cinder_scheduler_context = {
'service_description': 'Cinder scheduler server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-scheduler',
'executable_name': '/usr/local/bin/cinder-scheduler',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-scheduler.log',
}
cinder_volume_context = {
'service_description': 'Cinder volume server',
'service_name': 'Cinder',
'user_name': 'cinder',
'start_dir': '/var/lib/cinder',
'process_name': 'cinder-volume',
'executable_name': '/usr/local/bin/cinder-volume',
'config_files': ['/etc/cinder/cinder.conf'],
'log_file': '/var/log/cinder/cinder-volume.log',
}
expected = [
call('cinder.conf', '/etc/cinder/cinder.conf', {}, owner='cinder',
group='cinder', perms=0o644),
call('git/cinder_tgt.conf', '/etc/tgt/conf.d', {}, owner='cinder',
group='cinder', perms=0o644),
call('git/logging.conf', '/etc/cinder/logging.conf', {},
owner='cinder', group='cinder', perms=0o644),
call('git/cinder_sudoers', '/etc/sudoers.d/cinder_sudoers', {},
owner='root', group='root', perms=0o440),
call('git.upstart', '/etc/init/cinder-api.conf',
cinder_api_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/cinder-backup.conf',
cinder_backup_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/cinder-scheduler.conf',
cinder_scheduler_context, perms=0o644,
templates_dir='joined-string'),
call('git.upstart', '/etc/init/cinder-volume.conf',
cinder_volume_context, perms=0o644,
templates_dir='joined-string'),
]
self.assertEquals(render.call_args_list, expected)
expected = [
call('tgtd'), call('haproxy'), call('apache2'),
call('cinder-api'), call('cinder-volume'),
call('cinder-scheduler'),
]
self.assertEquals(service_restart.call_args_list, expected)