1 #!/usr/bin/env /usr/share/plc_api/plcsh
5 # Copyright (C) 2006 The Trustees of Princeton University
9 from pprint import pprint
10 from string import letters, digits, punctuation
11 from traceback import print_exc
20 from qa.Config import Config
21 from qa.logger import Logfile, log
22 from random import Random
28 try: boot_states = GetBootStates()
29 except: boot_states = [u'boot', u'dbg', u'inst', u'new', u'rcnf', u'rins']
31 try: roles = [role['name'] for role in GetRoles()]
32 except: roles = [u'admin', u'pi', u'user', u'tech']
34 try: methods = GetNetworkMethods()
35 except: methods = [u'static', u'dhcp', u'proxy', u'tap', u'ipmi', u'unknown']
37 try: key_types = GetKeyTypes()
38 except: key_types = [u'ssh']
40 try:types = GetNetworkTypes()
41 except: types = [u'ipv4']
43 def randfloat(min = 0.0, max = 1.0):
44 return float(min) + (random.random() * (float(max) - float(min)))
46 def randint(min = 0, max = 1):
47 return int(randfloat(min, max + 1))
49 # See "2.2 Characters" in the XML specification:
51 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
53 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
55 ascii_xml_chars = map(unichr, [0x9, 0xA, 0xD])
56 ascii_xml_chars += map(unichr, xrange(0x20, 0x7F - 1))
57 low_xml_chars = list(ascii_xml_chars)
58 low_xml_chars += map(unichr, xrange(0x84 + 1, 0x86 - 1))
59 low_xml_chars += map(unichr, xrange(0x9F + 1, 0xFF))
60 valid_xml_chars = list(low_xml_chars)
61 valid_xml_chars += map(unichr, xrange(0xFF + 1, 0xD7FF))
62 valid_xml_chars += map(unichr, xrange(0xE000, 0xFDD0 - 1))
63 valid_xml_chars += map(unichr, xrange(0xFDDF + 1, 0xFFFD))
65 def randstr(length, pool = valid_xml_chars, encoding = "utf-8"):
66 sample = random.sample(pool, min(length, len(pool)))
69 bytes = len(s.encode(encoding))
73 sample += random.sample(pool, min(length - bytes, len(pool)))
74 random.shuffle(sample)
80 # 1. Each part begins and ends with a letter or number.
81 # 2. Each part except the last can contain letters, numbers, or hyphens.
82 # 3. Each part is between 1 and 64 characters, including the trailing dot.
83 # 4. At least two parts.
84 # 5. Last part can only contain between 2 and 6 letters.
85 hostname = 'a' + randstr(61, letters + digits + '-') + '1.' + \
86 'b' + randstr(61, letters + digits + '-') + '2.' + \
87 'c' + randstr(5, letters)
92 for i in range(randint(1, 10)):
93 parts.append(randstr(randint(1, 30), ascii_xml_chars))
94 return os.sep.join(parts)[0:length]
97 return (randstr(100, letters + digits) + "@" + randhostname()).lower()
99 def randkey(bits = 2048):
100 key_types = ["ssh-dss", "ssh-rsa"]
101 key_type = random.sample(key_types, 1)[0]
102 return ' '.join([key_type,
103 base64.b64encode(''.join(randstr(bits / 8).encode("utf-8"))),
108 'name': randstr(254),
109 'abbreviated_name': randstr(50),
110 'login_base': randstr(20, letters).lower(),
111 'latitude': int(randfloat(-90.0, 90.0) * 1000) / 1000.0,
112 'longitude': int(randfloat(-180.0, 180.0) * 1000) / 1000.0,
115 def random_address_type():
118 'description': randstr(254),
121 def random_address():
123 'line1': randstr(254),
124 'line2': randstr(254),
125 'line3': randstr(254),
126 'city': randstr(254),
127 'state': randstr(254),
128 'postalcode': randstr(64),
129 'country': randstr(128),
134 'first_name': randstr(128),
135 'last_name': randstr(128),
136 'email': randemail(),
138 # Accounts are disabled by default
140 'password': randstr(254),
145 'key_type': random.sample(key_types, 1)[0],
151 'name': site['login_base'] + "_" + randstr(11, letters).lower(),
152 'url': "http://" + randhostname() + "/",
153 'description': randstr(2048),
156 def random_nodegroup():
159 'description': randstr(200),
164 'hostname': randhostname(),
165 'boot_state': random.sample(boot_states, 1)[0],
166 'model': randstr(255),
167 'version': randstr(64),
170 def random_nodenetwork():
171 nodenetwork_fields = {
172 'method': random.sample(methods, 1)[0],
173 'type': random.sample(types, 1)[0],
174 'bwlimit': randint(500000, 10000000),
178 ip = randint(0, 0xffffffff)
179 netmask = (0xffffffff << randint(2, 31)) & 0xffffffff
180 network = ip & netmask
181 broadcast = ((ip & netmask) | ~netmask) & 0xffffffff
182 gateway = randint(network + 1, broadcast - 1)
183 dns1 = randint(0, 0xffffffff)
185 for field in 'ip', 'netmask', 'network', 'broadcast', 'gateway', 'dns1':
186 nodenetwork_fields[field] = socket.inet_ntoa(struct.pack('>L', locals()[field]))
188 return nodenetwork_fields
192 'hostname': randhostname(),
193 'ip': socket.inet_ntoa(struct.pack('>L', randint(0, 0xffffffff))),
194 'protocol': randstr(16),
195 'username': randstr(254),
196 'password': randstr(254),
197 'notes': randstr(254),
198 'model': randstr(32),
201 def random_conf_file():
203 'enabled': bool(randint()),
204 'source': randpath(255),
205 'dest': randpath(255),
206 'file_permissions': "%#o" % randint(0, 512),
207 'file_owner': randstr(32, letters + '_' + digits),
208 'file_group': randstr(32, letters + '_' + digits),
209 'preinstall_cmd': randpath(100),
210 'postinstall_cmd': randpath(100),
211 'error_cmd': randpath(100),
212 'ignore_cmd_errors': bool(randint()),
213 'always_update': bool(randint()),
216 def random_attribute_type():
218 'name': randstr(100),
219 'description': randstr(254),
220 'min_role_id': random.sample(roles.values(), 1)[0],
223 def isequal(object_fields, expected_fields):
225 for field in expected_fields:
226 assert field in object_fields
227 assert object_fields[field] == expected_fields[field]
232 def islistequal(list1, list2):
234 assert set(list1) == set(list2)
239 def isunique(id, id_list):
241 assert id not in id_list
246 class api_unit_test(Test):
261 self.all_methods = set(system.listMethods())
262 self.methods_tested = set()
263 self.methods_failed = set()
265 # Begin testing methods
268 #self.boot_state_ids = self.BootStates(boot_states)
269 self.site_ids = self.Sites(sites)
270 #self.peer_ids = self.Peers(peers)
271 self.address_type_ids = self.AddressTypes(address_types)
272 self.address_ids = self.Addresses(addresses)
273 #self.conf_files = self.ConfFiles(conf_files)
274 #self.network_method_ids = self.NetworkMethods()
275 #self.network_type_ids = self.NetworkTypes()
276 #self.nodegroup_ids = self.NodeGroups()
277 self.node_ids = self.Nodes(nodes)
278 #self.node_network_ids = self.NodeNetworks(node_networks)
279 #self.node_network_setting_type_ids = self.NodeNetworkSettingsTypes(node_network_settings_types)
280 #self.node_network_setting_ids = self.NodeNetworkSettings(node_network_settings)
281 #self.pcu_protocol_types_ids = self.PCUProtocolTypes(pcu_protocol_types)
282 #self.pcus_ids = self.PCUs(pcus)
283 #self.pcu_types_ids = self.PCUTypes(pcu_types)
284 #self.role_ids = self.Roles(roles)
285 #self.key_types = self.KeyTypes(key_types)
286 #self.slice_attribute_type_ids = self.SliceAttributeTypes(slice_attribute_types)
287 #self.slice_instantiation_ids = self.SliceInstantiations(slice_instantiations)
288 self.slice_ids = self.Slices(slices)
289 #self.slice_attribute_ids = self.SliceAttributes(slice_attributes)
290 #self.initscript_ids = self.InitScripts(initscripts)
291 self.person_ids = self.Persons(persons)
292 self.key_ids = self.Keys(keys)
293 # self.message_ids = self.Messages(messages)
295 # Test GetEventObject only
296 #self.event_object_ids = self.GetEventObjects()
297 #self.event_ids = self.GetEvents()
305 logfile = Logfile("api-unittest.log")
306 methods_ok = list(self.methods_tested.difference(self.methods_failed))
307 methods_failed = list(self.methods_failed)
308 methods_untested = list(self.all_methods.difference(self.methods_tested))
310 methods_failed.sort()
311 methods_untested.sort()
312 print >> logfile, "\n".join([m+": [OK]" for m in methods_ok])
313 print >> logfile, "\n".join([m+": [FAILED]" for m in methods_failed])
314 print >> logfile, "\n".join([m+": [Not Tested]" for m in methods_untested])
316 def isequal(self, object_fields, expected_fields, method_name):
318 for field in expected_fields:
319 assert field in object_fields
320 assert object_fields[field] == expected_fields[field]
322 self.methods_failed.update([method_name])
326 def islistequal(self, list1, list2, method_name):
327 try: assert set(list1) == set(list2)
329 self.methods_failed.update([method_name])
333 def isunique(self, id, id_list, method_name):
334 try: assert id not in id_list
336 self.methods_failed.update([method_name])
340 def debug(self, method, method_name=None):
341 if method_name is None:
342 method_name = method.name
344 self.methods_tested.update([method_name])
345 def wrapper(*args, **kwds):
347 return method(*args, **kwds)
349 self.methods_failed.update([method_name])
355 if hasattr(self, 'initscript_ids'): self.DeleteInitScripts()
356 if hasattr(self, 'slice_attribute_ids'): self.DeleteSliceAttributes()
357 if hasattr(self, 'slice_ids'): self.DeleteSlices()
358 if hasattr(self, 'slice_instantiation_ids'): self.DeleteSliceInstantiations()
359 if hasattr(self, 'slice_attribute_type_ids'): self.DeleteSliceAttributeTypes()
360 if hasattr(self, 'slice_attribute_ids'): self.DeleteSliceAttributes()
361 if hasattr(self, 'key_type_ids'): self.DeleteKeyTypes()
362 if hasattr(self, 'key_ids'): self.DeleteKeys()
363 if hasattr(self, 'person_ids'): self.DeletePersons()
364 if hasattr(self, 'role_ids'): self.DeleteRoles()
365 if hasattr(self, 'pcu_type_ids'): self.DeletePCUTypes()
366 if hasattr(self, 'pcu_ids'): self.DeletePCUs()
367 if hasattr(self, 'pcu_protocol_type_ids'): self.DeleteProtocolTypes()
368 if hasattr(self, 'node_network_setting_ids'): self.DeleteNodeNetworkSettings()
369 if hasattr(self, 'address_ids'): self.DeleteAddresses()
370 if hasattr(self, 'attress_type_ids'): self.DeleteAddressTypes()
371 if hasattr(self, 'node_ids'): self.DeleteNodes()
372 if hasattr(self, 'site_ids'): self.DeleteSites()
375 def Sites(self, n=4):
379 site_fields = random_site()
380 AddSite = self.debug(shell.AddSite)
381 site_id = AddSite(site_fields)
382 if site_id is None: continue
384 # Should return a unique id
385 self.isunique(site_id, site_ids, 'AddSite - isunique')
386 site_ids.append(site_id)
387 GetSites = self.debug(shell.GetSites)
388 sites = GetSites([site_id])
389 if sites is None: continue
391 self.isequal(site, site_fields, 'AddSite - isequal')
394 site_fields = random_site()
395 UpdateSite = self.debug(shell.UpdateSite)
396 result = UpdateSite(site_id, site_fields)
399 sites = GetSites([site_id])
400 if sites is None: continue
402 self.isequal(site, site_fields, 'UpdateSite - isequal')
404 sites = GetSites(site_ids)
405 if sites is not None:
406 self.islistequal(site_ids, [site['site_id'] for site in sites], 'GetSites - isequal')
408 if self.config.verbose:
409 utils.header("Added sites: %s" % site_ids)
414 def DeleteSites(self):
416 DeleteSite = self.debug(shell.DeleteSite)
417 for site_id in self.site_ids:
418 result = DeleteSite(site_id)
420 # Check if sites are deleted
421 GetSites = self.debug(shell.GetSites)
422 sites = GetSites(self.site_ids)
423 self.islistequal(sites, [], 'DeleteSite - check')
425 if self.config.verbose:
426 utils.header("Deleted sites: %s" % self.site_ids)
430 def Nodes(self, n=4):
434 node_fields = random_node()
435 site_id = random.sample(self.site_ids, 1)[0]
436 AddNode = self.debug(shell.AddNode)
437 node_id = AddNode(site_id, node_fields)
438 if node_id is None: continue
440 # Should return a unique id
441 self.isunique(node_id, node_ids, 'AddNode - isunique')
442 node_ids.append(node_id)
445 GetNodes = self.debug(shell.GetNodes)
446 nodes = GetNodes([node_id])
447 if nodes is None: continue
449 self.isequal(node, node_fields, 'AddNode - isequal')
452 node_fields = random_node()
453 UpdateNode = self.debug(shell.UpdateNode)
454 result = UpdateNode(node_id, node_fields)
457 nodes = GetNodes([node_id])
458 if nodes is None: continue
460 self.isequal(node, node_fields, 'UpdateNode - isequal')
462 nodes = GetNodes(node_ids)
463 if nodes is not None:
464 self.islistequal(node_ids, [node['node_id'] for node in nodes], 'GetNodes - isequal')
466 if self.config.verbose:
467 utils.header("Added nodes: %s" % node_ids)
471 def DeleteNodes(self):
472 DeleteNode = self.debug(shell.DeleteNode)
473 for node_id in self.node_ids:
474 result = DeleteNode(node_id)
476 # Check if nodes are deleted
477 GetNodes = self.debug(shell.GetNodes)
478 nodes = GetNodes(self.node_ids)
479 self.islistequal(nodes, [], 'DeleteNode Check')
481 if self.config.verbose:
482 utils.header("Deleted nodes: %s" % self.node_ids)
486 def AddressTypes(self, n = 3):
487 address_type_ids = []
489 address_type_fields = random_address_type()
490 AddAddressType = self.debug(shell.AddAddressType)
491 address_type_id = AddAddressType(address_type_fields)
492 if address_type_id is None: continue
494 # Should return a unique address_type_id
495 self.isunique(address_type_id, address_type_ids, 'AddAddressType - isunique')
496 address_type_ids.append(address_type_id)
499 GetAddressTypes = self.debug(shell.GetAddressTypes)
500 address_types = GetAddressTypes([address_type_id])
501 if address_types is None: continue
502 address_type = address_types[0]
503 self.isequal(address_type, address_type_fields, 'AddAddressType - isequal')
505 # Update address type
506 address_type_fields = random_address_type()
507 UpdateAddressType = self.debug(shell.UpdateAddressType)
508 result = UpdateAddressType(address_type_id, address_type_fields)
509 if result is None: continue
511 # Check address type again
512 address_types = GetAddressTypes([address_type_id])
513 if address_types is None: continue
514 address_type = address_types[0]
515 self.isequal(address_type, address_type_fields, 'UpdateAddressType - isequal')
517 # Check get all address types
518 address_types = GetAddressTypes(address_type_ids)
519 if address_types is not None:
520 self.islistequal(address_type_ids, [address_type['address_type_id'] for address_type in address_types], 'GetAddressTypes - isequal')
522 if self.config.verbose:
523 utils.header("Added address types: %s " % address_type_ids)
525 return address_type_ids
527 def DeleteAddressTypes(self):
529 DeleteAddressType = self.debug(shell.DeleteAddressType)
530 for address_type_id in self.address_type_ids:
531 DeleteAddressType(ddress_type_id)
533 GetAddressTypes = self.debug(shell.GetAddressTypes)
534 address_types = GetAddressTypes(self.address_type_ids)
535 self.islistequal(address_types, [], 'DeleteAddressType - check')
537 if self.config.verbose:
538 utils.header("Deleted address types: " % self.address_type_ids)
540 self.address_type_ids = []
542 def Addresses(self, n = 3):
545 address_fields = random_address()
546 site_id = random.sample(self.site_ids, 1)[0]
547 AddSiteAddress = self.debug(shell.AddSiteAddress)
548 address_id = AddSiteAddress(site_id, address_fields)
549 if address_id is None: continue
551 # Should return a unique address_id
552 self.isunique(address_id, address_ids, 'AddSiteAddress - isunique')
553 address_ids.append(address_id)
556 GetAddresses = self.debug(shell.GetAddresses)
557 addresses = GetAddresses([address_id])
558 if addresses is None: continue
559 address = addresses[0]
560 self.isequal(address, address_fields, 'AddSiteAddress - isequal')
563 address_fields = random_address()
564 UpdateAddress = self.debug(shell.UpdateAddress)
565 result = UpdateAddress(address_id, address_fields)
568 addresses = GetAddresses([address_id])
569 if addresses is None: continue
570 address = addresses[0]
571 self.isequal(address, address_fields, 'UpdateAddress - isequal')
573 addresses = GetAddresses(address_ids)
574 if addresses is not None:
575 self.islistequal(address_ids, [ad['address_id'] for ad in addresses], 'GetAddresses - isequal')
577 if self.config.verbose:
578 utils.header("Added addresses: %s" % address_ids)
582 def DeleteAddresses(self):
584 DeleteAddress = self.debug(shell.DeleteAddress)
585 # Delete site addresses
586 for address_id in self.address_ids:
587 result = DeleteAddress(address_id)
590 GetAddresses = self.debug(shell.GetAddresses)
591 addresses = GetAddresses(self.address_ids)
592 self.islistequal(addresses, [], 'DeleteAddress - check')
593 if self.config.verbose:
594 utils.header("Deleted addresses: %s" % self.address_ids)
596 self.address_ids = []
598 def Slices(self, n = 3):
602 slice_fields = random_slice()
603 AddSlice = self.debug(shell.Slice)
604 slice_id = AddSlice(slice_fields)
605 if slice_id is None: continue
607 # Should return a unique id
608 self.isunique(slice_id, slice_ids, 'AddSlicel - isunique')
609 slice_ids.append(slice_id)
610 GetSlices = self.debug(shell.GetSlices)
611 slices = GetSlices([slice_id])
612 if slices is None: continue
614 self.isequal(slice, slice_fields, 'AddSlice - isequal')
617 slice_fields = random_slice()
618 UpdateSite = self.debug(shell.UpdateSlice)
619 result = UpdateSlice(slice_id, slice_fields)
622 slices = GetSites([slice_id])
623 if slices is None: continue
625 self.isequal(slice, slice_fields, 'UpdateSlice - isequal')
631 slices = GetSites(slice_ids)
632 if slices is not None:
633 self.islistequal(slice_ids, [slice['slice_id'] for slice in slices], 'GetSlices - isequal')
635 if self.config.verbose:
636 utils.header("Added slices: %s" % slice_ids)
640 def DeleteSlices(self):
642 # XX manually delete attributes for first slice
643 slices = GetSlices(self.slice_ids, ['slice_attribute_ids', 'node_ids'])
646 DeleteSlice = self.debug(shell.DeleteSlice)
647 # Have DeleteSlice automatically delete attriubtes for the rest
648 for slice_id in self.slice_ids:
650 DeleteSlice(slice_id)
652 # Check if persons are deleted
653 GetSlices = self.debug(shell.GetSlices)
654 slices = GetSlices(self.slice_ids)
655 self.islistequal(slices, [], 'DeleteSlice - check')
658 utils.header("Deleted slices: %s" % self.slice_ids)
663 def Persons(self, n = 3):
669 person_fields = random_person()
670 AddPerson = self.debug(shell.AddPerson)
671 person_id = AddPerson(person_fields)
672 if person_id is None: continue
674 # Should return a unique person_id
675 self.isunique(person_id, person_ids, 'AddPerson - isunique')
676 person_ids.append(person_id)
677 GetPersons = self.debug(shell.GetPersons)
678 persons = GetPersons([person_id])
679 if persons is None: continue
681 self.isequal(person, person_fields, 'AddPerson - isequal')
684 person_fields = random_person()
685 person_fields['enabled'] = True
686 UpdatePerson = self.debug(shell.UpdatePerson)
687 result = UpdatePerson(person_id, person_fields)
690 AddRoleToPerson = self.debug(shell.AddRoleToPerson)
691 role = random.sample(roles, 1)[0]
692 result = AddRoleToPerson(role, person_id)
696 key_id = AddPersonKey = self.debug(shell.AddPersonKey)
697 AddPersonKey(person_id, key)
700 site_id = random.sample(self.site_ids, 1)[0]
701 AddPersonToSite = self.debug(shell.AddPersonToSite)
702 AddPersonToSite(person_id, site_id)
704 # Add person to slice
705 slice_id = random.sample(self.slice_ids, 1)[0]
706 AddPersonToSlice = self.debug(self.AddPersonToSlice)
707 AddPersonToSlice(person_id, slice_id)
709 # check role, key, site, slice
710 persons = GetPersons([person_id], ['roles', 'key_ids', 'site_ids', 'slice_ids'])
711 if persons is None or not persons: continue
713 self.islistequal([role], person['roles'], 'AddRoleToPerson - check')
714 self.islistequal([key_id], person['key_ids'], 'AddPersonKey - check')
715 self.islistequal([site_id], person['site_ids'], 'AddPersonToSite - check')
716 self.islistequal([slice_id], person['slice_ids'], 'AddPersonToSlice - check')
718 persons = GetPersons(person_ids)
719 if persons is not None:
720 self.islistequal(person_ids, [p['person_id'] for p in persons], 'GetPersons - isequal')
723 utils.header("Added users: %s" % self.person_ids)
725 def DeletePersons(self):
727 # Delete attributes manually for first person
728 persons = GetPersons(self.person_ids, ['person_id' , 'key_ids', 'site_ids', 'slice_ids'])
729 if persons is None or not persons: return 0
733 DeleteRoleFromPerson = self.debug(shell.DeleteRoleFromPerson)
734 DeleteRoleFromPerson(person['role'], person['person_id'])
737 DeleteKey = self.debug(shell.DeleteKey)
738 DeleteKey(person['key_id'])
740 # Remove person from site
741 DeletePersonFromSite = self.debug(shell.DeletePersonFromSite)
742 DeletePersonFromSite(person['person_id'], person['site_id'])
744 # Remove person from slice
745 DeletePersonFromSlice = self.debug(shell.DeletePersonFromSlice)
746 DeletePersonFromSlice(person['person_id'], person['slice_id'])
748 # check role, key, site, slice
749 persons = GetPersons([person['person_id']], ['roles', 'key_ids', 'site_ids', 'slice_ids'])
750 if persons is None or not persons: return 0
752 self.islistequal([], person['roles'], 'DeleteRoleFromPerson - check')
753 self.islistequal([], person['key_ids'], 'DeleteKey - check')
754 self.islistequal([], person['site_ids'], 'DeletePersonFromSite - check')
755 self.islistequal([], person['slice_ids'], 'DeletePersonFromSlice - check')
757 DeletePerson = self.debug(shell.DeletePerson)
758 # Have DeletePeson automatically delete attriubtes for all other persons
759 for person_id in self.person_ids:
761 DeletePerson(person_id)
763 # Check if persons are deleted
764 GetPersons = self.debug(shell.GetPersons)
765 persons = GetPersons(self.person_ids)
766 self.islistequal(persons, [], 'DeletePerson - check')
769 utils.header("Deleted users: %s" % self.person_ids)
774 if __name__ == '__main__':
775 args = tuple(sys.argv[1:])
776 api_unit_test()(*args)