一、衔接
接xm create 命令解析(一),这次该执行到return True,cmd(args).
通过上一篇博文的分析,这里我们已经知道了cmd在这里等于什么了。这里cmd(args)就是等于xm_importcommand(c,args),想必这里的c都能猜出来了——既然是create,那c当然就等于‘create’了,args就是我们在执行xm create命令事传递进来的参数,如果事xm create xxx.cfg,这里的args就是xxx.cfg,只不过是一列表的形式存在。
二、xm_importcommand(c,args)
到了这里,我们该分析xm_importcommand(c,args)这个函数了,函数定义还是在main.py文件中,源码如下:
-
def xm_importcommand(command, args):
-
cmd = __import__(command, globals(), locals(), 'xen.xm')
-
cmd.main([command] + args)
从源码上看,这个xm_importcommand函数确实的确非常简单,只有两行,但是它却涉及到了python的另一个知识点,这里不得不分析。
__import__( name[, globals[, locals[, fromlist]]])这个函数用于动态导入模块(或者包)。作用与import和from xxx import xxx相同,与二者不同的事,import可以动态导入,即在程序执行到该语句时再导入。比如我们可以动态的导入模块module=__import(modle_name),等价与import module_name。这些都不是难点,这个函数比较难理解的地方在与它的第四个参数:fromlist,本人在网上查找资料最终得出的结论是:fromlist和name一起搭配,用于实现from xxx import yyy的形式,这里yyy就是这里的fromlist,而xxx就充当name。比如cmd = __import__('xen.xm',globals(),locals(),'create')就等价与from xen.xm import create,然而实际上却不是如此,__import__这一句只能返回的只是xen.xm,得不到xen.xm.create。至于具体的原因如下:
-
This function is invoked by the import statement. It mainly exists so that you can replace it with another function that has a compatible interface, in order to change the semantics of the import statement. For examples of why and how you would do this, see the standard library modules ihooks and rexec. See also the built-in module imp, which defines some useful operations out of which you can build your own __import__() function.
-
-
For example, the statement "import spam" results in the following call: __import__('spam', globals(), locals(), []); the statement "from spam.ham import eggs" results in "__import__('spam.ham', globals(), locals(), ['eggs'])". Note that even though locals() and ['eggs'] are passed in as arguments, the __import__() function does not set the local variable named eggs; this is done by subsequent code that is generated for the import statement. (In fact, the standard implementation does not use its locals argument at all, and uses its globals only to determine the package context of the import statement.
-
When the name variable is of the form package.module, normally, the top-level package (the name up till the first dot) is returned, not the module named by name. However, when a non-empty fromlist argument is given, the module named by name is returned. This is done for compatibility with the bytecode generated for the different kinds of import statement; when using "import spam.ham.eggs", the top-level package spam must be placed in the importing namespace, but when using "from spam.ham import eggs", the spam.ham subpackage must be used to find the eggs variable. As a workaround for this behavior, use getattr() to extract the desired components. For example, you could define the following helper:
-
def my_import(name):
-
mod = __import__(name)
-
components = name.split('.')
-
for comp in components[1:]:
-
mod = getattr(mod, comp)
-
return mod
从上面这段英文的解释上我们看出了__import__的第三个和第四参数实际上用处不是特别大,尤其是第三个参数,至少是在我们理解xm create这里的这段代码是没有多大用处的。至于fromlist参数,有没有的区别,想必大家也都猜到了吧。如果name是包加模块的形式,比如xen.xm.create,那么from有与没有就有区别了,如果为空(即[]),那么__import__只能返回最顶层的包(这里就是xen),如果非空(内容无所谓),那么import就会放回模块(这里就是xen.xm.create)。如果name只是一个模块(比如"create"),那么fromlist就完全没有影响了。
了解了这些之后,在回来看xm_importcommand()代码,我们就明白了,cmd=__import__(command,globals(),locals(),'xen.xm')返回的就是create,而这里的fromlist参数"xen.xm",大概就是起到注释的作用吧。
再次回到xm_importcommand()这个函数,现在看第而行,就很明显了,这里调用的是xen.xm.create模块中的main函数,下面我们将进入到create.py这个模块中来看一看。
三、create.py模块
按照调用关系,在main.py模块中,调用的是create.py的main函数,所以我们从main函数开始分析。
-
def main(argv):
-
is_xml = False
-
try:
-
(opts, config) = parseCommandLine(argv)
-
except StandardError, ex:
-
err(str(ex))
-
if not opts:
-
return
-
if not opts.is_xml:
-
if type(config) == str:#type() judge the variable's type
-
# here config's type is list
-
-
try:
-
config = sxp.parse(file(config))[0]
-
except IOError, exn:
-
raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
-
if serverType == SERVER_XEN_API:
-
from xen.xm.xenapi_create import sxp2xml
-
sxp2xml_inst = sxp2xml()
-
doc = sxp2xml_inst.convert_sxp_to_xml(config, transient=True)
-
-
if opts.vals.dryrun and not opts.is_xml:
-
SXPPrettyPrint.prettyprint(config)
-
-
if opts.vals.xmldryrun and serverType == SERVER_XEN_API:
-
print doc.toprettyxml()
-
-
if opts.vals.dryrun or opts.vals.xmldryrun:
-
return
-
-
if opts.vals.console_autoconnect:
-
do_console(sxp.child_value(config, 'name', -1))
-
-
if serverType == SERVER_XEN_API: #serverType == SERVER_LEGACY_XMLRPC
-
from xen.xm.xenapi_create import xenapi_create
-
xenapi_create_inst = xenapi_create()
-
if opts.is_xml:
-
vm_refs = xenapi_create_inst.create(filename = config,
-
skipdtd = opts.vals.skipdtd)
-
else:
-
vm_refs = xenapi_create_inst.create(document = doc,
-
skipdtd = opts.vals.skipdtd)
-
-
map(lambda vm_ref: server.xenapi.VM.start(vm_ref, 0), vm_refs)
-
elif not opts.is_xml:
-
dom = make_domain(opts, config)
-
-
if opts.vals.vncconsole:
-
domid = domain_name_to_domid(sxp.child_value(config, 'name', -1))
-
vncviewer_autopass = getattr(opts.vals,'vncviewer-autopass', False)
-
console.runVncViewer(domid, vncviewer_autopass, True)
在main函数中,首先调用的就是parseCommandLine这个函数,我只能说这是一个非常非常但疼的函数,因为它的操作对象很复杂。先说这个函数的功能吧,从函数的名字就可以看出来,这个函数是解析某些东西的。的确,这个函数就是在解析我们的domU配置文件,对于xm create winxp.cfg,解析就是winxp.cfg文件内容的,函数将最终的解析结果以列表的形式返回给config。
知道了这个函数的功能,下面就来看一看这个蛋疼的函数。
-
def parseCommandLine(argv):
-
gopts.reset()
-
args = gopts.parse(argv)
-
if gopts.vals.help or gopts.vals.help_config:
-
if gopts.vals.help_config:
-
print gopts.val_usage()
-
return (None, None)
-
-
if not gopts.vals.display:
-
gopts.vals.display = os.getenv("DISPLAY")
-
-
if not gopts.vals.xauthority:
-
gopts.vals.xauthority = get_xauthority()
-
-
gopts.is_xml = False
-
for arg in args:
-
if '=' in arg:
-
(var, val) = arg.strip().split('=', 1)
-
gopts.setvar(var.strip(), val.strip())
-
if gopts.vals.config:
-
config = gopts.vals.config
-
else:
-
try:
-
gopts.load_defconfig()
-
preprocess(gopts.vals)
-
if not gopts.getopt('name') and gopts.getopt('defconfig'):
-
gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
-
config = make_config(gopts.vals)
-
except XMLFileError, ex:
-
XMLFile = ex.getFile()
-
gopts.is_xml = True
-
config = ex.getFile()
-
-
return (gopts, config)
这个函数的第一行:gopts.reset(),显然,gopts是一个全局变量,所以我们得往create.py文件的最前面找。看到了这个gopts的定义,大家该明白了为什么说这个函数比较蛋疼了。create.py竟然花了六百多行来初始化这个该gopts。这个还好说,因为这些初始化方法比较相似,而且还比较统一,所以也就没必要害怕的。主要的是用于表示gopts这个类的定义才是比较难说清楚的。
根据gopts的定义:
-
gopts = Opts(use="""[options] [vars]
-
-
Create a domain.
-
-
Domain creation parameters can be set by command-line switches, from
-
a python configuration script or an SXP config file. See documentation
-
for --defconfig, --config. Configuration variables can be set using
-
VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
-
-
""")
我们可以看到gopts事Opts类型的。关于这个类的定义在xen.xm.opts中,这个opts.py模块定义了关于选项的所有信息,代码比较多,肯定不会网上贴,在opts的Opts类中有几个比较关键的方法,分析到的时候再贴出来。
阅读(1071) | 评论(0) | 转发(0) |