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