]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph orch <start/stop/restart> commands should work for service name osd and added... 60690/head
authorShweta Bhosale <Shweta.Bhosale1@ibm.com>
Mon, 11 Nov 2024 09:49:32 +0000 (15:19 +0530)
committerShweta Bhosale <Shweta.Bhosale1@ibm.com>
Thu, 5 Dec 2024 06:59:29 +0000 (12:29 +0530)
Fixes: https://tracker.ceph.com/issues/68695
Signed-off-by: Shweta Bhosale <Shweta.Bhosale1@ibm.com>
src/cephadm/cephadm.py
src/cephadm/cephadmlib/file_utils.py
src/pybind/mgr/cephadm/module.py
src/pybind/mgr/orchestrator/_interface.py
src/pybind/mgr/orchestrator/module.py

index d2ddf56411692b5efb072fa58e3cd3b98a9f2b11..a8616980e4dd8f3921bcf6fbae527fc81488c482 100755 (executable)
@@ -111,6 +111,7 @@ from cephadmlib.file_utils import (
     unlink_file,
     write_new,
     write_tmp,
+    update_meta_file,
 )
 from cephadmlib.net_utils import (
     build_addrv_params,
@@ -3453,6 +3454,7 @@ def list_daemons(
     detail: bool = True,
     legacy_dir: Optional[str] = None,
     daemon_name: Optional[str] = None,
+    type_of_daemon: Optional[str] = None,
 ) -> List[Dict[str, str]]:
     host_version: Optional[str] = None
     ls = []
@@ -3489,6 +3491,8 @@ def list_daemons(
     if os.path.exists(data_dir):
         for i in os.listdir(data_dir):
             if i in ['mon', 'osd', 'mds', 'mgr', 'rgw']:
+                if type_of_daemon and type_of_daemon != i:
+                    continue
                 daemon_type = i
                 for j in os.listdir(os.path.join(data_dir, i)):
                     if '-' not in j:
@@ -3525,6 +3529,8 @@ def list_daemons(
                         if daemon_name and name != daemon_name:
                             continue
                         (daemon_type, daemon_id) = j.split('.', 1)
+                        if type_of_daemon and type_of_daemon != daemon_type:
+                            continue
                         unit_name = get_unit_name(fsid,
                                                   daemon_type,
                                                   daemon_id)
@@ -4705,6 +4711,34 @@ def command_list_images(ctx: CephadmContext) -> None:
     # print default images
     cp_obj.write(sys.stdout)
 
+
+def update_service_for_daemon(ctx: CephadmContext,
+                              available_daemons: list,
+                              update_daemons: list) -> None:
+    """ Update the unit.meta file of daemon with required service name for valid daemons"""
+
+    data = {'service_name': ctx.service_name}
+    # check if all the daemon names are valid
+    if not set(update_daemons).issubset(set(available_daemons)):
+        raise Error(f'Error EINVAL: one or more daemons of {update_daemons} does not exist on this host')
+    for name in update_daemons:
+        path = os.path.join(ctx.data_dir, ctx.fsid, name, 'unit.meta')
+        update_meta_file(path, data)
+        print(f'Successfully updated daemon {name} with service {ctx.service_name}')
+
+
+@infer_fsid
+def command_update_osd_service(ctx: CephadmContext) -> int:
+    """update service for provided daemon"""
+    update_daemons = [f'osd.{osd_id}' for osd_id in ctx.osd_ids.split(',')]
+    daemons = list_daemons(ctx, detail=False, type_of_daemon='osd')
+    if not daemons:
+        raise Error(f'Daemon {ctx.osd_ids} does not exists on this host')
+    available_daemons = [d['name'] for d in daemons]
+    update_service_for_daemon(ctx, available_daemons, update_daemons)
+    return 0
+
+
 ##################################
 
 
@@ -5571,6 +5605,14 @@ def _get_parser():
     parser_list_images = subparsers.add_parser(
         'list-images', help='list all the default images')
     parser_list_images.set_defaults(func=command_list_images)
+
+    parser_update_service = subparsers.add_parser(
+        'update-osd-service', help='update service for provided daemon')
+    parser_update_service.set_defaults(func=command_update_osd_service)
+    parser_update_service.add_argument('--fsid', help='cluster FSID')
+    parser_update_service.add_argument('--osd-ids', required=True, help='Comma-separated OSD IDs')
+    parser_update_service.add_argument('--service-name', required=True, help='OSD service name')
+
     return parser
 
 
index 27e70e3175637707b76ea1efdae4f8b36782c972..4dd88cc36712fcbf6348cc133d83120a08a598c3 100644 (file)
@@ -5,6 +5,7 @@ import datetime
 import logging
 import os
 import tempfile
+import json
 
 from contextlib import contextmanager
 from pathlib import Path
@@ -157,3 +158,26 @@ def unlink_file(
     except Exception:
         if not ignore_errors:
             raise
+
+
+def update_meta_file(file_path: str, update_key_val: dict) -> None:
+    """Update key in the file with provided value"""
+    try:
+        with open(file_path, 'r') as fh:
+            data = json.load(fh)
+        file_stat = os.stat(file_path)
+    except FileNotFoundError:
+        raise
+    except Exception:
+        logger.exception(f'Failed to update {file_path}')
+        raise
+    data.update(
+        {key: value for key, value in update_key_val.items() if key in data}
+    )
+
+    with write_new(
+        file_path,
+        owner=(file_stat.st_uid, file_stat.st_gid),
+        perms=(file_stat.st_mode & 0o777),
+    ) as fh:
+        fh.write(json.dumps(data, indent=4) + '\n')
index bf14f8d1715bddb49dde258b540eac4ea5d954a1..6690153d435af4473a02a116bd3da3f919c35bd0 100644 (file)
@@ -2460,7 +2460,7 @@ Then run the following:
 
     @handle_orch_error
     def service_action(self, action: str, service_name: str) -> List[str]:
-        if service_name not in self.spec_store.all_specs.keys():
+        if service_name not in self.spec_store.all_specs.keys() and service_name != 'osd':
             raise OrchestratorError(f'Invalid service name "{service_name}".'
                                     + ' View currently running services using "ceph orch ls"')
         dds: List[DaemonDescription] = self.cache.get_daemons_by_service(service_name)
@@ -3924,6 +3924,50 @@ Then run the following:
         """
         return self.to_remove_osds.all_osds()
 
+    @handle_orch_error
+    def set_osd_spec(self, service_name: str, osd_ids: List[str]) -> str:
+        """
+        Update unit.meta file for osd with service name
+        """
+        if service_name not in self.spec_store:
+            raise OrchestratorError(f"Cannot find service '{service_name}' in the inventory. "
+                                    "Please try again after applying an OSD service that matches "
+                                    "the service name to which you want to attach OSDs.")
+
+        daemons: List[orchestrator.DaemonDescription] = self.cache.get_daemons_by_type('osd')
+        update_osd = defaultdict(list)
+        for daemon in daemons:
+            if daemon.daemon_id in osd_ids and daemon.hostname:
+                update_osd[daemon.hostname].append(daemon.daemon_id)
+
+        if not update_osd:
+            raise OrchestratorError(f"Unable to find OSDs: {osd_ids}")
+
+        failed_osds = []
+        success_osds = []
+        for host in update_osd:
+            osds = ",".join(update_osd[host])
+            # run cephadm command with all host osds on specific host,
+            # if it fails, continue with other hosts
+            try:
+                with self.async_timeout_handler(host):
+                    outs, errs, _code = self.wait_async(
+                        CephadmServe(self)._run_cephadm(host,
+                                                        cephadmNoImage,
+                                                        'update-osd-service',
+                                                        ['--service-name', service_name, '--osd-ids', osds]))
+                    if _code:
+                        self.log.error(f"Failed to update service for {osds} osd. Cephadm error: {errs}")
+                        failed_osds.extend(update_osd[host])
+                    else:
+                        success_osds.extend(update_osd[host])
+            except Exception:
+                self.log.exception(f"Failed to set service name for {osds}")
+                failed_osds.extend(update_osd[host])
+            self.cache.invalidate_host_daemons(host)
+        self._kick_serve_loop()
+        return f"Updated service for osd {','.join(success_osds)}" + (f" and failed for {','.join(failed_osds)}" if failed_osds else "")
+
     @handle_orch_error
     @host_exists()
     def drain_host(self, hostname: str, force: bool = False, keep_conf_keyring: bool = False, zap_osd_devices: bool = False) -> str:
index a505801eea5e51f161fbef89a09d85cf9588740f..4fbc975ae9f04daa587d70f88ca766477f38ce8a 100644 (file)
@@ -747,6 +747,10 @@ class Orchestrator(object):
         """
         raise NotImplementedError()
 
+    def set_osd_spec(self, service_name: str, osd_ids: List[str]) -> OrchResult:
+        """ set service of osd """
+        raise NotImplementedError()
+
     def blink_device_light(self, ident_fault: str, on: bool, locations: List['DeviceLightLoc']) -> OrchResult[List[str]]:
         """
         Instructs the orchestrator to enable or disable either the ident or the fault LED.
index 332bc75d862e7c11ebbaff6fb709519b0e70efa0..6adee15eb9075a14e31c1a56334e331c437b5333 100644 (file)
@@ -1472,6 +1472,14 @@ Usage:
 
         return HandleCommandResult(stdout=out)
 
+    @_cli_write_command('orch osd set-spec-affinity')
+    def _osd_set_spec(self, service_name: str, osd_id: List[str]) -> HandleCommandResult:
+        """Set service spec affinity for osd"""
+        completion = self.set_osd_spec(service_name, osd_id)
+        res = raise_if_exception(completion)
+
+        return HandleCommandResult(stdout=res)
+
     @_cli_write_command('orch daemon add')
     def daemon_add_misc(self,
                         daemon_type: Optional[ServiceType] = None,