def __init__(self, driver, name, pool=None, snapshot=None, read_only=False): client, ioctx = driver._connect_to_rados(pool) try: self.volume = tpool.Proxy(rbd.Image(ioctx, name, snapshot=snapshot, read_only=read_only)) except rbd.ImageNotFound: with excutils.save_and_reraise_exception(): LOG.debug("rbd image %s does not exist", name) driver._disconnect_from_rados(client, ioctx) except rbd.Error: with excutils.save_and_reraise_exception(): LOG.exception(_("error opening rbd image %s"), name) driver._disconnect_from_rados(client, ioctx) self.driver = driver self.client = client self.ioctx = ioctx
def _main(self): """ """ starttime = time.time() offset = self.offset with rados.Rados(conffile='/etc/ceph/ceph.conf') as cluster: with cluster.open_ioctx(self.pool) as ioctx: rbd_inst = rbd.RBD() with rbd.Image(ioctx, self.image) as image: data = '1' * self.block for i in range(self.filesize / self.block): image.write(data, offset) offset = offset + self.block endtime = time.time() print "time: ", (endtime - starttime) print "speed = %sKB/s " % (self.filesize / (endtime - starttime) / 1024)
def rbd_clone(dbg, cluster, parent_pool, parent, snapshot, clone_pool, clone): log.debug("%s: xcpng.librbd.rbd_utils.rbd_clone: Cluster ID: %s Parent Pool: %s Parent: %s Snapshot: %s Clone Pool: %s Clone: %s" % (dbg, cluster.get_fsid(), parent_pool, parent, snapshot, clone_pool, clone)) p_ioctx = cluster.open_ioctx(parent_pool) p_image = rbd.Image(p_ioctx, parent) c_ioctx = cluster.open_ioctx(clone_pool) rbd_inst = rbd.RBD() try: if not p_image.is_protected_snap(snapshot): p_image.protect_snap(snapshot) rbd_inst.clone(p_ioctx, parent, snapshot, c_ioctx, clone) except Exception as e: log.error("%s: xcpng.librbd.rbd_utils.rbd_clone: Failed to make a clone: Cluster ID: %s Parent Pool: %s Parent: %s Snapshot: %s Clone Pool: %s Clone: %s" % (dbg, cluster.get_fsid(), parent_pool, parent, snapshot, clone_pool, clone)) raise Exception(e) finally: p_ioctx.close() c_ioctx.close()
def _delete_image(self, target_pool, image_name, snapshot_name=None, context=None): """ Delete RBD image and snapshot. :param image_name Image's name :param snapshot_name Image snapshot's name :raises NotFound if image does not exist; InUseByStore if image is in use or snapshot unprotect failed """ with rados.Rados(conffile=self.conf_file, rados_id=self.user) as conn: with conn.open_ioctx(target_pool) as ioctx: try: # First remove snapshot. if snapshot_name is not None: with rbd.Image(ioctx, image_name) as image: try: image.unprotect_snap(snapshot_name) except rbd.ImageBusy: log_msg = _("snapshot %(image)s@%(snap)s " "could not be unprotected because " "it is in use") LOG.debug(log_msg % { 'image': image_name, 'snap': snapshot_name }) raise exceptions.InUseByStore() image.remove_snap(snapshot_name) # Then delete image. rbd.RBD().remove(ioctx, image_name) except rbd.ImageNotFound: raise exceptions.NotFound( message=_("RBD image %s does not exist") % image_name) except rbd.ImageBusy: log_msg = _("image %s could not be removed " "because it is in use") LOG.debug(log_msg % image_name) raise exceptions.InUseByStore()
def __iter__(self): try: with rados.Rados(conffile=self.conf_file, rados_id=self.user) as conn: with conn.open_ioctx(self.pool) as ioctx: with rbd.Image(ioctx, self.name, snapshot=self.snapshot) as image: img_info = image.stat() size = img_info['size'] bytes_left = size while bytes_left > 0: length = min(self.chunk_size, bytes_left) data = image.read(size - bytes_left, length) bytes_left -= len(data) yield data raise StopIteration() except rbd.ImageNotFound: raise exceptions.NotFound( _('RBD image %s does not exist') % self.name)
def queue_flatten(self, image_spec): image_spec = self.extract_image_spec(image_spec) authorize_request(self.module, image_spec[0], image_spec[1]) self.log.info("queue_flatten: {}".format(image_spec)) refs = { TASK_REF_ACTION: TASK_REF_ACTION_FLATTEN, TASK_REF_POOL_NAME: image_spec[0], TASK_REF_POOL_NAMESPACE: image_spec[1], TASK_REF_IMAGE_NAME: image_spec[2] } with self.open_ioctx(image_spec) as ioctx: try: with rbd.Image(ioctx, image_spec[2]) as image: refs[TASK_REF_IMAGE_ID] = image.id() try: parent_image_id = image.parent_id() except rbd.ImageNotFound: parent_image_id = None except rbd.ImageNotFound: pass task = self.find_task(refs) if task: return 0, task.to_json(), '' if TASK_REF_IMAGE_ID not in refs: raise rbd.ImageNotFound("Image {} does not exist".format( self.format_image_spec(image_spec)), errno=errno.ENOENT) if not parent_image_id: raise rbd.ImageNotFound( "Image {} does not have a parent".format( self.format_image_spec(image_spec)), errno=errno.ENOENT) return 0, self.add_task( ioctx, "Flattening image {}".format( self.format_image_spec(image_spec)), refs), ""
def _get_meta_data_tcmu(self): """ query the rbd to get the features and size of the rbd :return: """ self.logger.debug("Refreshing image metadata") with rados.Rados(conffile=settings.config.cephconf, name=settings.config.cluster_client_name) as cluster: with cluster.open_ioctx(self.pool) as ioctx: try: with rbd.Image(ioctx, self.image) as rbd_image: self.exists = True self.size = rbd_image.size() self.size_h = human_size(self.size) self.features = rbd_image.features() self.feature_list = self._get_features() self._parse_snapshots(list(rbd_image.list_snaps())) except rbd.ImageNotFound: self.exists = False
def open_r(self) -> None: self._read_executor = JobExecutor(name='IO-Read', workers=self._simultaneous_reads, blocking_submit=False) re_match = re.match('^([^/]+)/([^@]+)(?:@(.+))?$', self.parsed_url.path) if not re_match: raise UsageError('URL {} is invalid . Need {}:<pool>/<imagename> or {}:<pool>/<imagename>@<snapshotname>.'.format( self.url, self.name, self.name)) self._pool_name, self._image_name, self._snapshot_name = re_match.groups() # try opening it and quit if that's not possible. try: ioctx = self._cluster.open_ioctx(self._pool_name) except rados.ObjectNotFound: raise FileNotFoundError('Ceph pool {} not found.'.format(self._pool_name)) from None try: rbd.Image(ioctx, self._image_name, self._snapshot_name, read_only=True) except rbd.ImageNotFound: raise FileNotFoundError('RBD image or snapshot {} not found.'.format(self.url)) from None
def _get(self): self.log.debug("rbd.list") names = self.rbd.list(self.ioctx) result = [] for name in names: i = rbd.Image(self.ioctx, name) stat = i.stat() stat['name'] = name try: parent_info = i.parent_info() parent = "{}@{}".format(parent_info[0], parent_info[1]) if parent_info[0] != self.pool: parent = "{}/{}".format(parent_info[0], parent) stat['parent'] = parent except rbd.ImageNotFound: pass result.append(stat) return result
def add(self, image_id, image_file, image_size): """ Stores an image file with supplied identifier to the backend storage system and returns an `glance.store.ImageAddResult` object containing information about the stored image. :param image_id: The opaque image identifier :param image_file: The image data to write, as a file-like object :param image_size: The size of the image data to write, in bytes :retval `glance.store.ImageAddResult` object :raises `glance.common.exception.Duplicate` if the image already existed """ checksum = hashlib.md5() image_name = str(image_id) with rados.Rados(conffile=self.conf_file, rados_id=self.user) as conn: fsid = None if hasattr(conn, 'get_fsid'): fsid = conn.get_fsid() with conn.open_ioctx(self.pool) as ioctx: order = int(math.log(self.chunk_size, 2)) LOG.debug('creating image %s with order %d', image_name, order) try: location = self._create_image(fsid, ioctx, image_name, image_size, order) except rbd.ImageExists: raise exception.Duplicate( _('RBD image %s already exists') % image_id) with rbd.Image(ioctx, image_name) as image: bytes_left = image_size while bytes_left > 0: length = min(self.chunk_size, bytes_left) data = image_file.read(length) image.write(data, image_size - bytes_left) bytes_left -= length checksum.update(data) if location.snapshot: image.create_snap(location.snapshot) image.protect_snap(location.snapshot) return (location.get_uri(), image_size, checksum.hexdigest())
def revert_snapshot(pool, volume_name, snap_name): # try: # cluster.connect() # ioctx = cluster.open_ioctx(pool) # try: # image = rbd.Image(ioctx, volume_name) # image.rollback_to_snap(snap_name) # print("The rbd snapshot rollback successful") # #except rados.Error: # except rbd.ImageNotFound: # print("The rbd snapshot ImageNotFound") # finally: # ioctx.close() # finally: # cluster.shutdown() with cluster.connect() as connect: with cluster.open_ioctx(pool) as ioctx: image = rbd.Image(ioctx, volume_name) image.rollback_to_snap(snap_name) print("The rbd snapshot rollback successful")
def backup(self, disk_id): for pool in pools: ioctx = cluster.open_ioctx(pool) rbd_obj = rbd.RBD() pool_rbd = rbd_obj.list(ioctx) for obj in pool_rbd: if disk_id in obj: ins_img = rbd.Image(ioctx, obj) now = dt.now() tstmp = now.strftime("%a-%Y-%b-%d-%H_%M_%S") snap_name = instance_name + '_' + 'snap-' + tstmp try: ins_img.create_snap(snap_name) print("Successfully created the disk " + obj + " snapshot " + snap_name + " for the instance " + instance_name + ".") except: print("Error occured while creating the disk " + obj + " snapshot " + snap_name + " for the instance " + instance_name + ".")
def loopRados(ioctx): for object in ioctx.list_objects(): if object.key[:7] == "rbd_id.": myrbd = rbd.Image(ioctx, name=object.key[7:]) fields = {"name": myrbd.get_name(), "id": myrbd.id()} if args.force: for myfield in antifield.keys(): print("Forcing {} entry for {}".format( myfield, fields['name'])) appendOmap(fields, myfield) else: with rados.ReadOpCtx() as read_op: for myfield in antifield.keys(): iter, ret = ioctx.get_omap_vals_by_keys( read_op, tuple([ "{}_{}".format(myfield, fields[myfield]), ])) ioctx.operate_read_op(read_op, "rbd_directory") checkIter(iter, fields, myfield)
def add(self, image_id, image_file, image_size): """ Stores an image file with supplied identifier to the backend storage system and returns a tuple containing information about the stored image. :param image_id: The opaque image identifier :param image_file: The image data to write, as a file-like object :param image_size: The size of the image data to write, in bytes :retval tuple of URL in backing store, bytes written, checksum and a dictionary with storage system specific information :raises `glance.common.exception.Duplicate` if the image already existed """ checksum = hashlib.md5() image_name = str(image_id) with rados.Rados(conffile=self.conf_file, rados_id=self.user) as conn: fsid = None if hasattr(conn, 'get_fsid'): fsid = conn.get_fsid() with conn.open_ioctx(self.pool) as ioctx: order = int(math.log(self.chunk_size, 2)) LOG.debug('creating image %s with order %d', image_name, order) try: location = self._create_image(fsid, ioctx, image_name, image_size, order) except rbd.ImageExists: raise exception.Duplicate( _('RBD image %s already exists') % image_id) with rbd.Image(ioctx, image_name) as image: offset = 0 chunks = utils.chunkreadable(image_file, self.chunk_size) for chunk in chunks: offset += image.write(chunk, offset) checksum.update(chunk) if location.snapshot: image.create_snap(location.snapshot) image.protect_snap(location.snapshot) return (location.get_uri(), image_size, checksum.hexdigest(), {})
def create_snapshot(self, pool_id, namespace, image_id): try: with self.module.rados.open_ioctx2(int(pool_id)) as ioctx: ioctx.set_namespace(namespace) with rbd.Image(ioctx, image_id=image_id) as image: mode = image.mirror_image_get_mode() if mode != rbd.RBD_MIRROR_IMAGE_MODE_SNAPSHOT: return info = image.mirror_image_get_info() if info['state'] != rbd.RBD_MIRROR_IMAGE_ENABLED or \ not info['primary']: return snap_id = image.mirror_image_create_snapshot() self.log.debug( "create_snapshot: {}/{}/{}: snap_id={}".format( ioctx.get_pool_name(), namespace, image.get_name(), snap_id)) except Exception as e: self.log.error( "exception when creating snapshot for {}/{}/{}: {}".format( pool_id, namespace, image_id, e))
def remove_snap(self, image_name: str, snap: str): ''' 删除一个rbd image快照 :param snap: 快照名称 :param image_name: rbd image名称 :return: True # success :raises: RadosError ''' cluster = self.get_cluster() try: with cluster.open_ioctx(self.pool_name) as ioctx: with rbd.Image(ioctx=ioctx, name=image_name) as image: if image.is_protected_snap(snap): # protected snap check image.unprotect_snap(snap) image.remove_snap(snap) except rbd.ObjectNotFound as e: return True except Exception as e: raise RadosError(f'remove_snap error:{str(e)}') return True
def get_size(self, location): """ Takes a `glance.store.location.Location` object that indicates where to find the image file, and returns the size :param location `glance.store.location.Location` object, supplied from glance.store.location.get_location_from_uri() :raises `glance.exception.NotFound` if image does not exist """ loc = location.store_location with rados.Rados(conffile=self.conf_file, rados_id=self.user) as conn: with conn.open_ioctx(self.pool) as ioctx: try: with rbd.Image(ioctx, loc.image, snapshot=loc.snapshot) as image: img_info = image.stat() return img_info['size'] except rbd.ImageNotFound: msg = _('RBD image %s does not exist') % loc.get_uri() LOG.debug(msg) raise exception.NotFound(msg)
def queue_remove(self, image_spec): image_spec = self.extract_image_spec(image_spec) authorize_request(self.module, image_spec[0], image_spec[1]) self.log.info("queue_remove: {}".format(image_spec)) refs = { TASK_REF_ACTION: TASK_REF_ACTION_REMOVE, TASK_REF_POOL_NAME: image_spec[0], TASK_REF_POOL_NAMESPACE: image_spec[1], TASK_REF_IMAGE_NAME: image_spec[2] } with self.open_ioctx(image_spec) as ioctx: try: with rbd.Image(ioctx, image_spec[2]) as image: refs[TASK_REF_IMAGE_ID] = image.id() snaps = list(image.list_snaps()) except rbd.ImageNotFound: pass task = self.find_task(refs) if task: return 0, task.to_json(), '' if TASK_REF_IMAGE_ID not in refs: raise rbd.ImageNotFound("Image {} does not exist".format( self.format_image_spec(image_spec)), errno=errno.ENOENT) if snaps: raise rbd.ImageBusy("Image {} has snapshots".format( self.format_image_spec(image_spec)), errno=errno.EBUSY) return 0, self.add_task( ioctx, "Removing image {}".format(self.format_image_spec(image_spec)), refs), ''
def queue_remove(self, image_spec): image_spec = self.extract_image_spec(image_spec) self.log.info("queue_remove: {}".format(image_spec)) refs = {TASK_REF_ACTION: TASK_REF_ACTION_REMOVE, TASK_REF_POOL_NAME: image_spec[0], TASK_REF_POOL_NAMESPACE: image_spec[1], TASK_REF_IMAGE_NAME: image_spec[2]} task = self.find_task(refs) if task: return 0, task.to_json(), '' with self.open_ioctx(image_spec) as ioctx: with rbd.Image(ioctx, image_spec[2]) as image: if list(image.list_snaps()): raise rbd.ImageBusy("Image {} has snapshots".format( self.format_image_spec(image_spec)), errno=errno.EBUSY) return 0, self.add_task(ioctx, "Removing image {}".format( self.format_image_spec(image_spec)), refs), ''
def _validate_image(pool, image, backstore, required_rbd_features, supported_rbd_features): try: ioctx = mgr.rados.open_ioctx(pool) try: with rbd.Image(ioctx, image) as img: if img.features( ) & required_rbd_features != required_rbd_features: raise DashboardException( msg='Image {} cannot be exported using {} ' 'backstore because required features are ' 'missing (required features are ' '{})'.format( image, backstore, format_bitmask(required_rbd_features)), code='image_missing_required_features', component='iscsi') if img.features() & supported_rbd_features != img.features( ): raise DashboardException( msg='Image {} cannot be exported using {} ' 'backstore because it contains unsupported ' 'features (supported features are ' '{})'.format( image, backstore, format_bitmask(supported_rbd_features)), code='image_contains_unsupported_features', component='iscsi') except rbd.ImageNotFound: raise DashboardException( msg='Image {} does not exist'.format(image), code='image_does_not_exist', component='iscsi') except rados.ObjectNotFound: raise DashboardException(msg='Pool {} does not exist'.format(pool), code='pool_does_not_exist', component='iscsi')
def _rbd_list(self, pool_name): ioctx = mgr.rados.open_ioctx(pool_name) self.rbd = rbd.RBD() names = self.rbd.list(ioctx) result = [] for name in names: i = rbd.Image(ioctx, name) stat = i.stat() stat['name'] = name features = i.features() stat['features'] = features stat['features_name'] = self._format_bitmask(features) try: parent_info = i.parent_info() parent = "{}@{}".format(parent_info[0], parent_info[1]) if parent_info[0] != pool_name: parent = "{}/{}".format(parent_info[0], parent) stat['parent'] = parent except rbd.ImageNotFound: pass result.append(stat) return result
def create_rbd(): # Get the cluster info and open cluster cluster = rados.Rados(conffile='/etc/ceph/ceph.conf') cluster.connect() # Open the data pool ioctx = cluster.open_ioctx(pool_name) # Get the RBD management instance and create RBD. rbd_inst = rbd.RBD() rbd_inst.create(ioctx, rbd_name, rbd_size) image = rbd.Image(ioctx, rbd_name) # Write date to rbd image eg. foofoofoo... data = 'foo' * 200 # write data with offset. write(data, offset) image.write(data, 0) # Close the image, context and cluster image.close() ioctx.close() cluster.shutdown()
def _get_disk_meta(self, cluster_ioctx, disk_meta): """ Use the provided cluster context to take an rbd image name from the queue and extract size and feature code. The resulting data is then stored in a shared dict accessible by all scan threads :param cluster_ioctx: cluster io context object :param disk_meta: dict of rbd images, holding metadata :return: None """ while True: time.sleep(Disks.scan_interval) try: rbd_name = self.scan_queue.get(block=False) except Queue.Empty: break else: pool, image = rbd_name.split('/') disk_meta[rbd_name] = {} with cluster_ioctx.open_ioctx(pool) as ioctx: try: with rbd.Image(ioctx, image) as rbd_image: size = rbd_image.size() features = rbd_image.features() snapshots = list(rbd_image.list_snaps()) self.scan_mutex.acquire() disk_meta[rbd_name] = { "size": size, "features": features, "snapshots": snapshots } self.scan_mutex.release() except rbd.ImageNotFound: pass
def _read(self, block): ioctx = self._cluster.open_ioctx(self.pool_name) with rbd.Image(ioctx, self.image_name, self.snapshot_name, read_only=True) as image: offset = block.id * self._block_size t1 = time.time() data = image.read(offset, block.size, rados.LIBRADOS_OP_FLAG_FADVISE_DONTNEED) t2 = time.time() if not data: raise EOFError('EOF reached on source when there should be data.') data_checksum = data_hexdigest(self._hash_function, data) logger.debug('{} read block {} (checksum {}...) in {:.2f}s'.format( threading.current_thread().name, block.id, data_checksum[:16], t2 - t1, )) return block, data, data_checksum
def create_snap(self, image_name: str, snap_name: str, protected: bool = False): ''' 为一个rbd image(卷)创建快照 :param image_name: 要创建快照的rbd卷名称 :param snap_name: 快照名称 :param protected: 是否设置快照protect; 默认False(不protect) :return: True # success raise RadosError # failed :raise class: `RadosError` ''' cluster = self.get_cluster() try: with cluster.open_ioctx(self.pool_name) as ioctx: with rbd.Image(ioctx=ioctx, name=image_name) as image: image.create_snap(snap_name) # Create a snapshot of the image. if protected: image.protect_snap(snap_name) except Exception as e: raise RadosError(f'create_snap error:{str(e)}') return True
def _list(ioctx): if self._image_name: # image config try: with rbd.Image(ioctx, self._image_name) as image: result = image.config_list() except rbd.ImageNotFound: result = [] else: # pool config pg_status = list(CephService.get_pool_pg_status(self._pool_name).keys()) if len(pg_status) == 1 and 'incomplete' in pg_status[0]: # If config_list would be called with ioctx if it's a bad pool, # the dashboard would stop working, waiting for the response # that would not happen. # # This is only a workaround for https://tracker.ceph.com/issues/43771 which # already got rejected as not worth the effort. # # Are more complete workaround for the dashboard will be implemented with # https://tracker.ceph.com/issues/44224 # # @TODO: If #44224 is addressed remove this workaround return [] result = self._rbd.config_list(ioctx) return list(result)
def get_rbd_images(ceph_pool='rbd'): """ Grab a dictionary of rbd images in a pool across all clusters """ all_images = dict() for cluster_name, cluster_config in get_ceph_clusters().iteritems(): all_images[cluster_name] = [] ceph_config = parse_ceph_config(cluster_config['conffile']) ceph_monitors = get_ceph_config_monitors(ceph_config) with Rados(**cluster_config) as cluster: with cluster.open_ioctx(ceph_pool) as ioctx: rbd_inst = rbd.RBD() for rbd_image_name in rbd_inst.list(ioctx): with rbd.Image(ioctx, rbd_image_name) as rbd_image: rbd_size = (rbd_image.size() / 1000000000) rbd_data = { 'name': rbd_image_name, 'size': rbd_size, 'monitors': ceph_monitors } all_images[cluster_name].append(rbd_data) return all_images
def _reader(self, id_): """ self._inqueue contains block_ids to be read. self._outqueue contains (block_id, data, data_checksum) """ ioctx = self.cluster.open_ioctx(self.pool_name) with rbd.Image(ioctx, self.image_name, self.snapshot_name, read_only=True) as image: while True: entry = self._inqueue.get() if entry is None: logger.debug("IO {} finishing.".format(id_)) self._outqueue.put(None) # also let the outqueue end self._inqueue.task_done() break block_id, read, metadata = entry if not read: self._outqueue.put((block_id, None, None, metadata)) else: offset = block_id * self.block_size t1 = time.time() self.reader_thread_status[id_] = STATUS_READING data = image.read(offset, self.block_size, rados.LIBRADOS_OP_FLAG_FADVISE_DONTNEED) self.reader_thread_status[id_] = STATUS_NOTHING t2 = time.time() if not data: raise RuntimeError( 'EOF reached on source when there should be data.') data_checksum = self.hash_function(data).hexdigest() self._outqueue.put( (block_id, data, data_checksum, metadata)) self._inqueue.task_done()
def open_r(self) -> None: self._read_executor = JobExecutor(name='IO-Read', workers=self._simultaneous_reads, blocking_submit=False) re_match = re.match('^([^/]+)/(?:([^/]*)/)?([^@]+)(?:@(.+))?$', self.parsed_url.path) if not re_match: raise UsageError( 'URL {} is invalid . Need {}:<pool>[/<namespace>]/<imagename>[@<snapshotname>].' .format(self.url, self.name)) self._pool_name, self._namespace_name, self._image_name, self._snapshot_name = re_match.groups( ) # try opening it and quit if that's not possible. try: ioctx = self._cluster.open_ioctx(self._pool_name) if self._namespace_name is not None and len( self._namespace_name) > 0: logger.debug( f'Configuring io context to use namespace {self._namespace_name}.' ) ioctx.set_namespace(self._namespace_name) except rados.ObjectNotFound: raise FileNotFoundError('Ceph pool {} not found.'.format( self._pool_name)) from None try: rbd.Image(ioctx, self._image_name, self._snapshot_name, read_only=True) except rbd.ImageNotFound: raise FileNotFoundError( 'RBD image or snapshot {} not found.'.format( self.url)) from None
def get_size(self, location, context=None): """ Takes a `glance_store.location.Location` object that indicates where to find the image file, and returns the size :param location `glance_store.location.Location` object, supplied from glance_store.location.get_location_from_uri() :raises `glance_store.exceptions.NotFound` if image does not exist """ loc = location.store_location # if there is a pool specific in the location, use it; otherwise # we fall back to the default pool specified in the config target_pool = loc.pool or self.pool with rados.Rados(conffile=self.conf_file, rados_id=self.user) as conn: with conn.open_ioctx(target_pool) as ioctx: try: with rbd.Image(ioctx, loc.image, snapshot=loc.snapshot) as image: img_info = image.stat() return img_info['size'] except rbd.ImageNotFound: msg = _('RBD image %s does not exist') % loc.get_uri() LOG.debug(msg) raise exceptions.NotFound(msg)