Chinaunix首页 | 论坛 | 博客
  • 博客访问: 158856
  • 博文数量: 45
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 273
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-28 10:30
文章分类
文章存档

2017年(6)

2016年(3)

2015年(8)

2014年(28)

我的朋友

分类: Python/Ruby

2014-07-22 22:36:25



xm是xen提供的在dom0中管理domU命令,主要包括xm create ,xm shutdown,xm destroy,
xm migrate(迁移)等许多命令,全部都是python脚本语言实现的。这些命令是我们在在linux的终端中输入的命令,但是实际负责功能的执行却不是这些命令,而是xen的xend服务负责执行,xend服务同样也是python脚本实现的。
带着学习python的目的,决定对xen tools(xm与xend)的脚本代码进行分析,然而这部分的代码实在太多,最终选择了其中的一个——xm create命令进行了跟踪分析。

一、xm与xend之间的链接

        刚刚开始分析之前需要了解至关重要的一点,xm create负责创建虚拟机,最终却由xend服务负责完成实际的创建工作,xm与xend之间是怎么联系起来的呢?这其中利用了python的xmlrpc机制,即远程过程调用。这个xmlrpc的其中原理,br不慎了解,但是使用起来却十分方便,只需要在服务端进行注册函数或者事实例(对象),客户但通过链接之后便可以直接调用。这在后面的代码分析中一看便知。
二、xm create
        首先从最开始的部分开始,xm的源码:在终端运行vim /esr/sbin/xm可以看到xm代码如下:
  1. #!/usr/bin/python
  2. # -*- mode: python; -*-
  3. import sys

  4. from xen.xm import main

  5. main.main(sys.argv)
        可以看到xm的代码非常的少,我们需要关注的只第五行和第七行,第五行from xen.xm import main意思是从xen.xm这个包中导入main模块。第七行main.main(sys.argv)就是调用main这个模块的main函数。sys.argv是命令行参数列表。例如xm create winxp.cfg ,则sys.argv 就是
['/usr/sbin/xm', 'create', 'winxp.cfg']。
          至于xen.xm目录的寻找,可以根据sys.path寻找。这里不多说,直接到/esr/lib64/python2.7/site-packages/xen目录,其中python2.7根据具体的python版本可能不同。在这个目录下可以看到如下内容:
  __init__.py  __init__.pyc  lowlevel  remus  sv  util  web  xend  xm  xsview
其中只有__init__.py和__init__.pyc是文件,其他均是目录,这里__init__.py 在这个目录,说明这个目录就是一个目录包,而__init_.pyc是由于import之后产生的,无关紧要。好了,直接就如到xm目录,xm目录下有很多.py文件,这些就是pthon脚本。找到我们需要的main.py,这就是我们在xm源码中看到的main。打开main.py,分析其中的main函数。

点击(此处)折叠或打开

  1. def main(argv=sys.argv):
  2.     if len(argv) < 2:
  3.         usage()

  4.     # intercept --help(-h) and output our own help
  5.     for help in ['--help', '-h']:
  6.         if help in argv[1:]:
  7.             if help == argv[1]:
  8.                 longHelp()
  9.                 sys.exit(0)
  10.             else:
  11.                 usage(argv[1])

  12.     cmd_name = argv[1]
  13.     cmd = xm_lookup_cmd(cmd_name)
  14.     if cmd:
  15.         # strip off prog name and subcmd
  16.         args = argv[2:]
  17.         _, rc = _run_cmd(cmd, cmd_name, args)
  18.         sys.exit(rc)
  19.     else:
  20.         err('Subcommand %s not found!' % cmd_name)
  21.         usage()
直接从14行开始看cmd_name = argv[1],argv就是我们在xm源码中看到的sys.argv,应为argv[0]是脚本程序名,所有命令参数从argv[1]开始,对于xm create来说,cmd_name 当然就等于create了。下面的cmd = xm_lookup_cmd(cmd_name)这一句比较关键,xm_lookup_cmd的定义也在main.py文件中,

点击(此处)折叠或打开

  1. ef xm_lookup_cmd(cmd):
  2.     if commands.has_key(cmd):
  3.         return commands[cmd]
  4.     elif aliases.has_key(cmd):
  5.         deprecated(cmd,aliases[cmd])
  6.         return commands[aliases[cmd]]
  7.     elif cmd == 'help':
  8.         longHelp()
  9.         sys.exit(0)
  10.     else:
  11.         # simulate getopt
对于这个函数,对于xm create命令,我们只需要关注第一个if中的内容。观察main的源码,我么可以看到commands是一个字典,直接查看commands,我们似乎找不到create命令,但是,在main.py中还有这样的两句:

点击(此处)折叠或打开

  1. for c in IMPORTED_COMMANDS:
  2.     commands[c] = eval('lambda args: xm_importcommand("%s", args)' % c)
在IMPORTED_COMMANDS中是有create的,所以
commands['create'] = eval('lambda args :xm_importcommand("%s",args)' %c),
因c = create,所以
commands['create'] = eval('lambda args:xm_importcommand(“create”,args)')。既然事抱着学习python的目的,所以这句里面的两个python的知识点还是有必要回忆一下的。
首先事lambda,这里采用了lambda args: expression这种用法。其实lambda就相当与定义了一个函数,args就是参数,后面的expression就相当与函数体(必须是表达式)。这样理解这一句就相当与:
def f(args):
        return xm_importcommand("create",args)
另一方面eval也是python中的一个知识点,对表达式求值。lambda不同函数,它是表达式。在具体的需要查看资料进行补充。这里我么只需要知道,以后可以这样使用:commands[c](args).而他实际上就是等价与xm_importcommand(c,args)
从xm_lookup_cmd返回,继续回到main。继续看第19行,这里有一个关于python中下滑线的知识点,详情参见:http://blog.chinaunix.net/uid-29680694-id-4369071.html。先面我们进入到_run_cmd这个函数。先记住我们传进去的参数,对于xm create,cmd和cmd_name不用多说,而args=argv[2:](这里是深度复制)。所有,args就是我们的domU的配置文件。

点击(此处)折叠或打开

  1. def _run_cmd(cmd, cmd_name, args):
  2.     global server

  3.     try:
  4.         if server is None:
  5.             if serverType == SERVER_XEN_API:
  6.                 server = XenAPI.Session(serverURI)
  7.                 username, password = parseAuthentication()
  8.                 server.login_with_password(username, password)
  9.                 def logout():
  10.                     try:
  11.                         server.xenapi.session.logout()
  12.                     except:
  13.                         pass
  14.                 atexit.register(logout)
  15.             else:
  16.                 server = ServerProxy(serverURI)

  17.         return True, cmd(args)
  18.    
以上的_run_cmd()的大部分代码已被去掉,这是因为后面的except代码对于我们分析没有关系。在这个函数的一开始 global server,用global表示这是一个全局变量。有必要先看看一看这个全局变量。由server有关的部分代码也在main.py中

点击(此处)折叠或打开

  1. ##
  2. # Configuration File Parsing
  3. ##

  4. xmConfigFile = os.getenv(XM_CONFIG_FILE_ENVVAR, XM_CONFIG_FILE_DEFAULT)
  5. config = None
  6. if os.path.isfile(xmConfigFile):
  7.     try:
  8.         config = xml.dom.minidom.parse(xmConfigFile)
  9.     except:
  10.         print >>sys.stderr, ('Ignoring invalid configuration file %s.' %
  11.                              xmConfigFile)
  12. print config,'main.py'
  13. def parseServer():
  14.     if config:
  15.         server = config.getElementsByTagName('server')
  16.         if server:
  17.         
  18.             st = server[0].getAttribute('type')
  19.             if st != SERVER_XEN_API and st != SERVER_LEGACY_XMLRPC:
  20.                 print >>sys.stderr, ('Invalid server type %s; using %s.' %
  21.                                      (st, SERVER_LEGACY_XMLRPC))
  22.                 st = SERVER_LEGACY_XMLRPC
  23.             return (st, server[0].getAttribute('uri'))

  24.     return SERVER_LEGACY_XMLRPC, XendClient.uri

  25. def parseAuthentication():
  26.     server = config.getElementsByTagName('server')[0]
  27.     return (server.getAttribute('username'),
  28.             server.getAttribute('password'))

  29. serverType, serverURI = parseServer()
  30. server = None
这里其实也看到了,最后一行server=None,所以,在_run_cmd()函数中见到的server就是None。但是为了得到serverType,serverURI,不得不多看一点。
        首先看第五行,xmConfigureFile = os.getenv(),os也是python的一个系统库,它的的getenv函数是为了获得系统的环境变量值,通常我们是没有XM_CONFIG_FILE_ENVVAR这个环境变量的,所以xmConfigureFile就等于了XM_CONFIG_FILE_DEFAULT这个默认值,即/etc/xen/ xm-config.xml
这个xml文件的内容很简单,下面就是其关键部分

点击(此处)折叠或打开

  1. <xm>
  2.    <server type='Xen-API'
  3.           uri=''
  4.           username='me'
  5.           password='mypassword' />
  6.    -->
  7. </xm>
第九行是对这个文件的解析,调用了python的用于解析xml文件的模块xm.dom.minidom,具体br不太了解。通过parseServer函数,和xm-configre.xml,我猜测serverType是“Xen-Api”,但是最终打印结果事“LegacyXMLRPC”,这涉及到python的xml文件的解析,就部扯远了,这里我们只要知道serverType就行了,至于serverURI,其值是httpu:///var/run/xend/xmlrpc.sock。好了,这部分我们就得到了这两个三个结果,server、serverType和serverURI。
继续回到_run_cmd(),既然server=None并且serverType是“LegacyXMLRPC”,所以直接到第十七行,
server=ServerProxy(serverURI)。
这一行看起来好像很复杂,但其实我们只需要知道怎么使用的就行了。这是python中xmlrpc机制在client端的用法。首先客户端需要由一个这样的server对象,而这个server对象的创建就是通过ServerProxy(uri)来完成的,其中uri就是服务端的ip和端口。这样之后,客户端就可以通过server.xxx调用服务端注册过的函数了。










阅读(1355) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~