diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index e64a106c..290c7a96 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -25,6 +25,7 @@ import sys import re import itertools import functools +import shutil import six import tempfile @@ -858,6 +859,47 @@ def git_yaml_value(projects_yaml, key): return None +def git_generate_systemd_init_files(templates_dir): + """ + Generate systemd init files. + + Generates and installs systemd init units and script files based on the + *.init.in files contained in the templates_dir directory. + + This code is based on the openstack-pkg-tools package and its init + script generation, which is used by the OpenStack packages. + """ + for f in os.listdir(templates_dir): + if f.endswith(".init.in"): + init_in_file = f + init_file = f[:-8] + service_file = "{}.service".format(init_file) + + init_in_source = os.path.join(templates_dir, init_in_file) + init_source = os.path.join(templates_dir, init_file) + service_source = os.path.join(templates_dir, service_file) + + init_dest = os.path.join('/etc/init.d', init_file) + service_dest = os.path.join('/lib/systemd/system', service_file) + + shutil.copyfile(init_in_source, init_source) + with open(init_source, 'a') as outfile: + template = '/usr/share/openstack-pkg-tools/init-script-template' + with open(template) as infile: + outfile.write('\n\n{}'.format(infile.read())) + + cmd = ['pkgos-gen-systemd-unit', init_in_source] + subprocess.check_call(cmd) + + if os.path.exists(init_dest): + os.remove(init_dest) + if os.path.exists(service_dest): + os.remove(service_dest) + shutil.move(init_source, init_dest) + shutil.move(service_source, service_dest) + os.chmod(init_dest, 0o755) + + def os_workload_status(configs, required_interfaces, charm_func=None): """ Decorator to set workload status based on complete contexts diff --git a/hooks/cinder_utils.py b/hooks/cinder_utils.py index 63eee2d6..bd8a5f52 100644 --- a/hooks/cinder_utils.py +++ b/hooks/cinder_utils.py @@ -78,11 +78,12 @@ from charmhelpers.contrib.openstack import ( from charmhelpers.contrib.openstack.utils import ( configure_installation_source, get_os_codename_install_source, - git_install_requested, git_clone_and_install, + git_generate_systemd_init_files, + git_install_requested, + git_pip_venv_dir, git_src_dir, git_yaml_value, - git_pip_venv_dir, os_release, set_os_workload_status, make_assess_status_func, @@ -123,6 +124,7 @@ BASE_GIT_PACKAGES = [ 'libxslt1-dev', 'libyaml-dev', 'lvm2', + 'openstack-pkg-tools', 'python-dev', 'python-pip', 'python-setuptools', @@ -783,61 +785,79 @@ def git_post_install(projects_yaml): os.chmod('/etc/sudoers.d', 0o750) bin_dir = os.path.join(git_pip_venv_dir(projects_yaml), 'bin') - 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': os.path.join(bin_dir, 'cinder-api'), - 'config_files': ['/etc/cinder/cinder.conf'], - 'log_file': '/var/log/cinder/cinder-api.log', - } + # Use systemd init units/scripts from ubuntu wily onward + if lsb_release()['DISTRIB_RELEASE'] >= '15.10': + templates_dir = os.path.join(charm_dir(), 'templates/git') + daemons = ['cinder-api', 'cinder-backup', 'cinder-scheduler', + 'cinder-volume'] + for daemon in daemons: + cinder_context = { + 'daemon_path': os.path.join(bin_dir, daemon), + } + template_file = 'git/{}.init.in.template'.format(daemon) + init_in_file = '{}.init.in'.format(daemon) + render(template_file, os.path.join(templates_dir, init_in_file), + cinder_context, perms=0o644) + git_generate_systemd_init_files(templates_dir) + else: + 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': os.path.join(bin_dir, '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': os.path.join(bin_dir, 'cinder-backup'), - 'config_files': ['/etc/cinder/cinder.conf'], - 'log_file': '/var/log/cinder/cinder-backup.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': os.path.join(bin_dir, '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': os.path.join(bin_dir, 'cinder-scheduler'), - 'config_files': ['/etc/cinder/cinder.conf'], - 'log_file': '/var/log/cinder/cinder-scheduler.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': os.path.join(bin_dir, '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': os.path.join(bin_dir, 'cinder-volume'), - 'config_files': ['/etc/cinder/cinder.conf'], - 'log_file': '/var/log/cinder/cinder-volume.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': os.path.join(bin_dir, '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) + 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) if not is_unit_paused_set(): service_restart('tgtd') diff --git a/templates/git/cinder-api.init.in.template b/templates/git/cinder-api.init.in.template new file mode 100644 index 00000000..39eef1ca --- /dev/null +++ b/templates/git/cinder-api.init.in.template @@ -0,0 +1,22 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: cinder-api +# Required-Start: $network $local_fs $remote_fs $syslog +# Required-Stop: $remote_fs +# Should-Start: postgresql mysql keystone rabbitmq-server ntp +# Should-Stop: postgresql mysql keystone rabbitmq-server ntp +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Cinder Api +# Description: Provides EBS like storage for your +# virtual machine instances +### END INIT INFO + +# Author: Julien Danjou , Thomas Goirand + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="OpenStack Cinder Api" +PROJECT_NAME=cinder +NAME=${PROJECT_NAME}-api +DAEMON={{ daemon_path }} diff --git a/templates/git/cinder-backup.init.in.template b/templates/git/cinder-backup.init.in.template new file mode 100644 index 00000000..10da61e2 --- /dev/null +++ b/templates/git/cinder-backup.init.in.template @@ -0,0 +1,21 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: cinder-backup +# Required-Start: $network $local_fs $remote_fs $syslog +# Required-Stop: $remote_fs +# Should-Start: postgresql mysql keystone rabbitmq-server ntp +# Should-Stop: postgresql mysql keystone rabbitmq-server ntp +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Cinder Backup +# Description: Provides Cinder Backup +### END INIT INFO + +# Author: Thomas Goirand + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="OpenStack Cinder Backup" +PROJECT_NAME=cinder +NAME=${PROJECT_NAME}-backup +DAEMON={{ daemon_path }} diff --git a/templates/git/cinder-scheduler.init.in.template b/templates/git/cinder-scheduler.init.in.template new file mode 100644 index 00000000..e30fd306 --- /dev/null +++ b/templates/git/cinder-scheduler.init.in.template @@ -0,0 +1,22 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: cinder-scheduler +# Required-Start: $network $local_fs $remote_fs $syslog +# Required-Stop: $remote_fs +# Should-Start: postgresql mysql keystone rabbitmq-server ntp +# Should-Stop: postgresql mysql keystone rabbitmq-server ntp +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Cinder Scheduler +# Description: Provides EBS like storage for your +# virtual machine instances +### END INIT INFO + +# Author: Julien Danjou , Thomas Goirand + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="OpenStack Cinder Scheduler" +PROJECT_NAME=cinder +NAME=${PROJECT_NAME}-scheduler +DAEMON={{ daemon_path }} diff --git a/templates/git/cinder-volume.init.in.template b/templates/git/cinder-volume.init.in.template new file mode 100644 index 00000000..54eebf8f --- /dev/null +++ b/templates/git/cinder-volume.init.in.template @@ -0,0 +1,22 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: cinder-volume +# Required-Start: $network $local_fs $remote_fs $syslog +# Required-Stop: $remote_fs +# Should-Start: postgresql mysql keystone rabbitmq-server ntp +# Should-Stop: postgresql mysql keystone rabbitmq-server ntp +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Cinder Volume +# Description: Provides EBS like storage for your +# virtual machine instances +### END INIT INFO + +# Author: Julien Danjou + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="OpenStack Cinder Volume" +PROJECT_NAME=cinder +NAME=${PROJECT_NAME}-volume +DAEMON={{ daemon_path }} diff --git a/unit_tests/test_cinder_utils.py b/unit_tests/test_cinder_utils.py index 6cef4c18..5254e62b 100644 --- a/unit_tests/test_cinder_utils.py +++ b/unit_tests/test_cinder_utils.py @@ -18,10 +18,18 @@ TO_PATCH = [ 'relation_set', 'local_unit', # helpers.core.host + 'lsb_release', 'mounts', 'umount', 'mkdir', 'service_restart', + # helpers.core.templating + 'render', + # helpers.contrib.openstack.utils + 'git_generate_systemd_init_files', + 'git_src_dir', + # helpers.contrib.python.packages + 'pip_install', # ceph utils # storage_utils 'create_lvm_physical_volume', @@ -48,7 +56,7 @@ TO_PATCH = [ 'service_stop', 'service_start', # cinder - 'ceph_config_file' + 'ceph_config_file', ] @@ -723,27 +731,21 @@ class TestCinderUtils(CharmTestCase): self.assertEquals(write_file.call_args_list, expected) @patch.object(cinder_utils, 'services') - @patch.object(cinder_utils, 'git_src_dir') - @patch.object(cinder_utils, 'service_restart') - @patch.object(cinder_utils, 'render') - @patch.object(cinder_utils, 'pip_install') @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') @patch('os.symlink') - def test_git_post_install(self, symlink, chmod, chown, grp, pwd, rmtree, - copytree, exists, join, pip_install, render, - service_restart, git_src_dir, services): + def test_git_post_install_upstart(self, symlink, chmod, chown, rmtree, + copytree, exists, join, services): services.return_value = ['svc1'] projects_yaml = openstack_origin_git join.return_value = 'joined-string' + self.lsb_release.return_value = {'DISTRIB_RELEASE': '15.04'} cinder_utils.git_post_install(projects_yaml) - pip_install('mysql-python', venv='joined-string') + self.pip_install('mysql-python', venv='joined-string') expected = [ call('joined-string', '/etc/cinder'), ] @@ -821,9 +823,45 @@ class TestCinderUtils(CharmTestCase): cinder_volume_context, perms=0o644, templates_dir='joined-string'), ] - self.assertEquals(render.call_args_list, expected) + self.assertEquals(self.render.call_args_list, expected) expected = [call('tgtd'), call('svc1')] - self.assertEquals(service_restart.call_args_list, expected) + self.assertEquals(self.service_restart.call_args_list, expected) + + @patch.object(cinder_utils, 'services') + @patch('os.path.join') + @patch('shutil.copytree') + @patch('shutil.rmtree') + @patch('pwd.getpwnam') + @patch('grp.getgrnam') + @patch('os.chown') + @patch('os.chmod') + @patch('os.symlink') + def test_git_post_install_systemd(self, symlink, chmod, chown, grp, pwd, + rmtree, copytree, join, services): + projects_yaml = openstack_origin_git + join.return_value = 'joined-string' + self.lsb_release.return_value = {'DISTRIB_RELEASE': '15.10'} + cinder_utils.git_post_install(projects_yaml) + + expected = [ + call('cinder.conf', '/etc/cinder/cinder.conf', {}, + group='cinder', owner='cinder', perms=420), + call('git/cinder_tgt.conf', '/etc/tgt/conf.d', {}, + group='cinder', owner='cinder', perms=420), + call('git/logging.conf', '/etc/cinder/logging.conf', {}, + group='cinder', owner='cinder', perms=420), + call('git/cinder_sudoers', '/etc/sudoers.d/cinder_sudoers', {}, + group='root', owner='root', perms=288), + call('git/cinder-api.init.in.template', 'joined-string', + {'daemon_path': 'joined-string'}, perms=420), + call('git/cinder-backup.init.in.template', 'joined-string', + {'daemon_path': 'joined-string'}, perms=420), + call('git/cinder-scheduler.init.in.template', 'joined-string', + {'daemon_path': 'joined-string'}, perms=420), + call('git/cinder-volume.init.in.template', 'joined-string', + {'daemon_path': 'joined-string'}, perms=420), + ] + self.assertEquals(self.render.call_args_list, expected) @patch.object(cinder_utils, 'local_unit', lambda *args: 'unit/0') def test_check_db_initialised_by_self(self):