summaryrefslogtreecommitdiffstats
path: root/src/ceph-volume/ceph_volume/systemd
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/ceph-volume/ceph_volume/systemd/__init__.py1
-rw-r--r--src/ceph-volume/ceph_volume/systemd/main.py108
-rw-r--r--src/ceph-volume/ceph_volume/systemd/systemctl.py101
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"