Migrate install handling to operator native
Change-Id: I0d06db0c8ac15b3a1fd3e9c4b4f0d2243fb1875e
This commit is contained in:
parent
1ee3d04fda
commit
3959512fb3
File diff suppressed because it is too large
Load Diff
|
@ -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")
|
|
@ -58,13 +58,10 @@ from charmhelpers.core.host import (
|
||||||
cmp_pkgrevno)
|
cmp_pkgrevno)
|
||||||
from charmhelpers.fetch import (
|
from charmhelpers.fetch import (
|
||||||
apt_install,
|
apt_install,
|
||||||
apt_update,
|
|
||||||
apt_purge,
|
|
||||||
filter_installed_packages,
|
filter_installed_packages,
|
||||||
add_source,
|
add_source,
|
||||||
get_upstream_version,
|
get_upstream_version,
|
||||||
)
|
)
|
||||||
from charmhelpers.payload.execd import execd_preinstall
|
|
||||||
from charmhelpers.contrib.openstack.alternatives import install_alternative
|
from charmhelpers.contrib.openstack.alternatives import install_alternative
|
||||||
from charmhelpers.contrib.openstack.utils import (
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
clear_unit_paused,
|
clear_unit_paused,
|
||||||
|
@ -158,25 +155,6 @@ def check_for_upgrade():
|
||||||
level=ERROR)
|
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():
|
def get_ceph_context():
|
||||||
networks = get_networks('ceph-public-network')
|
networks = get_networks('ceph-public-network')
|
||||||
public_network = ', '.join(networks)
|
public_network = ', '.join(networks)
|
||||||
|
|
26
src/charm.py
26
src/charm.py
|
@ -2,16 +2,40 @@
|
||||||
|
|
||||||
from ops.main import main
|
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 ops_openstack.core
|
||||||
|
import charms_ceph.utils as ceph
|
||||||
|
|
||||||
import ceph_hooks as hooks
|
import ceph_hooks as hooks
|
||||||
|
|
||||||
|
|
||||||
class CephMonCharm(ops_openstack.core.OSBaseCharm):
|
class CephMonCharm(ops_openstack.core.OSBaseCharm):
|
||||||
|
|
||||||
|
release = 'quincy'
|
||||||
|
|
||||||
|
PACKAGES = [
|
||||||
|
'ceph', 'gdisk',
|
||||||
|
'radosgw', 'lvm2', 'parted', 'smartmontools',
|
||||||
|
]
|
||||||
|
|
||||||
# General charm control callbacks.
|
# General charm control callbacks.
|
||||||
|
|
||||||
|
# TODO: Figure out how to do hardening in an operator-framework
|
||||||
|
# world
|
||||||
def on_install(self, event):
|
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):
|
def on_config(self, event):
|
||||||
hooks.config_changed()
|
hooks.config_changed()
|
||||||
|
|
Loading…
Reference in New Issue