Utility to launch and manage containers using YAML based configuration data
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
8.5KB

  1. # Copyright 2018 Red Hat, Inc.
  2. # All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. import os
  16. import shutil
  17. from paunch import constants
  18. from paunch.utils import common
  19. from paunch.utils import systemctl
  20. DROP_IN_MARKER_FILE = '/etc/sysconfig/podman_drop_in'
  21. def service_create(container, cconfig, sysdir=constants.SYSTEMD_DIR, log=None):
  22. """Create a service in systemd
  23. :param container: container name
  24. :type container: String
  25. :param cconfig: container configuration
  26. :type cconfig: Dictionary
  27. :param sysdir: systemd unit files directory
  28. :type sysdir: String
  29. :param log: optional pre-defined logger for messages
  30. :type log: logging.RootLogger
  31. """
  32. log = log or common.configure_logging(__name__)
  33. # We prefix the SystemD service so we can identify them better:
  34. # e.g. systemctl list-unit-files | grep tripleo_
  35. # It'll help to not conflict when rpms are installed on the host and
  36. # have the same service name as their container name.
  37. # For example haproxy rpm and haproxy container would have the same
  38. # service name so the prefix will help to not having this conflict
  39. # when removing the rpms during a cleanup by the operator.
  40. service = 'tripleo_' + container
  41. wants = " ".join(systemctl.format_name(str(x)) for x in
  42. cconfig.get('depends_on', []))
  43. restart = cconfig.get('restart', 'always')
  44. stop_grace_period = cconfig.get('stop_grace_period', '10')
  45. # Please refer to systemd.exec documentation for those entries
  46. # https://www.freedesktop.org/software/systemd/man/systemd.exec.html
  47. sys_exec = cconfig.get('systemd_exec_flags', {})
  48. # SystemD doesn't have the equivalent of docker unless-stopped.
  49. # Let's force 'always' so containers aren't restarted when stopped by
  50. # systemd, but restarted when in failure. Also this code is only for
  51. # podman now, so nothing changed for Docker deployments.
  52. if restart == 'unless-stopped':
  53. restart = 'always'
  54. # If the service depends on other services, it must be stopped
  55. # in a specific order. The host can be configured to prevent
  56. # systemd from stopping the associated systemd scopes too early,
  57. # so make sure to generate the start command accordingly.
  58. if (len(cconfig.get('depends_on', [])) > 0 and
  59. os.path.exists(DROP_IN_MARKER_FILE)):
  60. start_cmd = '/usr/libexec/paunch-start-podman-container %s' % container
  61. else:
  62. start_cmd = '/usr/bin/podman start %s' % container
  63. sysd_unit_f = sysdir + systemctl.format_name(service)
  64. log.debug('Creating systemd unit file: %s' % sysd_unit_f)
  65. s_config = {
  66. 'name': container,
  67. 'start_cmd': start_cmd,
  68. 'wants': wants,
  69. 'restart': restart,
  70. 'stop_grace_period': stop_grace_period,
  71. 'sys_exec': '\n'.join(['%s=%s' % (x, y) for x, y in sys_exec.items()]),
  72. }
  73. # Ensure we don't have some trailing .requires directory and content for
  74. # this service
  75. if os.path.exists(sysd_unit_f + '.requires'):
  76. shutil.rmtree(sysd_unit_f + '.requires')
  77. with open(sysd_unit_f, 'w') as unit_file:
  78. os.chmod(unit_file.name, 0o644)
  79. unit_file.write("""[Unit]
  80. Description=%(name)s container
  81. After=paunch-container-shutdown.service
  82. Wants=%(wants)s
  83. [Service]
  84. Restart=%(restart)s
  85. ExecStart=%(start_cmd)s
  86. ExecStop=/usr/bin/podman stop -t %(stop_grace_period)s %(name)s
  87. KillMode=none
  88. Type=forking
  89. PIDFile=/var/run/%(name)s.pid
  90. %(sys_exec)s
  91. [Install]
  92. WantedBy=multi-user.target""" % s_config)
  93. try:
  94. systemctl.daemon_reload()
  95. systemctl.enable(service, now=True)
  96. except systemctl.SystemctlException:
  97. log.exception("systemctl failed")
  98. raise
  99. def service_delete(container, sysdir=constants.SYSTEMD_DIR, log=None):
  100. """Delete a service in systemd
  101. :param container: container name
  102. :type container: String
  103. :param sysdir: systemd unit files directory
  104. :type sysdir: string
  105. :param log: optional pre-defined logger for messages
  106. :type log: logging.RootLogger
  107. """
  108. log = log or common.configure_logging(__name__)
  109. # prefix is explained in the service_create().
  110. service = 'tripleo_' + container
  111. sysd_unit_f = systemctl.format_name(service)
  112. sysd_health_f = systemctl.format_name(service + '_healthcheck')
  113. sysd_timer_f = service + '_healthcheck.timer'
  114. sysd_health_req_d = sysd_unit_f + '.requires'
  115. for sysd_f in sysd_unit_f, sysd_health_f, sysd_timer_f:
  116. if os.path.isfile(sysdir + sysd_f):
  117. log.debug('Stopping and disabling systemd service for %s' %
  118. service)
  119. try:
  120. systemctl.stop(sysd_f)
  121. systemctl.disable(sysd_f)
  122. except systemctl.SystemctlException:
  123. log.exception("systemctl failed")
  124. raise
  125. log.debug('Removing systemd unit file %s' % sysd_f)
  126. os.remove(sysdir + sysd_f)
  127. else:
  128. log.info('No systemd unit file was found for %s' % sysd_f)
  129. # Now that the service is removed, we can remove its ".requires"
  130. if os.path.exists(os.path.join(sysdir, sysd_health_req_d)):
  131. log.info('Removing healthcheck require for %s' % service)
  132. shutil.rmtree(os.path.join(sysdir, sysd_health_req_d))
  133. def healthcheck_create(container, sysdir='/etc/systemd/system/',
  134. log=None, test='/openstack/healthcheck'):
  135. """Create a healthcheck for a service in systemd
  136. :param container: container name
  137. :type container: String
  138. :param sysdir: systemd unit files directory
  139. :type sysdir: String
  140. :param log: optional pre-defined logger for messages
  141. :type log: logging.RootLogger
  142. :param test: optional test full command
  143. :type test: String
  144. """
  145. log = log or common.configure_logging(__name__)
  146. service = 'tripleo_' + container
  147. healthcheck = systemctl.format_name(service + '_healthcheck')
  148. sysd_unit_f = sysdir + healthcheck
  149. log.debug('Creating systemd unit file: %s' % sysd_unit_f)
  150. s_config = {
  151. 'name': container,
  152. 'service': service,
  153. 'restart': 'restart',
  154. 'test': test,
  155. }
  156. with open(sysd_unit_f, 'w') as unit_file:
  157. os.chmod(unit_file.name, 0o644)
  158. unit_file.write("""[Unit]
  159. Description=%(name)s healthcheck
  160. After=paunch-container-shutdown.service %(service)s.service
  161. Requisite=%(service)s.service
  162. [Service]
  163. Type=oneshot
  164. ExecStart=/usr/bin/podman exec %(name)s %(test)s
  165. SyslogIdentifier=healthcheck_%(name)s
  166. [Install]
  167. WantedBy=multi-user.target
  168. """ % s_config)
  169. def healthcheck_timer_create(container, cconfig, sysdir='/etc/systemd/system/',
  170. log=None):
  171. """Create a systemd timer for a healthcheck
  172. :param container: container name
  173. :type container: String
  174. :param cconfig: container configuration
  175. :type cconfig: Dictionary
  176. :param sysdir: systemd unit files directory
  177. :type sysdir: string
  178. :param log: optional pre-defined logger for messages
  179. :type log: logging.RootLogger
  180. """
  181. log = log or common.configure_logging(__name__)
  182. service = 'tripleo_' + container
  183. healthcheck_timer = service + '_healthcheck.timer'
  184. sysd_timer_f = sysdir + healthcheck_timer
  185. log.debug('Creating systemd timer file: %s' % sysd_timer_f)
  186. interval = cconfig.get('check_interval', 60)
  187. s_config = {
  188. 'name': container,
  189. 'service': service,
  190. 'interval': interval,
  191. 'randomize': int(interval) * 3 / 4
  192. }
  193. with open(sysd_timer_f, 'w') as timer_file:
  194. os.chmod(timer_file.name, 0o644)
  195. timer_file.write("""[Unit]
  196. Description=%(name)s container healthcheck
  197. PartOf=%(service)s.service
  198. [Timer]
  199. OnActiveSec=120
  200. OnUnitActiveSec=%(interval)s
  201. RandomizedDelaySec=%(randomize)s
  202. [Install]
  203. WantedBy=timers.target""" % s_config)
  204. try:
  205. systemctl.enable(healthcheck_timer, now=True)
  206. systemctl.add_requires(systemctl.format_name(service),
  207. healthcheck_timer)
  208. systemctl.daemon_reload()
  209. except systemctl.SystemctlException:
  210. log.exception("systemctl failed")
  211. raise