8 # Munge import path to insert build location for libvirt mod
9 sys
.path
.insert(0, sys
.argv
[1])
15 # Path to the libvirt API XML file
19 tree
= lxml
.etree
.parse(f
)
29 # Phase 1: Identify all functions and enums in public API
30 set = tree
.xpath('/api/files/file/exports[@type="function"]/@symbol')
32 wantfunctions
.append(n
)
34 set = tree
.xpath('/api/symbols/enum')
36 typ
= n
.attrib
['type']
37 name
= n
.attrib
['name']
38 val
= n
.attrib
['value']
40 if typ
not in enumvals
:
43 # If the value cannot be converted to int, it is reference to
44 # another enum and needs to be sorted out later on
51 enumvals
[typ
][name
] = int(val
)
54 typ
= n
.attrib
['type']
55 name
= n
.attrib
['name']
56 val
= n
.attrib
['value']
58 for v
in enumvals
.values():
65 print("Cannot get a value of enum %s (originally %s)" % (val
, name
))
66 enumvals
[typ
][name
] = val
68 set = tree
.xpath('/api/files/file/exports[@type="enum"]/@symbol')
70 for enumval
in enumvals
.values():
75 if n
.endswith('_LAST') and enum
[n
] == max(enum
.values()):
79 # Phase 2: Identify all classes and methods in the 'libvirt' python module
82 gotfunctions
= { "libvirt": [] }
84 for name
in dir(libvirt
):
87 thing
= getattr(libvirt
, name
)
88 # Special-case libvirtError to deal with python 2.4 difference
89 # in Exception class type reporting.
90 if type(thing
) in (int, long):
92 elif type(thing
) == type or name
== "libvirtError":
94 gotfunctions
[name
] = []
96 gotfunctions
["libvirt"].append(name
)
100 for enum
in wantenums
:
101 if enum
not in gotenums
:
103 for typ
, enumval
in enumvals
.items():
105 print("FAIL Missing exported enum %s of type %s" % (enum
, typ
))
107 for klassname
in gottypes
:
108 klassobj
= getattr(libvirt
, klassname
)
109 for name
in dir(klassobj
):
112 if name
== 'c_pointer':
114 thing
= getattr(klassobj
, name
)
116 gotfunctions
[klassname
].append(name
)
121 # Phase 3: First cut at mapping of C APIs to python classes + methods
124 for cname
in wantfunctions
:
126 # Some virConnect APIs have stupid names
127 if name
[0:7] == "virNode" and name
[0:13] != "virNodeDevice":
128 name
= "virConnect" + name
[7:]
129 if name
[0:7] == "virConn" and name
[0:10] != "virConnect":
130 name
= "virConnect" + name
[7:]
132 # The typed param APIs are only for internal use
133 if name
[0:14] == "virTypedParams":
136 if name
[0:23] == "virNetworkDHCPLeaseFree":
139 if name
[0:28] == "virDomainStatsRecordListFree":
142 if name
[0:19] == "virDomainFSInfoFree":
145 if name
[0:21] == "virDomainListGetStats":
146 name
= "virConnectDomainListGetStats"
148 # These aren't functions, they're callback signatures
149 if name
in ["virConnectAuthCallbackPtr", "virConnectCloseFunc",
150 "virStreamSinkFunc", "virStreamSourceFunc", "virStreamEventCallback",
151 "virEventHandleCallback", "virEventTimeoutCallback", "virFreeCallback"]:
153 if name
[0:21] == "virConnectDomainEvent" and name
[-8:] == "Callback":
155 if name
[0:22] == "virConnectNetworkEvent" and name
[-8:] == "Callback":
159 # virEvent APIs go into main 'libvirt' namespace not any class
160 if name
[0:8] == "virEvent":
161 if name
[-4:] == "Func":
163 basicklassmap
[name
] = ["libvirt", name
, cname
]
166 # To start with map APIs to classes based on the
167 # naming prefix. Mistakes will be fixed in next
169 for klassname
in gottypes
:
170 klen
= len(klassname
)
171 if name
[0:klen
] == klassname
:
173 if name
not in basicklassmap
:
174 basicklassmap
[name
] = [klassname
, name
[klen
:], cname
]
175 elif len(basicklassmap
[name
]) < klen
:
176 basicklassmap
[name
] = [klassname
, name
[klen
:], cname
]
178 # Anything which can't map to a class goes into the
181 basicklassmap
[name
] = ["libvirt", name
[3:], cname
]
184 # Phase 4: Deal with oh so many special cases in C -> python mapping
187 for name
in sorted(basicklassmap
):
188 klass
= basicklassmap
[name
][0]
189 func
= basicklassmap
[name
][1]
190 cname
= basicklassmap
[name
][2]
192 # The object lifecycle APIs are irrelevant since they're
193 # used inside the object constructors/destructors.
194 if func
in ["Ref", "Free", "New", "GetConnect", "GetDomain"]:
195 if klass
== "virStream" and func
== "New":
202 # All the error handling methods need special handling
203 if klass
== "libvirt":
204 if func
in ["CopyLastError", "DefaultErrorFunc",
205 "ErrorFunc", "FreeError",
206 "SaveLastError", "ResetError"]:
208 elif func
in ["GetLastError", "GetLastErrorMessage", "ResetLastError", "Initialize"]:
210 elif func
== "SetErrorFunc":
211 func
= "RegisterErrorHandler"
212 elif klass
== "virConnect":
213 if func
in ["CopyLastError", "SetErrorFunc"]:
215 elif func
in ["GetLastError", "ResetLastError"]:
216 func
= "virConn" + func
218 # Remove 'Get' prefix from most APIs, except those in virConnect
219 # and virDomainSnapshot namespaces which stupidly used a different
220 # convention which we now can't fix without breaking API
221 if func
[0:3] == "Get" and klass
not in ["virConnect", "virDomainSnapshot", "libvirt"]:
222 if func
not in ["GetCPUStats", "GetTime"]:
225 # The object creation and lookup APIs all have to get re-mapped
226 # into the parent class
227 if func
in ["CreateXML", "CreateLinux", "CreateXMLWithFiles",
228 "DefineXML", "CreateXMLFrom", "LookupByUUID",
229 "LookupByUUIDString", "LookupByVolume" "LookupByName",
230 "LookupByID", "LookupByName", "LookupByKey", "LookupByPath",
231 "LookupByMACString", "LookupByUsage", "LookupByVolume",
232 "LookupSCSIHostByWWN", "Restore", "RestoreFlags",
233 "SaveImageDefineXML", "SaveImageGetXMLDesc"]:
234 if klass
!= "virDomain":
235 func
= klass
[3:] + func
237 if klass
== "virDomainSnapshot":
240 elif klass
== "virStorageVol" and func
in ["StorageVolCreateXMLFrom", "StorageVolCreateXML"]:
241 klass
= "virStoragePool"
243 elif func
== "StoragePoolLookupByVolume":
244 klass
= "virStorageVol"
245 elif func
== "StorageVolLookupByName":
246 klass
= "virStoragePool"
250 # The open methods get remapped to primary namespace
251 if klass
== "virConnect" and func
in ["Open", "OpenAuth", "OpenReadOnly"]:
254 # These are inexplicably renamed in the python API
255 if func
== "ListDomains":
256 func
= "ListDomainsID"
257 elif func
== "ListAllNodeDevices":
258 func
= "ListAllDevices"
259 elif func
== "ListNodeDevices":
262 # The virInterfaceChangeXXXX APIs go into virConnect. Stupidly
263 # they have lost their 'interface' prefix in names, but we can't
265 if func
[0:6] == "Change":
268 # Need to special case the snapshot APIs
269 if klass
== "virDomainSnapshot" and func
in ["Current", "ListNames", "Num"]:
271 func
= "snapshot" + func
273 # Names should start with lowercase letter...
274 func
= func
[0:1].lower() + func
[1:]
275 if func
[0:8] == "nWFilter":
276 func
= "nwfilter" + func
[8:]
277 if func
[0:8] == "fSFreeze" or func
[0:6] == "fSThaw" or func
[0:6] == "fSInfo":
278 func
= "fs" + func
[2:]
280 if klass
== "virNetwork":
281 func
= func
.replace("dHCP", "DHCP")
283 # ...except when they don't. More stupid naming
284 # decisions we can't fix
289 if func
== "uUIDString":
293 if func
== "xMLDesc":
295 if func
== "mACString":
298 finalklassmap
[name
] = [klass
, func
, cname
]
301 # Phase 5: Validate sure that every C API is mapped to a python API
303 for name
in sorted(finalklassmap
):
304 klass
= finalklassmap
[name
][0]
305 func
= finalklassmap
[name
][1]
307 if func
in gotfunctions
[klass
]:
308 usedfunctions
["%s.%s" % (klass
, func
)] = 1
310 print("PASS %s -> %s.%s" % (name
, klass
, func
))
312 print("FAIL %s -> %s.%s (C API not mapped to python)" % (name
, klass
, func
))
316 # Phase 6: Validate that every python API has a corresponding C API
317 for klass
in gotfunctions
:
318 if klass
== "libvirtError":
320 for func
in sorted(gotfunctions
[klass
]):
321 # These are pure python methods with no C APi
322 if func
in ["connect", "getConnect", "domain", "getDomain"]:
325 key
= "%s.%s" % (klass
, func
)
326 if not key
in usedfunctions
:
327 print("FAIL %s.%s (Python API not mapped to C)" % (klass
, func
))
331 print("PASS %s.%s" % (klass
, func
))
333 # Phase 7: Validate that all the low level C APIs have binding
334 for name
in sorted(finalklassmap
):
335 cname
= finalklassmap
[name
][2]
338 if pyname
== "virSetErrorFunc":
339 pyname
= "virRegisterErrorHandler"
340 elif pyname
== "virConnectListDomains":
341 pyname
= "virConnectListDomainsID"
343 # These exist in C and exist in python, but we've got
344 # a pure-python impl so don't check them
345 if name
in ["virStreamRecvAll", "virStreamSendAll"]:
349 thing
= getattr(libvirt
.libvirtmod
, pyname
)
350 except AttributeError:
351 print("FAIL libvirt.libvirtmod.%s (C binding does not exist)" % pyname
)