401 lines
16 KiB
Python
401 lines
16 KiB
Python
# Copyright 2016 Canonical Ltd
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from mock import MagicMock, patch, call
|
|
import os
|
|
from collections import OrderedDict
|
|
import charmhelpers.contrib.openstack.templating as templating
|
|
templating.OSConfigRenderer = MagicMock()
|
|
import horizon_utils as horizon_utils
|
|
|
|
from test_utils import (
|
|
CharmTestCase
|
|
)
|
|
|
|
TO_PATCH = [
|
|
'config',
|
|
'get_os_codename_install_source',
|
|
'apt_update',
|
|
'apt_upgrade',
|
|
'apt_install',
|
|
'configure_installation_source',
|
|
'log',
|
|
'cmp_pkgrevno',
|
|
'os_release',
|
|
'os_application_version_set',
|
|
'reset_os_release',
|
|
]
|
|
|
|
openstack_origin_git = \
|
|
"""repositories:
|
|
- {name: requirements,
|
|
repository: 'git://git.openstack.org/openstack/requirements',
|
|
branch: stable/juno}
|
|
- {name: horizon,
|
|
repository: 'git://git.openstack.org/openstack/horizon',
|
|
branch: stable/juno}"""
|
|
|
|
|
|
class TestHorizohorizon_utils(CharmTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestHorizohorizon_utils, self).setUp(horizon_utils, TO_PATCH)
|
|
|
|
@patch.object(horizon_utils, 'get_os_codename_install_source')
|
|
@patch.object(horizon_utils, 'git_install_requested')
|
|
def test_determine_packages(self, _git_install_requested,
|
|
_get_os_codename_install_source):
|
|
_git_install_requested.return_value = False
|
|
_get_os_codename_install_source.return_value = 'icehouse'
|
|
self.assertEqual(horizon_utils.determine_packages(), [
|
|
'haproxy',
|
|
'python-novaclient',
|
|
'python-keystoneclient',
|
|
'openstack-dashboard-ubuntu-theme',
|
|
'python-memcache',
|
|
'openstack-dashboard',
|
|
'memcached'])
|
|
|
|
@patch.object(horizon_utils, 'get_os_codename_install_source')
|
|
@patch.object(horizon_utils, 'git_install_requested')
|
|
def test_determine_packages_mitaka(self, _git_install_requested,
|
|
_get_os_codename_install_source):
|
|
_git_install_requested.return_value = False
|
|
_get_os_codename_install_source.return_value = 'mitaka'
|
|
self.assertTrue('python-pymysql' in horizon_utils.determine_packages())
|
|
|
|
@patch('subprocess.call')
|
|
def test_enable_ssl(self, _call):
|
|
horizon_utils.enable_ssl()
|
|
_call.assert_has_calls([
|
|
call(['a2ensite', 'default-ssl']),
|
|
call(['a2enmod', 'ssl'])
|
|
])
|
|
|
|
def test_restart_map(self):
|
|
ex_map = OrderedDict([
|
|
('/etc/openstack-dashboard/local_settings.py',
|
|
['apache2', 'memcached']),
|
|
('/etc/apache2/conf.d/openstack-dashboard.conf',
|
|
['apache2', 'memcached']),
|
|
('/etc/apache2/conf-available/openstack-dashboard.conf',
|
|
['apache2', 'memcached']),
|
|
('/etc/apache2/sites-available/default-ssl',
|
|
['apache2', 'memcached']),
|
|
('/etc/apache2/sites-available/default-ssl.conf',
|
|
['apache2', 'memcached']),
|
|
('/etc/apache2/sites-available/default',
|
|
['apache2', 'memcached']),
|
|
('/etc/apache2/sites-available/000-default.conf',
|
|
['apache2', 'memcached']),
|
|
('/etc/apache2/ports.conf',
|
|
['apache2', 'memcached']),
|
|
('/etc/haproxy/haproxy.cfg',
|
|
['haproxy']),
|
|
('/usr/share/openstack-dashboard/openstack_dashboard/enabled/'
|
|
'_40_router.py',
|
|
['apache2', 'memcached']),
|
|
('/usr/share/openstack-dashboard/openstack_dashboard/conf/'
|
|
'keystonev3_policy.json',
|
|
['apache2', 'memcached']),
|
|
])
|
|
self.assertEqual(horizon_utils.restart_map(), ex_map)
|
|
|
|
@patch.object(horizon_utils, 'determine_packages')
|
|
def test_do_openstack_upgrade(self, determine_packages):
|
|
self.config.return_value = 'cloud:precise-havana'
|
|
self.get_os_codename_install_source.return_value = 'havana'
|
|
configs = MagicMock()
|
|
determine_packages.return_value = ['testpkg']
|
|
horizon_utils.do_openstack_upgrade(configs)
|
|
configs.set_release.assert_called_with(openstack_release='havana')
|
|
self.assertTrue(self.log.called)
|
|
self.apt_update.assert_called_with(fatal=True)
|
|
dpkg_opts = [
|
|
'--option', 'Dpkg::Options::=--force-confnew',
|
|
'--option', 'Dpkg::Options::=--force-confdef',
|
|
]
|
|
self.apt_upgrade.assert_called_with(options=dpkg_opts,
|
|
dist=True, fatal=True)
|
|
self.apt_install.assert_called_with(['testpkg'], fatal=True)
|
|
self.reset_os_release.assert_called()
|
|
self.configure_installation_source.assert_called_with(
|
|
'cloud:precise-havana'
|
|
)
|
|
|
|
@patch('os.path.isdir')
|
|
def test_register_configs(self, _isdir):
|
|
_isdir.return_value = True
|
|
self.os_release.return_value = 'havana'
|
|
self.cmp_pkgrevno.return_value = -1
|
|
configs = horizon_utils.register_configs()
|
|
confs = [horizon_utils.LOCAL_SETTINGS,
|
|
horizon_utils.HAPROXY_CONF,
|
|
horizon_utils.PORTS_CONF,
|
|
horizon_utils.APACHE_DEFAULT,
|
|
horizon_utils.APACHE_CONF,
|
|
horizon_utils.APACHE_SSL]
|
|
calls = []
|
|
for conf in confs:
|
|
calls.append(
|
|
call(conf,
|
|
horizon_utils.CONFIG_FILES[conf]['hook_contexts']))
|
|
configs.register.assert_has_calls(calls)
|
|
|
|
@patch('os.remove')
|
|
@patch('os.path.isfile')
|
|
@patch('os.path.isdir')
|
|
def test_register_configs_apache24(self, _isdir, _isfile, _remove):
|
|
_isdir.return_value = True
|
|
_isfile.return_value = True
|
|
self.os_release.return_value = 'havana'
|
|
self.cmp_pkgrevno.return_value = 1
|
|
configs = horizon_utils.register_configs()
|
|
confs = [horizon_utils.LOCAL_SETTINGS,
|
|
horizon_utils.HAPROXY_CONF,
|
|
horizon_utils.PORTS_CONF,
|
|
horizon_utils.APACHE_24_DEFAULT,
|
|
horizon_utils.APACHE_24_CONF,
|
|
horizon_utils.APACHE_24_SSL]
|
|
calls = []
|
|
for conf in confs:
|
|
calls.append(
|
|
call(conf, horizon_utils.CONFIG_FILES[conf]['hook_contexts']))
|
|
configs.register.assert_has_calls(calls)
|
|
oldconfs = [horizon_utils.APACHE_CONF,
|
|
horizon_utils.APACHE_SSL,
|
|
horizon_utils.APACHE_DEFAULT]
|
|
rmcalls = []
|
|
for conf in oldconfs:
|
|
rmcalls.append(call(conf))
|
|
_remove.assert_has_calls(rmcalls)
|
|
|
|
@patch('os.path.isdir')
|
|
def test_register_configs_pre_install(self, _isdir):
|
|
_isdir.return_value = False
|
|
self.os_release.return_value = 'havana'
|
|
configs = horizon_utils.register_configs()
|
|
confs = [horizon_utils.LOCAL_SETTINGS,
|
|
horizon_utils.HAPROXY_CONF,
|
|
horizon_utils.PORTS_CONF,
|
|
horizon_utils.APACHE_DEFAULT,
|
|
horizon_utils.APACHE_CONF,
|
|
horizon_utils.APACHE_SSL]
|
|
calls = []
|
|
for conf in confs:
|
|
calls.append(
|
|
call(conf, horizon_utils.CONFIG_FILES[conf]['hook_contexts']))
|
|
configs.register.assert_has_calls(calls)
|
|
|
|
@patch.object(horizon_utils, 'git_install_requested')
|
|
@patch.object(horizon_utils, 'git_clone_and_install')
|
|
@patch.object(horizon_utils, 'git_post_install')
|
|
@patch.object(horizon_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
|
|
horizon_utils.git_install(projects_yaml)
|
|
self.assertTrue(git_pre.called)
|
|
git_clone_and_install.assert_called_with(openstack_origin_git,
|
|
core_project='horizon')
|
|
self.assertTrue(git_post.called)
|
|
|
|
@patch.object(horizon_utils, 'mkdir')
|
|
@patch.object(horizon_utils, 'add_user_to_group')
|
|
@patch.object(horizon_utils, 'add_group')
|
|
@patch.object(horizon_utils, 'adduser')
|
|
@patch('subprocess.check_call')
|
|
def test_git_pre_install(self, check_call, adduser, add_group,
|
|
add_user_to_group, mkdir):
|
|
horizon_utils.git_pre_install()
|
|
adduser.assert_called_with('horizon', shell='/bin/bash',
|
|
system_user=True)
|
|
check_call.assert_called_with(['usermod', '--home',
|
|
'/usr/share/openstack-dashboard/',
|
|
'horizon'])
|
|
add_group.assert_called_with('horizon', system_group=True)
|
|
add_user_to_group.assert_called_with('horizon', 'horizon')
|
|
them_dir = '/usr/share/openstack-dashboard-ubuntu-theme'
|
|
expected = [
|
|
call('/etc/openstack-dashboard', owner='root',
|
|
group='root', perms=0755, force=False),
|
|
call('/usr/share/openstack-dashboard', owner='root',
|
|
group='root', perms=0755, force=False),
|
|
call('/usr/share/openstack-dashboard/bin/less', owner='root',
|
|
group='root', perms=0755, force=False),
|
|
call(os.path.join(them_dir, 'static/ubuntu/css'),
|
|
owner='root', group='root', perms=0755, force=False),
|
|
call(os.path.join(them_dir, 'static/ubuntu/img'),
|
|
owner='root', group='root', perms=0755, force=False),
|
|
call(os.path.join(them_dir, 'templates'),
|
|
owner='root', group='root', perms=0755, force=False),
|
|
call('/var/lib/openstack-dashboard', owner='horizon',
|
|
group='horizon', perms=0700, force=False),
|
|
]
|
|
self.assertEqual(mkdir.call_args_list, expected)
|
|
|
|
@patch.object(horizon_utils, 'git_src_dir')
|
|
@patch.object(horizon_utils, 'service_restart')
|
|
@patch('shutil.copyfile')
|
|
@patch('shutil.copytree')
|
|
@patch('os.path.join')
|
|
@patch('os.path.exists')
|
|
@patch('os.symlink')
|
|
@patch('os.chmod')
|
|
@patch('os.chown')
|
|
@patch('os.lchown')
|
|
@patch('os.walk')
|
|
@patch('subprocess.check_call')
|
|
@patch('pwd.getpwnam')
|
|
@patch('grp.getgrnam')
|
|
def test_git_post_install(self, grnam, pwnam, check_call, walk, lchown,
|
|
chown, chmod, symlink, exists, join, copytree,
|
|
copyfile, service_restart, git_src_dir):
|
|
class IDs(object):
|
|
pw_uid = 999
|
|
gr_gid = 999
|
|
pwnam.return_value = IDs
|
|
grnam.return_value = IDs
|
|
projects_yaml = openstack_origin_git
|
|
join.return_value = 'joined-string'
|
|
walk.return_value = yield '/root', ['dir'], ['file']
|
|
exists.return_value = False
|
|
horizon_utils.git_post_install(projects_yaml)
|
|
expected = [
|
|
call('joined-string',
|
|
'/usr/share/openstack-dashboard/manage.py'),
|
|
call('joined-string',
|
|
'/usr/share/openstack-dashboard/settings.py'),
|
|
call('joined-string',
|
|
'/etc/openstack-dashboard/local_settings.py'),
|
|
call('joined-string',
|
|
'/etc/apache2/conf-available/openstack-dashboard.conf'),
|
|
call('joined-string',
|
|
'/etc/openstack-dashboard/ubuntu_theme.py'),
|
|
]
|
|
copyfile.assert_has_calls(expected, any_order=True)
|
|
expected = [
|
|
call('joined-string',
|
|
'/usr/share/openstack-dashboard/openstack_dashboard'),
|
|
call('joined-string', 'joined-string'),
|
|
call('joined-string', 'joined-string'),
|
|
call('joined-string', 'joined-string'),
|
|
]
|
|
copytree.assert_has_calls(expected)
|
|
expected = [
|
|
call('/usr/share/openstack-dashboard/static'),
|
|
call('joined-string'),
|
|
call('joined-string'),
|
|
call('joined-string'),
|
|
call('joined-string'),
|
|
]
|
|
exists.assert_has_calls(expected, any_order=True)
|
|
dist_pkgs_dir = '/usr/local/lib/python2.7/dist-packages'
|
|
expected = [
|
|
call('/usr/share/openstack-dashboard/openstack_dashboard/static',
|
|
'/usr/share/openstack-dashboard/static'),
|
|
call('/etc/openstack-dashboard/ubuntu_theme.py', 'joined-string'),
|
|
call('/usr/share/openstack-dashboard-ubuntu-theme/static/ubuntu',
|
|
'joined-string'),
|
|
call('/etc/openstack-dashboard/local_settings.py',
|
|
'joined-string'),
|
|
call(os.path.join(dist_pkgs_dir, 'horizon/static/horizon/'),
|
|
'joined-string'),
|
|
]
|
|
symlink.assert_has_calls(expected, any_order=True)
|
|
expected = [
|
|
call('/var/lib/openstack-dashboard', 0o750),
|
|
call('/share/openstack-dashboard/manage.py', 0o755),
|
|
]
|
|
chmod.assert_has_calls(expected)
|
|
expected = [
|
|
call(['/usr/share/openstack-dashboard/manage.py',
|
|
'collectstatic', '--noinput']),
|
|
call(['/usr/share/openstack-dashboard/manage.py',
|
|
'compress', '--force']),
|
|
call(['a2enconf', 'openstack-dashboard']),
|
|
]
|
|
check_call.assert_has_calls(expected)
|
|
expected = [
|
|
call('horizon'),
|
|
]
|
|
pwnam.assert_has_calls(expected)
|
|
grnam.assert_has_calls(expected)
|
|
expected = [
|
|
call('/etc/openstack-dashboard', 999, 999),
|
|
call('/usr/share/openstack-dashboard/openstack_dashboard/static',
|
|
999, 999),
|
|
call('/var/lib/openstack-dashboard', 999, 999),
|
|
]
|
|
chown.assert_has_calls(expected)
|
|
expected = [
|
|
call('/share/openstack-dashboard/openstack_dashboard/static'),
|
|
]
|
|
walk.assert_has_calls(expected)
|
|
expected = [
|
|
call('/root/dir', 999, 999),
|
|
call('/root/file', 999, 999),
|
|
]
|
|
lchown.assert_has_calls(expected)
|
|
expected = [
|
|
call('apache2'),
|
|
]
|
|
self.assertEqual(service_restart.call_args_list, expected)
|
|
|
|
def test_assess_status(self):
|
|
with patch.object(horizon_utils, 'assess_status_func') as asf:
|
|
callee = MagicMock()
|
|
asf.return_value = callee
|
|
horizon_utils.assess_status('test-config')
|
|
asf.assert_called_once_with('test-config')
|
|
callee.assert_called_once_with()
|
|
self.os_application_version_set.assert_called_with(
|
|
horizon_utils.VERSION_PACKAGE
|
|
)
|
|
|
|
@patch.object(horizon_utils, 'REQUIRED_INTERFACES')
|
|
@patch.object(horizon_utils, 'services')
|
|
@patch.object(horizon_utils, 'make_assess_status_func')
|
|
def test_assess_status_func(self,
|
|
make_assess_status_func,
|
|
services,
|
|
REQUIRED_INTERFACES):
|
|
services.return_value = 's1'
|
|
horizon_utils.assess_status_func('test-config')
|
|
# ports=None whilst port checks are disabled.
|
|
make_assess_status_func.assert_called_once_with(
|
|
'test-config', REQUIRED_INTERFACES, services='s1', ports=None)
|
|
|
|
def test_pause_unit_helper(self):
|
|
with patch.object(horizon_utils, '_pause_resume_helper') as prh:
|
|
horizon_utils.pause_unit_helper('random-config')
|
|
prh.assert_called_once_with(horizon_utils.pause_unit,
|
|
'random-config')
|
|
with patch.object(horizon_utils, '_pause_resume_helper') as prh:
|
|
horizon_utils.resume_unit_helper('random-config')
|
|
prh.assert_called_once_with(horizon_utils.resume_unit,
|
|
'random-config')
|
|
|
|
@patch.object(horizon_utils, 'services')
|
|
def test_pause_resume_helper(self, services):
|
|
f = MagicMock()
|
|
services.return_value = 's1'
|
|
with patch.object(horizon_utils, 'assess_status_func') as asf:
|
|
asf.return_value = 'assessor'
|
|
horizon_utils._pause_resume_helper(f, 'some-config')
|
|
asf.assert_called_once_with('some-config')
|
|
# ports=None whilst port checks are disabled.
|
|
f.assert_called_once_with('assessor', services='s1', ports=None)
|