Add c_pointer method to classes.
[libvirt-python/ericb.git] / sanitytest.py
blob9907f3d0cfdb06c65fb4cea3d767e6b6fae6cbda
1 #!/usr/bin/python
3 import sys
4 import lxml
5 import lxml.etree
6 import string
8 # Munge import path to insert build location for libvirt mod
9 sys.path.insert(0, sys.argv[1])
10 import libvirt
12 if sys.version > '3':
13 long = int
15 # Path to the libvirt API XML file
16 xml = sys.argv[2]
18 f = open(xml, "r")
19 tree = lxml.etree.parse(f)
21 verbose = False
22 fail = False
24 enumvals = {}
25 second_pass = []
26 wantenums = []
27 wantfunctions = []
29 # Phase 1: Identify all functions and enums in public API
30 set = tree.xpath('/api/files/file/exports[@type="function"]/@symbol')
31 for n in set:
32 wantfunctions.append(n)
34 set = tree.xpath('/api/symbols/enum')
35 for n in set:
36 typ = n.attrib['type']
37 name = n.attrib['name']
38 val = n.attrib['value']
40 if typ not in enumvals:
41 enumvals[typ] = {}
43 # If the value cannot be converted to int, it is reference to
44 # another enum and needs to be sorted out later on
45 try:
46 val = int(val)
47 except ValueError:
48 second_pass.append(n)
49 continue
51 enumvals[typ][name] = int(val)
53 for n in second_pass:
54 typ = n.attrib['type']
55 name = n.attrib['name']
56 val = n.attrib['value']
58 for v in enumvals.values():
59 if val in v:
60 val = int(v[val])
61 break
63 if type(val) != int:
64 fail = True
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')
69 for n in set:
70 for enumval in enumvals.values():
71 if n in enumval:
72 enum = enumval
73 break
74 # Eliminate sentinels
75 if n.endswith('_LAST') and enum[n] == max(enum.values()):
76 continue
77 wantenums.append(n)
79 # Phase 2: Identify all classes and methods in the 'libvirt' python module
80 gotenums = []
81 gottypes = []
82 gotfunctions = { "libvirt": [] }
84 for name in dir(libvirt):
85 if name[0] == '_':
86 continue
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):
91 gotenums.append(name)
92 elif type(thing) == type or name == "libvirtError":
93 gottypes.append(name)
94 gotfunctions[name] = []
95 elif callable(thing):
96 gotfunctions["libvirt"].append(name)
97 else:
98 pass
100 for enum in wantenums:
101 if enum not in gotenums:
102 fail = True
103 for typ, enumval in enumvals.items():
104 if enum in enumval:
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):
110 if name[0] == '_':
111 continue
112 if name == 'c_pointer':
113 continue
114 thing = getattr(klassobj, name)
115 if callable(thing):
116 gotfunctions[klassname].append(name)
117 else:
118 pass
121 # Phase 3: First cut at mapping of C APIs to python classes + methods
122 basicklassmap = {}
124 for cname in wantfunctions:
125 name = cname
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":
134 continue
136 if name[0:23] == "virNetworkDHCPLeaseFree":
137 continue
139 if name[0:28] == "virDomainStatsRecordListFree":
140 continue
142 if name[0:19] == "virDomainFSInfoFree":
143 continue
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"]:
152 continue
153 if name[0:21] == "virConnectDomainEvent" and name[-8:] == "Callback":
154 continue
155 if name[0:22] == "virConnectNetworkEvent" and name[-8:] == "Callback":
156 continue
159 # virEvent APIs go into main 'libvirt' namespace not any class
160 if name[0:8] == "virEvent":
161 if name[-4:] == "Func":
162 continue
163 basicklassmap[name] = ["libvirt", name, cname]
164 else:
165 found = False
166 # To start with map APIs to classes based on the
167 # naming prefix. Mistakes will be fixed in next
168 # loop
169 for klassname in gottypes:
170 klen = len(klassname)
171 if name[0:klen] == klassname:
172 found = True
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
179 # global namespaces
180 if not found:
181 basicklassmap[name] = ["libvirt", name[3:], cname]
184 # Phase 4: Deal with oh so many special cases in C -> python mapping
185 finalklassmap = {}
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":
196 klass = "virConnect"
197 func = "NewStream"
198 else:
199 continue
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"]:
207 continue
208 elif func in ["GetLastError", "GetLastErrorMessage", "ResetLastError", "Initialize"]:
209 func = "vir" + func
210 elif func == "SetErrorFunc":
211 func = "RegisterErrorHandler"
212 elif klass == "virConnect":
213 if func in ["CopyLastError", "SetErrorFunc"]:
214 continue
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"]:
223 func = func[3:]
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":
238 klass = "virDomain"
239 func = func[6:]
240 elif klass == "virStorageVol" and func in ["StorageVolCreateXMLFrom", "StorageVolCreateXML"]:
241 klass = "virStoragePool"
242 func = func[10:]
243 elif func == "StoragePoolLookupByVolume":
244 klass = "virStorageVol"
245 elif func == "StorageVolLookupByName":
246 klass = "virStoragePool"
247 else:
248 klass = "virConnect"
250 # The open methods get remapped to primary namespace
251 if klass == "virConnect" and func in ["Open", "OpenAuth", "OpenReadOnly"]:
252 klass = "libvirt"
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":
260 func = "ListDevices"
262 # The virInterfaceChangeXXXX APIs go into virConnect. Stupidly
263 # they have lost their 'interface' prefix in names, but we can't
264 # fix this name
265 if func[0:6] == "Change":
266 klass = "virConnect"
268 # Need to special case the snapshot APIs
269 if klass == "virDomainSnapshot" and func in ["Current", "ListNames", "Num"]:
270 klass = "virDomain"
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
285 if func == "iD":
286 func = "ID"
287 if func == "uUID":
288 func = "UUID"
289 if func == "uUIDString":
290 func = "UUIDString"
291 if func == "oSType":
292 func = "OSType"
293 if func == "xMLDesc":
294 func = "XMLDesc"
295 if func == "mACString":
296 func = "MACString"
298 finalklassmap[name] = [klass, func, cname]
301 # Phase 5: Validate sure that every C API is mapped to a python API
302 usedfunctions = {}
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
309 if verbose:
310 print("PASS %s -> %s.%s" % (name, klass, func))
311 else:
312 print("FAIL %s -> %s.%s (C API not mapped to python)" % (name, klass, func))
313 fail = True
316 # Phase 6: Validate that every python API has a corresponding C API
317 for klass in gotfunctions:
318 if klass == "libvirtError":
319 continue
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"]:
323 continue
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))
328 fail = True
329 else:
330 if verbose:
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]
337 pyname = cname
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"]:
346 continue
348 try:
349 thing = getattr(libvirt.libvirtmod, pyname)
350 except AttributeError:
351 print("FAIL libvirt.libvirtmod.%s (C binding does not exist)" % pyname)
352 fail = True
354 if fail:
355 sys.exit(1)
356 else:
357 sys.exit(0)