summaryrefslogtreecommitdiffstats
path: root/src/ceph-volume/ceph_volume/systemd/main.py
blob: 2cb1d1b80ad47e87f970faa03ca089e9121c7a40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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)