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)

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM