Ovirt程序员

【Ovirt 笔记】engine-image-uploader

2018-05-15  本文已影响5人  58bc06151329

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

分析整理的版本为 Ovirt 3.4.5 版本。

命令使用方式:
engine-image-uploader [options] list 显示导出存储域列表
engine-image-uploader [options] upload [file | directory] 上传开发格式为 OVF 的虚拟机。

选项组 说明
--version 显示程序的版本号
-h,--help 显示帮助信息
--quiet 控制台简洁输出(默认 false)
--log-file=PATH 日志文件路径(默认为 /var/log/ovirt-engine/ovirt-image-uploader/ovirt-image-uploader-yyyyMMddHHmmss.log
--conf-file=PATH 配置文件路径(默认为 /etc/ovirt-engine/imageuploader.conf
engine 配置组 说明
-u,--user= restApi 用户,例如:user@engine.example.com,默认 admin@internal
-r,--engine= restApi IP 地址,例如:localhost:443
--cert-file=PATH CA 证书用来验证引擎(默认为 /etc/pki/ovirt-engine/ca.pem
--insecure 不验证引擎(默认 off)
导出域配置组 说明
-e,--export-domain 指定上传文件的导出域
-n,--nfs-server= 指定上传文件的 NFS 服务器,此选项是导出域的替代方案,不与导出域组合,例如:--nfs-server=example.com:/path/to/some/dir
-i,--ovf-id 如果不想更新 OVF 的 UUID,可以使用此选项指定,默认生成新的 UUID
-d,--disk-instance-id 如果不想更新磁盘的 UUID,可以使用此选项指定,默认生成新的 UUID(需要确保磁盘 UUID 没有冲突)
-m,--mac-address 如果不想更新 MAC 地址,可以使用此选项指定,默认生成新的 MAC 地址(需要确保 MAC 地址没有冲突)
-N,-name= 重命名新的镜像文件名称
|-- images
|   |-- <Image Group UUID>
|        |--- <Image UUID (this is the disk image)>
|        |--- <Image UUID (this is the disk image)>.meta
|-- master
|   |---vms
|       |--- <UUID>
|             |--- <UUID>.ovf
[root@localhost ~]# engine-image-uploader list
Please provide the REST API password for the admin@internal oVirt Engine user (CTRL+D to abort): 
Export Storage Domain Name     | Datacenter                | Export Domain Status
myexportdom                    | Myowndc                   | active

命令采用了 python 方式进行实现。

optparse 模块

from optparse import OptionParser
parser = OptionParser(...)
parser.add_option(.....)
参数 说明
usage 可以打印用法。
version 在使用 %prog --version 的时候输出版本信息。
description 描述信息
参数 说明
action 指示 optparser 解析参数时候该如何处理。默认是 ' store ' 将命令行参数值保存 options 对象里 。action 的值有 store、store_true、store_false、store_const、append、count、callback。
type 默认是 string,也可以是 int、float 等。
dest 如果没有指定 dest 参数,将用命令行参数名来对 options 对象的值进行存取。
store store 可以为 store_true 和 store_false 两种形式。用于处理命令行参数后面不带值的情况。如 -v、-q 等命令行参数。
default 设置默认值。
help 指定帮助文档。
metavar 提示用户期望参数。
group = OptionGroup(parser)
group.add_option()
parser.add_option_group(group)

shutil 模块

命令 说明
shutil.copyfileobj(fsrc, fdst[, length]) 将文件内容拷贝到另一个文件中
shutil.copyfile(src, dst) 拷贝文件
shutil.copy(src, dst) 拷贝文件和权限
shutil.copy2(src, dst) 拷贝文件和状态信息
shutil.copymode(src, dst) 仅拷贝权限。内容、组、用户均不变
shutil.copystat(src, dst) 仅拷贝状态的信息,即文件属性,包括:mode bits, atime, mtime, flags
shutil.ignore_patterns(*patterns) 忽略哪个文件,有选择性的拷贝
shutil.copytree(src, dst, symlinks=False, ignore=None) 递归的去拷贝文件夹
shutil.rmtree(path[, ignore_errors[, onerror]]) 递归的去删除文件
shutil.move(src, dst) 递归的去移动文件,它类似 mv 命令,其实就是重命名。
shutil.make_archive(base_name, format,...) 创建压缩包并返回文件路径,例如:zip、tar

engine-image-uploader 命令执行流程

解析参数和加载配置文件

conf = None
conf = Configuration(parser)
class Configuration(dict)
......
if not parser:
    raise Exception("Configuration requires a parser")
self.options, self.args = self.parser.parse_args()

self.load_config_file()

if self.args:
   self.from_args(self.args)

镜像上传功能

imageup = ImageUploader(conf)
组装不同的 cmd 命令
class Caller(object):
    """
    Utility class for forking programs.
    """
    def __init__(self, configuration):
        self.configuration = configuration

    def prep(self, cmd):
        _cmd = cmd % self.configuration
        logging.debug(_cmd)
        return shlex.split(_cmd)

    def call(self, cmds):
        """Uses the configuration to fork a subprocess and run cmds"""
        _cmds = self.prep(cmds)
        logging.debug("_cmds(%s)" % _cmds)
        proc = subprocess.Popen(_cmds,
                   stdout=subprocess.PIPE,
                   stderr=subprocess.PIPE)
        stdout, stderr = proc.communicate()
        returncode = proc.returncode
        logging.debug("returncode(%s)" % returncode)
        logging.debug("STDOUT(%s)" % stdout)
        logging.debug("STDERR(%s)" % stderr)

        if returncode == 0:
            return (stdout,returncode)
        else:
            raise Exception(stderr)
根据命令类型的不同执行不同的方法
if self.api is None:
            # The API has not been initialized yet.
            try:
                self.configuration.prompt(
                    "engine",
                    msg=_("hostname of oVirt Engine")
                )
                self.configuration.prompt(
                    "user",
                    msg=_("REST API username for oVirt Engine")
                )
                self.configuration.getpass(
                    "passwd",
                    msg=(
                        _("REST API password for the %s oVirt Engine user") %
                        self.configuration.get("user")
                    )
                )
            except Configuration.SkipException:
                raise Exception(
                    "Insufficient information provided to communicate with "
                    "the oVirt Engine REST API."
                )

            url = "https://" + self.configuration.get("engine") + "/api"
MOUNT='/bin/mount'
NFS_MOUNT_OPTS = '-t nfs -o rw,sync,soft'
def format_nfs_command(self, address, export, dir):
        cmd = '%s %s %s:%s %s' % (MOUNT, NFS_MOUNT_OPTS, address, export, dir)
        logging.debug('NFS mount command (%s)' % cmd)
        return cmd
logging.debug('OVF data %s is a directory' % ovf_file)
                    ovf_file_size = self.get_ovf_dir_space(ovf_file)
                    if (ovf_file_size != -1 and self.update_ovf_xml(ovf_file)):
                        self.copy_files_nfs(ovf_file, dest_dir, address, ovf_file_size, ovf_file)
for root, dirs, files in os.walk(source_dir, topdown=True):
            for name in files:
                for paths in files_to_copy:
                    if str(paths).endswith(name):
                        remote_file = os.path.join(remote_dir, paths)
                        if name.endswith('.ovf'):
                            ovf_file = os.path.join(root,name)
                            remote_ovf_file = remote_file
                        else:
                            if not self.copy_file_nfs(os.path.join(root,name),
                                                      remote_file,
                                                      NUMERIC_VDSM_ID,
                                                      NUMERIC_VDSM_ID):
                                return
self.copy_file_nfs(ovf_file, remote_ovf_file, NUMERIC_VDSM_ID,NUMERIC_VDSM_ID)
def unpack_ovf(self, ovf_file, dest_dir):
        '''Given a path to an OVF .tgz this function will unpack it into dest_dir. '''
        retVal = True
        try:
            tar = tarfile.open(ovf_file, "r:gz")
            tar.extractall(dest_dir)
        except Exception, e:
            retVal = False
            logging.error(_("Problem unpacking %s.  Message %s"
                    % (ovf_file,
                       str(e).strip())))
        finally:
            tar.close()
        return retVal
UMOUNT='/bin/umount'
NFS_UMOUNT_OPTS = '-t nfs -f '
cmd = '%s %s %s' % (UMOUNT, NFS_UMOUNT_OPTS, mount_dir)
logging.debug(cmd)
self.caller.call(cmd)
finally:
       try:
              logging.debug("Cleaning up OVF extract directory %s" % ovf_extract_dir)
              shutil.rmtree(ovf_extract_dir)
       except Exception, e:
              ExitCodes.exit_code=ExitCodes.CLEANUP_ERR
              logging.debug(e)
上一篇 下一篇

猜你喜欢

热点阅读