Beispiel #1
0
    def remove_task(self,
                    ioctx: rados.Ioctx,
                    task: Task,
                    remove_in_memory: bool = True) -> None:
        self.log.info("remove_task: task={}".format(str(task)))
        omap_keys = (task.sequence_key, )
        try:
            with rados.WriteOpCtx() as write_op:
                ioctx.remove_omap_keys(write_op, omap_keys)
                ioctx.operate_write_op(write_op, RBD_TASK_OID)
        except rados.ObjectNotFound:
            pass

        if remove_in_memory:
            try:
                del self.tasks_by_id[task.task_id]
                del self.tasks_by_sequence[task.sequence]

                # keep a record of the last N tasks to help avoid command replay
                # races
                if not task.failed and not task.canceled:
                    self.log.debug("remove_task: moving to completed tasks")
                    self.completed_tasks.append(task)
                    self.completed_tasks = self.completed_tasks[
                        -MAX_COMPLETED_TASKS:]

            except KeyError:
                pass
Beispiel #2
0
    def add_task(self, ioctx: rados.Ioctx, message: str,
                 refs: TaskRefsT) -> str:
        self.log.debug("add_task: message={}, refs={}".format(message, refs))

        # ensure unique uuid across all pools
        while True:
            task_id = str(uuid.uuid4())
            if task_id not in self.tasks_by_id:
                break

        self.sequence += 1
        task = Task(self.sequence, task_id, message, refs)

        # add the task to the rbd_task omap
        task_json = task.to_json()
        omap_keys = (task.sequence_key, )
        omap_vals = (str.encode(task_json), )
        self.log.info("adding task: %s %s", omap_keys[0].decode(),
                      omap_vals[0].decode())

        with rados.WriteOpCtx() as write_op:
            ioctx.set_omap(write_op, omap_keys, omap_vals)
            ioctx.operate_write_op(write_op, RBD_TASK_OID)
        self.append_task(task)

        self.condition.notify()
        return task_json
Beispiel #3
0
    def load_pool_images(self, ioctx: rados.Ioctx, epoch: int,
                         images: Dict[str, Dict[str, Dict[str, str]]]) -> None:
        pool_id = str(ioctx.get_pool_id())
        pool_name = ioctx.get_pool_name()
        images[pool_id] = {}

        self.log.debug("load_pool_images: pool={}".format(pool_name))

        try:
            namespaces = [''] + rbd.RBD().namespace_list(ioctx)
            for namespace in namespaces:
                if not self.schedules.intersects(
                        LevelSpec.from_pool_spec(int(pool_id), pool_name,
                                                 namespace)):
                    continue
                self.log.debug(
                    "load_pool_images: pool={}, namespace={}".format(
                        pool_name, namespace))
                images[pool_id][namespace] = {}
                ioctx.set_namespace(namespace)
                updated = self.watchers.check(pool_id, namespace, epoch)
                if not updated:
                    self.log.debug(
                        "load_pool_images: {}/{} not updated".format(
                            pool_name, namespace))
                    with self.lock:
                        images[pool_id][namespace] = \
                            self.images[pool_id][namespace]
                    continue
                mirror_images = dict(rbd.RBD().mirror_image_info_list(
                    ioctx, rbd.RBD_MIRROR_IMAGE_MODE_SNAPSHOT))
                if not mirror_images:
                    continue
                image_names = dict([
                    (x['id'], x['name'])
                    for x in filter(lambda x: x['id'] in mirror_images,
                                    rbd.RBD().list2(ioctx))
                ])
                for image_id, info in mirror_images.items():
                    if not info['primary']:
                        continue
                    image_name = image_names.get(image_id)
                    if not image_name:
                        continue
                    if namespace:
                        name = "{}/{}/{}".format(pool_name, namespace,
                                                 image_name)
                    else:
                        name = "{}/{}".format(pool_name, image_name)
                    self.log.debug(
                        "load_pool_images: adding image {}".format(name))
                    images[pool_id][namespace][image_id] = name
        except Exception as e:
            self.log.error(
                "load_pool_images: exception when scanning pool {}: {}".format(
                    pool_name, e))
Beispiel #4
0
    def put_device_metrics(self, ioctx: rados.Ioctx, devid: str, data: Any) -> None:
        assert devid
        old_key = datetime.utcnow() - timedelta(
            seconds=self.retention_period)
        prune = old_key.strftime(TIME_FORMAT)
        self.log.debug('put_device_metrics device %s prune %s' %
                       (devid, prune))
        erase = []
        try:
            with rados.ReadOpCtx() as op:
                # FIXME
                omap_iter, ret = ioctx.get_omap_keys(op, "", MAX_SAMPLES)
                assert ret == 0
                ioctx.operate_read_op(op, devid)
                for key, _ in list(omap_iter):
                    if key >= prune:
                        break
                    erase.append(key)
        except rados.ObjectNotFound:
            # The object doesn't already exist, no problem.
            pass
        except rados.Error as e:
            # Do not proceed with writes if something unexpected
            # went wrong with the reads.
            self.log.exception("Error reading OMAP: {0}".format(e))
            return

        key = datetime.utcnow().strftime(TIME_FORMAT)
        self.log.debug('put_device_metrics device %s key %s = %s, erase %s' %
                       (devid, key, data, erase))
        with rados.WriteOpCtx() as op:
            ioctx.set_omap(op, (key,), (str(json.dumps(data)),))
            if len(erase):
                ioctx.remove_omap_keys(op, tuple(erase))
            ioctx.operate_write_op(op, devid)
Beispiel #5
0
    def load_from_pool(self, ioctx: rados.Ioctx,
                       namespace_validator: Optional[Callable],
                       image_validator: Optional[Callable]) -> None:
        pool_id = ioctx.get_pool_id()
        pool_name = ioctx.get_pool_name()
        stale_keys = []
        start_after = ''
        try:
            while True:
                with rados.ReadOpCtx() as read_op:
                    self.handler.log.info(
                        "load_schedules: {}, start_after={}".format(
                            pool_name, start_after))
                    it, ret = ioctx.get_omap_vals(read_op, start_after, "",
                                                  128)
                    ioctx.operate_read_op(read_op, self.handler.SCHEDULE_OID)

                    it = list(it)
                    for k, v in it:
                        start_after = k
                        v = v.decode()
                        self.handler.log.info("load_schedule: {} {}".format(
                            k, v))
                        try:
                            try:
                                level_spec = LevelSpec.from_id(
                                    self.handler, k, namespace_validator,
                                    image_validator)
                            except ValueError:
                                self.handler.log.debug(
                                    "Stale schedule key %s in pool %s", k,
                                    pool_name)
                                stale_keys.append(k)
                                continue

                            self.level_specs[level_spec.id] = level_spec
                            schedule = Schedule.from_json(level_spec.name, v)
                            self.schedules[level_spec.id] = schedule
                        except ValueError:
                            self.handler.log.error(
                                "Failed to decode schedule: pool={}, {} {}".
                                format(pool_name, k, v))
                    if not it:
                        break

        except StopIteration:
            pass
        except rados.ObjectNotFound:
            pass

        if stale_keys:
            with rados.WriteOpCtx() as write_op:
                ioctx.remove_omap_keys(write_op, stale_keys)
                ioctx.operate_write_op(write_op, self.handler.SCHEDULE_OID)
Beispiel #6
0
    def load_pool(self, ioctx: rados.Ioctx, pools: Dict[str, Dict[str, str]]) -> None:
        pool_id = str(ioctx.get_pool_id())
        pool_name = ioctx.get_pool_name()
        pools[pool_id] = {}
        pool_namespaces = ['']

        self.log.debug("load_pool: {}".format(pool_name))

        try:
            pool_namespaces += rbd.RBD().namespace_list(ioctx)
        except rbd.OperationNotSupported:
            self.log.debug("namespaces not supported")
        except Exception as e:
            self.log.error("exception when scanning pool {}: {}".format(
                pool_name, e))

        for namespace in pool_namespaces:
            pools[pool_id][namespace] = pool_name
Beispiel #7
0
 def _load_legacy_object(self, ioctx: rados.Ioctx, oid: str) -> bool:
     MAX_OMAP = 10000
     self.log.debug(f"loading object {oid}")
     if re.search(self.devre, oid) is None:
         return False
     with rados.ReadOpCtx() as op:
         it, rc = ioctx.get_omap_vals(op, None, None, MAX_OMAP)
         if rc == 0:
             ioctx.operate_read_op(op, oid)
             count = 0
             for t, raw_smart in it:
                 self.log.debug(f"putting {oid} {t}")
                 self._legacy_put_device_metrics(t, oid, raw_smart)
                 count += 1
             assert count < MAX_OMAP
     self.log.debug(f"removing object {oid}")
     ioctx.remove_object(oid)
     return True
Beispiel #8
0
    def load_task_queue(self, ioctx: rados.Ioctx, pool_name: str) -> None:
        pool_spec = pool_name
        if ioctx.nspace:
            pool_spec += "/{}".format(ioctx.nspace)

        start_after = ''
        try:
            while True:
                with rados.ReadOpCtx() as read_op:
                    self.log.info("load_task_task: {}, start_after={}".format(
                        pool_spec, start_after))
                    it, ret = ioctx.get_omap_vals(read_op, start_after, "",
                                                  128)
                    ioctx.operate_read_op(read_op, RBD_TASK_OID)

                    it = list(it)
                    for k, v in it:
                        start_after = k
                        v = v.decode()
                        self.log.info("load_task_task: task={}".format(v))

                        try:
                            task = Task.from_json(v)
                            self.append_task(task)
                        except ValueError:
                            self.log.error(
                                "Failed to decode task: pool_spec={}, task={}".
                                format(pool_spec, v))

                    if not it:
                        break

        except StopIteration:
            pass
        except rados.ObjectNotFound:
            # rbd_task DNE
            pass
Beispiel #9
0
def namespace_validator(ioctx: rados.Ioctx) -> None:
    mode = rbd.RBD().mirror_mode_get(ioctx)
    if mode != rbd.RBD_MIRROR_MODE_IMAGE:
        raise ValueError("namespace {} is not in mirror image mode".format(
            ioctx.get_namespace()))