summaryrefslogtreecommitdiffstats
path: root/ipaplatform/redhat/services.py
blob: 849737059d54df5af47ae288ef97b933d9e869fe (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Author: Alexander Bokovoy <abokovoy@redhat.com>
#         Tomas Babej <tbabej@redhat.com>
#
# Copyright (C) 2011-2014   Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

"""
Contains Red Hat OS family-specific service class implementations.
"""

import os
import time
import contextlib

from ipaplatform.tasks import tasks
from ipaplatform.base import services as base_services

from ipapython import ipautil, dogtag
from ipapython.ipa_log_manager import root_logger
from ipalib import api
from ipaplatform.paths import paths

# Mappings from service names as FreeIPA code references to these services
# to their actual systemd service names

# For beginning just remap names to add .service
# As more services will migrate to systemd, unit names will deviate and
# mapping will be kept in this dictionary
redhat_system_units = dict((x, "%s.service" % x)
                           for x in base_services.wellknownservices)

redhat_system_units['rpcgssd'] = 'nfs-secure.service'
redhat_system_units['rpcidmapd'] = 'nfs-idmap.service'

# Rewrite dirsrv and pki-tomcatd services as they support instances via separate
# service generator. To make this working, one needs to have both foo@.servic
# and foo.target -- the latter is used when request should be coming for
# all instances (like stop). systemd, unfortunately, does not allow one
# to request action for all service instances at once if only foo@.service
# unit is available. To add more, if any of those services need to be
# started/stopped automagically, one needs to manually create symlinks in
# /etc/systemd/system/foo.target.wants/ (look into systemd.py's enable()
# code).

redhat_system_units['dirsrv'] = 'dirsrv@.service'
# Our PKI instance is pki-tomcatd@pki-tomcat.service
redhat_system_units['pki-tomcatd'] = 'pki-tomcatd@pki-tomcat.service'
redhat_system_units['pki_tomcatd'] = redhat_system_units['pki-tomcatd']
redhat_system_units['ipa-otpd'] = 'ipa-otpd.socket'
redhat_system_units['ipa-dnskeysyncd'] = 'ipa-dnskeysyncd.service'
redhat_system_units['named-regular'] = 'named.service'
redhat_system_units['named-pkcs11'] = 'named-pkcs11.service'
redhat_system_units['named'] = redhat_system_units['named-pkcs11']
redhat_system_units['ods-enforcerd'] = 'ods-enforcerd.service'
redhat_system_units['ods_enforcerd'] = redhat_system_units['ods-enforcerd']
redhat_system_units['ods-signerd'] = 'ods-signerd.service'
redhat_system_units['ods_signerd'] = redhat_system_units['ods-signerd']


# Service classes that implement Red Hat OS family-specific behaviour

class RedHatService(base_services.SystemdService):
    system_units = redhat_system_units

    def __init__(self, service_name):
        systemd_name = service_name
        if service_name in self.system_units:
            systemd_name = self.system_units[service_name]
        else:
            if '.' not in service_name:
                # if service_name does not have a dot, it is not foo.service
                # and not a foo.target. Thus, not correct service name for
                # systemd, default to foo.service style then
                systemd_name = "%s.service" % (service_name)
        super(RedHatService, self).__init__(service_name, systemd_name)


class RedHatDirectoryService(RedHatService):

    def tune_nofile_platform(self, num=8192, fstore=None):
        """
        Increase the number of files descriptors available to directory server
        from the default 1024 to 8192. This will allow to support a greater
        number of clients out of the box.

        This is a part of the implementation that is systemd-specific.

        Returns False if the setting of the nofile limit needs to be skipped.
        """

        if os.path.exists(paths.SYSCONFIG_DIRSRV_SYSTEMD):
            # We need to enable LimitNOFILE=8192 in the dirsrv@.service
            # Since 389-ds-base-1.2.10-0.8.a7 the configuration of the
            # service parameters is performed via
            # /etc/sysconfig/dirsrv.systemd file which is imported by systemd
            # into dirsrv@.service unit

            replacevars = {'LimitNOFILE': str(num)}
            ipautil.inifile_replace_variables(paths.SYSCONFIG_DIRSRV_SYSTEMD,
                                              'service',
                                              replacevars=replacevars)
            tasks.restore_context(paths.SYSCONFIG_DIRSRV_SYSTEMD)
            ipautil.run(["/bin/systemctl", "--system", "daemon-reload"],
                        raiseonerr=False)

        return True

    def restart(self, instance_name="", capture_output=True, wait=True,
                ldapi=False):
    # We need to explicitly enable instances to install proper symlinks as
    # dirsrv.target.wants/ dependencies. Standard systemd service class does it
    # on enable() method call. Unfortunately, ipa-server-install does not do
    # explicit dirsrv.enable() because the service startup is handled by ipactl.
    #
    # If we wouldn't do this, our instances will not be started as systemd would
    # not have any clue about instances (PKI-IPA and the domain we serve)
    # at all. Thus, hook into dirsrv.restart().

        if instance_name:
            elements = self.systemd_name.split("@")

            srv_etc = os.path.join(paths.ETC_SYSTEMD_SYSTEM_DIR,
                                   self.systemd_name)
            srv_tgt = os.path.join(paths.ETC_SYSTEMD_SYSTEM_DIR,
                                   self.SYSTEMD_SRV_TARGET % (elements[0]))
            srv_lnk = os.path.join(srv_tgt,
                                   self.service_instance(instance_name))

            if not os.path.exists(srv_etc):
                self.enable(instance_name)
            elif not os.path.samefile(srv_etc, srv_lnk):
                os.unlink(srv_lnk)
                os.symlink(srv_etc, srv_lnk)

        with self.__wait(instance_name, wait, ldapi) as wait:
            super(RedHatDirectoryService, self).restart(
                instance_name, capture_output=capture_output, wait=wait)

    def start(self, instance_name="", capture_output=True, wait=True,
              ldapi=False):
        with self.__wait(instance_name, wait, ldapi) as wait:
            super(RedHatDirectoryService, self).start(
                instance_name, capture_output=capture_output, wait=wait)

    @contextlib.contextmanager
    def __wait(self, instance_name, wait, ldapi):
        if ldapi:
            instance_name = self.service_instance(instance_name)
            if instance_name.endswith('.service'):
                instance_name = instance_name[:-8]
            if instance_name.startswith('dirsrv'):
                # this is intentional, return the empty string if the instance
                # name is 'dirsrv'
                instance_name = instance_name[7:]
            if not instance_name:
                ldapi = False
        if ldapi:
            yield False
            socket_name = paths.SLAPD_INSTANCE_SOCKET_TEMPLATE % instance_name
            ipautil.wait_for_open_socket(socket_name,
                                         self.api.env.startup_timeout)
        else:
            yield wait


class RedHatIPAService(RedHatService):
    # Enforce restart of IPA services when we do enable it
    # This gets around the fact that after ipa-server-install systemd thinks
    # ipa.service is not yet started but all services were actually started
    # already.
    def enable(self, instance_name=""):
        super(RedHatIPAService, self).enable(instance_name)
        self.restart(instance_name)


class RedHatSSHService(RedHatService):
    def get_config_dir(self, instance_name=""):
        return '/etc/ssh'


class RedHatCAService(RedHatService):
    def wait_until_running(self):
        root_logger.debug('Waiting until the CA is running')
        timeout = float(api.env.startup_timeout)
        op_timeout = time.time() + timeout
        while time.time() < op_timeout:
            try:
                status = dogtag.ca_status()
            except Exception as e:
                status = 'check interrupted due to error: %s' % e
            root_logger.debug('The CA status is: %s' % status)
            if status == 'running':
                break
            root_logger.debug('Waiting for CA to start...')
            time.sleep(1)
        else:
            raise RuntimeError('CA did not start in %ss' % timeout)

    def start(self, instance_name="", capture_output=True, wait=True):
        super(RedHatCAService, self).start(
            instance_name, capture_output=capture_output, wait=wait)
        if wait:
            self.wait_until_running()

    def restart(self, instance_name="", capture_output=True, wait=True):
        super(RedHatCAService, self).restart(
            instance_name, capture_output=capture_output, wait=wait)
        if wait:
            self.wait_until_running()

    def is_running(self, instance_name="", wait=True):
        if instance_name:
            return super(RedHatCAService, self).is_running(instance_name)
        try:
            status = dogtag.ca_status()
            if status == 'running':
                return True
            elif status == 'starting' and wait:
                # Exception is raised if status is 'starting' even after wait
                self.wait_until_running()
                return True
        except Exception as e:
            root_logger.debug(
                'Failed to check CA status: {err}'.format(err=e)
            )
        return False


# Function that constructs proper Red Hat OS family-specific server classes for
# services of specified name

def redhat_service_class_factory(name):
    if name == 'dirsrv':
        return RedHatDirectoryService(name)
    if name == 'ipa':
        return RedHatIPAService(name)
    if name == 'sshd':
        return RedHatSSHService(name)
    if name in ('pki-tomcatd', 'pki_tomcatd'):
        return RedHatCAService(name)
    return RedHatService(name)


# Magicdict containing RedHatService instances.

class RedHatServices(base_services.KnownServices):
    def service_class_factory(self, name):
        return redhat_service_class_factory(name)

    def __init__(self):
        services = dict()
        for s in base_services.wellknownservices:
            services[s] = self.service_class_factory(s)
        # Call base class constructor. This will lock services to read-only
        super(RedHatServices, self).__init__(services)


# Objects below are expected to be exported by platform module

timedate_services = base_services.timedate_services
service = redhat_service_class_factory
knownservices = RedHatServices()