From: Daniel Pivonka Date: Wed, 18 Aug 2021 21:04:05 +0000 (-0400) Subject: cephadm: fix tracebacks that could occur during apply spec X-Git-Tag: v17.1.0~552^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=d546498bc9ab5c44ce9e90d71ee4f69fed2b8429;p=ceph.git cephadm: fix tracebacks that could occur during apply spec Signed-off-by: Daniel Pivonka --- diff --git a/src/cephadm/cephadm b/src/cephadm/cephadm index 19a76b2c130c..4ef409947e1a 100755 --- a/src/cephadm/cephadm +++ b/src/cephadm/cephadm @@ -27,7 +27,7 @@ import struct import ssl from enum import Enum -from typing import Dict, List, Tuple, Optional, Union, Any, NoReturn, Callable, IO, Sequence, TypeVar, cast, Set +from typing import Dict, List, Tuple, Optional, Union, Any, NoReturn, Callable, IO, Sequence, TypeVar, cast, Set, Iterable import re import uuid @@ -4415,6 +4415,65 @@ def finish_bootstrap_config( pass +# funcs to process spec file for apply spec +def _parse_yaml_docs(f: Iterable[str]) -> List[List[str]]: + docs = [] + current_doc = [] # type: List[str] + for line in f: + if '---' in line: + if current_doc: + docs.append(current_doc) + current_doc = [] + else: + current_doc.append(line.rstrip()) + if current_doc: + docs.append(current_doc) + return docs + + +def _parse_yaml_obj(doc: List[str]) -> Dict[str, str]: + # note: this only parses the first layer of yaml + obj = {} # type: Dict[str, str] + current_key = '' + for line in doc: + if line.startswith(' '): + obj[current_key] += line.strip() + elif line.endswith(':'): + current_key = line.strip(':') + obj[current_key] = '' + else: + current_key, val = line.split(':') + obj[current_key] = val.strip() + return obj + + +def parse_yaml_objs(f: Iterable[str]) -> List[Dict[str, str]]: + objs = [] + for d in _parse_yaml_docs(f): + objs.append(_parse_yaml_obj(d)) + return objs + + +def _distribute_ssh_keys(ctx: CephadmContext, host_spec: Dict[str, str], bootstrap_hostname: str) -> int: + # copy ssh key to hosts in host spec (used for apply spec) + ssh_key = '/etc/ceph/ceph.pub' + if ctx.ssh_public_key: + ssh_key = ctx.ssh_public_key.name + + if bootstrap_hostname != host_spec['hostname']: + if 'addr' in host_spec: + addr = host_spec['addr'] + else: + addr = host_spec['hostname'] + out, err, code = call(ctx, ['sudo', '-u', ctx.ssh_user, 'ssh-copy-id', '-f', '-i', ssh_key, '-o StrictHostKeyChecking=no', '%s@%s' % (ctx.ssh_user, addr)]) + if code: + logger.info('\nCopying ssh key to host %s at address %s failed!\n' % (host_spec['hostname'], addr)) + return 1 + else: + logger.info('Added ssh key to host %s at address %s\n' % (host_spec['hostname'], addr)) + return 0 + + @default_image def command_bootstrap(ctx): # type: (CephadmContext) -> int @@ -4606,25 +4665,22 @@ def command_bootstrap(ctx): if ctx.apply_spec: logger.info('Applying %s to cluster' % ctx.apply_spec) - + # copy ssh key to hosts in spec file with open(ctx.apply_spec) as f: - for line in f: - if 'hostname:' in line: - line = line.replace('\n', '') - split = line.split(': ') - if split[1] != hostname: - logger.info('Adding ssh key to %s' % split[1]) - - ssh_key = '/etc/ceph/ceph.pub' - if ctx.ssh_public_key: - ssh_key = ctx.ssh_public_key.name - out, err, code = call_throws(ctx, ['sudo', '-u', ctx.ssh_user, 'ssh-copy-id', '-f', '-i', ssh_key, '-o StrictHostKeyChecking=no', '%s@%s' % (ctx.ssh_user, split[1])]) + try: + for spec in parse_yaml_objs(f): + if spec.get('service_type') == 'host': + _distribute_ssh_keys(ctx, spec, hostname) + except ValueError: + logger.info('Unable to parse %s succesfully' % ctx.apply_spec) mounts = {} mounts[pathify(ctx.apply_spec)] = '/tmp/spec.yml:ro' - - out = cli(['orch', 'apply', '-i', '/tmp/spec.yml'], extra_mounts=mounts) - logger.info(out) + try: + out = cli(['orch', 'apply', '-i', '/tmp/spec.yml'], extra_mounts=mounts) + logger.info(out) + except Exception: + logger.info('\nApplying %s to cluster failed!\n' % ctx.apply_spec) logger.info('You can access the Ceph CLI with:\n\n' '\tsudo %s shell --fsid %s -c %s -k %s\n' % ( diff --git a/src/cephadm/tests/test_cephadm.py b/src/cephadm/tests/test_cephadm.py index d95f76ea1b8f..ccad1868bb1e 100644 --- a/src/cephadm/tests/test_cephadm.py +++ b/src/cephadm/tests/test_cephadm.py @@ -798,6 +798,9 @@ class TestBootstrap(object): *args, ] + +###############################################3 + def test_config(self, cephadm_fs): conf_file = 'foo' cmd = self._get_cmd( @@ -1378,3 +1381,62 @@ class TestPull: with pytest.raises(cd.Error) as e: cd.command_pull(ctx) assert err in str(e.value) + + +class TestApplySpec: + + def test_parse_yaml(self, cephadm_fs): + yaml = '''service_type: host +hostname: vm-00 +addr: 192.168.122.44 +labels: + - example1 + - example2 +--- +service_type: host +hostname: vm-01 +addr: 192.168.122.247 +labels: + - grafana +--- +service_type: host +hostname: vm-02 +addr: 192.168.122.165''' + + cephadm_fs.create_file('spec.yml', contents=yaml) + + retdic = [{'service_type': 'host', 'hostname': 'vm-00', 'addr': '192.168.122.44', 'labels': '- example1- example2'}, + {'service_type': 'host', 'hostname': 'vm-01', 'addr': '192.168.122.247', 'labels': '- grafana'}, + {'service_type': 'host', 'hostname': 'vm-02', 'addr': '192.168.122.165'}] + + with open('spec.yml') as f: + dic = cd.parse_yaml_objs(f) + assert dic == retdic + + @mock.patch('cephadm.call', return_value=('', '', 0)) + def test_distribute_ssh_keys(self, call): + ctx = cd.CephadmContext() + ctx.ssh_public_key = None + ctx.ssh_user = 'root' + + host_spec = {'service_type': 'host', 'hostname': 'vm-02', 'addr': '192.168.122.165'} + + retval = cd._distribute_ssh_keys(ctx, host_spec, 'bootstrap_hostname') + + assert retval == 0 + + call.return_value = ('', '', 1) + + retval = cd._distribute_ssh_keys(ctx, host_spec, 'bootstrap_hostname') + + assert retval == 1 + + + + + + + + + +