]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cephadm: fix tracebacks that could occur during apply spec
authorDaniel Pivonka <dpivonka@redhat.com>
Wed, 18 Aug 2021 21:04:05 +0000 (17:04 -0400)
committerSebastian Wagner <sewagner@redhat.com>
Wed, 17 Nov 2021 10:25:57 +0000 (11:25 +0100)
Signed-off-by: Daniel Pivonka <dpivonka@redhat.com>
(cherry picked from commit d546498bc9ab5c44ce9e90d71ee4f69fed2b8429)

src/cephadm/cephadm
src/cephadm/tests/test_cephadm.py

index a41f70f7ef61b5262605dc6050b4e1216f85e642..1dfcf33d18379ef9a0e7015ba119f2136204a05c 100755 (executable)
@@ -31,7 +31,7 @@ from contextlib import redirect_stdout
 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
@@ -4136,6 +4136,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
@@ -4350,25 +4409,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' % (
index 9f09349cf5043d4e5347d16a515beac029b33db7..5d45fdb40df43993b82eb7f845905a60b02be0d0 100644 (file)
@@ -1071,6 +1071,9 @@ class TestBootstrap(object):
             *args,
         ]
 
+
+###############################################3
+
     def test_config(self, cephadm_fs):
         conf_file = 'foo'
         cmd = self._get_cmd(
@@ -1652,3 +1655,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
+
+
+
+
+
+
+
+
+
+