zoukankan      html  css  js  c++  java
  • oVirt (Open Virtual) 之 VDSM 学习笔记 vdsm/vdsm (一)

    简介:

    VDSM 是 oVirt 项目中的一个重要部分,oVirt 可以参见 oVirt 官网:http://www.ovirt.org/。

    VDSM 可以通过 oVirt 官网找到,也可通过 GIT 仓库下载:git clone http://gerrit.ovirt.org/p/vdsm.git。

    VDSM 模块开始运行的文件是: vdsm/vdsm。是 Python 语言编写的。

    Let's go!

     1 loggerConfFile = constants.P_VDSM_CONF + 'logger.conf'
     2 
     3 if config.getboolean('devel', 'python_warnings_enable'):
     4     warnings.simplefilter("always")
     5     if hasattr(logging, 'captureWarnings'):
     6         # Python 2.6 does not have captureWarnings
     7         logging.captureWarnings(True)
     8 
     9 
    10 if __name__ == '__main__':
    11     try:
    12         main()
    13     except FatalError as e:
    14         syslog.syslog("VDSM failed to start: %s" % e)
    15         # Make it easy to debug via the shell
    16         raise

    这是一个通用的写法, 如果某个模块是被通过关键字 import 导入的,那么其 __name__ 属性将是模块名。

    如果是以通过 Python 解释器直接执行,那么 __name__ 属性将为:'__main__'。对于此种写法,代码:

    if __name__ == '__main__':

    部分的代码一般用作测试此模块代码。“ vdsm/vdsm ” 将被 Python 直接解释执行,所以将执行下面的代码。

    调用 main() 方法,main() 方法将是我们关注的对象。而 syslog.syslog 是调用了 syslog 模块的 syslog()

    方法,记录日志。

    小技巧:

    查看 Python 代码时,对于这种导入库的使用,可以采用启动 Python 解释器后,通过 “ import <模块名> ”,

    再使用“ help(<模块名>) ”的方式查看其帮助手册。

    出现异常的时候,使用 raise 来讲程序挂起,便于调试(O__O 哥,从代码注释都能看出来的呢)。

    Main 开始:

    3个断言( assert )语句,用于保证程序运行的基本条件;

    根据配置 [vars] 里的 'core_dump_enable' 值来设置程序跑飞(DUMP/CORE)时的事件记录资源设置;

    设置组ID,保证运行,然后..., 跑。

     1 def main():
     2     __assertVdsmUser()
     3     __assertLogPermission()
     4     __assertSudoerPermissions()
     5 
     6     if not config.getboolean('vars', 'core_dump_enable'):
     7         resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
     8 
     9     if os.getsid(0) != os.getpid():
    10         # Modern init systems such as systemd can provide us a clean daemon
    11         # environment. So we setpgrp only when run by traditional init system
    12         # that does not prepare the environment.
    13         os.setpgrp()
    14     run()

    断言:

    __assertVdsmUser(): 用于保证程序运行的用户和用户组为:vdsm/kvm。

    1 def __assertVdsmUser():
    2     username = getpass.getuser()
    3     if username != constants.VDSM_USER:
    4         raise FatalError("Not running as %r, trying to run as %r"
    5                          % (constants.VDSM_USER, username))
    6     group = grp.getgrnam(constants.VDSM_GROUP)
    7     if (constants.VDSM_USER not in group.gr_mem) and 
    8        (pwd.getpwnam(constants.VDSM_USER).pw_gid != group.gr_gid):
    9         raise FatalError("Vdsm user is not in KVM group")

    __assertLogPermission(): 用于保证程序具有对日志文件的写的权限。

     1 def __assertLogPermission():
     2     if not os.access(constants.P_VDSM_LOG, os.W_OK):
     3         raise FatalError("Cannot access vdsm log dirctory")
     4 
     5     logfile = constants.P_VDSM_LOG + "/vdsm.log"
     6     if not os.path.exists(logfile):
     7         # if file not exist, and vdsm has an access to log directory- continue
     8         return
     9 
    10     if not os.access(logfile, os.W_OK):
    11         raise FatalError("Cannot access vdsm log file")

    __assertSudoerPermissions(): 用于保证程序具有调用某些特权命令(如:/sbin/multipath)的权限。

     1 def __assertSudoerPermissions():
     2     rc = 1
     3     with tempfile.NamedTemporaryFile() as dst:
     4         # This cmd choice is arbitrary to validate that sudoes.d/50_vdsm file
     5         # is read properly
     6         cmd = [constants.EXT_CHOWN, "%s:%s" %
     7                (constants.VDSM_USER, constants.QEMU_PROCESS_GROUP), dst.name]
     8         rc, _, stderr = utils.execCmd(cmd, sudo=True)
     9 
    10     if rc != 0:
    11         raise FatalError("Vdsm user could not manage to run sudo operation: "
    12                          "(stderr: %s). Verify sudoer rules configuration" %
    13                          (stderr))

    从注释中可以注意到到 sudoes.d/50_vdsm 就是其特权命令的规则描述。

    红帽( RedHat )系列Linux 为 /etc/sudoes.d/50_vdsm。

    基本配置:

    config.getboolean('type', 'des'): 将读取如下格式的配置文件,并返回“ = ”后面的值。

    main() 函数里是设置程序DUMP(如 C 语言工程师讨厌的:段错误)的时候,内核将对其问题

    追踪产生一些资源(文件),对程序的资源进行限制。

    # 模板
    # [type]
    # des=xxx

    [vars] core_dump_enable=True
    # Comments [string] name=YBHello addr=ChengDu

      

    跑:

    检查会话( Session ) ID 是否和进程 ID 相等:

    如果相等,等价于此程序是一后台程序运行,使用 init 机制可以保证(注释里都这样说的);

    如果不等,将进程自己的 ID ( PID )设置为组 ID。

    【使用旧版本的 service vdsmd restart 的方式重启,程序部分日志将写到终端】

    因为 VDSM 的需要这样的设置来保证环境(注释翻译)。

    1     if os.getsid(0) != os.getpid():
    2         # Modern init systems such as systemd can provide us a clean daemon
    3         # environment. So we setpgrp only when run by traditional init system
    4         # that does not prepare the environment.
    5         os.setpgrp()
    6     run()

    run() 起来:

    输入:pidfile 将被写入 vdsm 程序的 PID 的值。

    日志配置文件的处理(日志配置文件为: etc/vdsm/logger.conf)

    添加一个 TRACE 日志级别(作者说:这很粗鲁但很有用 O__O "…)

    导入 VDSM 调试插件

    导入 覆盖率测试 模块(参见代码覆盖率测试)

    将进程的 PID 写入 pidfile 中(pidfile 作为 run() 函数的参数传入),并记录日志

    调用  serve_clients(log) ,正常运行下,serve_clients 不会退出(将继续追踪下去)

    如果 serve_clients(log) 返回了,将所有的线程关闭

     1 def run():
     2     try:
     3         lconfig.fileConfig(loggerConfFile, disable_existing_loggers=False)
     4     except RuntimeError as e:
     5         raise FatalError("Cannot configure logging: %s" % e)
     6 
     7     logging.addLevelName(5, 'TRACE')
     8     logging.TRACE = 5  # impolite but helpful
     9 
    10     # Used to enable code coverage. On production machines
    11     # "coverage" should not exists and COVERAGE_PROCESS_START should not be
    12     # set.
    13     try:
    14         import coverage
    15         coverage.process_startup()
    16     except ImportError:
    17         pass
    18 
    19     log = logging.getLogger('vds')
    20     try:
    21         logging.root.handlers.append(logging.StreamHandler())
    22         log.handlers.append(logging.StreamHandler())
    23 
    24         sysname, nodename, release, version, machine = os.uname()
    25         log.info('(PID: %s) I am the actual vdsm %s %s (%s)',
    26                  os.getpid(), dsaversion.raw_version_revision, nodename,
    27                  release)
    28 
    29         __set_cpu_affinity()
    30 
    31         serve_clients(log)
    32     except:
    33         log.error("Exception raised", exc_info=True)
    34 
    35     log.info("VDSM main thread ended. Waiting for %d other threads..." %
    36              (threading.activeCount() - 1))
    37     for t in threading.enumerate():
    38         if hasattr(t, 'stop'):
    39             t.stop()
    40         log.info(str(t))

    serve_clients():

    添加 SIGTERM、SIGUSR1 的信号处理函数

    概要信息统计线程开始运行(包括:CPU 和内存两部分)

    开启与 libvirt 的事件循环处理(线程)

    根据配置文件的 [irs] 的 irs_enable 的值设置环境

    scheduler(调度)实例线程

    cif(Client InterFace )实例线程

    开启周期执行线程

    等待信号到来,如果信号到来,停止各个线程,函数放回,程序将结束

     1 def serve_clients(log):
     2     cif = None
     3     irs = None
     4     scheduler = None
     5     running = [True]
     6 
     7     def sigtermHandler(signum, frame):
     8         log.debug("Received signal %s" % signum)
     9         running[0] = False
    10 
    11     def sigusr1Handler(signum, frame):
    12         if irs:
    13             log.debug("Received signal %s" % signum)
    14             irs.spmStop(
    15                 irs.getConnectedStoragePoolsList()['poollist'][0])
    16 
    17     sigutils.register()
    18     signal.signal(signal.SIGTERM, sigtermHandler)
    19     signal.signal(signal.SIGUSR1, sigusr1Handler)
    20     zombiereaper.registerSignalHandler()
    21 
    22     profile.start()
    23 
    24     libvirtconnection.start_event_loop()
    25 
    26     try:
    27         if config.getboolean('irs', 'irs_enable'):
    28             try:
    29                 irs = Dispatcher(HSM())
    30             except:
    31                 utils.panic("Error initializing IRS")
    32 
    33         from clientIF import clientIF  # must import after config is read
    34         cif = clientIF.getInstance(irs, log)
    35 
    36         install_manhole({'irs': irs, 'cif': cif})
    37 
    38         scheduler = schedule.Scheduler(name="vdsm.Scheduler",
    39                                        clock=utils.monotonic_time)
    40         scheduler.start()
    41         cif.start()
    42         periodic.start(cif, scheduler)
    43         try:
    44             while running[0]:
    45                 sigutils.wait_for_signal()
    46 
    47             profile.stop()
    48         finally:
    49             periodic.stop()
    50             cif.prepareForShutdown()
    51             scheduler.stop()
    52     finally:
    53         libvirtconnection.stop_event_loop(wait=False)

      

  • 相关阅读:
    DDD实战2 创建领域基础类库项目
    DDD实战1
    MVC中 跳转出某一个Area的方法
    在codefirst迁移的时候 为新增的属性添加一个默认值
    在.net MVC项目中使用ajax进行数据验证
    将枚举转成SelectListItem
    在.net MVC项目的区域中使用模板页
    在MVC项目中分页使用MvcPager插件
    通过控制台查看电脑中.net framework的版本
    全面了解浏览器(内核)发展史
  • 原文地址:https://www.cnblogs.com/YBhello/p/4834024.html
Copyright © 2011-2022 走看看