OpenStack:glance_store:_drivers/filesystem.py文件源码学习-02
来源:互联网 发布:微纳制造技术 知乎 编辑:程序博客网 时间:2024/06/14 12:10
继续01部分的学习。
方法_check_directory_paths(self, datadir_path, directory_paths, priority_paths)
检查传入的路径datadir_path
是否已经在集合directory_paths
中存在。
def _check_directory_paths(self, datadir_path, directory_paths, priority_paths): if datadir_path in directory_paths: msg = (_("Directory %(datadir_path)s specified " "multiple times in filesystem_store_datadirs " "option of filesystem configuration") % {'datadir_path': datadir_path}) # If present with different priority it's a bad configuration if datadir_path not in priority_paths: LOG.exception(msg) raise exceptions.BadStoreConfiguration( store_name="filesystem", reason=msg) # Present with same prio (exact duplicate) only deserves a warning LOG.warning(msg)
01 若路径datadir_path不在集合directory_paths中,什么都不做,方法结束。
02 若存在,继续判断传入的路径是否在列表priority_paths中,若不存在,则异常。这一步的判断其实是对配置进行了一种保护,因为第一步能通过,说明之前相同的路径已经存在了,第二步判断优先级列表中又没有,说明同一路径配置了不同的优先级,这是不正确的。
03 若未抛出异常并完成了上述判断,这里打印日志。
方法_get_datadir_path_and_priority(self, datadir)
从glance-api.conf配置文件的filesystem_store_datadirs
选项中,获取路径及对应的优先级。
这里得说明一下,传入的参数是单条路径,而非一个列表或是其它集合。
def _get_datadir_path_and_priority(self, datadir): priority = 0 parts = [part.strip() for part in datadir.rsplit(":", 1)] datadir_path = parts[0] if len(parts) == 2 and parts[1]: priority = parts[1] if not priority.isdigit(): msg = (_("Invalid priority value %(priority)s in " "filesystem configuration") % {'priority': priority}) LOG.exception(msg) raise exceptions.BadStoreConfiguration( store_name="filesystem", reason=msg) if not datadir_path: msg = _("Invalid directory specified in filesystem configuration") LOG.exception(msg) raise exceptions.BadStoreConfiguration( store_name="filesystem", reason=msg) return datadir_path, priority
01 将传入的datadir
按冒号分割,获取路径和优先级信息。这里注意语句:parts = [part.strip() for part in datadir.rsplit(":", 1)]
,我自己试了一下如下:
>>> datadir = "wo:1:shi:2:shui:3">>> datadir.rsplit(":", 1)['wo:1:shi:2:shui', '3']
不过因为传入的都是单条路径,结构都是<path>:<priority>
,所以解析出来的都是一个只包含路径和对应信息的列表。其实就是转换了信息形式方便处理。
02 如果传入的datadir
能取到两部分信息(此时还不能判断路径是否是None),那么判断配置的优先级的值是否是数字,不是则异常。
03 判断获取到的路径信息是否为真,不为真异常。
04 返回获取的路径信息和对应的优先级信息。
方法_resolve_location(location)
该方法为静态方法,它获取指定位置的文件路径和文件大小。
@staticmethoddef _resolve_location(location): filepath = location.store_location.path if not os.path.exists(filepath): raise exceptions.NotFound(image=filepath) filesize = os.path.getsize(filepath) return filepath, filesize
01 若指定位置中的文件路径不存在,异常。方法是Python的os.path.exists(filepath)
02 获取路径下文件的大小。方法是os.path.getsize(filepath)
,注意返回值是byte为单位的,不是Kb或其它单位。
03 返回文件路径和大小。
方法_get_metadata(self, filepath)
该方法返回元数据目录。
如果元数据作为字典的列表提供,那么会返回一个包含了“id”和“mountpoint”的字典。
def _get_metadata(self, filepath): if self.FILESYSTEM_STORE_METADATA: for image_meta in self.FILESYSTEM_STORE_METADATA: if filepath.startswith(image_meta['mountpoint']): return image_meta reason = (_LE("The image path %(path)s does not match with " "any of the mountpoint defined in " "metadata: %(metadata)s. An empty dictionary " "will be returned to the client.") % dict(path=filepath, metadata=self.FILESYSTEM_STORE_METADATA)) LOG.error(reason) return {}
01 若当前调用者中FILESYSTEM_STORE_METADATA
存在值,则进一步处理,否则直接返回空字典。
查看代码,在模块中,只有在当前类的方法_validate_metadata(self, metadata_file)
中对其进行了赋值,其来源就是JSON文件。
02 遍历FILESYSTEM_STORE_METADATA
,在每次循环中判断指定的文件路径,是否以当前循环中镜像元数据的挂载点开始,若是,则直接返回本次循环中的镜像元数据。
03 若遍历所有都未找到,记录日志。
04 返回空字典。
方法get(self, location, offset=0, chunk_size=None, context=None)
入参中携带了一个glance_store.location.Location
类型对象,指明在哪里找到镜像文件,并返回一个生成器(用于读取镜像文件)及镜像大小image_size
。
@capabilities.checkdef get(self, location, offset=0, chunk_size=None, context=None): filepath, filesize = self._resolve_location(location) msg = _("Found image at %s. Returning in ChunkedFile.") % filepath LOG.debug(msg) return (ChunkedFile(filepath, offset=offset, chunk_size=self.READ_CHUNKSIZE, partial_length=chunk_size), chunk_size or filesize)
01 调用类中的_resolve_location(location)
方法获取镜像文件路径和镜像文件大小。
02 调用ChunkedFile()
类构造方法生成实例对象,并将对象与镜像大小/块大小组合成元组返回。
方法get_size(self, location, context=None)
入参中携带一个glance_store.location.Location
类型的对象,用于指明镜像位置并返回镜像大小。
def get_size(self, location, context=None): filepath, filesize = self._resolve_location(location) msg = _("Found image at %s.") % filepath LOG.debug(msg) return filesize
01 调用类中的_resolve_location(location)
方法获取镜像文件路径和镜像文件大小。
02 返回镜像大小。
方法delete(self, location, context=None)
入参携带一个glance_store.location.Location
类型的对象,用于指明从哪里找到对应镜像去删除。
@capabilities.checkdef delete(self, location, context=None): """ Takes a `glance_store.location.Location` object that indicates where to find the image file to delete :param location: `glance_store.location.Location` object, supplied from glance_store.location.get_location_from_uri() :raises: NotFound if image does not exist :raises: Forbidden if cannot delete because of permissions """ loc = location.store_location fn = loc.path if os.path.exists(fn): try: LOG.debug(_("Deleting image at %(fn)s"), {'fn': fn}) os.unlink(fn) except OSError: raise exceptions.Forbidden( message=(_("You cannot delete file %s") % fn)) else: raise exceptions.NotFound(image=fn)
01 从入参location
中获取存储镜像的位置。
02 调用os.path.exists()
方法判断指定的位置是否存在,不存在异常。存在则继续处理。
03 调用os.unlink()
方法进行删除指定的镜像目录。
方法_get_capacity_info(self, mount_point)
计算给定挂载点(其实就是某个目录)的所有可用空间大小。挂载点是Glance数据目录的路径。
def _get_capacity_info(self, mount_point): # Calculate total available space stvfs_result = os.statvfs(mount_point) total_available_space = stvfs_result.f_bavail * stvfs_result.f_bsize return max(0, total_available_space)
01 调用os.statvfs(mount_point)
方法获取指定路径的系统信息。主要包括了该目录下存储相关的信息,比如文件系统块大小、可用块数等等。
02 将非超级用户可获取的块数(f_bavail)与文件系统块大小(f_bsize)相乘作为结果。
03 为保证万一,返回该结果与0比较的最大值。
方法_find_best_datadir(self, image_size)
根据路径优先级和剩余空间大小,选择最优的镜像路径。
遍历整个目录,并根据路径优先级,返回第一个有足够空间的路径。如果有两个合适的路径含有相同的优先级,选择剩余空间最多的那个。
def _find_best_datadir(self, image_size): if not self.multiple_datadirs: return self.datadir best_datadir = None max_free_space = 0 for priority in self.priority_list: for datadir in self.priority_data_map.get(priority): free_space = self._get_capacity_info(datadir) if free_space >= image_size and free_space > max_free_space: max_free_space = free_space best_datadir = datadir # If datadir is found which can accommodate image and has maximum # free space for the given priority then break the loop, # else continue to lookup further. if best_datadir: break else: msg = (_("There is no enough disk space left on the image " "storage media. requested=%s") % image_size) LOG.exception(msg) raise exceptions.StorageFull(message=msg) return best_datadir
01 判断调用者自身是否是有多条路径,若没有,直接返回调用者自身的datadir变量值。
02 若调用者已获取各路径的优先级列表priority_list
及路径和优先级构成的字典priority_data_map
,则进行遍历处理。外层循环为优先级列表,内层为根据该优先级获取的路径。
(1) 通过调用_get_capacity_info(datadir)
方法,获取当前循环中目录的剩余空间大小。
(2) 判断剩余空间大小是否大于等于指定的镜像大小,并且该剩余空间大小是否大于当前记录的最大剩余空间值max_free_space。若都满足,则将本次循环中得到的剩余空间大小赋值给max_free_space,并将当前目录赋值给变量best_datadir,作为临时的最佳目录。
(3) 若在当前内层循环后,得到了一个最优目录,那么跳出整个循环。
03 若内外两层遍历结束后仍未找到合适的目录,则异常。
04 返回找出的最优目录。
这个方法要注意遍历过程中内外层的顺序,为什么外层是优先级,内层是目录:
因为设置优先级就是为了方便处理,按照之前理解的,获取的优先级列表会进行一个反序重排。因此正常情况下,会先在低优先级的目录下进行查找,逐步上升。
方法add(self, image_id, image_file, image_size, context=None, verifier=None)
作为镜像上传的关键方法,需要重点了解。
该方法使用一个提供的标识符在后端存储系统上存储一个镜像文件,成功后返回一个包含这个已存储镜像的信息的元组。
@capabilities.checkdef add(self, image_id, image_file, image_size, context=None, verifier=None): datadir = self._find_best_datadir(image_size) filepath = os.path.join(datadir, str(image_id)) if os.path.exists(filepath): raise exceptions.Duplicate(image=filepath) checksum = hashlib.md5() bytes_written = 0 try: with open(filepath, 'wb') as f: for buf in utils.chunkreadable(image_file, self.WRITE_CHUNKSIZE): bytes_written += len(buf) checksum.update(buf) if verifier: verifier.update(buf) f.write(buf) except IOError as e: if e.errno != errno.EACCES: self._delete_partial(filepath, image_id) errors = {errno.EFBIG: exceptions.StorageFull(), errno.ENOSPC: exceptions.StorageFull(), errno.EACCES: exceptions.StorageWriteDenied()} raise errors.get(e.errno, e) except Exception: with excutils.save_and_reraise_exception(): self._delete_partial(filepath, image_id) checksum_hex = checksum.hexdigest() metadata = self._get_metadata(filepath) LOG.debug(_("Wrote %(bytes_written)d bytes to %(filepath)s with " "checksum %(checksum_hex)s"), {'bytes_written': bytes_written, 'filepath': filepath, 'checksum_hex': checksum_hex}) if self.conf.glance_store.filesystem_store_file_perm > 0: perm = int(str(self.conf.glance_store.filesystem_store_file_perm), 8) try: os.chmod(filepath, perm) except (IOError, OSError): LOG.warning(_LW("Unable to set permission to image: %s") % filepath) return ('file://%s' % filepath, bytes_written, checksum_hex, metadata)
01 调用_find_best_datadir(image_size)
方法,获取一个最优存放目录。
02 调用os.path.join(datadir, str(image_id))
,拼接待存放文件的路径。
03 如果该路径已存在,则异常,说明路径重复。
04 通过方法hashlib.md5()
生成校验需要的对象checksum
。
05 在指定路径下,写入镜像文件。
(1) 首先将镜像文件封装成一个产生最佳块大小的读取器,这里块大小是64Kb。
(2) 对镜像文件进行遍历。
(3) 遍历中一次取一个块,累计块大小,并对每一个块调用checksum.update()
方法。为了是后面得到镜像文件的MD5值:checksum_hex = checksum.hexdigest()
。
(4) 遍历中判断是否传入了verifier,它是用于验证镜像签名的一个对象。若传入还得继续处理当前数据块。
(5) 写入当前数据库。
06 得到镜像文件的MD5值。通过语句checksum_hex = checksum.hexdigest()
来获取。
07 调用方法_get_metadata(filepath)
方法获取镜像文件的元数据。
08 判断配置中filesystem_store_file_perm
选项值是否大于0(表示需要设置权限),若是,则调用os.chmod(filepath, perm)
方法设置镜像文件的权限。
09 返回镜像文件的信息元组:('file://%s' % filepath, bytes_written, checksum_hex, metadata)
。
方法_delete_partial(filepath, iid)
通过调用os.unlink(filepath)
方法删除指定的目录及内容。
@staticmethoddef _delete_partial(filepath, iid): try: os.unlink(filepath) except Exception as e: msg = _('Unable to remove partial image ' 'data for image %(iid)s: %(e)s') LOG.error(msg % dict(iid=iid, e=encodeutils.exception_to_unicode(e)))
该方法实现比较简单,可参加其调用的os.unlink(filepath)
。
- OpenStack:glance_store:_drivers/filesystem.py文件源码学习-02
- OpenStack:glance_store:_drivers/filesystem.py文件源码学习-01
- OpenStack:glance_store:driver.py文件源码学习
- OpenStack:glance_store:location.py文件源码学习
- Python源码学习七 .py文件的解释
- OpenStack Swift源码初探--proxy下的server.py
- [Openstack]client api源码学习
- openstack nova schedule 源码学习
- 排错经历:openstack 报错ERROR oslo.messaging._drivers.impl_qpid......
- hadoop FileSystem源码分析
- openstack nova 源码分析3-nova目录下的service.py
- openstack nova 源码分析4-nova目录下的driver.py
- openstack nova 源码分析5-1 -nova/virt/libvirt目录下的connection.py
- openstack nova 源码分析5-2 -nova/virt/libvirt目录下的connection.py
- openstack nova 源码分析5-3 -nova/virt/libvirt目录下的connection.py
- openstack nova 源码分析5-4 -nova/virt/libvirt目录下的connection.py
- Odoo学习: __openerp__.py文件详解
- [openstack][G版]keystone源码学习
- [LeetCode]15.3Sum
- JSON-lib框架介绍
- 学生会三年经历与体会——大学价值观树立
- vs输出框用OutputDebugString输出不了log了
- Android之旅第五站——进度条对话框dialog …
- OpenStack:glance_store:_drivers/filesystem.py文件源码学习-02
- grub2中命令的执行
- java内部类详解
- 如何恢复或重置FreeBSD & Linux的root密码
- Java线程(篇外篇):阻塞队列BlockingQueue
- leetcode_middle_15_494. Target Sum
- 正则表达式应用
- 第三方App接入微信登录 解读
- 欢迎使用CSDN-markdown编辑器