diff options
Diffstat (limited to 'src/ceph-volume/ceph_volume/systemd')
-rw-r--r-- | src/ceph-volume/ceph_volume/systemd/__init__.py | 1 | ||||
-rw-r--r-- | src/ceph-volume/ceph_volume/systemd/main.py | 108 | ||||
-rw-r--r-- | src/ceph-volume/ceph_volume/systemd/systemctl.py | 101 |
3 files changed, 210 insertions, 0 deletions
diff --git a/src/ceph-volume/ceph_volume/systemd/__init__.py b/src/ceph-volume/ceph_volume/systemd/__init__.py new file mode 100644 index 000000000..493b8814b --- /dev/null +++ b/src/ceph-volume/ceph_volume/systemd/__init__.py @@ -0,0 +1 @@ +from .main import main # noqa diff --git a/src/ceph-volume/ceph_volume/systemd/main.py b/src/ceph-volume/ceph_volume/systemd/main.py new file mode 100644 index 000000000..2cb1d1b80 --- /dev/null +++ b/src/ceph-volume/ceph_volume/systemd/main.py @@ -0,0 +1,108 @@ +""" +This file is used only by systemd units that are passing their instance suffix +as arguments to this script so that it can parse the suffix into arguments that +``ceph-volume <sub command>`` can consume +""" +import os +import sys +import time +import logging +from ceph_volume import log, process +from ceph_volume.exceptions import SuffixParsingError + + +def parse_subcommand(string): + subcommand = string.split('-', 1)[0] + if not subcommand: + raise SuffixParsingError('subcommand', string) + return subcommand + + +def parse_extra_data(string): + # get the subcommand to split on that + sub_command = parse_subcommand(string) + + # the split will leave data with a dash, so remove that + data = string.split(sub_command)[-1] + if not data: + raise SuffixParsingError('data', string) + return data.lstrip('-') + + +def parse_osd_id(string): + osd_id = string.split('-', 1)[0] + if not osd_id: + raise SuffixParsingError('OSD id', string) + if osd_id.isdigit(): + return osd_id + raise SuffixParsingError('OSD id', string) + + +def parse_osd_uuid(string): + osd_id = '%s-' % parse_osd_id(string) + osd_subcommand = '-%s' % parse_subcommand(string) + # remove the id first + trimmed_suffix = string.split(osd_id)[-1] + # now remove the sub command + osd_uuid = trimmed_suffix.split(osd_subcommand)[0] + if not osd_uuid: + raise SuffixParsingError('OSD uuid', string) + return osd_uuid + + +def main(args=None): + """ + Main entry point for the ``ceph-volume-systemd`` executable. ``args`` are + optional for easier testing of arguments. + + Expected input is similar to:: + + ['/path/to/ceph-volume-systemd', '<type>-<extra metadata>'] + + For example:: + + [ + '/usr/bin/ceph-volume-systemd', + 'lvm-0-8715BEB4-15C5-49DE-BA6F-401086EC7B41' + ] + + The first part of the argument is the only interesting bit, which contains + the metadata needed to proxy the call to ``ceph-volume`` itself. + + Reusing the example, the proxy call to ``ceph-volume`` would look like:: + + ceph-volume lvm trigger 0-8715BEB4-15C5-49DE-BA6F-401086EC7B41 + + That means that ``lvm`` is used as the subcommand and it is **expected** + that a ``trigger`` sub-commmand will be present to make sense of the extra + piece of the string. + + """ + log.setup(name='ceph-volume-systemd.log', log_path='/var/log/ceph/ceph-volume-systemd.log') + logger = logging.getLogger('systemd') + + args = args if args is not None else sys.argv + try: + suffix = args[-1] + except IndexError: + raise RuntimeError('no arguments supplied') + sub_command = parse_subcommand(suffix) + extra_data = parse_extra_data(suffix) + logger.info('raw systemd input received: %s', suffix) + logger.info('parsed sub-command: %s, extra data: %s', sub_command, extra_data) + command = ['ceph-volume', sub_command, 'trigger', extra_data] + + tries = int(os.environ.get('CEPH_VOLUME_SYSTEMD_TRIES', 30)) + interval = int(os.environ.get('CEPH_VOLUME_SYSTEMD_INTERVAL', 5)) + while tries > 0: + try: + # don't log any output to the terminal, just rely on stderr/stdout + # going to logging + process.run(command, terminal_logging=False) + logger.info('successfully triggered activation for: %s', extra_data) + break + except RuntimeError as error: + logger.warning(error) + logger.warning('failed activating OSD, retries left: %s', tries) + tries -= 1 + time.sleep(interval) diff --git a/src/ceph-volume/ceph_volume/systemd/systemctl.py b/src/ceph-volume/ceph_volume/systemd/systemctl.py new file mode 100644 index 000000000..778ad1479 --- /dev/null +++ b/src/ceph-volume/ceph_volume/systemd/systemctl.py @@ -0,0 +1,101 @@ +""" +Utilities to control systemd units +""" +import logging + +from ceph_volume import process + +logger = logging.getLogger(__name__) + +def start(unit): + process.run(['systemctl', 'start', unit]) + + +def stop(unit): + process.run(['systemctl', 'stop', unit]) + + +def enable(unit, runtime=False): + if runtime: + process.run(['systemctl', 'enable', '--runtime', unit]) + else: + process.run(['systemctl', 'enable', unit]) + + +def disable(unit): + process.run(['systemctl', 'disable', unit]) + + +def mask(unit): + process.run(['systemctl', 'mask', unit]) + + +def is_active(unit): + out, err, rc = process.call( + ['systemctl', 'is-active', unit], + verbose_on_failure=False + ) + return rc == 0 + +def get_running_osd_ids(): + out, err, rc = process.call([ + 'systemctl', + 'show', + '--no-pager', + '--property=Id', + '--state=running', + 'ceph-osd@*', + ]) + osd_ids = [] + if rc == 0: + for line in out: + if line: + # example line looks like: Id=ceph-osd@1.service + try: + osd_id = line.split("@")[1].split(".service")[0] + osd_ids.append(osd_id) + except (IndexError, TypeError): + logger.warning("Failed to parse output from systemctl: %s", line) + return osd_ids + +def start_osd(id_): + return start(osd_unit % id_) + + +def stop_osd(id_): + return stop(osd_unit % id_) + + +def enable_osd(id_): + return enable(osd_unit % id_, runtime=True) + + +def disable_osd(id_): + return disable(osd_unit % id_) + + +def osd_is_active(id_): + return is_active(osd_unit % id_) + + +def enable_volume(id_, fsid, device_type='lvm'): + return enable(volume_unit % (device_type, id_, fsid)) + + +def mask_ceph_disk(): + # systemctl allows using a glob like '*' for masking, but there was a bug + # in that it wouldn't allow this for service templates. This means that + # masking ceph-disk@* will not work, so we must link the service directly. + # /etc/systemd takes precedence regardless of the location of the unit + process.run( + ['ln', '-sf', '/dev/null', '/etc/systemd/system/ceph-disk@.service'] + ) + + +# +# templates +# + +osd_unit = "ceph-osd@%s" +ceph_disk_unit = "ceph-disk@%s" +volume_unit = "ceph-volume@%s-%s-%s" |