Exemple #1
0
    def prepare_drivegroup(self, drive_group: DriveGroupSpec) -> List[Tuple[str, DriveSelection]]:
        # 1) use fn_filter to determine matching_hosts
        matching_hosts = drive_group.placement.filter_matching_hostspecs(
            self.mgr.inventory.all_specs())
        # 2) Map the inventory to the InventoryHost object
        host_ds_map = []

        # set osd_id_claims

        def _find_inv_for_host(hostname: str, inventory_dict: dict) -> List[Device]:
            # This is stupid and needs to be loaded with the host
            for _host, _inventory in inventory_dict.items():
                if _host == hostname:
                    return _inventory
            raise OrchestratorError("No inventory found for host: {}".format(hostname))

        # 3) iterate over matching_host and call DriveSelection
        logger.debug(f"Checking matching hosts -> {matching_hosts}")
        for host in matching_hosts:
            inventory_for_host = _find_inv_for_host(host, self.mgr.cache.devices)
            logger.debug(f"Found inventory for host {inventory_for_host}")

            # List of Daemons on that host
            dd_for_spec = self.mgr.cache.get_daemons_by_service(drive_group.service_name())
            dd_for_spec_and_host = [dd for dd in dd_for_spec if dd.hostname == host]

            drive_selection = DriveSelection(drive_group, inventory_for_host,
                                             existing_daemons=len(dd_for_spec_and_host))
            logger.debug(f"Found drive selection {drive_selection}")
            host_ds_map.append((host, drive_selection))
        return host_ds_map
Exemple #2
0
    def create_single_host(self,
                           drive_group: DriveGroupSpec,
                           host: str, cmd: str, replace_osd_ids: List[str],
                           env_vars: Optional[List[str]] = None) -> str:
        out, err, code = self._run_ceph_volume_command(host, cmd, env_vars=env_vars)

        if code == 1 and ', it is already prepared' in '\n'.join(err):
            # HACK: when we create against an existing LV, ceph-volume
            # returns an error and the above message.  To make this
            # command idempotent, tolerate this "error" and continue.
            logger.debug('the device was already prepared; continuing')
            code = 0
        if code:
            raise RuntimeError(
                'cephadm exited with an error code: %d, stderr:%s' % (
                    code, '\n'.join(err)))
        return self.deploy_osd_daemons_for_existing_osds(host, drive_group.service_name(),
                                                         replace_osd_ids)
Exemple #3
0
    def create_single_host(self,
                           drive_group: DriveGroupSpec,
                           host: str,
                           cmd: str,
                           replace_osd_ids: List[str],
                           env_vars: Optional[List[str]] = None) -> str:
        out, err, code = self._run_ceph_volume_command(host,
                                                       cmd,
                                                       env_vars=env_vars)

        if code == 1 and ', it is already prepared' in '\n'.join(err):
            # HACK: when we create against an existing LV, ceph-volume
            # returns an error and the above message.  To make this
            # command idempotent, tolerate this "error" and continue.
            logger.debug('the device was already prepared; continuing')
            code = 0
        if code:
            raise RuntimeError(
                'cephadm exited with an error code: %d, stderr:%s' %
                (code, '\n'.join(err)))

        # check result
        out, err, code = CephadmServe(self.mgr)._run_cephadm(
            host, 'osd', 'ceph-volume', [
                '--',
                'lvm',
                'list',
                '--format',
                'json',
            ])
        before_osd_uuid_map = self.mgr.get_osd_uuid_map(only_up=True)
        try:
            osds_elems = json.loads('\n'.join(out))
        except ValueError:
            logger.exception('Cannot decode JSON: \'%s\'' % '\n'.join(out))
            osds_elems = {}
        fsid = self.mgr._cluster_fsid
        osd_uuid_map = self.mgr.get_osd_uuid_map()
        created = []
        for osd_id, osds in osds_elems.items():
            for osd in osds:
                if osd['tags']['ceph.cluster_fsid'] != fsid:
                    logger.debug('mismatched fsid, skipping %s' % osd)
                    continue
                if osd_id in before_osd_uuid_map and osd_id not in replace_osd_ids:
                    # if it exists but is part of the replacement operation, don't skip
                    continue
                if osd_id not in osd_uuid_map:
                    logger.debug(
                        'osd id {} does not exist in cluster'.format(osd_id))
                    continue
                if osd_uuid_map.get(osd_id) != osd['tags']['ceph.osd_fsid']:
                    logger.debug('mismatched osd uuid (cluster has %s, osd '
                                 'has %s)' % (osd_uuid_map.get(osd_id),
                                              osd['tags']['ceph.osd_fsid']))
                    continue

                created.append(osd_id)
                daemon_spec: CephadmDaemonDeploySpec = CephadmDaemonDeploySpec(
                    service_name=drive_group.service_name(),
                    daemon_id=osd_id,
                    host=host,
                    daemon_type='osd',
                )
                daemon_spec.final_config, daemon_spec.deps = self.generate_config(
                    daemon_spec)
                CephadmServe(self.mgr)._create_daemon(
                    daemon_spec, osd_uuid_map=osd_uuid_map)

        if created:
            self.mgr.cache.invalidate_host_devices(host)
            return "Created osd(s) %s on host '%s'" % (','.join(created), host)
        else:
            return "Created no osd(s) on host %s; already created?" % host
Exemple #4
0
    def prepare_drivegroup(
            self,
            drive_group: DriveGroupSpec) -> List[Tuple[str, DriveSelection]]:
        # 1) use fn_filter to determine matching_hosts
        matching_hosts = drive_group.placement.filter_matching_hostspecs(
            self.mgr.cache.get_schedulable_hosts())
        # 2) Map the inventory to the InventoryHost object
        host_ds_map = []

        # set osd_id_claims

        def _find_inv_for_host(hostname: str,
                               inventory_dict: dict) -> List[Device]:
            # This is stupid and needs to be loaded with the host
            for _host, _inventory in inventory_dict.items():
                if _host == hostname:
                    return _inventory
            raise OrchestratorError(
                "No inventory found for host: {}".format(hostname))

        # 3) iterate over matching_host and call DriveSelection
        logger.debug(f"Checking matching hosts -> {matching_hosts}")
        for host in matching_hosts:
            inventory_for_host = _find_inv_for_host(host,
                                                    self.mgr.cache.devices)
            logger.debug(f"Found inventory for host {inventory_for_host}")

            # List of Daemons on that host
            dd_for_spec = self.mgr.cache.get_daemons_by_service(
                drive_group.service_name())
            dd_for_spec_and_host = [
                dd for dd in dd_for_spec if dd.hostname == host
            ]

            drive_selection = DriveSelection(
                drive_group,
                inventory_for_host,
                existing_daemons=len(dd_for_spec_and_host))
            logger.debug(f"Found drive selection {drive_selection}")
            if drive_group.method and drive_group.method == 'raw':
                # ceph-volume can currently only handle a 1:1 mapping
                # of data/db/wal devices for raw mode osds. If db/wal devices
                # are defined and the number does not match the number of data
                # devices, we need to bail out
                if drive_selection.data_devices(
                ) and drive_selection.db_devices():
                    if len(drive_selection.data_devices()) != len(
                            drive_selection.db_devices()):
                        raise OrchestratorError(
                            'Raw mode only supports a 1:1 ratio of data to db devices. Found '
                            f'{len(drive_selection.data_devices())} potential data device(s) and '
                            f'{len(drive_selection.db_devices())} potential db device(s) on host {host}'
                        )
                if drive_selection.data_devices(
                ) and drive_selection.wal_devices():
                    if len(drive_selection.data_devices()) != len(
                            drive_selection.wal_devices()):
                        raise OrchestratorError(
                            'Raw mode only supports a 1:1 ratio of data to wal devices. Found '
                            f'{len(drive_selection.data_devices())} potential data device(s) and '
                            f'{len(drive_selection.wal_devices())} potential wal device(s) on host {host}'
                        )
            host_ds_map.append((host, drive_selection))
        return host_ds_map