diff options
Diffstat (limited to 'src/cephadm')
-rwxr-xr-x | src/cephadm/build.py | 9 | ||||
-rwxr-xr-x | src/cephadm/cephadm.py | 164 | ||||
-rw-r--r-- | src/cephadm/tests/test_cephadm.py | 14 |
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' } |