3 # Test script utility class
5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2006 The Trustees of Princeton University
9 from pprint import pprint
10 from string import letters, digits, punctuation, whitespace
11 from traceback import print_exc
12 from optparse import OptionParser
19 from PLC.Shell import Shell
21 from random import Random
24 # note about namelengths
25 # original version uses full lengths for all fields for testing overflows and things
26 # however for a realistic test, involving a web UI, this is not appropriate, so we
27 # use smaller identifiers
29 def randfloat(min = 0.0, max = 1.0):
30 return float(min) + (random.random() * (float(max) - float(min)))
32 def randint(min = 0, max = 1):
33 return int(randfloat(min, max + 1))
35 # See "2.2 Characters" in the XML specification:
37 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
39 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
42 ascii_xml_chars = map(unichr, [0x9, 0xA])
43 # xmlrpclib uses xml.parsers.expat, which always converts either '\r'
44 # (#xD) or '\n' (#xA) to '\n'. So avoid using '\r', too, if this is
46 if xmlrpclib.loads(xmlrpclib.dumps(('\r',)))[0][0] == '\r':
47 ascii_xml_chars.append('\r')
48 ascii_xml_chars += map(unichr, xrange(0x20, 0x7F - 1))
49 low_xml_chars = list(ascii_xml_chars)
50 low_xml_chars += map(unichr, xrange(0x84 + 1, 0x86 - 1))
51 low_xml_chars += map(unichr, xrange(0x9F + 1, 0xFF))
52 valid_xml_chars = list(low_xml_chars)
53 valid_xml_chars += map(unichr, xrange(0xFF + 1, 0xD7FF))
54 valid_xml_chars += map(unichr, xrange(0xE000, 0xFDD0 - 1))
55 valid_xml_chars += map(unichr, xrange(0xFDDF + 1, 0xFFFD))
57 def randstr(length, pool = valid_xml_chars, encoding = "utf-8"):
58 sample = random.sample(pool, min(length, len(pool)))
61 bytes = len(s.encode(encoding))
65 sample += random.sample(pool, min(length - bytes, len(pool)))
66 random.shuffle(sample)
71 def randhostname(namelengths):
72 # 1. Each part begins and ends with a letter or number.
73 # 2. Each part except the last can contain letters, numbers, or hyphens.
74 # 3. Each part is between 1 and 64 characters, including the trailing dot.
75 # 4. At least two parts.
76 # 5. Last part can only contain between 2 and 6 letters.
77 hostname = 'a' + randstr(namelengths['hostname1'], letters + digits + '-') + '1.' + \
78 'b' + randstr(namelengths['hostname1'], letters + digits + '-') + '2.' + \
79 'c' + randstr(namelengths['hostname2'], letters)
80 return hostname.lower()
84 for i in range(randint(1, 10)):
85 parts.append(randstr(randint(1, 30), ascii_xml_chars))
86 return u'/'.join(parts)[0:length]
88 def randemail(namelengths):
89 return (randstr(namelengths['email'], letters + digits) + "@" + randhostname(namelengths)).lower()
91 def randkey(namelengths,bits = 2048):
92 ssh_key_types = ["ssh-dss", "ssh-rsa"]
93 key_type = random.sample(ssh_key_types, 1)[0]
94 return ' '.join([key_type,
95 base64.b64encode(''.join(randstr(bits / 8).encode("utf-8"))),
96 randemail(namelengths)])
100 'peername': randstr(24,letters + ' ' + digits),
101 'peer_url': "https://" + randhostname ({'hostname1':8,'hostname2':3}) + ":443/PLCAPI/",
102 'key' : randstr(1024,letters+digits),
103 'cacert' : randstr(1024,letters+digits),
104 'shortname' : randstr(1,letters) + 'LAB',
105 'hrn_root' : 'planetlab.' + randstr (3,letters),
108 def random_site(namelengths):
109 sitename=randstr(namelengths['sitename'],namelengths['sitename_contents'])
110 abbreviated_name=randstr(namelengths['abbreviated_name'],namelengths['abbreviated_name_contents'])
112 print 'nl[a] in random_site',namelengths['abbreviated_name'],'actual',len(abbreviated_name)
115 'abbreviated_name': abbreviated_name,
116 'login_base': randstr(namelengths['login_base'], letters).lower(),
117 'latitude': int(randfloat(-90.0, 90.0) * 1000) / 1000.0,
118 'longitude': int(randfloat(-180.0, 180.0) * 1000) / 1000.0,
121 def random_address_type():
124 'description': randstr(254),
127 def random_address():
129 'line1': randstr(254),
130 'line2': randstr(254),
131 'line3': randstr(254),
132 'city': randstr(254),
133 'state': randstr(254),
134 'postalcode': randstr(64),
135 'country': randstr(128),
138 def random_person(namelengths):
140 'first_name': randstr(namelengths['first_name']),
141 'last_name': randstr(namelengths['last_name']),
142 'email': randemail(namelengths),
144 # Accounts are disabled by default
146 'password': randstr(254),
149 def random_key(key_types,namelengths):
151 'key_type': random.sample(key_types, 1)[0],
152 'key': randkey(namelengths)
155 def random_tag_type (role_ids):
156 return {'tagname': randstr(12,letters+digits),
157 'category':randstr(4,letters+digits)+'/'+randstr(6,letters+digits),
158 'description' : randstr(128,letters+digits+whitespace+punctuation),
161 def random_nodegroup():
162 return {'groupname' : randstr(30, letters+digits+whitespace) }
164 def random_roles(role_ids):
165 nb_roles=len(role_ids)
166 return random.sample(role_ids,random.choice(range(1,nb_roles+1)))
169 def random_node(node_types,boot_states,namelengths):
171 'hostname': randhostname(namelengths),
172 'node_type': random.sample(node_types,1)[0],
173 'boot_state': random.sample(boot_states, 1)[0],
174 'model': randstr(namelengths['model']),
175 'version': randstr(64),
176 # for testing node tags
180 def random_interface(method, type,namelengths):
184 'bwlimit': randint(500000, 10000000),
188 ip = randint(0, 0xffffffff)
189 netmask = (0xffffffff << randint(2, 31)) & 0xffffffff
190 network = ip & netmask
191 broadcast = ((ip & netmask) | ~netmask) & 0xffffffff
192 gateway = randint(network + 1, broadcast - 1)
193 dns1 = randint(0, 0xffffffff)
195 for field in 'ip', 'netmask', 'network', 'broadcast', 'gateway', 'dns1':
196 interface_fields[field] = socket.inet_ntoa(struct.pack('>L', locals()[field]))
198 interface_fields['hostname']=randhostname(namelengths);
200 return interface_fields
205 def random_pcu(namelengths):
207 'hostname': randhostname(namelengths),
208 'ip': socket.inet_ntoa(struct.pack('>L', randint(0, 0xffffffff))),
209 'protocol': randstr(16),
210 'username': randstr(254),
211 'password': randstr(254),
212 'notes': randstr(254),
213 'model': randstr(32),
216 def random_conf_file():
218 'enabled': bool(randint()),
219 'source': randpath(255),
220 'dest': randpath(255),
221 'file_permissions': "%#o" % randint(0, 512),
222 'file_owner': randstr(32, letters + '_' + digits),
223 'file_group': randstr(32, letters + '_' + digits),
224 'preinstall_cmd': randpath(100),
225 'postinstall_cmd': randpath(100),
226 'error_cmd': randpath(100),
227 'ignore_cmd_errors': bool(randint()),
228 'always_update': bool(randint()),
231 def random_slice(login_base,namelengths):
233 'name': login_base + "_" + randstr(11, letters).lower(),
234 'url': "http://" + randhostname(namelengths) + "/",
235 'description': randstr(2048),
242 'addresses_per_site': 1,
243 'persons_per_site': 1,
244 'keys_per_person': 1,
248 'interfaces_per_node': 1,
252 'slices_per_site': 1,
253 'attributes_per_slice': 1,
259 'addresses_per_site': 2,
260 'persons_per_site': 4,
261 'keys_per_person': 2,
265 'interfaces_per_node': 1,
269 'slices_per_site': 4,
270 'attributes_per_slice': 2,
276 'addresses_per_site': 2,
277 'persons_per_site': 5,
278 'keys_per_person': 2,
282 'interfaces_per_node': 2,
286 'slices_per_site': 10,
287 'attributes_per_slice': 4,
293 'addresses_per_site': 2,
294 'persons_per_site': 5,
295 'keys_per_person': 2,
299 'interfaces_per_node': 2,
303 'slices_per_site': 10,
304 'attributes_per_slice': 4,
307 namelengths_default = {
312 'sitename_contents':letters+digits,
313 'abbreviated_name':50,
314 'abbreviated_name_contents':letters+digits+whitespace+punctuation,
321 namelengths_short = {
326 'sitename_contents':letters+digits,
327 'abbreviated_name':24,
328 'abbreviated_name_contents':letters+digits+whitespace+punctuation,
335 def __init__(self, api, check, verbose, preserve, federating):
338 self.verbose = verbose
339 self.preserve = preserve
340 self.federating = federating
343 self.address_type_ids = []
344 self.address_ids = []
347 self.slice_type_ids = []
348 self.nodegroup_type_ids = []
349 self.ilink_type_ids = []
350 self.nodegroup_ids = []
352 self.interface_ids = []
355 self.conf_file_ids = []
357 self.slice_tag_ids = []
359 def Cardinals (self):
360 return [len(x) for x in (
361 self.api.GetNodes({},['node_id']),
362 self.api.GetSites({},['site_id']),
363 self.api.GetPersons({},['person_id']),
364 self.api.GetSlices({},['slice_id']),
367 def Run(self, **kwds):
369 Run a complete database and API consistency test. Populates
370 the database with a set of random entities, updates them, then
371 deletes them. Examples:
373 test.Run() # Defaults
374 test.Run(**Test.sizes_default) # Defaults
375 test.Run(**Test.sizes_tiny) # Tiny set
376 test.Run(sites = 123, slices_per_site = 4) # Defaults with overrides
379 cardinals_before=self.Cardinals()
380 print 'Cardinals before test (n,s,p,sl)',cardinals_before
383 # if federating : we're done
385 if self.federating or self.preserve:
386 print 'Preserving - update & delete skipped'
391 cardinals_after=self.Cardinals()
392 print 'Cardinals after test (n,s,p,sl)',cardinals_after
394 if cardinals_before != cardinals_after:
395 raise Exception, 'cardinals before and after differ - check deletion mechanisms'
397 def Add(self, **kwds):
399 Populate the database with a set of random entities. Examples:
404 sizes = self.sizes_default.copy()
407 if not self.federating:
408 self.AddSites(sizes['sites'])
409 self.AddAddressTypes(sizes['address_types'])
410 self.AddAddresses(sizes['addresses_per_site'])
411 self.AddPersons(sizes['persons_per_site'])
412 self.AddKeys(sizes['keys_per_person'])
413 self.AddTagTypes(sizes['slice_tags'],sizes['nodegroups'],sizes['ilinks'])
414 self.AddNodeGroups(sizes['nodegroups'])
415 self.AddNodes(sizes['nodes_per_site'])
416 self.AddInterfaces(sizes['interfaces_per_node'])
417 self.AddIlinks (sizes['ilinks'])
418 self.AddPCUs(sizes['pcus_per_site'])
419 self.AddConfFiles(sizes['conf_files'])
420 self.AddSlices(sizes['slices_per_site'])
421 self.AddSliceTags(sizes['attributes_per_slice'])
425 self.AddSites(sizes['sites'])
426 self.AddPersons(sizes['persons_per_site'])
427 self.AddKeys(sizes['keys_per_person'])
428 self.AddNodes(sizes['nodes_per_site'])
429 self.AddSlices(sizes['slices_per_site'])
430 # create peer and add newly created entities
436 self.UpdateAddressTypes()
437 self.UpdateAddresses()
440 self.UpdateTagTypes()
441 self.UpdateNodeGroups()
443 self.UpdateInterfaces()
446 self.UpdateConfFiles()
448 self.UpdateSliceTags()
451 self.DeleteSliceTags()
454 self.DeleteConfFiles()
457 self.DeleteInterfaces()
460 self.DeleteNodeGroups()
461 self.DeleteTagTypes()
462 self.DeleteAddresses()
463 self.DeleteAddressTypes()
466 # record current (old) objects
467 def RecordStatus (self):
468 self.old_site_ids = [ s['site_id'] for s in self.api.GetSites({},['site_id']) ]
469 self.old_person_ids = [ s['person_id'] for s in self.api.GetPersons({},['person_id']) ]
470 self.old_key_ids = [ s['key_id'] for s in self.api.GetKeys({},['key_id']) ]
471 self.old_node_ids = [ s['node_id'] for s in self.api.GetNodes({},['node_id']) ]
472 self.old_slice_ids = [ s['slice_id'] for s in self.api.GetSlices({},['slice_id']) ]
475 peer_id=self.api.AddPeer (random_peer())
476 peer = GetPeers([peer_id])[0]
478 print "Added peer",peer_id
480 # add new sites (the ones not in self.site_ids) in the peer
482 for site in self.api.GetSites ({'~site_id':self.old_site_ids}):
483 peer.add_site(site,site['site_id'])
484 for person in self.api.GetPersons ({'~person_id':self.old_person_ids}):
485 peer.add_person(person,person['person_id'])
486 for key in self.api.GetKeys ({'~key_id':self.old_key_ids}):
487 peer.add_key(key,key['key_id'])
488 for node in self.api.GetNodes ({'~node_id':self.old_node_ids}):
489 peer.add_node(node,node['node_id'])
490 for slice in self.api.GetSlices ({'~slice_id':self.old_slice_ids}):
491 peer.add_slice(slice,slice['slice_id'])
493 def AddSites(self, n = 10):
495 Add a number of random sites.
500 site_fields = random_site(self.namelengths)
501 site_id = self.api.AddSite(site_fields)
503 # Should return a unique site_id
504 assert site_id not in self.site_ids
505 self.site_ids.append(site_id)
507 # Enable slice creation
508 site_fields['max_slices'] = randint(1, 10)
509 self.api.UpdateSite(site_id, site_fields)
513 site = self.api.GetSites([site_id])[0]
514 for field in site_fields:
515 assert site[field] == site_fields[field]
518 print "Added site", site_id
520 def UpdateSites(self):
522 Make random changes to any sites we may have added.
525 for site_id in self.site_ids:
527 site_fields = random_site(self.namelengths)
528 # Do not change login_base
529 if 'login_base' in site_fields:
530 del site_fields['login_base']
531 self.api.UpdateSite(site_id, site_fields)
535 site = self.api.GetSites([site_id])[0]
536 for field in site_fields:
537 assert site[field] == site_fields[field]
540 print "Updated site", site_id
542 def DeleteSites(self):
544 Delete any random sites we may have added.
547 for site_id in self.site_ids:
548 self.api.DeleteSite(site_id)
551 assert not self.api.GetSites([site_id])
554 print "Deleted site", site_id
557 assert not self.api.GetSites(self.site_ids)
561 def AddAddressTypes(self, n = 2):
563 Add a number of random address types.
567 address_type_fields = random_address_type()
568 address_type_id = self.api.AddAddressType(address_type_fields)
570 # Should return a unique address_type_id
571 assert address_type_id not in self.address_type_ids
572 self.address_type_ids.append(address_type_id)
576 address_type = self.api.GetAddressTypes([address_type_id])[0]
577 for field in address_type_fields:
578 assert address_type[field] == address_type_fields[field]
581 print "Added address type", address_type_id
583 def UpdateAddressTypes(self):
585 Make random changes to any address types we may have added.
588 for address_type_id in self.address_type_ids:
589 # Update address_type
590 address_type_fields = random_address_type()
591 self.api.UpdateAddressType(address_type_id, address_type_fields)
595 address_type = self.api.GetAddressTypes([address_type_id])[0]
596 for field in address_type_fields:
597 assert address_type[field] == address_type_fields[field]
600 print "Updated address_type", address_type_id
602 def DeleteAddressTypes(self):
604 Delete any random address types we may have added.
607 for address_type_id in self.address_type_ids:
608 self.api.DeleteAddressType(address_type_id)
611 assert not self.api.GetAddressTypes([address_type_id])
614 print "Deleted address type", address_type_id
617 assert not self.api.GetAddressTypes(self.address_type_ids)
619 self.address_type_ids = []
621 def AddAddresses(self, per_site = 2):
623 Add a number of random addresses to each site.
626 for site_id in self.site_ids:
627 for i in range(per_site):
628 address_fields = random_address()
629 address_id = self.api.AddSiteAddress(site_id, address_fields)
631 # Should return a unique address_id
632 assert address_id not in self.address_ids
633 self.address_ids.append(address_id)
635 # Add random address type
636 if self.address_type_ids:
637 for address_type_id in random.sample(self.address_type_ids, 1):
638 self.api.AddAddressTypeToAddress(address_type_id, address_id)
642 address = self.api.GetAddresses([address_id])[0]
643 for field in address_fields:
644 assert address[field] == address_fields[field]
647 print "Added address", address_id, "to site", site_id
649 def UpdateAddresses(self):
651 Make random changes to any addresses we may have added.
654 for address_id in self.address_ids:
656 address_fields = random_address()
657 self.api.UpdateAddress(address_id, address_fields)
661 address = self.api.GetAddresses([address_id])[0]
662 for field in address_fields:
663 assert address[field] == address_fields[field]
666 print "Updated address", address_id
668 def DeleteAddresses(self):
670 Delete any random addresses we may have added.
673 for address_id in self.address_ids:
674 # Remove address types
675 address = self.api.GetAddresses([address_id])[0]
676 for address_type_id in address['address_type_ids']:
677 self.api.DeleteAddressTypeFromAddress(address_type_id, address_id)
680 address = self.api.GetAddresses([address_id])[0]
681 assert not address['address_type_ids']
683 self.api.DeleteAddress(address_id)
686 assert not self.api.GetAddresses([address_id])
689 print "Deleted address", address_id
692 assert not self.api.GetAddresses(self.address_ids)
694 self.address_ids = []
696 def AddPersons(self, per_site = 10):
698 Add a number of random users to each site.
701 for site_id in self.site_ids:
702 for i in range(per_site):
704 person_fields = random_person(self.namelengths)
705 person_id = self.api.AddPerson(person_fields)
707 # Should return a unique person_id
708 assert person_id not in self.person_ids
709 self.person_ids.append(person_id)
713 person = self.api.GetPersons([person_id])[0]
714 for field in person_fields:
715 if field != 'password':
716 assert person[field] == person_fields[field]
718 auth = {'AuthMethod': "password",
719 'Username': person_fields['email'],
720 'AuthString': person_fields['password']}
723 # Check that user is disabled
725 assert not self.api.AuthCheck(auth)
729 # Add random set of roles
730 role_ids = random.sample([20, 30, 40], randint(1, 3))
731 for role_id in role_ids:
732 self.api.AddRoleToPerson(role_id, person_id)
735 person = self.api.GetPersons([person_id])[0]
736 assert set(role_ids) == set(person['role_ids'])
739 self.api.UpdatePerson(person_id, {'enabled': True})
742 # Check that user is enabled
743 assert self.api.AuthCheck(auth)
745 # Associate user with site
746 self.api.AddPersonToSite(person_id, site_id)
747 self.api.SetPersonPrimarySite(person_id, site_id)
750 person = self.api.GetPersons([person_id])[0]
751 assert person['site_ids'][0] == site_id
754 print "Added user", person_id, "to site", site_id
756 def UpdatePersons(self):
758 Make random changes to any users we may have added.
761 for person_id in self.person_ids:
763 person_fields = random_person(self.namelengths)
765 person_fields['enabled'] = True
766 self.api.UpdatePerson(person_id, person_fields)
770 person = self.api.GetPersons([person_id])[0]
771 for field in person_fields:
772 if field != 'password':
773 assert person[field] == person_fields[field]
776 print "Updated person", person_id
778 person = self.api.GetPersons([person_id])[0]
780 # Associate user with a random set of sites
781 site_ids = random.sample(self.site_ids, randint(0, len(self.site_ids)))
782 for site_id in (set(site_ids) - set(person['site_ids'])):
783 self.api.AddPersonToSite(person_id, site_id)
784 for site_id in (set(person['site_ids']) - set(site_ids)):
785 self.api.DeletePersonFromSite(person_id, site_id)
788 self.api.SetPersonPrimarySite(person_id, site_ids[0])
791 person = self.api.GetPersons([person_id])[0]
792 assert set(site_ids) == set(person['site_ids'])
795 print "Updated person", person_id, "to sites", site_ids
797 def DeletePersons(self):
799 Delete any random users we may have added.
802 for person_id in self.person_ids:
804 person = self.api.GetPersons([person_id])[0]
805 for site_id in person['site_ids']:
806 self.api.DeletePersonFromSite(person_id, site_id)
809 person = self.api.GetPersons([person_id])[0]
810 assert not person['site_ids']
813 for role_id in person['role_ids']:
814 self.api.DeleteRoleFromPerson(role_id, person_id)
817 person = self.api.GetPersons([person_id])[0]
818 assert not person['role_ids']
821 self.api.UpdatePerson(person_id, {'enabled': False})
824 person = self.api.GetPersons([person_id])[0]
825 assert not person['enabled']
828 self.api.DeletePerson(person_id)
831 assert not self.api.GetPersons([person_id])
834 print "Deleted user", person_id
837 assert not self.api.GetPersons(self.person_ids)
841 def AddKeys(self, per_person = 2):
843 Add a number of random keys to each user.
846 key_types = self.api.GetKeyTypes()
848 raise Exception, "No key types"
850 for person_id in self.person_ids:
851 for i in range(per_person):
853 key_fields = random_key(key_types,self.namelengths)
854 key_id = self.api.AddPersonKey(person_id, key_fields)
856 # Should return a unique key_id
857 assert key_id not in self.key_ids
858 self.key_ids.append(key_id)
862 key = self.api.GetKeys([key_id])[0]
863 for field in key_fields:
864 assert key[field] == key_fields[field]
866 # Add and immediately blacklist a key
867 key_fields = random_key(key_types,self.namelengths)
868 key_id = self.api.AddPersonKey(person_id, key_fields)
870 self.api.BlacklistKey(key_id)
872 # Is effectively deleted
873 assert not self.api.GetKeys([key_id])
875 # Cannot be added again
877 key_id = self.api.AddPersonKey(person_id, key_fields)
883 print "Added key", key_id, "to user", person_id
885 def UpdateKeys(self):
887 Make random changes to any keys we may have added.
890 key_types = self.api.GetKeyTypes()
892 raise Exception, "No key types"
894 for key_id in self.key_ids:
896 key_fields = random_key(key_types,self.namelengths)
897 self.api.UpdateKey(key_id, key_fields)
901 key = self.api.GetKeys([key_id])[0]
902 for field in key_fields:
903 assert key[field] == key_fields[field]
906 print "Updated key", key_id
908 def DeleteKeys(self):
910 Delete any random keys we may have added.
913 for key_id in self.key_ids:
914 self.api.DeleteKey(key_id)
917 assert not self.api.GetKeys([key_id])
920 print "Deleted key", key_id
923 assert not self.api.GetKeys(self.key_ids)
927 def AddNodeGroups(self, n = 10):
929 Add a number of random node groups.
934 tag_type_id = self.nodegroup_type_ids[i]
935 tagname=self.api.GetTagTypes([tag_type_id])[0]['tagname']
938 groupname = random_nodegroup() ['groupname']
940 nodegroup_id = self.api.AddNodeGroup(groupname, tagname, value)
942 # Should return a unique nodegroup_id
943 assert nodegroup_id not in self.nodegroup_ids
944 self.nodegroup_ids.append(nodegroup_id)
948 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
949 assert nodegroup['groupname'] == groupname
950 assert nodegroup['tagname'] == tagname
951 assert nodegroup['value'] == value
954 print "Added node group", nodegroup_id
956 def UpdateNodeGroups(self):
958 Make random changes to any node groups we may have added.
961 for nodegroup_id in self.nodegroup_ids:
963 groupname = random_nodegroup()['groupname']
964 # cannot change tagname
965 nodegroup_fields = { 'groupname':groupname }
966 self.api.UpdateNodeGroup(nodegroup_id, nodegroup_fields)
970 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
971 for field in nodegroup_fields:
972 assert nodegroup[field] == nodegroup_fields[field]
975 print "Updated node group", nodegroup_id
977 def DeleteNodeGroups(self):
979 Delete any random node groups we may have added.
982 for nodegroup_id in self.nodegroup_ids:
983 self.api.DeleteNodeGroup(nodegroup_id)
986 assert not self.api.GetNodeGroups([nodegroup_id])
989 print "Deleted node group", nodegroup_id
992 assert not self.api.GetNodeGroups(self.nodegroup_ids)
994 self.nodegroup_ids = []
996 def AddNodes(self, per_site = 2):
998 Add a number of random nodes to each site. Each node will also
999 be added to a random node group if AddNodeGroups() was
1003 node_types = self.api.GetNodeTypes()
1005 raise Exception, "No node types"
1006 boot_states = self.api.GetBootStates()
1008 raise Exception, "No boot states"
1010 for site_id in self.site_ids:
1011 for i in range(per_site):
1013 node_fields = random_node(node_types,boot_states,self.namelengths)
1014 node_id = self.api.AddNode(site_id, node_fields)
1016 # Should return a unique node_id
1017 assert node_id not in self.node_ids
1018 self.node_ids.append(node_id)
1020 # Add to a random set of node groups
1021 nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
1022 for nodegroup_id in nodegroup_ids:
1023 tagname = self.api.GetNodeGroups([nodegroup_id])[0]['tagname']
1024 self.api.AddNodeTag( node_id, tagname, 'yes' )
1028 node = self.api.GetNodes([node_id])[0]
1029 for field in node_fields:
1030 if field not in tag_fields:
1031 assert node[field] == node_fields[field]
1034 print "Added node", node_id
1036 def UpdateNodes(self):
1038 Make random changes to any nodes we may have added.
1041 node_types = self.api.GetNodeTypes()
1043 raise Exception, "No node types"
1044 boot_states = self.api.GetBootStates()
1046 raise Exception, "No boot states"
1048 for node_id in self.node_ids:
1050 node_fields = random_node(node_types,boot_states,self.namelengths)
1051 self.api.UpdateNode(node_id, node_fields)
1053 node = self.api.GetNodes([node_id])[0]
1055 # Add to a random set of node groups
1056 nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
1057 for nodegroup_id in (set(nodegroup_ids) - set(node['nodegroup_ids'])):
1058 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1059 tagname = nodegroup['tagname']
1060 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
1062 self.api.AddNodeTag(node_id,tagname,'yes')
1064 node_tag=node_tags[0]
1065 self.api.UpdateNodeTag(node_tag['node_tag_id'],'yes')
1066 for nodegroup_id in (set(node['nodegroup_ids']) - set(nodegroup_ids)):
1067 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1068 tagname = nodegroup['tagname']
1069 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
1071 self.api.AddNodeTag(node_id,tagname,'no')
1073 node_tag=node_tags[0]
1074 self.api.UpdateNodeTag(node_tag['node_tag_id'],'no')
1078 node = self.api.GetNodes([node_id])[0]
1079 for field in node_fields:
1080 if field not in tag_fields:
1081 if node[field] != node_fields[field]:
1082 raise Exception, "Unexpected field %s in node after GetNodes()"%field
1083 assert set(nodegroup_ids) == set(node['nodegroup_ids'])
1085 # again but we are now fetching 'arch' explicitly
1086 node2 = self.api.GetNodes([node_id],node_fields.keys())[0]
1087 for field in node_fields:
1088 if node2[field] != node_fields[field]:
1089 raise Exception, "Unexpected field %s in node after GetNodes(tags)"%field
1092 print "Updated node", node_id
1094 def DeleteNodes(self):
1096 Delete any random nodes we may have added.
1099 for node_id in self.node_ids:
1100 # Remove from node groups
1101 node = self.api.GetNodes([node_id])[0]
1102 for node_tag in self.api.GetNodeTags ( {'node_id': node_id} ):
1103 self.api.UpdateNodeTag(node_tag['node_tag_id'],'')
1106 node = self.api.GetNodes([node_id])[0]
1107 assert not node['nodegroup_ids']
1109 self.api.DeleteNode(node_id)
1112 assert not self.api.GetNodes([node_id])
1115 print "Deleted node", node_id
1118 assert not self.api.GetNodes(self.node_ids)
1122 def AddInterfaces(self, per_node = 1):
1124 Add a number of random network interfaces to each node.
1127 network_methods = self.api.GetNetworkMethods()
1128 if not network_methods:
1129 raise Exception, "No network methods"
1131 network_types = self.api.GetNetworkTypes()
1132 if not network_types:
1133 raise Exception, "No network types"
1135 for node_id in self.node_ids:
1136 for i in range(per_node):
1137 method = random.sample(network_methods, 1)[0]
1138 type = random.sample(network_types, 1)[0]
1141 interface_fields = random_interface(method, type,self.namelengths)
1142 interface_id = self.api.AddInterface(node_id, interface_fields)
1144 # Should return a unique interface_id
1145 assert interface_id not in self.interface_ids
1146 self.interface_ids.append(interface_id)
1150 interface = self.api.GetInterfaces([interface_id])[0]
1151 for field in interface_fields:
1152 assert interface[field] == interface_fields[field]
1155 print "Added interface", interface_id, "to node", node_id
1157 def UpdateInterfaces(self):
1159 Make random changes to any network interfaces we may have added.
1162 network_methods = self.api.GetNetworkMethods()
1163 if not network_methods:
1164 raise Exception, "No network methods"
1166 network_types = self.api.GetNetworkTypes()
1167 if not network_types:
1168 raise Exception, "No network types"
1170 for interface_id in self.interface_ids:
1171 method = random.sample(network_methods, 1)[0]
1172 type = random.sample(network_types, 1)[0]
1175 interface_fields = random_interface(method, type,self.namelengths)
1176 self.api.UpdateInterface(interface_id, interface_fields)
1180 interface = self.api.GetInterfaces([interface_id])[0]
1181 for field in interface_fields:
1182 assert interface[field] == interface_fields[field]
1185 print "Updated interface", interface_id
1187 def DeleteInterfaces(self):
1189 Delete any random network interfaces we may have added.
1192 for interface_id in self.interface_ids:
1193 self.api.DeleteInterface(interface_id)
1196 assert not self.api.GetInterfaces([interface_id])
1199 print "Deleted interface", interface_id
1202 assert not self.api.GetInterfaces(self.interface_ids)
1204 self.interface_ids = []
1206 def AddIlinks (self, n):
1208 Add random links between interfaces.
1212 src = random.sample(self.interface_ids,1)[0]
1213 dst = random.sample(self.interface_ids,1)[0]
1214 ilink_id = self.api.AddIlink (src,dst,
1215 self.ilink_type_ids[i],
1218 assert ilink_id not in self.ilink_ids
1219 self.ilink_ids.append(ilink_id)
1222 print 'Added Ilink',ilink_id,' - attached interface',src,'to',dst
1225 retrieve=GetIlinks({'src_interface_id':src,'dst_interface_id':dst,
1226 'tag_type_id':self.ilink_type_ids[i]})
1227 assert ilink_id==retrieve[0]['ilink_id']
1230 def UpdateIlinks (self):
1232 for ilink_id in self.ilink_ids:
1233 new_value=random_ilink()
1234 self.api.UpdateIlink(ilink_id,new_value)
1237 ilink=self.api.GetIlinks([ilink_id])[0]
1238 assert ilink['value'] == new_value
1241 print 'Updated Ilink',ilink_id
1243 def DeleteIlinks (self):
1244 for ilink_id in self.ilink_ids:
1245 self.api.DeleteIlink(ilink_id)
1248 assert not self.api.GetIlinks({'ilink_id':ilink_id})
1251 print 'Deleted Ilink',ilink_id
1254 assert not self.api.GetIlinks(self.ilink_ids)
1259 def AddPCUs(self, per_site = 1):
1261 Add a number of random PCUs to each site. Each node at the
1262 site will be added to a port on the PCU if AddNodes() was
1266 for site_id in self.site_ids:
1267 for i in range(per_site):
1269 pcu_fields = random_pcu(self.namelengths)
1270 pcu_id = self.api.AddPCU(site_id, pcu_fields)
1272 # Should return a unique pcu_id
1273 assert pcu_id not in self.pcu_ids
1274 self.pcu_ids.append(pcu_id)
1276 # Add each node at this site to a different port on this PCU
1277 site = self.api.GetSites([site_id])[0]
1278 port = randint(1, 10)
1279 for node_id in site['node_ids']:
1280 self.api.AddNodeToPCU(node_id, pcu_id, port)
1285 pcu = self.api.GetPCUs([pcu_id])[0]
1286 for field in pcu_fields:
1287 assert pcu[field] == pcu_fields[field]
1290 print "Added PCU", pcu_id, "to site", site_id
1292 def UpdatePCUs(self):
1294 Make random changes to any PCUs we may have added.
1297 for pcu_id in self.pcu_ids:
1299 pcu_fields = random_pcu(self.namelengths)
1300 self.api.UpdatePCU(pcu_id, pcu_fields)
1304 pcu = self.api.GetPCUs([pcu_id])[0]
1305 for field in pcu_fields:
1306 assert pcu[field] == pcu_fields[field]
1309 print "Updated PCU", pcu_id
1311 def DeletePCUs(self):
1313 Delete any random nodes we may have added.
1316 for pcu_id in self.pcu_ids:
1317 # Remove nodes from PCU
1318 pcu = self.api.GetPCUs([pcu_id])[0]
1319 for node_id in pcu['node_ids']:
1320 self.api.DeleteNodeFromPCU(node_id, pcu_id)
1323 pcu = self.api.GetPCUs([pcu_id])[0]
1324 assert not pcu['node_ids']
1326 self.api.DeletePCU(pcu_id)
1329 assert not self.api.GetPCUs([pcu_id])
1332 print "Deleted PCU", pcu_id
1335 assert not self.api.GetPCUs(self.pcu_ids)
1339 def AddConfFiles(self, n = 10):
1341 Add a number of random global configuration files.
1347 # Add a random configuration file
1348 conf_files.append(random_conf_file())
1351 # Add a nodegroup override file
1352 nodegroup_conf_file = conf_files[0].copy()
1353 nodegroup_conf_file['source'] = randpath(255)
1354 conf_files.append(nodegroup_conf_file)
1356 # Add a node override file
1357 node_conf_file = conf_files[0].copy()
1358 node_conf_file['source'] = randpath(255)
1359 conf_files.append(node_conf_file)
1361 for conf_file_fields in conf_files:
1362 conf_file_id = self.api.AddConfFile(conf_file_fields)
1364 # Should return a unique conf_file_id
1365 assert conf_file_id not in self.conf_file_ids
1366 self.conf_file_ids.append(conf_file_id)
1369 if conf_file_fields == nodegroup_conf_file and self.nodegroup_ids:
1370 nodegroup_id = random.sample(self.nodegroup_ids, 1)[0]
1371 self.api.AddConfFileToNodeGroup(conf_file_id, nodegroup_id)
1376 if conf_file_fields == node_conf_file and self.node_ids:
1377 node_id = random.sample(self.node_ids, 1)[0]
1378 self.api.AddConfFileToNode(conf_file_id, node_id)
1383 # Check configuration file
1384 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1385 for field in conf_file_fields:
1386 assert conf_file[field] == conf_file_fields[field]
1389 print "Added configuration file", conf_file_id,
1390 if nodegroup_id is not None:
1391 print "to node group", nodegroup_id,
1392 elif node_id is not None:
1393 print "to node", node_id,
1396 def UpdateConfFiles(self):
1398 Make random changes to any configuration files we may have added.
1401 for conf_file_id in self.conf_file_ids:
1402 # Update configuration file
1403 conf_file_fields = random_conf_file()
1404 # Do not update dest so that it remains an override if set
1405 if 'dest' in conf_file_fields:
1406 del conf_file_fields['dest']
1407 self.api.UpdateConfFile(conf_file_id, conf_file_fields)
1410 # Check configuration file
1411 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1412 for field in conf_file_fields:
1413 assert conf_file[field] == conf_file_fields[field]
1416 print "Updated configuration file", conf_file_id
1418 def DeleteConfFiles(self):
1420 Delete any random configuration files we may have added.
1423 for conf_file_id in self.conf_file_ids:
1424 self.api.DeleteConfFile(conf_file_id)
1427 assert not self.api.GetConfFiles([conf_file_id])
1430 print "Deleted configuration file", conf_file_id
1433 assert not self.api.GetConfFiles(self.conf_file_ids)
1435 self.conf_file_ids = []
1437 def AddTagTypes(self,n_sa,n_ng,n_il):
1439 Add as many tag types as there are nodegroups,
1440 will use value=yes for each nodegroup
1443 roles = self.api.GetRoles()
1445 raise Exception, "No roles"
1446 role_ids = [role['role_id'] for role in roles]
1448 for i in range (n_sa + n_ng + n_il):
1449 tag_type_fields = random_tag_type (role_ids)
1450 tag_type_id = self.api.AddTagType (tag_type_fields)
1452 assert tag_type_id not in \
1453 self.slice_type_ids + \
1454 self.nodegroup_type_ids + \
1457 tt_role_ids=random_roles(role_ids)
1458 for tt_role_id in tt_role_ids:
1459 self.api.AddRoleToTagType(tt_role_id,tag_type_id)
1462 self.slice_type_ids.append(tag_type_id)
1463 elif i < n_sa+n_ng :
1464 self.nodegroup_type_ids.append(tag_type_id)
1466 self.ilink_type_ids.append(tag_type_id)
1469 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1470 for field in tag_type_fields:
1471 assert tag_type[field] == tag_type_fields[field]
1472 for tt_role_id in tt_role_ids:
1473 assert tt_role_id in tag_type['role_ids']
1475 print "Created tag type", tag_type_id
1477 def UpdateTagTypes(self):
1479 Make random changes to any slice attribute types we may have added.
1482 roles = self.api.GetRoles()
1484 raise Exception, "No roles"
1485 role_ids = [role['role_id'] for role in roles]
1487 for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1488 # Update slice attribute type
1489 tag_type_fields = random_tag_type(role_ids)
1490 self.api.UpdateTagType(tag_type_id, tag_type_fields)
1493 # Check slice attribute type
1494 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1495 for field in tag_type_fields:
1496 assert tag_type[field] == tag_type_fields[field]
1498 print "Updated tag type", tag_type_id
1500 def DeleteTagTypes(self):
1502 Delete any random slice attribute types we may have added.
1505 for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1506 self.api.DeleteTagType(tag_type_id)
1509 assert not self.api.GetTagTypes([tag_type_id])
1512 print "Deleted tag type", tag_type_id
1515 assert not self.api.GetTagTypes(self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1517 self.slice_type_ids = []
1518 self.nodegroup_type_ids = []
1520 def AddSlices(self, per_site = 10):
1522 Add a number of random slices per site.
1525 for site in self.api.GetSites(self.site_ids):
1526 for i in range(min(per_site, site['max_slices'])):
1528 slice_fields = random_slice(site['login_base'],self.namelengths)
1529 slice_id = self.api.AddSlice(slice_fields)
1531 # Should return a unique slice_id
1532 assert slice_id not in self.slice_ids
1533 self.slice_ids.append(slice_id)
1535 # Add slice to a random set of nodes
1536 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1538 self.api.AddSliceToNodes(slice_id, node_ids)
1540 # Add random set of site users to slice
1541 person_ids = random.sample(site['person_ids'], randint(0, len(site['person_ids'])))
1542 for person_id in person_ids:
1543 self.api.AddPersonToSlice(person_id, slice_id)
1547 slice = self.api.GetSlices([slice_id])[0]
1548 for field in slice_fields:
1549 assert slice[field] == slice_fields[field]
1551 assert set(node_ids) == set(slice['node_ids'])
1552 assert set(person_ids) == set(slice['person_ids'])
1555 print "Added slice", slice_id, "to site", site['site_id'],
1557 print "and nodes", node_ids,
1560 print "Added users", site['person_ids'], "to slice", slice_id
1562 def UpdateSlices(self):
1564 Make random changes to any slices we may have added.
1567 for slice_id in self.slice_ids:
1569 slice_fields = random_slice("unused",self.namelengths)
1570 # Cannot change slice name
1571 if 'name' in slice_fields:
1572 del slice_fields['name']
1573 self.api.UpdateSlice(slice_id, slice_fields)
1575 slice = self.api.GetSlices([slice_id])[0]
1577 # Add slice to a random set of nodes
1578 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1579 self.api.AddSliceToNodes(slice_id, list(set(node_ids) - set(slice['node_ids'])))
1580 self.api.DeleteSliceFromNodes(slice_id, list(set(slice['node_ids']) - set(node_ids)))
1582 # Add random set of users to slice
1583 person_ids = random.sample(self.person_ids, randint(0, len(self.person_ids)))
1584 for person_id in (set(person_ids) - set(slice['person_ids'])):
1585 self.api.AddPersonToSlice(person_id, slice_id)
1586 for person_id in (set(slice['person_ids']) - set(person_ids)):
1587 self.api.DeletePersonFromSlice(person_id, slice_id)
1590 slice = self.api.GetSlices([slice_id])[0]
1591 for field in slice_fields:
1592 assert slice[field] == slice_fields[field]
1593 assert set(node_ids) == set(slice['node_ids'])
1594 assert set(person_ids) == set(slice['person_ids'])
1597 print "Updated slice", slice_id
1598 print "Added nodes", node_ids, "to slice", slice_id
1599 print "Added persons", person_ids, "to slice", slice_id
1601 def DeleteSlices(self):
1603 Delete any random slices we may have added.
1606 for slice_id in self.slice_ids:
1607 self.api.DeleteSlice(slice_id)
1610 assert not self.api.GetSlices([slice_id])
1613 print "Deleted slice", slice_id
1616 assert not self.api.GetSlices(self.slice_ids)
1620 def AddSliceTags(self, per_slice = 2):
1622 Add a number of random slices per site.
1625 if not self.slice_type_ids:
1628 for slice_id in self.slice_ids:
1629 slice = self.api.GetSlices([slice_id])[0]
1631 for i in range(per_slice):
1632 # Set a random slice/sliver attribute
1633 for tag_type_id in random.sample(self.slice_type_ids, 1):
1634 value = randstr(16, letters + '_' + digits)
1635 # Make it a sliver attribute with 50% probability
1636 if slice['node_ids']:
1637 node_id = random.sample(slice['node_ids'] + [None] * len(slice['node_ids']), 1)[0]
1641 # Add slice attribute
1643 slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value)
1645 slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value, node_id)
1647 # Should return a unique slice_tag_id
1648 assert slice_tag_id not in self.slice_tag_ids
1649 self.slice_tag_ids.append(slice_tag_id)
1652 # Check slice attribute
1653 slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1654 for field in 'tag_type_id', 'slice_id', 'node_id', 'slice_tag_id', 'value':
1655 assert slice_tag[field] == locals()[field]
1658 print "Added slice attribute", slice_tag_id, "of type", tag_type_id,
1659 if node_id is not None:
1660 print "to node", node_id,
1663 def UpdateSliceTags(self):
1665 Make random changes to any slice attributes we may have added.
1668 for slice_tag_id in self.slice_tag_ids:
1669 # Update slice attribute
1670 value = randstr(16, letters + '_' + digits)
1671 self.api.UpdateSliceTag(slice_tag_id, value)
1673 # Check slice attribute again
1674 slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1675 assert slice_tag['value'] == value
1678 print "Updated slice attribute", slice_tag_id
1680 def DeleteSliceTags(self):
1682 Delete any random slice attributes we may have added.
1685 for slice_tag_id in self.slice_tag_ids:
1686 self.api.DeleteSliceTag(slice_tag_id)
1689 assert not self.api.GetSliceTags([slice_tag_id])
1692 print "Deleted slice attribute", slice_tag_id
1695 assert not self.api.GetSliceTags(self.slice_tag_ids)
1697 self.slice_tag_ids = []
1699 # convenience for cleaning up
1700 # not exactly accurate -- use on test plcs only
1701 def WipeSitesFromLength(self):
1702 for site in self.api.GetSites():
1703 abbrev=site['abbreviated_name']
1704 # print 'matching',len(abbrev),'against',self.namelengths['abbreviated_name']
1705 if len(abbrev)==self.namelengths['abbreviated_name']:
1706 # if len(abbrev)==17:
1707 print 'wiping site %d (%s)'%(site['site_id'],site['name'])
1708 self.api.DeleteSite(site['site_id'])
1711 parser = OptionParser()
1712 parser.add_option("-c", "--check", action = "store_true", default = False,
1713 help = "Check most actions (default: %default)")
1714 parser.add_option("-q", "--quiet", action = "store_true", default = False,
1715 help = "Be quiet (default: %default)")
1716 parser.add_option("-p","--preserve", action="store_true", default =False,
1717 help = "Do not delete created objects")
1718 parser.add_option("-t", "--tiny", action = "store_true", default = False,
1719 help = "Run a tiny test (default: %default)")
1720 parser.add_option("-l", "--large", action = "store_true", default = False,
1721 help = "Run a large test (default: %default)")
1722 parser.add_option("-x", "--xlarge", action = "store_true", default = False,
1723 help = "Run an XL test (default: %default)")
1724 parser.add_option("-s", "--short-names", action="store_true", dest="short_names", default = False,
1725 help = "Generate smaller names for checking UI rendering")
1726 parser.add_option ("-f", "--foreign", action="store_true", dest="federating", default = False,
1727 help = "Create a fake peer and add items in it (no update, no delete)")
1728 parser.add_option ("-w", "--wipe", action="store_true", dest="wipe", default = False,
1729 help = "Wipe sites whose abbrev matches what the tests created")
1730 (options, args) = parser.parse_args()
1732 test = Test(api = Shell(),
1733 check = options.check,
1734 verbose = not options.quiet,
1735 preserve = options.preserve,
1736 federating = options.federating)
1738 if options.short_names:
1739 test.namelengths = Test.namelengths_short
1741 test.namelengths = Test.namelengths_default
1744 test.WipeSitesFromLength()
1748 sizes = Test.sizes_tiny
1750 sizes = Test.sizes_large
1751 elif options.xlarge:
1752 sizes = Test.sizes_xlarge
1754 sizes = Test.sizes_default
1757 if __name__ == "__main__":