3 # pylint: disable=c0111, c0302
6 # WARNING: as opposed to the rest of the python code in this repo
7 # the current script runs on top of plcsh and so it is for now
8 # pinned as python2 code
11 # Test script utility class
13 # Mark Huang <mlhuang@cs.princeton.edu>
14 # Copyright (C) 2006 The Trustees of Princeton University
17 # NOTE on porting to python3
19 # this file gets fed to plcsh on the tested myplc, so
20 # it needs to remain python2 for now
23 from string import ascii_letters as letters, digits, punctuation, whitespace
24 from optparse import OptionParser
30 from PLC.Shell import Shell
32 from random import Random
35 # note about namelengths
36 # original version uses full lengths for all fields for testing overflows and things
37 # however for a realistic test, involving a web UI, this is not appropriate, so we
38 # use smaller identifiers
41 def randfloat(min=0.0, max=1.0):
42 return float(min) + (random.random() * (float(max) - float(min)))
45 def randint(min=0, max=1):
46 return int(randfloat(min, max + 1))
48 # See "2.2 Characters" in the XML specification:
50 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
52 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
56 ascii_xml_chars = list(map(chr, [0x9, 0xA]))
57 # xmlrpclib uses xml.parsers.expat, which always converts either '\r'
58 # (#xD) or '\n' (#xA) to '\n'. So avoid using '\r', too, if this is
60 if xmlrpc.client.loads(xmlrpc.client.dumps(('\r',)))[0][0] == '\r':
61 ascii_xml_chars.append('\r')
62 ascii_xml_chars += list(map(chr, range(0x20, 0x7F - 1)))
63 low_xml_chars = list(ascii_xml_chars)
64 low_xml_chars += list(map(chr, range(0x84 + 1, 0x86 - 1)))
65 low_xml_chars += list(map(chr, range(0x9F + 1, 0xFF)))
66 valid_xml_chars = list(low_xml_chars)
67 valid_xml_chars += list(map(chr, range(0xFF + 1, 0xD7FF)))
68 valid_xml_chars += list(map(chr, range(0xE000, 0xFDD0 - 1)))
69 valid_xml_chars += list(map(chr, range(0xFDDF + 1, 0xFFFD)))
72 def randstr(length, pool=valid_xml_chars, encoding="utf-8"):
73 sample = random.sample(pool, min(length, len(pool)))
76 bytes = len(s.encode(encoding))
80 sample += random.sample(pool, min(length - bytes, len(pool)))
81 random.shuffle(sample)
87 def randhostname(namelengths):
88 # 1. Each part begins and ends with a letter or number.
89 # 2. Each part except the last can contain letters, numbers, or hyphens.
90 # 3. Each part is between 1 and 64 characters, including the trailing dot.
91 # 4. At least two parts.
92 # 5. Last part can only contain between 2 and 6 letters.
93 all_chars = letters + digits + '-'
96 randstr(namelengths['hostname1'], all_chars) +
98 randstr(namelengths['hostname1'], all_chars) +
100 randstr(namelengths['hostname2'], letters))
101 return hostname.lower()
104 def randpath(length):
106 for i in range(randint(1, 10)):
107 parts.append(randstr(randint(1, 30), ascii_xml_chars))
108 return '/'.join(parts)[0:length]
111 def randemail(namelengths):
112 return (randstr(namelengths['email'], letters + digits) + "@" + randhostname(namelengths)).lower()
115 def randkey(namelengths, bits=2048):
116 ssh_key_types = ["ssh-dss", "ssh-rsa"]
117 key_type = random.sample(ssh_key_types, 1)[0]
118 return ' '.join([key_type,
119 base64.b64encode(randstr(bits // 8).encode()).decode(),
120 randemail(namelengths)])
125 'peername': randstr(24, letters + ' ' + digits),
126 'peer_url': "https://" + randhostname({'hostname1': 8,
127 'hostname2': 3}) + ":443/PLCAPI/",
128 'key': randstr(1024, letters + digits),
129 'cacert': randstr(1024, letters + digits),
130 'shortname': randstr(1, letters) + 'LAB',
131 'hrn_root': 'planetlab.' + randstr(3, letters),
135 def random_site(namelengths):
136 sitename = randstr(namelengths['sitename'],
137 namelengths['sitename_contents'])
138 abbreviated_name = randstr(
139 namelengths['abbreviated_name'], namelengths['abbreviated_name_contents'])
141 print('nl[a] in random_site', namelengths['abbreviated_name'],
142 'actual', len(abbreviated_name))
145 'abbreviated_name': abbreviated_name,
146 'login_base': randstr(namelengths['login_base'], letters).lower(),
147 'latitude': int(randfloat(-90.0, 90.0) * 1000) / 1000.0,
148 'longitude': int(randfloat(-180.0, 180.0) * 1000) / 1000.0,
152 def random_address_type():
155 'description': randstr(254),
159 def random_address():
161 'line1': randstr(254),
162 'line2': randstr(254),
163 'line3': randstr(254),
164 'city': randstr(254),
165 'state': randstr(254),
166 'postalcode': randstr(64),
167 'country': randstr(128),
171 def random_person(namelengths):
173 'first_name': randstr(namelengths['first_name']),
174 'last_name': randstr(namelengths['last_name']),
175 'email': randemail(namelengths),
177 # Accounts are disabled by default
179 'password': randstr(254),
183 def random_key(key_types, namelengths):
185 'key_type': random.sample(key_types, 1)[0],
186 'key': randkey(namelengths)
190 def random_tag_type(role_ids):
191 return {'tagname': randstr(12, letters+digits),
192 'category': randstr(4, letters+digits)+'/'+randstr(6, letters+digits),
193 'description': randstr(128, letters+digits+whitespace+punctuation),
197 def random_nodegroup():
198 return {'groupname': randstr(30, letters+digits+whitespace)}
201 def random_roles(role_ids):
202 nb_roles = len(role_ids)
203 return random.sample(role_ids, random.choice(list(range(1, nb_roles+1))))
206 tag_fields = ['arch']
209 def random_node(node_types, boot_states, namelengths):
211 'hostname': randhostname(namelengths),
212 'node_type': random.sample(node_types, 1)[0],
213 'boot_state': random.sample(boot_states, 1)[0],
214 'model': randstr(namelengths['model']),
215 'version': randstr(64),
216 # for testing node tags
221 def random_interface(method, type, namelengths):
225 'bwlimit': randint(500000, 10000000),
229 ip = randint(0, 0xffffffff)
230 netmask = (0xffffffff << randint(2, 31)) & 0xffffffff
231 network = ip & netmask
232 broadcast = ((ip & netmask) | ~netmask) & 0xffffffff
233 gateway = randint(network + 1, broadcast - 1)
234 dns1 = randint(0, 0xffffffff)
236 for field in 'ip', 'netmask', 'network', 'broadcast', 'gateway', 'dns1':
237 interface_fields[field] = socket.inet_ntoa(
238 struct.pack('>L', locals()[field]))
240 interface_fields['hostname'] = randhostname(namelengths)
242 return interface_fields
249 def random_pcu(namelengths):
251 'hostname': randhostname(namelengths),
252 'ip': socket.inet_ntoa(struct.pack('>L', randint(0, 0xffffffff))),
253 'protocol': randstr(16),
254 'username': randstr(254),
255 'password': randstr(254),
256 'notes': randstr(254),
257 'model': randstr(32),
261 def random_conf_file():
263 'enabled': bool(randint()),
264 'source': randpath(255),
265 'dest': randpath(255),
266 'file_permissions': "%#o" % randint(0, 512),
267 'file_owner': randstr(32, letters + '_' + digits),
268 'file_group': randstr(32, letters + '_' + digits),
269 'preinstall_cmd': randpath(100),
270 'postinstall_cmd': randpath(100),
271 'error_cmd': randpath(100),
272 'ignore_cmd_errors': bool(randint()),
273 'always_update': bool(randint()),
277 def random_slice(login_base, namelengths):
279 'name': login_base + "_" + randstr(11, letters).lower(),
280 'url': "http://" + randhostname(namelengths) + "/",
281 'description': randstr(2048),
289 'addresses_per_site': 1,
290 'persons_per_site': 1,
291 'keys_per_person': 1,
295 'interfaces_per_node': 1,
299 'slices_per_site': 1,
300 'attributes_per_slice': 1,
306 'addresses_per_site': 2,
307 'persons_per_site': 4,
308 'keys_per_person': 2,
312 'interfaces_per_node': 1,
316 'slices_per_site': 4,
317 'attributes_per_slice': 2,
323 'addresses_per_site': 2,
324 'persons_per_site': 5,
325 'keys_per_person': 2,
329 'interfaces_per_node': 2,
333 'slices_per_site': 10,
334 'attributes_per_slice': 4,
340 'addresses_per_site': 2,
341 'persons_per_site': 5,
342 'keys_per_person': 2,
346 'interfaces_per_node': 2,
350 'slices_per_site': 10,
351 'attributes_per_slice': 4,
354 namelengths_default = {
359 'sitename_contents': letters+digits,
360 'abbreviated_name': 50,
361 'abbreviated_name_contents': letters+digits+whitespace+punctuation,
368 namelengths_short = {
373 'sitename_contents': letters+digits,
374 'abbreviated_name': 24,
375 'abbreviated_name_contents': letters+digits+whitespace+punctuation,
382 def __init__(self, api, check, verbose, preserve, federating):
385 self.verbose = verbose
386 self.preserve = preserve
387 self.federating = federating
390 self.address_type_ids = []
391 self.address_ids = []
394 self.slice_type_ids = []
395 self.nodegroup_type_ids = []
396 self.ilink_type_ids = []
397 self.nodegroup_ids = []
399 self.interface_ids = []
402 self.conf_file_ids = []
404 self.slice_tag_ids = []
407 return [len(x) for x in (
408 self.api.GetNodes({}, ['node_id']),
409 self.api.GetSites({}, ['site_id']),
410 self.api.GetPersons({}, ['person_id']),
411 self.api.GetSlices({}, ['slice_id']),
414 def Run(self, **kwds):
416 Run a complete database and API consistency test. Populates
417 the database with a set of random entities, updates them, then
418 deletes them. Examples:
420 test.Run() # Defaults
421 test.Run(**Test.sizes_default) # Defaults
422 test.Run(**Test.sizes_tiny) # Tiny set
423 test.Run(sites = 123, slices_per_site = 4) # Defaults with overrides
426 cardinals_before = self.Cardinals()
427 print('Cardinals before test (n,s,p,sl)', cardinals_before)
430 # if federating : we're done
432 if self.federating or self.preserve:
433 print('Preserving - update & delete skipped')
438 cardinals_after = self.Cardinals()
439 print('Cardinals after test (n,s,p,sl)', cardinals_after)
441 if cardinals_before != cardinals_after:
443 'cardinals before and after differ - check deletion mechanisms')
445 def Add(self, **kwds):
447 Populate the database with a set of random entities. Examples:
452 sizes = self.sizes_default.copy()
455 if not self.federating:
456 self.AddSites(sizes['sites'])
457 self.AddAddressTypes(sizes['address_types'])
458 self.AddAddresses(sizes['addresses_per_site'])
459 self.AddPersons(sizes['persons_per_site'])
460 self.AddKeys(sizes['keys_per_person'])
461 self.AddTagTypes(sizes['slice_tags'],
462 sizes['nodegroups'], sizes['ilinks'])
463 self.AddNodeGroups(sizes['nodegroups'])
464 self.AddNodes(sizes['nodes_per_site'])
465 self.AddInterfaces(sizes['interfaces_per_node'])
466 self.AddIlinks(sizes['ilinks'])
467 self.AddPCUs(sizes['pcus_per_site'])
468 self.AddConfFiles(sizes['conf_files'])
469 self.AddSlices(sizes['slices_per_site'])
470 self.AddSliceTags(sizes['attributes_per_slice'])
474 self.AddSites(sizes['sites'])
475 self.AddPersons(sizes['persons_per_site'])
476 self.AddKeys(sizes['keys_per_person'])
477 self.AddNodes(sizes['nodes_per_site'])
478 self.AddSlices(sizes['slices_per_site'])
479 # create peer and add newly created entities
484 self.UpdateAddressTypes()
485 self.UpdateAddresses()
488 self.UpdateTagTypes()
489 self.UpdateNodeGroups()
491 self.UpdateInterfaces()
494 self.UpdateConfFiles()
496 self.UpdateSliceTags()
499 self.DeleteSliceTags()
502 self.DeleteConfFiles()
505 self.DeleteInterfaces()
508 self.DeleteNodeGroups()
509 self.DeleteTagTypes()
510 self.DeleteAddresses()
511 self.DeleteAddressTypes()
514 # record current (old) objects
515 def RecordStatus(self):
516 self.old_site_ids = [s['site_id']
517 for s in self.api.GetSites({}, ['site_id'])]
518 self.old_person_ids = [s['person_id']
519 for s in self.api.GetPersons({}, ['person_id'])]
520 self.old_key_ids = [s['key_id']
521 for s in self.api.GetKeys({}, ['key_id'])]
522 self.old_node_ids = [s['node_id']
523 for s in self.api.GetNodes({}, ['node_id'])]
524 self.old_slice_ids = [s['slice_id']
525 for s in self.api.GetSlices({}, ['slice_id'])]
528 peer_id = self.api.AddPeer(random_peer())
529 peer = GetPeers([peer_id])[0]
531 print("Added peer", peer_id)
533 # add new sites (the ones not in self.site_ids) in the peer
535 for site in self.api.GetSites({'~site_id': self.old_site_ids}):
536 peer.add_site(site, site['site_id'])
537 for person in self.api.GetPersons({'~person_id': self.old_person_ids}):
538 peer.add_person(person, person['person_id'])
539 for key in self.api.GetKeys({'~key_id': self.old_key_ids}):
540 peer.add_key(key, key['key_id'])
541 for node in self.api.GetNodes({'~node_id': self.old_node_ids}):
542 peer.add_node(node, node['node_id'])
543 for slice in self.api.GetSlices({'~slice_id': self.old_slice_ids}):
544 peer.add_slice(slice, slice['slice_id'])
546 def AddSites(self, n=10):
548 Add a number of random sites.
553 site_fields = random_site(self.namelengths)
554 site_id = self.api.AddSite(site_fields)
556 # Should return a unique site_id
557 assert site_id not in self.site_ids
558 self.site_ids.append(site_id)
560 # Enable slice creation
561 site_fields['max_slices'] = randint(1, 10)
562 self.api.UpdateSite(site_id, site_fields)
566 site = self.api.GetSites([site_id])[0]
567 for field in site_fields:
568 assert site[field] == site_fields[field]
571 print("Added site", site_id)
573 def UpdateSites(self):
575 Make random changes to any sites we may have added.
578 for site_id in self.site_ids:
580 site_fields = random_site(self.namelengths)
581 # Do not change login_base
582 if 'login_base' in site_fields:
583 del site_fields['login_base']
584 self.api.UpdateSite(site_id, site_fields)
588 site = self.api.GetSites([site_id])[0]
589 for field in site_fields:
590 assert site[field] == site_fields[field]
593 print("Updated site", site_id)
595 def DeleteSites(self):
597 Delete any random sites we may have added.
600 for site_id in self.site_ids:
601 self.api.DeleteSite(site_id)
604 assert not self.api.GetSites([site_id])
607 print("Deleted site", site_id)
610 assert not self.api.GetSites(self.site_ids)
614 def AddAddressTypes(self, n=2):
616 Add a number of random address types.
620 address_type_fields = random_address_type()
621 address_type_id = self.api.AddAddressType(address_type_fields)
623 # Should return a unique address_type_id
624 assert address_type_id not in self.address_type_ids
625 self.address_type_ids.append(address_type_id)
629 address_type = self.api.GetAddressTypes([address_type_id])[0]
630 for field in address_type_fields:
631 assert address_type[field] == address_type_fields[field]
634 print("Added address type", address_type_id)
636 def UpdateAddressTypes(self):
638 Make random changes to any address types we may have added.
641 for address_type_id in self.address_type_ids:
642 # Update address_type
643 address_type_fields = random_address_type()
644 self.api.UpdateAddressType(address_type_id, address_type_fields)
648 address_type = self.api.GetAddressTypes([address_type_id])[0]
649 for field in address_type_fields:
650 assert address_type[field] == address_type_fields[field]
653 print("Updated address_type", address_type_id)
655 def DeleteAddressTypes(self):
657 Delete any random address types we may have added.
660 for address_type_id in self.address_type_ids:
661 self.api.DeleteAddressType(address_type_id)
664 assert not self.api.GetAddressTypes([address_type_id])
667 print("Deleted address type", address_type_id)
670 assert not self.api.GetAddressTypes(self.address_type_ids)
672 self.address_type_ids = []
674 def AddAddresses(self, per_site=2):
676 Add a number of random addresses to each site.
679 for site_id in self.site_ids:
680 for i in range(per_site):
681 address_fields = random_address()
682 address_id = self.api.AddSiteAddress(site_id, address_fields)
684 # Should return a unique address_id
685 assert address_id not in self.address_ids
686 self.address_ids.append(address_id)
688 # Add random address type
689 if self.address_type_ids:
690 for address_type_id in random.sample(self.address_type_ids, 1):
691 self.api.AddAddressTypeToAddress(
692 address_type_id, address_id)
696 address = self.api.GetAddresses([address_id])[0]
697 for field in address_fields:
698 assert address[field] == address_fields[field]
701 print("Added address", address_id, "to site", site_id)
703 def UpdateAddresses(self):
705 Make random changes to any addresses we may have added.
708 for address_id in self.address_ids:
710 address_fields = random_address()
711 self.api.UpdateAddress(address_id, address_fields)
715 address = self.api.GetAddresses([address_id])[0]
716 for field in address_fields:
717 assert address[field] == address_fields[field]
720 print("Updated address", address_id)
722 def DeleteAddresses(self):
724 Delete any random addresses we may have added.
727 for address_id in self.address_ids:
728 # Remove address types
729 address = self.api.GetAddresses([address_id])[0]
730 for address_type_id in address['address_type_ids']:
731 self.api.DeleteAddressTypeFromAddress(
732 address_type_id, address_id)
735 address = self.api.GetAddresses([address_id])[0]
736 assert not address['address_type_ids']
738 self.api.DeleteAddress(address_id)
741 assert not self.api.GetAddresses([address_id])
744 print("Deleted address", address_id)
747 assert not self.api.GetAddresses(self.address_ids)
749 self.address_ids = []
751 def AddPersons(self, per_site=10):
753 Add a number of random users to each site.
756 for site_id in self.site_ids:
757 for i in range(per_site):
759 person_fields = random_person(self.namelengths)
760 person_id = self.api.AddPerson(person_fields)
762 # Should return a unique person_id
763 assert person_id not in self.person_ids
764 self.person_ids.append(person_id)
768 person = self.api.GetPersons([person_id])[0]
769 for field in person_fields:
770 if field != 'password':
771 assert person[field] == person_fields[field]
773 auth = {'AuthMethod': "password",
774 'Username': person_fields['email'],
775 'AuthString': person_fields['password']}
778 # Check that user is disabled
780 assert not self.api.AuthCheck(auth)
784 # Add random set of roles
785 role_ids = random.sample([20, 30, 40], randint(1, 3))
786 for role_id in role_ids:
787 self.api.AddRoleToPerson(role_id, person_id)
790 person = self.api.GetPersons([person_id])[0]
791 assert set(role_ids) == set(person['role_ids'])
794 self.api.UpdatePerson(person_id, {'enabled': True})
797 # Check that user is enabled
798 assert self.api.AuthCheck(auth)
800 # Associate user with site
801 self.api.AddPersonToSite(person_id, site_id)
802 self.api.SetPersonPrimarySite(person_id, site_id)
805 person = self.api.GetPersons([person_id])[0]
806 assert person['site_ids'][0] == site_id
809 print("Added user", person_id, "to site", site_id)
811 def UpdatePersons(self):
813 Make random changes to any users we may have added.
816 for person_id in self.person_ids:
818 person_fields = random_person(self.namelengths)
820 person_fields['enabled'] = True
821 self.api.UpdatePerson(person_id, person_fields)
825 person = self.api.GetPersons([person_id])[0]
826 for field in person_fields:
827 if field != 'password':
828 assert person[field] == person_fields[field]
831 print("Updated person", person_id)
833 person = self.api.GetPersons([person_id])[0]
835 # Associate user with a random set of sites
836 site_ids = random.sample(
837 self.site_ids, randint(0, len(self.site_ids)))
838 for site_id in (set(site_ids) - set(person['site_ids'])):
839 self.api.AddPersonToSite(person_id, site_id)
840 for site_id in (set(person['site_ids']) - set(site_ids)):
841 self.api.DeletePersonFromSite(person_id, site_id)
844 self.api.SetPersonPrimarySite(person_id, site_ids[0])
847 person = self.api.GetPersons([person_id])[0]
848 assert set(site_ids) == set(person['site_ids'])
851 print("Updated person", person_id, "to sites", site_ids)
853 def DeletePersons(self):
855 Delete any random users we may have added.
858 for person_id in self.person_ids:
860 person = self.api.GetPersons([person_id])[0]
861 for site_id in person['site_ids']:
862 self.api.DeletePersonFromSite(person_id, site_id)
865 person = self.api.GetPersons([person_id])[0]
866 assert not person['site_ids']
869 for role_id in person['role_ids']:
870 self.api.DeleteRoleFromPerson(role_id, person_id)
873 person = self.api.GetPersons([person_id])[0]
874 assert not person['role_ids']
877 self.api.UpdatePerson(person_id, {'enabled': False})
880 person = self.api.GetPersons([person_id])[0]
881 assert not person['enabled']
884 self.api.DeletePerson(person_id)
887 assert not self.api.GetPersons([person_id])
890 print("Deleted user", person_id)
893 assert not self.api.GetPersons(self.person_ids)
897 def AddKeys(self, per_person=2):
899 Add a number of random keys to each user.
902 key_types = self.api.GetKeyTypes()
904 raise Exception("No key types")
906 for person_id in self.person_ids:
907 for i in range(per_person):
909 key_fields = random_key(key_types, self.namelengths)
910 key_id = self.api.AddPersonKey(person_id, key_fields)
912 # Should return a unique key_id
913 assert key_id not in self.key_ids
914 self.key_ids.append(key_id)
918 key = self.api.GetKeys([key_id])[0]
919 for field in key_fields:
920 assert key[field] == key_fields[field]
922 # Add and immediately blacklist a key
923 key_fields = random_key(key_types, self.namelengths)
924 key_id = self.api.AddPersonKey(person_id, key_fields)
926 self.api.BlacklistKey(key_id)
928 # Is effectively deleted
929 assert not self.api.GetKeys([key_id])
931 # Cannot be added again
933 key_id = self.api.AddPersonKey(person_id, key_fields)
935 except Exception as e:
939 print("Added key", key_id, "to user", person_id)
941 def UpdateKeys(self):
943 Make random changes to any keys we may have added.
946 key_types = self.api.GetKeyTypes()
948 raise Exception("No key types")
950 for key_id in self.key_ids:
952 key_fields = random_key(key_types, self.namelengths)
953 self.api.UpdateKey(key_id, key_fields)
957 key = self.api.GetKeys([key_id])[0]
958 for field in key_fields:
959 assert key[field] == key_fields[field]
962 print("Updated key", key_id)
964 def DeleteKeys(self):
966 Delete any random keys we may have added.
969 for key_id in self.key_ids:
970 self.api.DeleteKey(key_id)
973 assert not self.api.GetKeys([key_id])
976 print("Deleted key", key_id)
979 assert not self.api.GetKeys(self.key_ids)
983 def AddNodeGroups(self, n=10):
985 Add a number of random node groups.
990 tag_type_id = self.nodegroup_type_ids[i]
991 tagname = self.api.GetTagTypes([tag_type_id])[0]['tagname']
994 groupname = random_nodegroup()['groupname']
996 nodegroup_id = self.api.AddNodeGroup(groupname, tagname, value)
998 # Should return a unique nodegroup_id
999 assert nodegroup_id not in self.nodegroup_ids
1000 self.nodegroup_ids.append(nodegroup_id)
1004 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1005 assert nodegroup['groupname'] == groupname
1006 assert nodegroup['tagname'] == tagname
1007 assert nodegroup['value'] == value
1010 print("Added node group", nodegroup_id)
1012 def UpdateNodeGroups(self):
1014 Make random changes to any node groups we may have added.
1017 for nodegroup_id in self.nodegroup_ids:
1019 groupname = random_nodegroup()['groupname']
1020 # cannot change tagname
1021 nodegroup_fields = {'groupname': groupname}
1022 self.api.UpdateNodeGroup(nodegroup_id, nodegroup_fields)
1026 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1027 for field in nodegroup_fields:
1028 assert nodegroup[field] == nodegroup_fields[field]
1031 print("Updated node group", nodegroup_id)
1033 def DeleteNodeGroups(self):
1035 Delete any random node groups we may have added.
1038 for nodegroup_id in self.nodegroup_ids:
1039 self.api.DeleteNodeGroup(nodegroup_id)
1042 assert not self.api.GetNodeGroups([nodegroup_id])
1045 print("Deleted node group", nodegroup_id)
1048 assert not self.api.GetNodeGroups(self.nodegroup_ids)
1050 self.nodegroup_ids = []
1052 def AddNodes(self, per_site=2):
1054 Add a number of random nodes to each site. Each node will also
1055 be added to a random node group if AddNodeGroups() was
1059 node_types = self.api.GetNodeTypes()
1061 raise Exception("No node types")
1062 boot_states = self.api.GetBootStates()
1064 raise Exception("No boot states")
1066 for site_id in self.site_ids:
1067 for i in range(per_site):
1069 node_fields = random_node(
1070 node_types, boot_states, self.namelengths)
1071 node_id = self.api.AddNode(site_id, node_fields)
1073 # Should return a unique node_id
1074 assert node_id not in self.node_ids
1075 self.node_ids.append(node_id)
1077 # Add to a random set of node groups
1078 nodegroup_ids = random.sample(
1079 self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
1080 for nodegroup_id in nodegroup_ids:
1081 tagname = self.api.GetNodeGroups(
1082 [nodegroup_id])[0]['tagname']
1083 self.api.AddNodeTag(node_id, tagname, 'yes')
1087 node = self.api.GetNodes([node_id])[0]
1088 for field in node_fields:
1089 if field not in tag_fields:
1090 assert node[field] == node_fields[field]
1093 print("Added node", node_id)
1095 def UpdateNodes(self):
1097 Make random changes to any nodes we may have added.
1100 node_types = self.api.GetNodeTypes()
1102 raise Exception("No node types")
1103 boot_states = self.api.GetBootStates()
1105 raise Exception("No boot states")
1107 for node_id in self.node_ids:
1109 node_fields = random_node(
1110 node_types, boot_states, self.namelengths)
1111 self.api.UpdateNode(node_id, node_fields)
1113 node = self.api.GetNodes([node_id])[0]
1115 # Add to a random set of node groups
1116 nodegroup_ids = random.sample(
1117 self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
1118 for nodegroup_id in (set(nodegroup_ids) - set(node['nodegroup_ids'])):
1119 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1120 tagname = nodegroup['tagname']
1121 node_tags = self.api.GetNodeTags(
1122 {'node_id': node_id, 'tagname': tagname})
1124 self.api.AddNodeTag(node_id, tagname, 'yes')
1126 node_tag = node_tags[0]
1127 self.api.UpdateNodeTag(node_tag['node_tag_id'], 'yes')
1128 for nodegroup_id in (set(node['nodegroup_ids']) - set(nodegroup_ids)):
1129 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1130 tagname = nodegroup['tagname']
1131 node_tags = self.api.GetNodeTags(
1132 {'node_id': node_id, 'tagname': tagname})
1134 self.api.AddNodeTag(node_id, tagname, 'no')
1136 node_tag = node_tags[0]
1137 self.api.UpdateNodeTag(node_tag['node_tag_id'], 'no')
1141 node = self.api.GetNodes([node_id])[0]
1142 for field in node_fields:
1143 if field not in tag_fields:
1144 if node[field] != node_fields[field]:
1146 "Unexpected field %s in node after GetNodes()" % field)
1147 assert set(nodegroup_ids) == set(node['nodegroup_ids'])
1149 # again but we are now fetching 'arch' explicitly
1150 node2 = self.api.GetNodes(
1151 [node_id], list(node_fields.keys()))[0]
1152 for field in node_fields:
1153 if node2[field] != node_fields[field]:
1155 "Unexpected field %s in node after GetNodes(tags)" % field)
1158 print("Updated node", node_id)
1160 def DeleteNodes(self):
1162 Delete any random nodes we may have added.
1165 for node_id in self.node_ids:
1166 # Remove from node groups
1167 node = self.api.GetNodes([node_id])[0]
1168 for node_tag in self.api.GetNodeTags({'node_id': node_id}):
1169 self.api.UpdateNodeTag(node_tag['node_tag_id'], '')
1172 node = self.api.GetNodes([node_id])[0]
1173 assert not node['nodegroup_ids']
1175 self.api.DeleteNode(node_id)
1178 assert not self.api.GetNodes([node_id])
1181 print("Deleted node", node_id)
1184 assert not self.api.GetNodes(self.node_ids)
1188 def AddInterfaces(self, per_node=1):
1190 Add a number of random network interfaces to each node.
1193 network_methods = self.api.GetNetworkMethods()
1194 if not network_methods:
1195 raise Exception("No network methods")
1197 network_types = self.api.GetNetworkTypes()
1198 if not network_types:
1199 raise Exception("No network types")
1201 for node_id in self.node_ids:
1202 for i in range(per_node):
1203 method = random.sample(network_methods, 1)[0]
1204 type = random.sample(network_types, 1)[0]
1207 interface_fields = random_interface(
1208 method, type, self.namelengths)
1209 interface_id = self.api.AddInterface(node_id, interface_fields)
1211 # Should return a unique interface_id
1212 assert interface_id not in self.interface_ids
1213 self.interface_ids.append(interface_id)
1217 interface = self.api.GetInterfaces([interface_id])[0]
1218 for field in interface_fields:
1219 assert interface[field] == interface_fields[field]
1222 print("Added interface", interface_id, "to node", node_id)
1224 def UpdateInterfaces(self):
1226 Make random changes to any network interfaces we may have added.
1229 network_methods = self.api.GetNetworkMethods()
1230 if not network_methods:
1231 raise Exception("No network methods")
1233 network_types = self.api.GetNetworkTypes()
1234 if not network_types:
1235 raise Exception("No network types")
1237 for interface_id in self.interface_ids:
1238 method = random.sample(network_methods, 1)[0]
1239 type = random.sample(network_types, 1)[0]
1242 interface_fields = random_interface(method, type, self.namelengths)
1243 self.api.UpdateInterface(interface_id, interface_fields)
1247 interface = self.api.GetInterfaces([interface_id])[0]
1248 for field in interface_fields:
1249 assert interface[field] == interface_fields[field]
1252 print("Updated interface", interface_id)
1254 def DeleteInterfaces(self):
1256 Delete any random network interfaces we may have added.
1259 for interface_id in self.interface_ids:
1260 self.api.DeleteInterface(interface_id)
1263 assert not self.api.GetInterfaces([interface_id])
1266 print("Deleted interface", interface_id)
1269 assert not self.api.GetInterfaces(self.interface_ids)
1271 self.interface_ids = []
1273 def AddIlinks(self, n):
1275 Add random links between interfaces.
1279 src = random.sample(self.interface_ids, 1)[0]
1280 dst = random.sample(self.interface_ids, 1)[0]
1281 ilink_id = self.api.AddIlink(src, dst,
1282 self.ilink_type_ids[i],
1285 assert ilink_id not in self.ilink_ids
1286 self.ilink_ids.append(ilink_id)
1289 print('Added Ilink', ilink_id,
1290 ' - attached interface', src, 'to', dst)
1293 retrieve = GetIlinks({'src_interface_id': src, 'dst_interface_id': dst,
1294 'tag_type_id': self.ilink_type_ids[i]})
1295 assert ilink_id == retrieve[0]['ilink_id']
1297 def UpdateIlinks(self):
1299 for ilink_id in self.ilink_ids:
1300 new_value = random_ilink()
1301 self.api.UpdateIlink(ilink_id, new_value)
1304 ilink = self.api.GetIlinks([ilink_id])[0]
1305 assert ilink['value'] == new_value
1308 print('Updated Ilink', ilink_id)
1310 def DeleteIlinks(self):
1311 for ilink_id in self.ilink_ids:
1312 self.api.DeleteIlink(ilink_id)
1315 assert not self.api.GetIlinks({'ilink_id': ilink_id})
1318 print('Deleted Ilink', ilink_id)
1321 assert not self.api.GetIlinks(self.ilink_ids)
1325 def AddPCUs(self, per_site=1):
1327 Add a number of random PCUs to each site. Each node at the
1328 site will be added to a port on the PCU if AddNodes() was
1332 for site_id in self.site_ids:
1333 for i in range(per_site):
1335 pcu_fields = random_pcu(self.namelengths)
1336 pcu_id = self.api.AddPCU(site_id, pcu_fields)
1338 # Should return a unique pcu_id
1339 assert pcu_id not in self.pcu_ids
1340 self.pcu_ids.append(pcu_id)
1342 # Add each node at this site to a different port on this PCU
1343 site = self.api.GetSites([site_id])[0]
1344 port = randint(1, 10)
1345 for node_id in site['node_ids']:
1346 self.api.AddNodeToPCU(node_id, pcu_id, port)
1351 pcu = self.api.GetPCUs([pcu_id])[0]
1352 for field in pcu_fields:
1353 assert pcu[field] == pcu_fields[field]
1356 print("Added PCU", pcu_id, "to site", site_id)
1358 def UpdatePCUs(self):
1360 Make random changes to any PCUs we may have added.
1363 for pcu_id in self.pcu_ids:
1365 pcu_fields = random_pcu(self.namelengths)
1366 self.api.UpdatePCU(pcu_id, pcu_fields)
1370 pcu = self.api.GetPCUs([pcu_id])[0]
1371 for field in pcu_fields:
1372 assert pcu[field] == pcu_fields[field]
1375 print("Updated PCU", pcu_id)
1377 def DeletePCUs(self):
1379 Delete any random nodes we may have added.
1382 for pcu_id in self.pcu_ids:
1383 # Remove nodes from PCU
1384 pcu = self.api.GetPCUs([pcu_id])[0]
1385 for node_id in pcu['node_ids']:
1386 self.api.DeleteNodeFromPCU(node_id, pcu_id)
1389 pcu = self.api.GetPCUs([pcu_id])[0]
1390 assert not pcu['node_ids']
1392 self.api.DeletePCU(pcu_id)
1395 assert not self.api.GetPCUs([pcu_id])
1398 print("Deleted PCU", pcu_id)
1401 assert not self.api.GetPCUs(self.pcu_ids)
1405 def AddConfFiles(self, n=10):
1407 Add a number of random global configuration files.
1413 # Add a random configuration file
1414 conf_files.append(random_conf_file())
1417 # Add a nodegroup override file
1418 nodegroup_conf_file = conf_files[0].copy()
1419 nodegroup_conf_file['source'] = randpath(255)
1420 conf_files.append(nodegroup_conf_file)
1422 # Add a node override file
1423 node_conf_file = conf_files[0].copy()
1424 node_conf_file['source'] = randpath(255)
1425 conf_files.append(node_conf_file)
1427 for conf_file_fields in conf_files:
1428 conf_file_id = self.api.AddConfFile(conf_file_fields)
1430 # Should return a unique conf_file_id
1431 assert conf_file_id not in self.conf_file_ids
1432 self.conf_file_ids.append(conf_file_id)
1435 if conf_file_fields == nodegroup_conf_file and self.nodegroup_ids:
1436 nodegroup_id = random.sample(self.nodegroup_ids, 1)[0]
1437 self.api.AddConfFileToNodeGroup(conf_file_id, nodegroup_id)
1442 if conf_file_fields == node_conf_file and self.node_ids:
1443 node_id = random.sample(self.node_ids, 1)[0]
1444 self.api.AddConfFileToNode(conf_file_id, node_id)
1449 # Check configuration file
1450 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1451 for field in conf_file_fields:
1452 assert conf_file[field] == conf_file_fields[field]
1455 print("Added configuration file", conf_file_id, end=' ')
1456 if nodegroup_id is not None:
1457 print("to node group", nodegroup_id, end=' ')
1458 elif node_id is not None:
1459 print("to node", node_id, end=' ')
1462 def UpdateConfFiles(self):
1464 Make random changes to any configuration files we may have added.
1467 for conf_file_id in self.conf_file_ids:
1468 # Update configuration file
1469 conf_file_fields = random_conf_file()
1470 # Do not update dest so that it remains an override if set
1471 if 'dest' in conf_file_fields:
1472 del conf_file_fields['dest']
1473 self.api.UpdateConfFile(conf_file_id, conf_file_fields)
1476 # Check configuration file
1477 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1478 for field in conf_file_fields:
1479 assert conf_file[field] == conf_file_fields[field]
1482 print("Updated configuration file", conf_file_id)
1484 def DeleteConfFiles(self):
1486 Delete any random configuration files we may have added.
1489 for conf_file_id in self.conf_file_ids:
1490 self.api.DeleteConfFile(conf_file_id)
1493 assert not self.api.GetConfFiles([conf_file_id])
1496 print("Deleted configuration file", conf_file_id)
1499 assert not self.api.GetConfFiles(self.conf_file_ids)
1501 self.conf_file_ids = []
1503 def AddTagTypes(self, n_sa, n_ng, n_il):
1505 Add as many tag types as there are nodegroups,
1506 will use value=yes for each nodegroup
1509 roles = self.api.GetRoles()
1511 raise Exception("No roles")
1512 role_ids = [role['role_id'] for role in roles]
1514 for i in range(n_sa + n_ng + n_il):
1515 tag_type_fields = random_tag_type(role_ids)
1516 tag_type_id = self.api.AddTagType(tag_type_fields)
1518 assert tag_type_id not in \
1519 self.slice_type_ids + \
1520 self.nodegroup_type_ids + \
1523 tt_role_ids = random_roles(role_ids)
1524 for tt_role_id in tt_role_ids:
1525 self.api.AddRoleToTagType(tt_role_id, tag_type_id)
1528 self.slice_type_ids.append(tag_type_id)
1530 self.nodegroup_type_ids.append(tag_type_id)
1532 self.ilink_type_ids.append(tag_type_id)
1535 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1536 for field in tag_type_fields:
1537 assert tag_type[field] == tag_type_fields[field]
1538 for tt_role_id in tt_role_ids:
1539 assert tt_role_id in tag_type['role_ids']
1541 print("Created tag type", tag_type_id)
1543 def UpdateTagTypes(self):
1545 Make random changes to any slice attribute types we may have added.
1548 roles = self.api.GetRoles()
1550 raise Exception("No roles")
1551 role_ids = [role['role_id'] for role in roles]
1553 for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1554 # Update slice attribute type
1555 tag_type_fields = random_tag_type(role_ids)
1556 self.api.UpdateTagType(tag_type_id, tag_type_fields)
1559 # Check slice attribute type
1560 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1561 for field in tag_type_fields:
1562 assert tag_type[field] == tag_type_fields[field]
1564 print("Updated tag type", tag_type_id)
1566 def DeleteTagTypes(self):
1568 Delete any random slice attribute types we may have added.
1571 for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1572 self.api.DeleteTagType(tag_type_id)
1575 assert not self.api.GetTagTypes([tag_type_id])
1578 print("Deleted tag type", tag_type_id)
1581 assert not self.api.GetTagTypes(
1582 self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1584 self.slice_type_ids = []
1585 self.nodegroup_type_ids = []
1587 def AddSlices(self, per_site=10):
1589 Add a number of random slices per site.
1592 for site in self.api.GetSites(self.site_ids):
1593 for i in range(min(per_site, site['max_slices'])):
1595 slice_fields = random_slice(
1596 site['login_base'], self.namelengths)
1597 slice_id = self.api.AddSlice(slice_fields)
1599 # Should return a unique slice_id
1600 assert slice_id not in self.slice_ids
1601 self.slice_ids.append(slice_id)
1603 # Add slice to a random set of nodes
1604 node_ids = random.sample(
1605 self.node_ids, randint(0, len(self.node_ids)))
1607 self.api.AddSliceToNodes(slice_id, node_ids)
1609 # Add random set of site users to slice
1610 person_ids = random.sample(
1611 site['person_ids'], randint(0, len(site['person_ids'])))
1612 for person_id in person_ids:
1613 self.api.AddPersonToSlice(person_id, slice_id)
1617 slice = self.api.GetSlices([slice_id])[0]
1618 for field in slice_fields:
1619 assert slice[field] == slice_fields[field]
1621 assert set(node_ids) == set(slice['node_ids'])
1622 assert set(person_ids) == set(slice['person_ids'])
1625 print("Added slice", slice_id, "to site",
1626 site['site_id'], end=' ')
1628 print("and nodes", node_ids, end=' ')
1631 print("Added users",
1632 site['person_ids'], "to slice", slice_id)
1634 def UpdateSlices(self):
1636 Make random changes to any slices we may have added.
1639 for slice_id in self.slice_ids:
1641 slice_fields = random_slice("unused", self.namelengths)
1642 # Cannot change slice name
1643 if 'name' in slice_fields:
1644 del slice_fields['name']
1645 self.api.UpdateSlice(slice_id, slice_fields)
1647 slice = self.api.GetSlices([slice_id])[0]
1649 # Add slice to a random set of nodes
1650 node_ids = random.sample(
1651 self.node_ids, randint(0, len(self.node_ids)))
1652 self.api.AddSliceToNodes(slice_id, list(
1653 set(node_ids) - set(slice['node_ids'])))
1654 self.api.DeleteSliceFromNodes(slice_id, list(
1655 set(slice['node_ids']) - set(node_ids)))
1657 # Add random set of users to slice
1658 person_ids = random.sample(
1659 self.person_ids, randint(0, len(self.person_ids)))
1660 for person_id in (set(person_ids) - set(slice['person_ids'])):
1661 self.api.AddPersonToSlice(person_id, slice_id)
1662 for person_id in (set(slice['person_ids']) - set(person_ids)):
1663 self.api.DeletePersonFromSlice(person_id, slice_id)
1666 slice = self.api.GetSlices([slice_id])[0]
1667 for field in slice_fields:
1668 assert slice[field] == slice_fields[field]
1669 assert set(node_ids) == set(slice['node_ids'])
1670 assert set(person_ids) == set(slice['person_ids'])
1673 print("Updated slice", slice_id)
1674 print("Added nodes", node_ids, "to slice", slice_id)
1675 print("Added persons", person_ids, "to slice", slice_id)
1677 def DeleteSlices(self):
1679 Delete any random slices we may have added.
1682 for slice_id in self.slice_ids:
1683 self.api.DeleteSlice(slice_id)
1686 assert not self.api.GetSlices([slice_id])
1689 print("Deleted slice", slice_id)
1692 assert not self.api.GetSlices(self.slice_ids)
1696 def AddSliceTags(self, per_slice=2):
1698 Add a number of random slices per site.
1701 if not self.slice_type_ids:
1704 for slice_id in self.slice_ids:
1705 slice = self.api.GetSlices([slice_id])[0]
1707 for i in range(per_slice):
1708 # Set a random slice/sliver attribute
1709 for tag_type_id in random.sample(self.slice_type_ids, 1):
1710 value = randstr(16, letters + '_' + digits)
1711 # Make it a sliver attribute with 50% probability
1712 if slice['node_ids']:
1713 node_id = random.sample(
1714 slice['node_ids'] + [None] * len(slice['node_ids']), 1)[0]
1718 # Add slice attribute
1720 slice_tag_id = self.api.AddSliceTag(
1721 slice_id, tag_type_id, value)
1723 slice_tag_id = self.api.AddSliceTag(
1724 slice_id, tag_type_id, value, node_id)
1726 # Should return a unique slice_tag_id
1727 assert slice_tag_id not in self.slice_tag_ids
1728 self.slice_tag_ids.append(slice_tag_id)
1731 # Check slice attribute
1732 slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1733 for field in 'tag_type_id', 'slice_id', 'node_id', 'slice_tag_id', 'value':
1734 assert slice_tag[field] == locals()[field]
1737 print("Added slice attribute", slice_tag_id,
1738 "of type", tag_type_id, end=' ')
1739 if node_id is not None:
1740 print("to node", node_id, end=' ')
1743 def UpdateSliceTags(self):
1745 Make random changes to any slice attributes we may have added.
1748 for slice_tag_id in self.slice_tag_ids:
1749 # Update slice attribute
1750 value = randstr(16, letters + '_' + digits)
1751 self.api.UpdateSliceTag(slice_tag_id, value)
1753 # Check slice attribute again
1754 slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1755 assert slice_tag['value'] == value
1758 print("Updated slice attribute", slice_tag_id)
1760 def DeleteSliceTags(self):
1762 Delete any random slice attributes we may have added.
1765 for slice_tag_id in self.slice_tag_ids:
1766 self.api.DeleteSliceTag(slice_tag_id)
1769 assert not self.api.GetSliceTags([slice_tag_id])
1772 print("Deleted slice attribute", slice_tag_id)
1775 assert not self.api.GetSliceTags(self.slice_tag_ids)
1777 self.slice_tag_ids = []
1779 # convenience for cleaning up
1780 # not exactly accurate -- use on test plcs only
1781 def WipeSitesFromLength(self):
1782 for site in self.api.GetSites():
1783 abbrev = site['abbreviated_name']
1784 # print 'matching',len(abbrev),'against',self.namelengths['abbreviated_name']
1785 if len(abbrev) == self.namelengths['abbreviated_name']:
1786 # if len(abbrev)==17:
1787 print('wiping site %d (%s)' % (site['site_id'], site['name']))
1788 self.api.DeleteSite(site['site_id'])
1792 parser = OptionParser()
1793 parser.add_option("-c", "--check", action="store_true", default=False,
1794 help="Check most actions (default: %default)")
1795 parser.add_option("-q", "--quiet", action="store_true", default=False,
1796 help="Be quiet (default: %default)")
1797 parser.add_option("-p", "--preserve", action="store_true", default=False,
1798 help="Do not delete created objects")
1799 parser.add_option("-t", "--tiny", action="store_true", default=False,
1800 help="Run a tiny test (default: %default)")
1801 parser.add_option("-l", "--large", action="store_true", default=False,
1802 help="Run a large test (default: %default)")
1803 parser.add_option("-x", "--xlarge", action="store_true", default=False,
1804 help="Run an XL test (default: %default)")
1805 parser.add_option("-s", "--short-names", action="store_true", dest="short_names", default=False,
1806 help="Generate smaller names for checking UI rendering")
1807 parser.add_option("-f", "--foreign", action="store_true", dest="federating", default=False,
1808 help="Create a fake peer and add items in it (no update, no delete)")
1809 parser.add_option("-w", "--wipe", action="store_true", dest="wipe", default=False,
1810 help="Wipe sites whose abbrev matches what the tests created")
1811 (options, args) = parser.parse_args()
1813 test = Test(api=Shell(),
1814 check=options.check,
1815 verbose=not options.quiet,
1816 preserve=options.preserve,
1817 federating=options.federating)
1819 if options.short_names:
1820 test.namelengths = Test.namelengths_short
1822 test.namelengths = Test.namelengths_default
1825 test.WipeSitesFromLength()
1829 sizes = Test.sizes_tiny
1831 sizes = Test.sizes_large
1832 elif options.xlarge:
1833 sizes = Test.sizes_xlarge
1835 sizes = Test.sizes_default
1839 if __name__ == "__main__":