summaryrefslogtreecommitdiffstats
path: root/src/cephadm/cephadmlib/daemon_form.py
blob: 4b89a52768e9a8da9433c115c56dd1d19cf899eb (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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# deamon_form.py - base class for creating and managing daemons

import abc

from typing import Type, TypeVar, List

from .context import CephadmContext
from .daemon_identity import DaemonIdentity


class DaemonForm(abc.ABC):
    """Base class for all types used to build, customize, or otherwise give
    form to a deaemon managed by cephadm.
    """

    @classmethod
    @abc.abstractmethod
    def for_daemon_type(cls, daemon_type: str) -> bool:
        """The for_daemon_type class method accepts a string identifying a
        daemon type and should return true if the class can form a daemon of
        the named type. Using a method allows supporting arbitrary daemon names
        and multiple names for a single class.
        """
        raise NotImplementedError()  # pragma: no cover

    @classmethod
    @abc.abstractmethod
    def create(
        cls, ctx: CephadmContext, ident: DaemonIdentity
    ) -> 'DaemonForm':
        """The create class method acts as a common interface for creating
        any DaemonForm instance. This means that each class implementing a
        DaemonForm can have an __init__ tuned to it's specific needs but
        this common interface can be used to intantiate any DaemonForm.
        """
        raise NotImplementedError()  # pragma: no cover

    @property
    @abc.abstractmethod
    def identity(self) -> DaemonIdentity:
        """All DaemonForm instances must be able to identify themselves.
        The identity property returns a DaemonIdentity tied to the form
        being created or manged.
        """
        raise NotImplementedError()  # pragma: no cover


DF = TypeVar('DF', bound=DaemonForm)


# Optional daemon form subtypes follow:
# These optional subtypes use the abc modules __subclasshook__ feature.
# Classes that implement these "interfaces" do not need to inherit
# directly from these classes, but can simply implment the optional
# methods. If these methods are avilable then `isinstance` and
# `issubclass` will return true for that class and you can
# safely use the desired method(s).
# Example:
# >>> # daemon1 implements get_sysctl_settings
# >>> assert isinstance(daemon1, SysctlDaemonForm)
# >>> daemon1.get_sysctl_settings()
# >>> # daemon2 doesn't implement get_sysctl_settings
# >>> assert not isinstance(daemon2, SysctlDaemonForm)


class SysctlDaemonForm(DaemonForm, metaclass=abc.ABCMeta):
    """The SysctlDaemonForm is an optional subclass that some DaemonForm
    types may choose to implement. A SysctlDaemonForm must implement
    get_sysctl_settings.
    """

    @abc.abstractmethod
    def get_sysctl_settings(self) -> List[str]:
        """Return a list of sysctl settings for the deamon."""
        raise NotImplementedError()  # pragma: no cover

    @classmethod
    def __subclasshook__(cls, other: Type[DF]) -> bool:
        return callable(getattr(other, 'get_sysctl_settings', None))


class FirewalledServiceDaemonForm(DaemonForm, metaclass=abc.ABCMeta):
    """The FirewalledServiceDaemonForm is an optional subclass that some
    DaemonForm types may choose to implement. A FirewalledServiceDaemonForm
    must implement firewall_service_name.
    """

    @abc.abstractmethod
    def firewall_service_name(self) -> str:
        """Return the name of the service known to the firewalld system."""
        raise NotImplementedError()  # pragma: no cover

    @classmethod
    def __subclasshook__(cls, other: Type[DF]) -> bool:
        return callable(getattr(other, 'firewall_service_name', None))


_DAEMON_FORMERS = []


class UnexpectedDaemonTypeError(KeyError):
    pass


def register(cls: Type[DF]) -> Type[DF]:
    """Decorator to be placed on DaemonForm types if the type is to be added to
    the daemon form registry.
    """
    _DAEMON_FORMERS.append(cls)
    return cls


def choose(daemon_type: str) -> Type[DF]:
    """Return a daemon form *class* that is compatible with the given daemon
    type name.
    """
    for dftype in _DAEMON_FORMERS:
        if dftype.for_daemon_type(daemon_type):
            return dftype
    raise UnexpectedDaemonTypeError(daemon_type)


def create(ctx: CephadmContext, ident: DaemonIdentity) -> DaemonForm:
    cls: Type[DaemonForm] = choose(ident.daemon_type)
    return cls.create(ctx, ident)