3 # Test script utility class
5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2006 The Trustees of Princeton University
11 from pprint import pprint
12 from string import letters, digits, punctuation, whitespace
13 from traceback import print_exc
14 from optparse import OptionParser
21 from PLC.Shell import Shell
23 from random import Random
26 # note about namelengths
27 # original version uses full lengths for all fields for testing overflows and things
28 # however for a realistic test, involving a web UI, this is not appropriate, so we
29 # use smaller identifiers
31 def randfloat(min = 0.0, max = 1.0):
32 return float(min) + (random.random() * (float(max) - float(min)))
34 def randint(min = 0, max = 1):
35 return int(randfloat(min, max + 1))
37 # See "2.2 Characters" in the XML specification:
39 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
41 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
44 ascii_xml_chars = map(unichr, [0x9, 0xA])
45 # xmlrpclib uses xml.parsers.expat, which always converts either '\r'
46 # (#xD) or '\n' (#xA) to '\n'. So avoid using '\r', too, if this is
48 if xmlrpclib.loads(xmlrpclib.dumps(('\r',)))[0][0] == '\r':
49 ascii_xml_chars.append('\r')
50 ascii_xml_chars += map(unichr, xrange(0x20, 0x7F - 1))
51 low_xml_chars = list(ascii_xml_chars)
52 low_xml_chars += map(unichr, xrange(0x84 + 1, 0x86 - 1))
53 low_xml_chars += map(unichr, xrange(0x9F + 1, 0xFF))
54 valid_xml_chars = list(low_xml_chars)
55 valid_xml_chars += map(unichr, xrange(0xFF + 1, 0xD7FF))
56 valid_xml_chars += map(unichr, xrange(0xE000, 0xFDD0 - 1))
57 valid_xml_chars += map(unichr, xrange(0xFDDF + 1, 0xFFFD))
59 def randstr(length, pool = valid_xml_chars, encoding = "utf-8"):
60 sample = random.sample(pool, min(length, len(pool)))
63 bytes = len(s.encode(encoding))
67 sample += random.sample(pool, min(length - bytes, len(pool)))
68 random.shuffle(sample)
73 def randhostname(namelengths):
74 # 1. Each part begins and ends with a letter or number.
75 # 2. Each part except the last can contain letters, numbers, or hyphens.
76 # 3. Each part is between 1 and 64 characters, including the trailing dot.
77 # 4. At least two parts.
78 # 5. Last part can only contain between 2 and 6 letters.
79 hostname = 'a' + randstr(namelengths['hostname1'], letters + digits + '-') + '1.' + \
80 'b' + randstr(namelengths['hostname1'], letters + digits + '-') + '2.' + \
81 'c' + randstr(namelengths['hostname2'], letters)
86 for i in range(randint(1, 10)):
87 parts.append(randstr(randint(1, 30), ascii_xml_chars))
88 return u'/'.join(parts)[0:length]
90 def randemail(namelengths):
91 return (randstr(namelengths['email'], letters + digits) + "@" + randhostname(namelengths)).lower()
93 def randkey(namelengths,bits = 2048):
94 ssh_key_types = ["ssh-dss", "ssh-rsa"]
95 key_type = random.sample(ssh_key_types, 1)[0]
96 return ' '.join([key_type,
97 base64.b64encode(''.join(randstr(bits / 8).encode("utf-8"))),
98 randemail(namelengths)])
100 def random_site(namelengths):
102 sitename=randstr(namelengths['sitename'],namelengths['sitename_contents'])
104 sitename=randstr(namelengths['sitename'])
106 abbreviated_name=randstr(namelengths['abbreviated_name'],namelengths['abbreviated_name_contents'])
108 abbreviated_name=randstr(namelengths['abbreviated_name'])
112 'abbreviated_name': abbreviated_name,
113 'login_base': randstr(namelengths['login_base'], letters).lower(),
114 'latitude': int(randfloat(-90.0, 90.0) * 1000) / 1000.0,
115 'longitude': int(randfloat(-180.0, 180.0) * 1000) / 1000.0,
118 def random_address_type():
121 'description': randstr(254),
124 def random_address():
126 'line1': randstr(254),
127 'line2': randstr(254),
128 'line3': randstr(254),
129 'city': randstr(254),
130 'state': randstr(254),
131 'postalcode': randstr(64),
132 'country': randstr(128),
135 def random_person(namelengths):
137 'first_name': randstr(namelengths['first_name']),
138 'last_name': randstr(namelengths['last_name']),
139 'email': randemail(namelengths),
141 # Accounts are disabled by default
143 'password': randstr(254),
146 def random_key(key_types,namelengths):
148 'key_type': random.sample(key_types, 1)[0],
149 'key': randkey(namelengths)
152 def random_tag_type (role_ids):
153 return {'tagname': randstr(12),
154 'category':randstr(8),
155 'min_role_id': random.sample(role_ids, 1)[0],
156 'description' : randstr(128),
159 def random_nodegroup():
160 return {'groupname' : randstr(50) }
163 def random_node(node_types,boot_states,namelengths):
165 'hostname': randhostname(namelengths),
166 'node_type': random.sample(node_types,1)[0],
167 'boot_state': random.sample(boot_states, 1)[0],
168 'model': randstr(namelengths['model']),
169 'version': randstr(64),
170 # for testing node tags
174 def random_interface(method, type):
178 'bwlimit': randint(500000, 10000000),
182 ip = randint(0, 0xffffffff)
183 netmask = (0xffffffff << randint(2, 31)) & 0xffffffff
184 network = ip & netmask
185 broadcast = ((ip & netmask) | ~netmask) & 0xffffffff
186 gateway = randint(network + 1, broadcast - 1)
187 dns1 = randint(0, 0xffffffff)
189 for field in 'ip', 'netmask', 'network', 'broadcast', 'gateway', 'dns1':
190 interface_fields[field] = socket.inet_ntoa(struct.pack('>L', locals()[field]))
192 return interface_fields
197 def random_pcu(namelengths):
199 'hostname': randhostname(namelengths),
200 'ip': socket.inet_ntoa(struct.pack('>L', randint(0, 0xffffffff))),
201 'protocol': randstr(16),
202 'username': randstr(254),
203 'password': randstr(254),
204 'notes': randstr(254),
205 'model': randstr(32),
208 def random_conf_file():
210 'enabled': bool(randint()),
211 'source': randpath(255),
212 'dest': randpath(255),
213 'file_permissions': "%#o" % randint(0, 512),
214 'file_owner': randstr(32, letters + '_' + digits),
215 'file_group': randstr(32, letters + '_' + digits),
216 'preinstall_cmd': randpath(100),
217 'postinstall_cmd': randpath(100),
218 'error_cmd': randpath(100),
219 'ignore_cmd_errors': bool(randint()),
220 'always_update': bool(randint()),
223 def random_slice(login_base,namelengths):
225 'name': login_base + "_" + randstr(11, letters).lower(),
226 'url': "http://" + randhostname(namelengths) + "/",
227 'description': randstr(2048),
234 'addresses_per_site': 1,
235 'persons_per_site': 1,
236 'keys_per_person': 1,
240 'interfaces_per_node': 1,
244 'slices_per_site': 1,
245 'attributes_per_slice': 1,
251 'addresses_per_site': 2,
252 'persons_per_site': 4,
253 'keys_per_person': 2,
257 'interfaces_per_node': 1,
261 'slices_per_site': 4,
262 'attributes_per_slice': 2,
265 namelengths_default = {
270 'abbreviated_name':50,
277 namelengths_short = {
282 'sitename_contents':letters+digits+whitespace+punctuation,
283 'abbreviated_name':24,
284 'abbreviated_name_contents':letters+digits+whitespace+punctuation,
291 def __init__(self, api, check = True, verbose = True, preserve = False):
294 self.verbose = verbose
295 self.preserve = preserve
298 self.address_type_ids = []
299 self.address_ids = []
302 self.slice_type_ids = []
303 self.nodegroup_type_ids = []
304 self.ilink_type_ids = []
305 self.nodegroup_ids = []
307 self.interface_ids = []
310 self.conf_file_ids = []
312 self.slice_tag_ids = []
314 def Cardinals (self):
315 return [len(x) for x in (
316 self.api.GetNodes({},['node_id']),
317 self.api.GetSites({},['site_id']),
318 self.api.GetPersons({},['person_id']),
319 self.api.GetSlices({},['slice_id']),
322 def Run(self, **kwds):
324 Run a complete database and API consistency test. Populates
325 the database with a set of random entities, updates them, then
326 deletes them. Examples:
328 test.Run() # Defaults
329 test.Run(**Test.sizes_default) # Defaults
330 test.Run(**Test.sizes_tiny) # Tiny set
331 test.Run(sites = 123, slices_per_site = 4) # Defaults with overrides
334 cardinals_before=self.Cardinals()
335 print 'Cardinals before test (n,s,p,sl)',cardinals_before
340 print 'Preserving - delete skipped'
344 cardinals_after=self.Cardinals()
345 print 'Cardinals after test (n,s,p,sl)',cardinals_after
347 if cardinals_before != cardinals_after:
348 raise Exception, 'cardinals before and after differ - check deletion mechanisms'
350 def Add(self, **kwds):
352 Populate the database with a set of random entities. Examples:
357 sizes = self.sizes_default.copy()
360 self.AddSites(sizes['sites'])
361 self.AddAddressTypes(sizes['address_types'])
362 self.AddAddresses(sizes['addresses_per_site'])
363 self.AddPersons(sizes['persons_per_site'])
364 self.AddKeys(sizes['keys_per_person'])
365 self.AddTagTypes(sizes['slice_tags'],sizes['nodegroups'],sizes['ilinks'])
366 self.AddNodeGroups(sizes['nodegroups'])
367 self.AddNodes(sizes['nodes_per_site'])
368 self.AddInterfaces(sizes['interfaces_per_node'])
369 self.AddIlinks (sizes['ilinks'])
370 self.AddPCUs(sizes['pcus_per_site'])
371 self.AddConfFiles(sizes['conf_files'])
372 self.AddSlices(sizes['slices_per_site'])
373 self.AddSliceTags(sizes['attributes_per_slice'])
377 self.UpdateAddressTypes()
378 self.UpdateAddresses()
381 self.UpdateTagTypes()
382 self.UpdateNodeGroups()
384 self.UpdateInterfaces()
387 self.UpdateConfFiles()
389 self.UpdateSliceTags()
392 self.DeleteSliceTags()
395 self.DeleteConfFiles()
398 self.DeleteInterfaces()
401 self.DeleteNodeGroups()
402 self.DeleteTagTypes()
403 self.DeleteAddresses()
404 self.DeleteAddressTypes()
407 def AddSites(self, n = 10):
409 Add a number of random sites.
414 site_fields = random_site(self.namelengths)
415 site_id = self.api.AddSite(site_fields)
417 # Should return a unique site_id
418 assert site_id not in self.site_ids
419 self.site_ids.append(site_id)
421 # Enable slice creation
422 site_fields['max_slices'] = randint(1, 10)
423 self.api.UpdateSite(site_id, site_fields)
427 site = self.api.GetSites([site_id])[0]
428 for field in site_fields:
429 assert site[field] == site_fields[field]
432 print "Added site", site_id
434 def UpdateSites(self):
436 Make random changes to any sites we may have added.
439 for site_id in self.site_ids:
441 site_fields = random_site(self.namelengths)
442 # Do not change login_base
443 if 'login_base' in site_fields:
444 del site_fields['login_base']
445 self.api.UpdateSite(site_id, site_fields)
449 site = self.api.GetSites([site_id])[0]
450 for field in site_fields:
451 assert site[field] == site_fields[field]
454 print "Updated site", site_id
456 def DeleteSites(self):
458 Delete any random sites we may have added.
461 for site_id in self.site_ids:
462 self.api.DeleteSite(site_id)
465 assert not self.api.GetSites([site_id])
468 print "Deleted site", site_id
471 assert not self.api.GetSites(self.site_ids)
475 def AddAddressTypes(self, n = 2):
477 Add a number of random address types.
481 address_type_fields = random_address_type()
482 address_type_id = self.api.AddAddressType(address_type_fields)
484 # Should return a unique address_type_id
485 assert address_type_id not in self.address_type_ids
486 self.address_type_ids.append(address_type_id)
490 address_type = self.api.GetAddressTypes([address_type_id])[0]
491 for field in address_type_fields:
492 assert address_type[field] == address_type_fields[field]
495 print "Added address type", address_type_id
497 def UpdateAddressTypes(self):
499 Make random changes to any address types we may have added.
502 for address_type_id in self.address_type_ids:
503 # Update address_type
504 address_type_fields = random_address_type()
505 self.api.UpdateAddressType(address_type_id, address_type_fields)
509 address_type = self.api.GetAddressTypes([address_type_id])[0]
510 for field in address_type_fields:
511 assert address_type[field] == address_type_fields[field]
514 print "Updated address_type", address_type_id
516 def DeleteAddressTypes(self):
518 Delete any random address types we may have added.
521 for address_type_id in self.address_type_ids:
522 self.api.DeleteAddressType(address_type_id)
525 assert not self.api.GetAddressTypes([address_type_id])
528 print "Deleted address type", address_type_id
531 assert not self.api.GetAddressTypes(self.address_type_ids)
533 self.address_type_ids = []
535 def AddAddresses(self, per_site = 2):
537 Add a number of random addresses to each site.
540 for site_id in self.site_ids:
541 for i in range(per_site):
542 address_fields = random_address()
543 address_id = self.api.AddSiteAddress(site_id, address_fields)
545 # Should return a unique address_id
546 assert address_id not in self.address_ids
547 self.address_ids.append(address_id)
549 # Add random address type
550 if self.address_type_ids:
551 for address_type_id in random.sample(self.address_type_ids, 1):
552 self.api.AddAddressTypeToAddress(address_type_id, address_id)
556 address = self.api.GetAddresses([address_id])[0]
557 for field in address_fields:
558 assert address[field] == address_fields[field]
561 print "Added address", address_id, "to site", site_id
563 def UpdateAddresses(self):
565 Make random changes to any addresses we may have added.
568 for address_id in self.address_ids:
570 address_fields = random_address()
571 self.api.UpdateAddress(address_id, address_fields)
575 address = self.api.GetAddresses([address_id])[0]
576 for field in address_fields:
577 assert address[field] == address_fields[field]
580 print "Updated address", address_id
582 def DeleteAddresses(self):
584 Delete any random addresses we may have added.
587 for address_id in self.address_ids:
588 # Remove address types
589 address = self.api.GetAddresses([address_id])[0]
590 for address_type_id in address['address_type_ids']:
591 self.api.DeleteAddressTypeFromAddress(address_type_id, address_id)
594 address = self.api.GetAddresses([address_id])[0]
595 assert not address['address_type_ids']
597 self.api.DeleteAddress(address_id)
600 assert not self.api.GetAddresses([address_id])
603 print "Deleted address", address_id
606 assert not self.api.GetAddresses(self.address_ids)
608 self.address_ids = []
610 def AddPersons(self, per_site = 10):
612 Add a number of random users to each site.
615 for site_id in self.site_ids:
616 for i in range(per_site):
618 person_fields = random_person(self.namelengths)
619 person_id = self.api.AddPerson(person_fields)
621 # Should return a unique person_id
622 assert person_id not in self.person_ids
623 self.person_ids.append(person_id)
627 person = self.api.GetPersons([person_id])[0]
628 for field in person_fields:
629 if field != 'password':
630 assert person[field] == person_fields[field]
632 auth = {'AuthMethod': "password",
633 'Username': person_fields['email'],
634 'AuthString': person_fields['password']}
637 # Check that user is disabled
639 assert not self.api.AuthCheck(auth)
643 # Add random set of roles
644 role_ids = random.sample([20, 30, 40], randint(1, 3))
645 for role_id in role_ids:
646 self.api.AddRoleToPerson(role_id, person_id)
649 person = self.api.GetPersons([person_id])[0]
650 assert set(role_ids) == set(person['role_ids'])
653 self.api.UpdatePerson(person_id, {'enabled': True})
656 # Check that user is enabled
657 assert self.api.AuthCheck(auth)
659 # Associate user with site
660 self.api.AddPersonToSite(person_id, site_id)
661 self.api.SetPersonPrimarySite(person_id, site_id)
664 person = self.api.GetPersons([person_id])[0]
665 assert person['site_ids'][0] == site_id
668 print "Added user", person_id, "to site", site_id
670 def UpdatePersons(self):
672 Make random changes to any users we may have added.
675 for person_id in self.person_ids:
677 person_fields = random_person(self.namelengths)
679 person_fields['enabled'] = True
680 self.api.UpdatePerson(person_id, person_fields)
684 person = self.api.GetPersons([person_id])[0]
685 for field in person_fields:
686 if field != 'password':
687 assert person[field] == person_fields[field]
690 print "Updated person", person_id
692 person = self.api.GetPersons([person_id])[0]
694 # Associate user with a random set of sites
695 site_ids = random.sample(self.site_ids, randint(0, len(self.site_ids)))
696 for site_id in (set(site_ids) - set(person['site_ids'])):
697 self.api.AddPersonToSite(person_id, site_id)
698 for site_id in (set(person['site_ids']) - set(site_ids)):
699 self.api.DeletePersonFromSite(person_id, site_id)
702 self.api.SetPersonPrimarySite(person_id, site_ids[0])
705 person = self.api.GetPersons([person_id])[0]
706 assert set(site_ids) == set(person['site_ids'])
709 print "Updated person", person_id, "to sites", site_ids
711 def DeletePersons(self):
713 Delete any random users we may have added.
716 for person_id in self.person_ids:
718 person = self.api.GetPersons([person_id])[0]
719 for site_id in person['site_ids']:
720 self.api.DeletePersonFromSite(person_id, site_id)
723 person = self.api.GetPersons([person_id])[0]
724 assert not person['site_ids']
727 for role_id in person['role_ids']:
728 self.api.DeleteRoleFromPerson(role_id, person_id)
731 person = self.api.GetPersons([person_id])[0]
732 assert not person['role_ids']
735 self.api.UpdatePerson(person_id, {'enabled': False})
738 person = self.api.GetPersons([person_id])[0]
739 assert not person['enabled']
742 self.api.DeletePerson(person_id)
745 assert not self.api.GetPersons([person_id])
748 print "Deleted user", person_id
751 assert not self.api.GetPersons(self.person_ids)
755 def AddKeys(self, per_person = 2):
757 Add a number of random keys to each user.
760 key_types = self.api.GetKeyTypes()
762 raise Exception, "No key types"
764 for person_id in self.person_ids:
765 for i in range(per_person):
767 key_fields = random_key(key_types,self.namelengths)
768 key_id = self.api.AddPersonKey(person_id, key_fields)
770 # Should return a unique key_id
771 assert key_id not in self.key_ids
772 self.key_ids.append(key_id)
776 key = self.api.GetKeys([key_id])[0]
777 for field in key_fields:
778 assert key[field] == key_fields[field]
780 # Add and immediately blacklist a key
781 key_fields = random_key(key_types,self.namelengths)
782 key_id = self.api.AddPersonKey(person_id, key_fields)
784 self.api.BlacklistKey(key_id)
786 # Is effectively deleted
787 assert not self.api.GetKeys([key_id])
789 # Cannot be added again
791 key_id = self.api.AddPersonKey(person_id, key_fields)
797 print "Added key", key_id, "to user", person_id
799 def UpdateKeys(self):
801 Make random changes to any keys we may have added.
804 key_types = self.api.GetKeyTypes()
806 raise Exception, "No key types"
808 for key_id in self.key_ids:
810 key_fields = random_key(key_types,self.namelengths)
811 self.api.UpdateKey(key_id, key_fields)
815 key = self.api.GetKeys([key_id])[0]
816 for field in key_fields:
817 assert key[field] == key_fields[field]
820 print "Updated key", key_id
822 def DeleteKeys(self):
824 Delete any random keys we may have added.
827 for key_id in self.key_ids:
828 self.api.DeleteKey(key_id)
831 assert not self.api.GetKeys([key_id])
834 print "Deleted key", key_id
837 assert not self.api.GetKeys(self.key_ids)
841 def AddNodeGroups(self, n = 10):
843 Add a number of random node groups.
848 tag_type_id = self.nodegroup_type_ids[i]
849 tagname=self.api.GetTagTypes([tag_type_id])[0]['tagname']
852 groupname = random_nodegroup() ['groupname']
854 nodegroup_id = self.api.AddNodeGroup(groupname, tagname, value)
856 # Should return a unique nodegroup_id
857 assert nodegroup_id not in self.nodegroup_ids
858 self.nodegroup_ids.append(nodegroup_id)
862 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
863 assert nodegroup['groupname'] == groupname
864 assert nodegroup['tagname'] == tagname
865 assert nodegroup['value'] == value
868 print "Added node group", nodegroup_id
870 def UpdateNodeGroups(self):
872 Make random changes to any node groups we may have added.
875 for nodegroup_id in self.nodegroup_ids:
877 groupname = random_nodegroup()['groupname']
878 # cannot change tagname
879 nodegroup_fields = { 'groupname':groupname }
880 self.api.UpdateNodeGroup(nodegroup_id, nodegroup_fields)
884 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
885 for field in nodegroup_fields:
886 assert nodegroup[field] == nodegroup_fields[field]
889 print "Updated node group", nodegroup_id
891 def DeleteNodeGroups(self):
893 Delete any random node groups we may have added.
896 for nodegroup_id in self.nodegroup_ids:
897 self.api.DeleteNodeGroup(nodegroup_id)
900 assert not self.api.GetNodeGroups([nodegroup_id])
903 print "Deleted node group", nodegroup_id
906 assert not self.api.GetNodeGroups(self.nodegroup_ids)
908 self.nodegroup_ids = []
910 def AddNodes(self, per_site = 2):
912 Add a number of random nodes to each site. Each node will also
913 be added to a random node group if AddNodeGroups() was
917 node_types = self.api.GetNodeTypes()
919 raise Exception, "No node types"
920 boot_states = self.api.GetBootStates()
922 raise Exception, "No boot states"
924 for site_id in self.site_ids:
925 for i in range(per_site):
927 node_fields = random_node(node_types,boot_states,self.namelengths)
928 node_id = self.api.AddNode(site_id, node_fields)
930 # Should return a unique node_id
931 assert node_id not in self.node_ids
932 self.node_ids.append(node_id)
934 # Add to a random set of node groups
935 nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
936 for nodegroup_id in nodegroup_ids:
937 tagname = self.api.GetNodeGroups([nodegroup_id])[0]['tagname']
938 self.api.AddNodeTag( node_id, tagname, 'yes' )
942 node = self.api.GetNodes([node_id])[0]
943 for field in node_fields:
944 if field not in tag_fields:
945 assert node[field] == node_fields[field]
948 print "Added node", node_id
950 def UpdateNodes(self):
952 Make random changes to any nodes we may have added.
955 node_types = self.api.GetNodeTypes()
957 raise Exception, "No node types"
958 boot_states = self.api.GetBootStates()
960 raise Exception, "No boot states"
962 for node_id in self.node_ids:
964 node_fields = random_node(node_types,boot_states,self.namelengths)
965 self.api.UpdateNode(node_id, node_fields)
967 node = self.api.GetNodes([node_id])[0]
969 # Add to a random set of node groups
970 nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
971 for nodegroup_id in (set(nodegroup_ids) - set(node['nodegroup_ids'])):
972 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
973 tagname = nodegroup['tagname']
974 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
976 self.api.AddNodeTag(node_id,tagname,'yes')
978 node_tag=node_tags[0]
979 self.api.UpdateNodeTag(node_tag['node_tag_id'],'yes')
980 for nodegroup_id in (set(node['nodegroup_ids']) - set(nodegroup_ids)):
981 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
982 tagname = nodegroup['tagname']
983 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
985 self.api.AddNodeTag(node_id,tagname,'no')
987 node_tag=node_tags[0]
988 self.api.UpdateNodeTag(node_tag['node_tag_id'],'no')
992 node = self.api.GetNodes([node_id])[0]
993 for field in node_fields:
994 if field not in tag_fields:
995 if node[field] != node_fields[field]:
996 raise Exception, "Unexpected field %s in node after GetNodes()"%field
997 assert set(nodegroup_ids) == set(node['nodegroup_ids'])
999 print 'WARNING: skipping updatenode with tags as this is not implemented yet'
1000 # again when fetching 'arch' explicitly
1001 node2 = self.api.GetNodes([node_id],node_fields.keys())[0]
1002 for field in node_fields:
1003 if node2[field] != node_fields[field]:
1004 raise Exception, "Unexpected field %s in node after GetNodes(tags)"%field
1007 print "Updated node", node_id
1009 def DeleteNodes(self):
1011 Delete any random nodes we may have added.
1014 for node_id in self.node_ids:
1015 # Remove from node groups
1016 node = self.api.GetNodes([node_id])[0]
1017 for node_tag in GetNodeTags ( {'node_id': node_id} ):
1018 self.api.UpdateNodeTag(node_tag['node_tag_id'],'')
1021 node = self.api.GetNodes([node_id])[0]
1022 assert not node['nodegroup_ids']
1024 self.api.DeleteNode(node_id)
1027 assert not self.api.GetNodes([node_id])
1030 print "Deleted node", node_id
1033 assert not self.api.GetNodes(self.node_ids)
1037 def AddInterfaces(self, per_node = 1):
1039 Add a number of random network interfaces to each node.
1042 network_methods = self.api.GetNetworkMethods()
1043 if not network_methods:
1044 raise Exception, "No network methods"
1046 network_types = self.api.GetNetworkTypes()
1047 if not network_types:
1048 raise Exception, "No network types"
1050 for node_id in self.node_ids:
1051 for i in range(per_node):
1052 method = random.sample(network_methods, 1)[0]
1053 type = random.sample(network_types, 1)[0]
1056 interface_fields = random_interface(method, type)
1057 interface_id = self.api.AddInterface(node_id, interface_fields)
1059 # Should return a unique interface_id
1060 assert interface_id not in self.interface_ids
1061 self.interface_ids.append(interface_id)
1065 interface = self.api.GetInterfaces([interface_id])[0]
1066 for field in interface_fields:
1067 assert interface[field] == interface_fields[field]
1070 print "Added interface", interface_id, "to node", node_id
1072 def UpdateInterfaces(self):
1074 Make random changes to any network interfaces we may have added.
1077 network_methods = self.api.GetNetworkMethods()
1078 if not network_methods:
1079 raise Exception, "No network methods"
1081 network_types = self.api.GetNetworkTypes()
1082 if not network_types:
1083 raise Exception, "No network types"
1085 for interface_id in self.interface_ids:
1086 method = random.sample(network_methods, 1)[0]
1087 type = random.sample(network_types, 1)[0]
1090 interface_fields = random_interface(method, type)
1091 self.api.UpdateInterface(interface_id, interface_fields)
1095 interface = self.api.GetInterfaces([interface_id])[0]
1096 for field in interface_fields:
1097 assert interface[field] == interface_fields[field]
1100 print "Updated interface", interface_id
1102 def DeleteInterfaces(self):
1104 Delete any random network interfaces we may have added.
1107 for interface_id in self.interface_ids:
1108 self.api.DeleteInterface(interface_id)
1111 assert not self.api.GetInterfaces([interface_id])
1114 print "Deleted interface", interface_id
1117 assert not self.api.GetInterfaces(self.interface_ids)
1119 self.interface_ids = []
1121 def AddIlinks (self, n):
1123 Add random links between interfaces.
1127 src = random.sample(self.interface_ids,1)[0]
1128 dst = random.sample(self.interface_ids,1)[0]
1129 ilink_id = self.api.AddIlink (src,dst,
1130 self.ilink_type_ids[i],
1133 assert ilink_id not in self.ilink_ids
1134 self.ilink_ids.append(ilink_id)
1137 print 'Added Ilink',ilink_id,' - attached interface',src,'to',dst
1140 retrieve=GetIlinks({'src_interface_id':src,'dst_interface_id':dst,
1141 'tag_type_id':self.ilink_type_ids[i]})
1142 assert ilink_id==retrieve[0]['ilink_id']
1145 def UpdateIlinks (self):
1147 for ilink_id in self.ilink_ids:
1148 new_value=random_ilink()
1149 self.api.UpdateIlink(ilink_id,new_value)
1152 ilink=self.api.GetIlinks([ilink_id])[0]
1153 assert ilink['value'] == new_value
1156 print 'Updated Ilink',ilink_id
1158 def DeleteIlinks (self):
1159 for ilink_id in self.ilink_ids:
1160 self.api.DeleteIlink(ilink_id)
1163 assert not self.api.GetIlinks({'ilink_id':ilink_id})
1166 print 'Deleted Ilink',ilink_id
1169 assert not self.api.GetIlinks(self.ilink_ids)
1174 def AddPCUs(self, per_site = 1):
1176 Add a number of random PCUs to each site. Each node at the
1177 site will be added to a port on the PCU if AddNodes() was
1181 for site_id in self.site_ids:
1182 for i in range(per_site):
1184 pcu_fields = random_pcu(self.namelengths)
1185 pcu_id = self.api.AddPCU(site_id, pcu_fields)
1187 # Should return a unique pcu_id
1188 assert pcu_id not in self.pcu_ids
1189 self.pcu_ids.append(pcu_id)
1191 # Add each node at this site to a different port on this PCU
1192 site = self.api.GetSites([site_id])[0]
1193 port = randint(1, 10)
1194 for node_id in site['node_ids']:
1195 self.api.AddNodeToPCU(node_id, pcu_id, port)
1200 pcu = self.api.GetPCUs([pcu_id])[0]
1201 for field in pcu_fields:
1202 assert pcu[field] == pcu_fields[field]
1205 print "Added PCU", pcu_id, "to site", site_id
1207 def UpdatePCUs(self):
1209 Make random changes to any PCUs we may have added.
1212 for pcu_id in self.pcu_ids:
1214 pcu_fields = random_pcu(self.namelengths)
1215 self.api.UpdatePCU(pcu_id, pcu_fields)
1219 pcu = self.api.GetPCUs([pcu_id])[0]
1220 for field in pcu_fields:
1221 assert pcu[field] == pcu_fields[field]
1224 print "Updated PCU", pcu_id
1226 def DeletePCUs(self):
1228 Delete any random nodes we may have added.
1231 for pcu_id in self.pcu_ids:
1232 # Remove nodes from PCU
1233 pcu = self.api.GetPCUs([pcu_id])[0]
1234 for node_id in pcu['node_ids']:
1235 self.api.DeleteNodeFromPCU(node_id, pcu_id)
1238 pcu = self.api.GetPCUs([pcu_id])[0]
1239 assert not pcu['node_ids']
1241 self.api.DeletePCU(pcu_id)
1244 assert not self.api.GetPCUs([pcu_id])
1247 print "Deleted PCU", pcu_id
1250 assert not self.api.GetPCUs(self.pcu_ids)
1254 def AddConfFiles(self, n = 10):
1256 Add a number of random global configuration files.
1262 # Add a random configuration file
1263 conf_files.append(random_conf_file())
1266 # Add a nodegroup override file
1267 nodegroup_conf_file = conf_files[0].copy()
1268 nodegroup_conf_file['source'] = randpath(255)
1269 conf_files.append(nodegroup_conf_file)
1271 # Add a node override file
1272 node_conf_file = conf_files[0].copy()
1273 node_conf_file['source'] = randpath(255)
1274 conf_files.append(node_conf_file)
1276 for conf_file_fields in conf_files:
1277 conf_file_id = self.api.AddConfFile(conf_file_fields)
1279 # Should return a unique conf_file_id
1280 assert conf_file_id not in self.conf_file_ids
1281 self.conf_file_ids.append(conf_file_id)
1284 if conf_file_fields == nodegroup_conf_file and self.nodegroup_ids:
1285 nodegroup_id = random.sample(self.nodegroup_ids, 1)[0]
1286 self.api.AddConfFileToNodeGroup(conf_file_id, nodegroup_id)
1291 if conf_file_fields == node_conf_file and self.node_ids:
1292 node_id = random.sample(self.node_ids, 1)[0]
1293 self.api.AddConfFileToNode(conf_file_id, node_id)
1298 # Check configuration file
1299 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1300 for field in conf_file_fields:
1301 assert conf_file[field] == conf_file_fields[field]
1304 print "Added configuration file", conf_file_id,
1305 if nodegroup_id is not None:
1306 print "to node group", nodegroup_id,
1307 elif node_id is not None:
1308 print "to node", node_id,
1311 def UpdateConfFiles(self):
1313 Make random changes to any configuration files we may have added.
1316 for conf_file_id in self.conf_file_ids:
1317 # Update configuration file
1318 conf_file_fields = random_conf_file()
1319 # Do not update dest so that it remains an override if set
1320 if 'dest' in conf_file_fields:
1321 del conf_file_fields['dest']
1322 self.api.UpdateConfFile(conf_file_id, conf_file_fields)
1325 # Check configuration file
1326 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1327 for field in conf_file_fields:
1328 assert conf_file[field] == conf_file_fields[field]
1331 print "Updated configuration file", conf_file_id
1333 def DeleteConfFiles(self):
1335 Delete any random configuration files we may have added.
1338 for conf_file_id in self.conf_file_ids:
1339 self.api.DeleteConfFile(conf_file_id)
1342 assert not self.api.GetConfFiles([conf_file_id])
1345 print "Deleted configuration file", conf_file_id
1348 assert not self.api.GetConfFiles(self.conf_file_ids)
1350 self.conf_file_ids = []
1352 def AddTagTypes(self,n_sa,n_ng,n_il):
1354 Add as many tag types as there are nodegroups,
1355 will use value=yes for each nodegroup
1358 roles = self.api.GetRoles()
1360 raise Exception, "No roles"
1361 role_ids = [role['role_id'] for role in roles]
1363 for i in range (n_sa + n_ng + n_il):
1364 tag_type_fields = random_tag_type (role_ids)
1365 tag_type_id = self.api.AddTagType (tag_type_fields)
1367 assert tag_type_id not in \
1368 self.slice_type_ids + \
1369 self.nodegroup_type_ids + \
1373 self.slice_type_ids.append(tag_type_id)
1374 elif i < n_sa+n_ng :
1375 self.nodegroup_type_ids.append(tag_type_id)
1377 self.ilink_type_ids.append(tag_type_id)
1380 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1381 for field in tag_type_fields:
1382 assert tag_type[field] == tag_type_fields[field]
1384 print "Updated slice attribute type", tag_type_id
1386 def UpdateTagTypes(self):
1388 Make random changes to any slice attribute types we may have added.
1391 roles = self.api.GetRoles()
1393 raise Exception, "No roles"
1394 role_ids = [role['role_id'] for role in roles]
1396 for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1397 # Update slice attribute type
1398 tag_type_fields = random_tag_type(role_ids)
1399 self.api.UpdateTagType(tag_type_id, tag_type_fields)
1402 # Check slice attribute type
1403 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1404 for field in tag_type_fields:
1405 assert tag_type[field] == tag_type_fields[field]
1407 print "Updated slice attribute type", tag_type_id
1409 def DeleteTagTypes(self):
1411 Delete any random slice attribute types we may have added.
1414 for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1415 self.api.DeleteTagType(tag_type_id)
1418 assert not self.api.GetTagTypes([tag_type_id])
1421 print "Deleted slice attribute type", tag_type_id
1424 assert not self.api.GetTagTypes(self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1426 self.slice_type_ids = []
1427 self.nodegroup_type_ids = []
1429 def AddSlices(self, per_site = 10):
1431 Add a number of random slices per site.
1434 for site in self.api.GetSites(self.site_ids):
1435 for i in range(min(per_site, site['max_slices'])):
1437 slice_fields = random_slice(site['login_base'],self.namelengths)
1438 slice_id = self.api.AddSlice(slice_fields)
1440 # Should return a unique slice_id
1441 assert slice_id not in self.slice_ids
1442 self.slice_ids.append(slice_id)
1444 # Add slice to a random set of nodes
1445 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1447 self.api.AddSliceToNodes(slice_id, node_ids)
1449 # Add random set of site users to slice
1450 person_ids = random.sample(site['person_ids'], randint(0, len(site['person_ids'])))
1451 for person_id in person_ids:
1452 self.api.AddPersonToSlice(person_id, slice_id)
1456 slice = self.api.GetSlices([slice_id])[0]
1457 for field in slice_fields:
1458 assert slice[field] == slice_fields[field]
1460 assert set(node_ids) == set(slice['node_ids'])
1461 assert set(person_ids) == set(slice['person_ids'])
1464 print "Added slice", slice_id, "to site", site['site_id'],
1466 print "and nodes", node_ids,
1469 print "Added users", site['person_ids'], "to slice", slice_id
1471 def UpdateSlices(self):
1473 Make random changes to any slices we may have added.
1476 for slice_id in self.slice_ids:
1478 slice_fields = random_slice("unused",self.namelengths)
1479 # Cannot change slice name
1480 if 'name' in slice_fields:
1481 del slice_fields['name']
1482 self.api.UpdateSlice(slice_id, slice_fields)
1484 slice = self.api.GetSlices([slice_id])[0]
1486 # Add slice to a random set of nodes
1487 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1488 self.api.AddSliceToNodes(slice_id, list(set(node_ids) - set(slice['node_ids'])))
1489 self.api.DeleteSliceFromNodes(slice_id, list(set(slice['node_ids']) - set(node_ids)))
1491 # Add random set of users to slice
1492 person_ids = random.sample(self.person_ids, randint(0, len(self.person_ids)))
1493 for person_id in (set(person_ids) - set(slice['person_ids'])):
1494 self.api.AddPersonToSlice(person_id, slice_id)
1495 for person_id in (set(slice['person_ids']) - set(person_ids)):
1496 self.api.DeletePersonFromSlice(person_id, slice_id)
1499 slice = self.api.GetSlices([slice_id])[0]
1500 for field in slice_fields:
1501 assert slice[field] == slice_fields[field]
1502 assert set(node_ids) == set(slice['node_ids'])
1503 assert set(person_ids) == set(slice['person_ids'])
1506 print "Updated slice", slice_id
1507 print "Added nodes", node_ids, "to slice", slice_id
1508 print "Added persons", person_ids, "to slice", slice_id
1510 def DeleteSlices(self):
1512 Delete any random slices we may have added.
1515 for slice_id in self.slice_ids:
1516 self.api.DeleteSlice(slice_id)
1519 assert not self.api.GetSlices([slice_id])
1522 print "Deleted slice", slice_id
1525 assert not self.api.GetSlices(self.slice_ids)
1529 def AddSliceTags(self, per_slice = 2):
1531 Add a number of random slices per site.
1534 if not self.slice_type_ids:
1537 for slice_id in self.slice_ids:
1538 slice = self.api.GetSlices([slice_id])[0]
1540 for i in range(per_slice):
1541 # Set a random slice/sliver attribute
1542 for tag_type_id in random.sample(self.slice_type_ids, 1):
1543 value = randstr(16, letters + '_' + digits)
1544 # Make it a sliver attribute with 50% probability
1545 if slice['node_ids']:
1546 node_id = random.sample(slice['node_ids'] + [None] * len(slice['node_ids']), 1)[0]
1550 # Add slice attribute
1552 slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value)
1554 slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value, node_id)
1556 # Should return a unique slice_tag_id
1557 assert slice_tag_id not in self.slice_tag_ids
1558 self.slice_tag_ids.append(slice_tag_id)
1561 # Check slice attribute
1562 slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1563 for field in 'tag_type_id', 'slice_id', 'node_id', 'slice_tag_id', 'value':
1564 assert slice_tag[field] == locals()[field]
1567 print "Added slice attribute", slice_tag_id, "of type", tag_type_id,
1568 if node_id is not None:
1569 print "to node", node_id,
1572 def UpdateSliceTags(self):
1574 Make random changes to any slice attributes we may have added.
1577 for slice_tag_id in self.slice_tag_ids:
1578 # Update slice attribute
1579 value = randstr(16, letters + '_' + digits)
1580 self.api.UpdateSliceTag(slice_tag_id, value)
1582 # Check slice attribute again
1583 slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1584 assert slice_tag['value'] == value
1587 print "Updated slice attribute", slice_tag_id
1589 def DeleteSliceTags(self):
1591 Delete any random slice attributes we may have added.
1594 for slice_tag_id in self.slice_tag_ids:
1595 self.api.DeleteSliceTag(slice_tag_id)
1598 assert not self.api.GetSliceTags([slice_tag_id])
1601 print "Deleted slice attribute", slice_tag_id
1604 assert not self.api.GetSliceTags(self.slice_tag_ids)
1606 self.slice_tag_ids = []
1609 parser = OptionParser()
1610 parser.add_option("-c", "--check", action = "store_true", default = False,
1611 help = "Check most actions (default: %default)")
1612 parser.add_option("-q", "--quiet", action = "store_true", default = False,
1613 help = "Be quiet (default: %default)")
1614 parser.add_option("-p","--preserve", action="store_true", default =False,
1615 help = "Do not delete created objects")
1616 parser.add_option("-t", "--tiny", action = "store_true", default = False,
1617 help = "Run a tiny test (default: %default)")
1618 parser.add_option("-s", "--short-names", action="store_true", dest="short_names", default = False,
1619 help = "Generate smaller names for checking UI rendering")
1620 (options, args) = parser.parse_args()
1622 test = Test(api = Shell(),
1623 check = options.check,
1624 verbose = not options.quiet,
1625 preserve = options.preserve)
1628 sizes = Test.sizes_tiny
1630 sizes = Test.sizes_default
1632 if options.short_names:
1633 test.namelengths = Test.namelengths_short
1635 test.namelengths = Test.namelengths_default
1639 if __name__ == "__main__":