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
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
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))
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)
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)
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
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
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
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()))