summaryrefslogtreecommitdiffstats
path: root/src/cephadm
diff options
context:
space:
mode:
Diffstat (limited to 'src/cephadm')
-rwxr-xr-xsrc/cephadm/build.py9
-rwxr-xr-xsrc/cephadm/cephadm.py164
-rw-r--r--src/cephadm/tests/test_cephadm.py14
3 files changed, 134 insertions, 53 deletions
diff --git a/src/cephadm/build.py b/src/cephadm/build.py
index 929668edb1f..4264b814f1e 100755
--- a/src/cephadm/build.py
+++ b/src/cephadm/build.py
@@ -6,6 +6,7 @@
# of python to build with? Even with the intermediate cmake layer?
import argparse
+import compileall
import logging
import os
import pathlib
@@ -78,6 +79,14 @@ def _build(dest, src, versioning_vars=None):
def _compile(dest, tempdir):
"""Compile the zipapp."""
+ log.info("Byte-compiling py to pyc")
+ compileall.compile_dir(
+ tempdir,
+ maxlevels=16,
+ legacy=True,
+ quiet=1,
+ workers=0,
+ )
# TODO we could explicitly pass a python version here
log.info("Constructing the zipapp file")
try:
diff --git a/src/cephadm/cephadm.py b/src/cephadm/cephadm.py
index 9fd40c99bb3..15fb31377e3 100755
--- a/src/cephadm/cephadm.py
+++ b/src/cephadm/cephadm.py
@@ -169,6 +169,17 @@ class ContainerInfo:
and self.version == other.version)
+class DeploymentType(Enum):
+ # Fresh deployment of a daemon.
+ DEFAULT = 'Deploy'
+ # Redeploying a daemon. Works the same as fresh
+ # deployment minus port checking.
+ REDEPLOY = 'Redeploy'
+ # Reconfiguring a daemon. Rewrites config
+ # files and potentially restarts daemon.
+ RECONFIG = 'Reconfig'
+
+
class BaseConfig:
def __init__(self) -> None:
@@ -961,7 +972,10 @@ class CephExporter(object):
self.image = image
self.sock_dir = config_json.get('sock-dir', '/var/run/ceph/')
- self.addrs = config_json.get('addrs', socket.gethostbyname(socket.gethostname()))
+ ipv4_addrs, ipv6_addrs = get_ip_addresses(get_hostname())
+ # use the first ipv4 (if any) otherwise use the first ipv6
+ addrs = next(iter(ipv4_addrs or ipv6_addrs), None)
+ self.addrs = config_json.get('addrs', addrs)
self.port = config_json.get('port', self.DEFAULT_PORT)
self.prio_limit = config_json.get('prio-limit', 5)
self.stats_period = config_json.get('stats-period', 5)
@@ -2243,6 +2257,19 @@ def infer_image(func: FuncT) -> FuncT:
return cast(FuncT, _infer_image)
+def require_image(func: FuncT) -> FuncT:
+ """
+ Require the global --image flag to be set
+ """
+ @wraps(func)
+ def _require_image(ctx: CephadmContext) -> Any:
+ if not ctx.image:
+ raise Error('This command requires the global --image option to be set')
+ return func(ctx)
+
+ return cast(FuncT, _require_image)
+
+
def default_image(func: FuncT) -> FuncT:
@wraps(func)
def _default_image(ctx: CephadmContext) -> Any:
@@ -3369,26 +3396,29 @@ def extract_uid_gid(ctx, img='', file_path='/var/lib/ceph'):
raise RuntimeError('uid/gid not found')
-def deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid,
- config=None, keyring=None,
- osd_fsid=None,
- reconfig=False,
- ports=None):
- # type: (CephadmContext, str, str, Union[int, str], Optional[CephContainer], int, int, Optional[str], Optional[str], Optional[str], Optional[bool], Optional[List[int]]) -> None
+def deploy_daemon(ctx: CephadmContext, fsid: str, daemon_type: str,
+ daemon_id: Union[int, str], c: Optional['CephContainer'],
+ uid: int, gid: int, config: Optional[str] = None,
+ keyring: Optional[str] = None, osd_fsid: Optional[str] = None,
+ deployment_type: DeploymentType = DeploymentType.DEFAULT,
+ ports: Optional[List[int]] = None) -> None:
ports = ports or []
- if any([port_in_use(ctx, port) for port in ports]):
- if daemon_type == 'mgr':
- # non-fatal for mgr when we are in mgr_standby_modules=false, but we can't
- # tell whether that is the case here.
- logger.warning(
- f"ceph-mgr TCP port(s) {','.join(map(str, ports))} already in use"
- )
- else:
- raise Error("TCP Port(s) '{}' required for {} already in use".format(','.join(map(str, ports)), daemon_type))
+ # only check port in use if fresh deployment since service
+ # we are redeploying/reconfiguring will already be using the port
+ if deployment_type == DeploymentType.DEFAULT:
+ if any([port_in_use(ctx, port) for port in ports]):
+ if daemon_type == 'mgr':
+ # non-fatal for mgr when we are in mgr_standby_modules=false, but we can't
+ # tell whether that is the case here.
+ logger.warning(
+ f"ceph-mgr TCP port(s) {','.join(map(str, ports))} already in use"
+ )
+ else:
+ raise Error("TCP Port(s) '{}' required for {} already in use".format(','.join(map(str, ports)), daemon_type))
data_dir = get_data_dir(fsid, ctx.data_dir, daemon_type, daemon_id)
- if reconfig and not os.path.exists(data_dir):
+ if deployment_type == DeploymentType.RECONFIG and not os.path.exists(data_dir):
raise Error('cannot reconfig, data path %s does not exist' % data_dir)
if daemon_type == 'mon' and not os.path.exists(data_dir):
assert config
@@ -3435,7 +3465,9 @@ def deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid,
uid, gid,
config, keyring)
- if not reconfig:
+ # only write out unit files and start daemon
+ # with systemd if this is not a reconfig
+ if deployment_type != DeploymentType.RECONFIG:
if daemon_type == CephadmAgent.daemon_type:
if ctx.config_json == '-':
config_js = get_parm('-')
@@ -3471,7 +3503,9 @@ def deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid,
fw.open_ports(ports + fw.external_ports.get(daemon_type, []))
fw.apply_rules()
- if reconfig and daemon_type not in Ceph.daemons:
+ # If this was a reconfig and the daemon is not a Ceph daemon, restart it
+ # so it can pick up potential changes to its configuration files
+ if deployment_type == DeploymentType.RECONFIG and daemon_type not in Ceph.daemons:
# ceph daemons do not need a restart; others (presumably) do to pick
# up the new config
call_throws(ctx, ['systemctl', 'reset-failed',
@@ -4899,6 +4933,7 @@ def _pull_image(ctx, image, insecure=False):
##################################
+@require_image
@infer_image
def command_inspect_image(ctx):
# type: (CephadmContext) -> int
@@ -5650,8 +5685,10 @@ def finish_bootstrap_config(
cli(['config', 'set', 'global', 'container_image', f'{ctx.image}'])
if mon_network:
- logger.info(f'Setting mon public_network to {mon_network}')
- cli(['config', 'set', 'mon', 'public_network', mon_network])
+ cp = read_config(ctx.config)
+ cfg_section = 'global' if cp.has_option('global', 'public_network') else 'mon'
+ logger.info(f'Setting public_network to {mon_network} in {cfg_section} config section')
+ cli(['config', 'set', cfg_section, 'public_network', mon_network])
if cluster_network:
logger.info(f'Setting cluster_network to {cluster_network}')
@@ -6133,6 +6170,24 @@ def get_deployment_container(ctx: CephadmContext,
return c
+def get_deployment_type(ctx: CephadmContext, daemon_type: str, daemon_id: str) -> DeploymentType:
+ deployment_type: DeploymentType = DeploymentType.DEFAULT
+ if ctx.reconfig:
+ deployment_type = DeploymentType.RECONFIG
+ unit_name = get_unit_name(ctx.fsid, daemon_type, daemon_id)
+ (_, state, _) = check_unit(ctx, unit_name)
+ if state == 'running' or is_container_running(ctx, CephContainer.for_daemon(ctx, ctx.fsid, daemon_type, daemon_id, 'bash')):
+ # if reconfig was set, that takes priority over redeploy. If
+ # this is considered a fresh deployment at this stage,
+ # mark it as a redeploy to avoid port checking
+ if deployment_type == DeploymentType.DEFAULT:
+ deployment_type = DeploymentType.REDEPLOY
+
+ logger.info(f'{deployment_type.value} daemon {ctx.name} ...')
+
+ return deployment_type
+
+
@default_image
def command_deploy(ctx):
# type: (CephadmContext) -> None
@@ -6144,18 +6199,7 @@ def command_deploy(ctx):
if daemon_type not in get_supported_daemons():
raise Error('daemon type %s not recognized' % daemon_type)
- redeploy = False
- unit_name = get_unit_name(ctx.fsid, daemon_type, daemon_id)
- (_, state, _) = check_unit(ctx, unit_name)
- if state == 'running' or is_container_running(ctx, CephContainer.for_daemon(ctx, ctx.fsid, daemon_type, daemon_id, 'bash')):
- redeploy = True
-
- if ctx.reconfig:
- logger.info('%s daemon %s ...' % ('Reconfig', ctx.name))
- elif redeploy:
- logger.info('%s daemon %s ...' % ('Redeploy', ctx.name))
- else:
- logger.info('%s daemon %s ...' % ('Deploy', ctx.name))
+ deployment_type: DeploymentType = get_deployment_type(ctx, daemon_type, daemon_id)
# Migrate sysctl conf files from /usr/lib to /etc
migrate_sysctl_dir(ctx, ctx.fsid)
@@ -6163,11 +6207,8 @@ def command_deploy(ctx):
# Get and check ports explicitly required to be opened
daemon_ports = [] # type: List[int]
- # only check port in use if not reconfig or redeploy since service
- # we are redeploying/reconfiguring will already be using the port
- if not ctx.reconfig and not redeploy:
- if ctx.tcp_ports:
- daemon_ports = list(map(int, ctx.tcp_ports.split()))
+ if ctx.tcp_ports:
+ daemon_ports = list(map(int, ctx.tcp_ports.split()))
if daemon_type in Ceph.daemons:
config, keyring = get_config_and_keyring(ctx)
@@ -6192,7 +6233,7 @@ def command_deploy(ctx):
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid,
config=config, keyring=keyring,
osd_fsid=ctx.osd_fsid,
- reconfig=ctx.reconfig,
+ deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type in Monitoring.components:
@@ -6214,11 +6255,12 @@ def command_deploy(ctx):
uid, gid = extract_uid_gid_monitoring(ctx, daemon_type)
c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid,
- reconfig=ctx.reconfig,
+ deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type == NFSGanesha.daemon_type:
- if not ctx.reconfig and not redeploy and not daemon_ports:
+ # only check ports if this is a fresh deployment
+ if deployment_type == DeploymentType.DEFAULT and not daemon_ports:
daemon_ports = list(NFSGanesha.port_map.values())
config, keyring = get_config_and_keyring(ctx)
@@ -6227,7 +6269,7 @@ def command_deploy(ctx):
c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid,
config=config, keyring=keyring,
- reconfig=ctx.reconfig,
+ deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type == CephIscsi.daemon_type:
@@ -6236,20 +6278,20 @@ def command_deploy(ctx):
c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid,
config=config, keyring=keyring,
- reconfig=ctx.reconfig,
+ deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type in Tracing.components:
uid, gid = 65534, 65534
c = get_container(ctx, ctx.fsid, daemon_type, daemon_id)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid,
- reconfig=ctx.reconfig,
+ deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type == HAproxy.daemon_type:
haproxy = HAproxy.init(ctx, ctx.fsid, daemon_id)
uid, gid = haproxy.extract_uid_gid_haproxy()
c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid,
- reconfig=ctx.reconfig,
+ deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type == Keepalived.daemon_type:
@@ -6257,19 +6299,20 @@ def command_deploy(ctx):
uid, gid = keepalived.extract_uid_gid_keepalived()
c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid,
- reconfig=ctx.reconfig,
+ deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type == CustomContainer.daemon_type:
cc = CustomContainer.init(ctx, ctx.fsid, daemon_id)
- if not ctx.reconfig and not redeploy:
+ # only check ports if this is a fresh deployment
+ if deployment_type == DeploymentType.DEFAULT:
daemon_ports.extend(cc.ports)
c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id,
privileged=cc.privileged,
ptrace=ctx.allow_ptrace)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c,
uid=cc.uid, gid=cc.gid, config=None,
- keyring=None, reconfig=ctx.reconfig,
+ keyring=None, deployment_type=deployment_type,
ports=daemon_ports)
elif daemon_type == CephadmAgent.daemon_type:
@@ -6277,13 +6320,16 @@ def command_deploy(ctx):
uid = os.getuid()
gid = os.getgid()
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, None,
- uid, gid, ports=daemon_ports)
+ uid, gid,
+ deployment_type=deployment_type,
+ ports=daemon_ports)
elif daemon_type == SNMPGateway.daemon_type:
sc = SNMPGateway.init(ctx, ctx.fsid, daemon_id)
c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id)
deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c,
sc.uid, sc.gid,
+ deployment_type=deployment_type,
ports=daemon_ports)
else:
@@ -7209,6 +7255,9 @@ def command_adopt_prometheus(ctx, daemon_id, fsid):
# type: (CephadmContext, str, str) -> None
daemon_type = 'prometheus'
(uid, gid) = extract_uid_gid_monitoring(ctx, daemon_type)
+ # should try to set the ports we know cephadm defaults
+ # to for these services in the firewall.
+ ports = Monitoring.port_map['prometheus']
_stop_and_disable(ctx, 'prometheus')
@@ -7230,7 +7279,8 @@ def command_adopt_prometheus(ctx, daemon_id, fsid):
make_var_run(ctx, fsid, uid, gid)
c = get_container(ctx, fsid, daemon_type, daemon_id)
- deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid)
+ deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid,
+ deployment_type=DeploymentType.REDEPLOY, ports=ports)
update_firewalld(ctx, daemon_type)
@@ -7239,6 +7289,9 @@ def command_adopt_grafana(ctx, daemon_id, fsid):
daemon_type = 'grafana'
(uid, gid) = extract_uid_gid_monitoring(ctx, daemon_type)
+ # should try to set the ports we know cephadm defaults
+ # to for these services in the firewall.
+ ports = Monitoring.port_map['grafana']
_stop_and_disable(ctx, 'grafana-server')
@@ -7284,7 +7337,8 @@ def command_adopt_grafana(ctx, daemon_id, fsid):
make_var_run(ctx, fsid, uid, gid)
c = get_container(ctx, fsid, daemon_type, daemon_id)
- deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid)
+ deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid,
+ deployment_type=DeploymentType.REDEPLOY, ports=ports)
update_firewalld(ctx, daemon_type)
@@ -7293,6 +7347,9 @@ def command_adopt_alertmanager(ctx, daemon_id, fsid):
daemon_type = 'alertmanager'
(uid, gid) = extract_uid_gid_monitoring(ctx, daemon_type)
+ # should try to set the ports we know cephadm defaults
+ # to for these services in the firewall.
+ ports = Monitoring.port_map['alertmanager']
_stop_and_disable(ctx, 'prometheus-alertmanager')
@@ -7314,7 +7371,8 @@ def command_adopt_alertmanager(ctx, daemon_id, fsid):
make_var_run(ctx, fsid, uid, gid)
c = get_container(ctx, fsid, daemon_type, daemon_id)
- deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid)
+ deploy_daemon(ctx, fsid, daemon_type, daemon_id, c, uid, gid,
+ deployment_type=DeploymentType.REDEPLOY, ports=ports)
update_firewalld(ctx, daemon_type)
@@ -7548,7 +7606,7 @@ def command_rm_cluster(ctx):
p.unlink()
# cleanup remaining ceph directories
- ceph_dirs = [f'/run/ceph/{ctx.fsid}', f'/tmp/var/lib/ceph/{ctx.fsid}', f'/var/run/ceph/{ctx.fsid}']
+ ceph_dirs = [f'/run/ceph/{ctx.fsid}', f'/tmp/cephadm-{ctx.fsid}', f'/var/run/ceph/{ctx.fsid}']
for dd in ceph_dirs:
shutil.rmtree(dd, ignore_errors=True)
diff --git a/src/cephadm/tests/test_cephadm.py b/src/cephadm/tests/test_cephadm.py
index 3dd64a605a8..a3de66c982e 100644
--- a/src/cephadm/tests/test_cephadm.py
+++ b/src/cephadm/tests/test_cephadm.py
@@ -152,6 +152,18 @@ class TestCephAdm(object):
args = _cephadm._parse_args(['--image', 'foo', 'version'])
assert args.image == 'foo'
+ def test_check_required_global_args(self):
+ ctx = _cephadm.CephadmContext()
+ mock_fn = mock.Mock()
+ mock_fn.return_value = 0
+ require_image = _cephadm.require_image(mock_fn)
+
+ with pytest.raises(_cephadm.Error, match='This command requires the global --image option to be set'):
+ require_image(ctx)
+
+ ctx.image = 'sample-image'
+ require_image(ctx)
+
@mock.patch('cephadm.logger')
def test_parse_mem_usage(self, _logger):
len, summary = _cephadm._parse_mem_usage(0, 'c6290e3f1489,-- / --')
@@ -299,6 +311,7 @@ class TestCephAdm(object):
@mock.patch('cephadm.migrate_sysctl_dir')
@mock.patch('cephadm.check_unit', lambda *args, **kwargs: (None, 'running', None))
@mock.patch('cephadm.get_unit_name', lambda *args, **kwargs: 'mon-unit-name')
+ @mock.patch('cephadm.extract_uid_gid', lambda *args, **kwargs: (0, 0))
@mock.patch('cephadm.get_deployment_container')
def test_mon_crush_location(self, _get_deployment_container, _migrate_sysctl, _make_var_run, _get_parm, _deploy_daemon, _file_lock, _logger):
"""
@@ -313,6 +326,7 @@ class TestCephAdm(object):
ctx.allow_ptrace = True
ctx.config_json = '-'
ctx.osd_fsid = '0'
+ ctx.tcp_ports = '3300 6789'
_get_parm.return_value = {
'crush_location': 'database=a'
}