简介:
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)