Migrate install handling to operator native

Change-Id: I0d06db0c8ac15b3a1fd3e9c4b4f0d2243fb1875e
This commit is contained in:
Chris MacNaughton 2022-08-23 09:53:37 -04:00
parent 1ee3d04fda
commit 3959512fb3
4 changed files with 1573 additions and 23 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,219 @@
# Copyright 2021 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Abstractions for stopping, starting and managing system services via systemd.
This library assumes that your charm is running on a platform that uses systemd. E.g.,
Centos 7 or later, Ubuntu Xenial (16.04) or later.
For the most part, we transparently provide an interface to a commonly used selection of
systemd commands, with a few shortcuts baked in. For example, service_pause and
service_resume with run the mask/unmask and enable/disable invocations.
Example usage:
```python
from charms.operator_libs_linux.v0.systemd import service_running, service_reload
# Start a service
if not service_running("mysql"):
success = service_start("mysql")
# Attempt to reload a service, restarting if necessary
success = service_reload("nginx", restart_on_failure=True)
```
"""
import logging
import subprocess
__all__ = [ # Don't export `_systemctl`. (It's not the intended way of using this lib.)
"service_pause",
"service_reload",
"service_restart",
"service_resume",
"service_running",
"service_start",
"service_stop",
"daemon_reload",
]
logger = logging.getLogger(__name__)
# The unique Charmhub library identifier, never change it
LIBID = "045b0d179f6b4514a8bb9b48aee9ebaf"
# Increment this major API version when introducing breaking changes
LIBAPI = 1
# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 0
class SystemdError(Exception):
pass
def _popen_kwargs():
return dict(
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
universal_newlines=True,
encoding="utf-8",
)
def _systemctl(
sub_cmd: str, service_name: str = None, now: bool = None, quiet: bool = None
) -> bool:
"""Control a system service.
Args:
sub_cmd: the systemctl subcommand to issue
service_name: the name of the service to perform the action on
now: passes the --now flag to the shell invocation.
quiet: passes the --quiet flag to the shell invocation.
"""
cmd = ["systemctl", sub_cmd]
if service_name is not None:
cmd.append(service_name)
if now is not None:
cmd.append("--now")
if quiet is not None:
cmd.append("--quiet")
if sub_cmd != "is-active":
logger.debug("Attempting to {} '{}' with command {}.".format(cmd, service_name, cmd))
else:
logger.debug("Checking if '{}' is active".format(service_name))
proc = subprocess.Popen(cmd, **_popen_kwargs())
last_line = ""
for line in iter(proc.stdout.readline, ""):
last_line = line
logger.debug(line)
proc.wait()
if sub_cmd == "is-active":
# If we are just checking whether a service is running, return True/False, rather
# than raising an error.
if proc.returncode < 1:
return True
if proc.returncode == 3: # Code returned when service is not active.
return False
if proc.returncode < 1:
return True
raise SystemdError(
"Could not {}{}: systemd output: {}".format(
sub_cmd, " {}".format(service_name) if service_name else "", last_line
)
)
def service_running(service_name: str) -> bool:
"""Determine whether a system service is running.
Args:
service_name: the name of the service
"""
return _systemctl("is-active", service_name, quiet=True)
def service_start(service_name: str) -> bool:
"""Start a system service.
Args:
service_name: the name of the service to stop
"""
return _systemctl("start", service_name)
def service_stop(service_name: str) -> bool:
"""Stop a system service.
Args:
service_name: the name of the service to stop
"""
return _systemctl("stop", service_name)
def service_restart(service_name: str) -> bool:
"""Restart a system service.
Args:
service_name: the name of the service to restart
"""
return _systemctl("restart", service_name)
def service_reload(service_name: str, restart_on_failure: bool = False) -> bool:
"""Reload a system service, optionally falling back to restart if reload fails.
Args:
service_name: the name of the service to reload
restart_on_failure: boolean indicating whether to fallback to a restart if the
reload fails.
"""
try:
return _systemctl("reload", service_name)
except SystemdError:
if restart_on_failure:
return _systemctl("restart", service_name)
else:
raise
def service_pause(service_name: str) -> bool:
"""Pause a system service.
Stop it, and prevent it from starting again at boot.
Args:
service_name: the name of the service to pause
"""
_systemctl("disable", service_name, now=True)
_systemctl("mask", service_name)
if not service_running(service_name):
return True
raise SystemdError("Attempted to pause '{}', but it is still running.".format(service_name))
def service_resume(service_name: str) -> bool:
"""Resume a system service.
Re-enable starting again at boot. Start the service.
Args:
service_name: the name of the service to resume
"""
_systemctl("unmask", service_name)
_systemctl("enable", service_name, now=True)
if service_running(service_name):
return True
raise SystemdError("Attempted to resume '{}', but it is not running.".format(service_name))
def daemon_reload() -> bool:
"""Reload systemd manager configuration."""
return _systemctl("daemon-reload")

View File

@ -58,13 +58,10 @@ from charmhelpers.core.host import (
cmp_pkgrevno)
from charmhelpers.fetch import (
apt_install,
apt_update,
apt_purge,
filter_installed_packages,
add_source,
get_upstream_version,
)
from charmhelpers.payload.execd import execd_preinstall
from charmhelpers.contrib.openstack.alternatives import install_alternative
from charmhelpers.contrib.openstack.utils import (
clear_unit_paused,
@ -158,25 +155,6 @@ def check_for_upgrade():
level=ERROR)
@hooks.hook('install.real')
@harden()
def install():
execd_preinstall()
add_source(config('source'), config('key'))
apt_update(fatal=True)
apt_install(packages=ceph.determine_packages(), fatal=True)
rm_packages = ceph.determine_packages_to_remove()
if rm_packages:
apt_purge(packages=rm_packages, fatal=True)
try:
# we defer and explicitly run `ceph-create-keys` from
# add_keyring_to_ceph() as part of bootstrap process
# LP: #1719436.
service_pause('ceph-create-keys')
except ValueError:
pass
def get_ceph_context():
networks = get_networks('ceph-public-network')
public_network = ', '.join(networks)

View File

@ -2,16 +2,40 @@
from ops.main import main
import charms.operator_libs_linux.v0.apt as apt
import charms.operator_libs_linux.v1.systemd as systemd
import ops_openstack.core
import charms_ceph.utils as ceph
import ceph_hooks as hooks
class CephMonCharm(ops_openstack.core.OSBaseCharm):
release = 'quincy'
PACKAGES = [
'ceph', 'gdisk',
'radosgw', 'lvm2', 'parted', 'smartmontools',
]
# General charm control callbacks.
# TODO: Figure out how to do hardening in an operator-framework
# world
def on_install(self, event):
hooks.install()
self.install_pkgs()
rm_packages = ceph.determine_packages_to_remove()
if rm_packages:
apt.remove_package(packages=rm_packages, fatal=True)
try:
# we defer and explicitly run `ceph-create-keys` from
# add_keyring_to_ceph() as part of bootstrap process
# LP: #1719436.
systemd.service_pause('ceph-create-keys')
except systemd.SystemdError:
pass
def on_config(self, event):
hooks.config_changed()