autopep8 + fix for bytes/str regarding base64 for py3
[tests.git] / system / plcsh_stress_test.py
1 #!/usr/bin/env plcsh
2
3 # pylint: disable=c0111, c0302
4
5 #
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
9 #
10 #
11 # Test script utility class
12 #
13 # Mark Huang <mlhuang@cs.princeton.edu>
14 # Copyright (C) 2006 The Trustees of Princeton University
15 #
16
17 # NOTE on porting to python3
18 #
19 # this file gets fed to plcsh on the tested myplc, so
20 # it needs to remain python2 for now
21 #
22
23 from string import ascii_letters as letters, digits, punctuation, whitespace
24 from optparse import OptionParser
25 import socket
26 import base64
27 import struct
28 import xmlrpc.client
29
30 from PLC.Shell import Shell
31
32 from random import Random
33 random = Random()
34
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
39
40
41 def randfloat(min=0.0, max=1.0):
42     return float(min) + (random.random() * (float(max) - float(min)))
43
44
45 def randint(min=0, max=1):
46     return int(randfloat(min, max + 1))
47
48 # See "2.2 Characters" in the XML specification:
49 #
50 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
51 # avoiding
52 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
53 #
54
55
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
59 # still the case.
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)))
70
71
72 def randstr(length, pool=valid_xml_chars, encoding="utf-8"):
73     sample = random.sample(pool, min(length, len(pool)))
74     while True:
75         s = ''.join(sample)
76         bytes = len(s.encode(encoding))
77         if bytes > length:
78             sample.pop()
79         elif bytes < length:
80             sample += random.sample(pool, min(length - bytes, len(pool)))
81             random.shuffle(sample)
82         else:
83             break
84     return s
85
86
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 + '-'
94     hostname = (
95         'a' +
96         randstr(namelengths['hostname1'], all_chars) +
97         '1.' + 'b' +
98         randstr(namelengths['hostname1'], all_chars) +
99         '2.' + 'c' +
100         randstr(namelengths['hostname2'], letters))
101     return hostname.lower()
102
103
104 def randpath(length):
105     parts = []
106     for i in range(randint(1, 10)):
107         parts.append(randstr(randint(1, 30), ascii_xml_chars))
108     return '/'.join(parts)[0:length]
109
110
111 def randemail(namelengths):
112     return (randstr(namelengths['email'], letters + digits) + "@" + randhostname(namelengths)).lower()
113
114
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)])
121
122
123 def random_peer():
124     return {
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),
132     }
133
134
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'])
140
141     print('nl[a] in random_site', namelengths['abbreviated_name'],
142           'actual', len(abbreviated_name))
143     return {
144         'name': sitename,
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,
149     }
150
151
152 def random_address_type():
153     return {
154         'name': randstr(20),
155         'description': randstr(254),
156     }
157
158
159 def random_address():
160     return {
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),
168     }
169
170
171 def random_person(namelengths):
172     return {
173         'first_name': randstr(namelengths['first_name']),
174         'last_name': randstr(namelengths['last_name']),
175         'email': randemail(namelengths),
176         'bio': randstr(254),
177         # Accounts are disabled by default
178         'enabled': False,
179         'password': randstr(254),
180     }
181
182
183 def random_key(key_types, namelengths):
184     return {
185         'key_type': random.sample(key_types, 1)[0],
186         'key': randkey(namelengths)
187     }
188
189
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),
194             }
195
196
197 def random_nodegroup():
198     return {'groupname': randstr(30, letters+digits+whitespace)}
199
200
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))))
204
205
206 tag_fields = ['arch']
207
208
209 def random_node(node_types, boot_states, namelengths):
210     return {
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
217         'arch': randstr(10),
218     }
219
220
221 def random_interface(method, type, namelengths):
222     interface_fields = {
223         'method': method,
224         'type': type,
225         'bwlimit': randint(500000, 10000000),
226     }
227
228     if method != 'dhcp':
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)
235
236         for field in 'ip', 'netmask', 'network', 'broadcast', 'gateway', 'dns1':
237             interface_fields[field] = socket.inet_ntoa(
238                 struct.pack('>L', locals()[field]))
239         if randint(0, 1):
240             interface_fields['hostname'] = randhostname(namelengths)
241
242     return interface_fields
243
244
245 def random_ilink():
246     return randstr(12)
247
248
249 def random_pcu(namelengths):
250     return {
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),
258     }
259
260
261 def random_conf_file():
262     return {
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()),
274     }
275
276
277 def random_slice(login_base, namelengths):
278     return {
279         'name': login_base + "_" + randstr(11, letters).lower(),
280         'url': "http://" + randhostname(namelengths) + "/",
281         'description': randstr(2048),
282     }
283
284
285 class Test:
286     sizes_tiny = {
287         'sites': 1,
288         'address_types': 1,
289         'addresses_per_site': 1,
290         'persons_per_site': 1,
291         'keys_per_person': 1,
292         'slice_tags': 1,
293         'nodegroups': 1,
294         'nodes_per_site': 1,
295         'interfaces_per_node': 1,
296         'ilinks': 1,
297         'pcus_per_site': 1,
298         'conf_files': 1,
299         'slices_per_site': 1,
300         'attributes_per_slice': 1,
301     }
302
303     sizes_default = {
304         'sites': 10,
305         'address_types': 2,
306         'addresses_per_site': 2,
307         'persons_per_site': 4,
308         'keys_per_person': 2,
309         'slice_tags': 10,
310         'nodegroups': 10,
311         'nodes_per_site': 2,
312         'interfaces_per_node': 1,
313         'ilinks': 20,
314         'pcus_per_site': 1,
315         'conf_files': 10,
316         'slices_per_site': 4,
317         'attributes_per_slice': 2,
318     }
319
320     sizes_large = {
321         'sites': 200,
322         'address_types': 4,
323         'addresses_per_site': 2,
324         'persons_per_site': 5,
325         'keys_per_person': 2,
326         'slice_tags': 4,
327         'nodegroups': 20,
328         'nodes_per_site': 2,
329         'interfaces_per_node': 2,
330         'ilinks': 100,
331         'pcus_per_site': 1,
332         'conf_files': 50,
333         'slices_per_site': 10,
334         'attributes_per_slice': 4,
335     }
336
337     sizes_xlarge = {
338         'sites': 1000,
339         'address_types': 4,
340         'addresses_per_site': 2,
341         'persons_per_site': 5,
342         'keys_per_person': 2,
343         'slice_tags': 4,
344         'nodegroups': 20,
345         'nodes_per_site': 2,
346         'interfaces_per_node': 2,
347         'ilinks': 100,
348         'pcus_per_site': 1,
349         'conf_files': 50,
350         'slices_per_site': 10,
351         'attributes_per_slice': 4,
352     }
353
354     namelengths_default = {
355         'hostname1': 61,
356         'hostname2': 5,
357         'login_base': 20,
358         'sitename': 254,
359         'sitename_contents': letters+digits,
360         'abbreviated_name': 50,
361         'abbreviated_name_contents': letters+digits+whitespace+punctuation,
362         'model': 255,
363         'first_name': 128,
364         'last_name': 128,
365         'email': 100,
366     }
367
368     namelengths_short = {
369         'hostname1': 8,
370         'hostname2': 3,
371         'login_base': 8,
372         'sitename': 64,
373         'sitename_contents': letters+digits,
374         'abbreviated_name': 24,
375         'abbreviated_name_contents': letters+digits+whitespace+punctuation,
376         'model': 40,
377         'first_name': 12,
378         'last_name': 20,
379         'email': 24,
380     }
381
382     def __init__(self, api, check, verbose, preserve, federating):
383         self.api = api
384         self.check = check
385         self.verbose = verbose
386         self.preserve = preserve
387         self.federating = federating
388
389         self.site_ids = []
390         self.address_type_ids = []
391         self.address_ids = []
392         self.person_ids = []
393         self.key_ids = []
394         self.slice_type_ids = []
395         self.nodegroup_type_ids = []
396         self.ilink_type_ids = []
397         self.nodegroup_ids = []
398         self.node_ids = []
399         self.interface_ids = []
400         self.ilink_ids = []
401         self.pcu_ids = []
402         self.conf_file_ids = []
403         self.slice_ids = []
404         self.slice_tag_ids = []
405
406     def Cardinals(self):
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']),
412                 )]
413
414     def Run(self, **kwds):
415         """
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:
419
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
424         """
425
426         cardinals_before = self.Cardinals()
427         print('Cardinals before test (n,s,p,sl)', cardinals_before)
428
429         self.Add(**kwds)
430         # if federating : we're done
431
432         if self.federating or self.preserve:
433             print('Preserving - update & delete skipped')
434         else:
435             self.Update()
436             self.Delete()
437
438             cardinals_after = self.Cardinals()
439             print('Cardinals after test (n,s,p,sl)', cardinals_after)
440
441             if cardinals_before != cardinals_after:
442                 raise Exception(
443                     'cardinals before and after differ - check deletion mechanisms')
444
445     def Add(self, **kwds):
446         """
447         Populate the database with a set of random entities. Examples:
448
449         same args as Run()
450         """
451
452         sizes = self.sizes_default.copy()
453         sizes.update(kwds)
454
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'])
471
472         else:
473             self.RecordStatus()
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
480             self.AddPeer()
481
482     def Update(self):
483         self.UpdateSites()
484         self.UpdateAddressTypes()
485         self.UpdateAddresses()
486         self.UpdatePersons()
487         self.UpdateKeys()
488         self.UpdateTagTypes()
489         self.UpdateNodeGroups()
490         self.UpdateNodes()
491         self.UpdateInterfaces()
492         self.UpdateIlinks()
493         self.UpdatePCUs()
494         self.UpdateConfFiles()
495         self.UpdateSlices()
496         self.UpdateSliceTags()
497
498     def Delete(self):
499         self.DeleteSliceTags()
500         self.DeleteSlices()
501         self.DeleteKeys()
502         self.DeleteConfFiles()
503         self.DeletePCUs()
504         self.DeleteIlinks()
505         self.DeleteInterfaces()
506         self.DeleteNodes()
507         self.DeletePersons()
508         self.DeleteNodeGroups()
509         self.DeleteTagTypes()
510         self.DeleteAddresses()
511         self.DeleteAddressTypes()
512         self.DeleteSites()
513
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'])]
526
527     def AddPeer(self):
528         peer_id = self.api.AddPeer(random_peer())
529         peer = GetPeers([peer_id])[0]
530         if self.verbose:
531             print("Added peer", peer_id)
532
533         # add new sites (the ones not in self.site_ids) in the peer
534         # cheating a bit
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'])
545
546     def AddSites(self, n=10):
547         """
548         Add a number of random sites.
549         """
550
551         for i in range(n):
552             # Add site
553             site_fields = random_site(self.namelengths)
554             site_id = self.api.AddSite(site_fields)
555
556             # Should return a unique site_id
557             assert site_id not in self.site_ids
558             self.site_ids.append(site_id)
559
560             # Enable slice creation
561             site_fields['max_slices'] = randint(1, 10)
562             self.api.UpdateSite(site_id, site_fields)
563
564             if self.check:
565                 # Check site
566                 site = self.api.GetSites([site_id])[0]
567                 for field in site_fields:
568                     assert site[field] == site_fields[field]
569
570             if self.verbose:
571                 print("Added site", site_id)
572
573     def UpdateSites(self):
574         """
575         Make random changes to any sites we may have added.
576         """
577
578         for site_id in self.site_ids:
579             # Update site
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)
585
586             if self.check:
587                 # Check site
588                 site = self.api.GetSites([site_id])[0]
589                 for field in site_fields:
590                     assert site[field] == site_fields[field]
591
592             if self.verbose:
593                 print("Updated site", site_id)
594
595     def DeleteSites(self):
596         """
597         Delete any random sites we may have added.
598         """
599
600         for site_id in self.site_ids:
601             self.api.DeleteSite(site_id)
602
603             if self.check:
604                 assert not self.api.GetSites([site_id])
605
606             if self.verbose:
607                 print("Deleted site", site_id)
608
609         if self.check:
610             assert not self.api.GetSites(self.site_ids)
611
612         self.site_ids = []
613
614     def AddAddressTypes(self, n=2):
615         """
616         Add a number of random address types.
617         """
618
619         for i in range(n):
620             address_type_fields = random_address_type()
621             address_type_id = self.api.AddAddressType(address_type_fields)
622
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)
626
627             if self.check:
628                 # Check address type
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]
632
633             if self.verbose:
634                 print("Added address type", address_type_id)
635
636     def UpdateAddressTypes(self):
637         """
638         Make random changes to any address types we may have added.
639         """
640
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)
645
646             if self.check:
647                 # Check address type
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]
651
652             if self.verbose:
653                 print("Updated address_type", address_type_id)
654
655     def DeleteAddressTypes(self):
656         """
657         Delete any random address types we may have added.
658         """
659
660         for address_type_id in self.address_type_ids:
661             self.api.DeleteAddressType(address_type_id)
662
663             if self.check:
664                 assert not self.api.GetAddressTypes([address_type_id])
665
666             if self.verbose:
667                 print("Deleted address type", address_type_id)
668
669         if self.check:
670             assert not self.api.GetAddressTypes(self.address_type_ids)
671
672         self.address_type_ids = []
673
674     def AddAddresses(self, per_site=2):
675         """
676         Add a number of random addresses to each site.
677         """
678
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)
683
684                 # Should return a unique address_id
685                 assert address_id not in self.address_ids
686                 self.address_ids.append(address_id)
687
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)
693
694                 if self.check:
695                     # Check address
696                     address = self.api.GetAddresses([address_id])[0]
697                     for field in address_fields:
698                         assert address[field] == address_fields[field]
699
700                 if self.verbose:
701                     print("Added address", address_id, "to site", site_id)
702
703     def UpdateAddresses(self):
704         """
705         Make random changes to any addresses we may have added.
706         """
707
708         for address_id in self.address_ids:
709             # Update address
710             address_fields = random_address()
711             self.api.UpdateAddress(address_id, address_fields)
712
713             if self.check:
714                 # Check address
715                 address = self.api.GetAddresses([address_id])[0]
716                 for field in address_fields:
717                     assert address[field] == address_fields[field]
718
719             if self.verbose:
720                 print("Updated address", address_id)
721
722     def DeleteAddresses(self):
723         """
724         Delete any random addresses we may have added.
725         """
726
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)
733
734             if self.check:
735                 address = self.api.GetAddresses([address_id])[0]
736                 assert not address['address_type_ids']
737
738             self.api.DeleteAddress(address_id)
739
740             if self.check:
741                 assert not self.api.GetAddresses([address_id])
742
743             if self.verbose:
744                 print("Deleted address", address_id)
745
746         if self.check:
747             assert not self.api.GetAddresses(self.address_ids)
748
749         self.address_ids = []
750
751     def AddPersons(self, per_site=10):
752         """
753         Add a number of random users to each site.
754         """
755
756         for site_id in self.site_ids:
757             for i in range(per_site):
758                 # Add user
759                 person_fields = random_person(self.namelengths)
760                 person_id = self.api.AddPerson(person_fields)
761
762                 # Should return a unique person_id
763                 assert person_id not in self.person_ids
764                 self.person_ids.append(person_id)
765
766                 if self.check:
767                     # Check user
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]
772
773                 auth = {'AuthMethod': "password",
774                         'Username': person_fields['email'],
775                         'AuthString': person_fields['password']}
776
777                 if self.check:
778                     # Check that user is disabled
779                     try:
780                         assert not self.api.AuthCheck(auth)
781                     except:
782                         pass
783
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)
788
789                 if self.check:
790                     person = self.api.GetPersons([person_id])[0]
791                     assert set(role_ids) == set(person['role_ids'])
792
793                 # Enable user
794                 self.api.UpdatePerson(person_id, {'enabled': True})
795
796                 if self.check:
797                     # Check that user is enabled
798                     assert self.api.AuthCheck(auth)
799
800                 # Associate user with site
801                 self.api.AddPersonToSite(person_id, site_id)
802                 self.api.SetPersonPrimarySite(person_id, site_id)
803
804                 if self.check:
805                     person = self.api.GetPersons([person_id])[0]
806                     assert person['site_ids'][0] == site_id
807
808                 if self.verbose:
809                     print("Added user", person_id, "to site", site_id)
810
811     def UpdatePersons(self):
812         """
813         Make random changes to any users we may have added.
814         """
815
816         for person_id in self.person_ids:
817             # Update user
818             person_fields = random_person(self.namelengths)
819             # Keep them enabled
820             person_fields['enabled'] = True
821             self.api.UpdatePerson(person_id, person_fields)
822
823             if self.check:
824                 # Check user
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]
829
830             if self.verbose:
831                 print("Updated person", person_id)
832
833             person = self.api.GetPersons([person_id])[0]
834
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)
842
843             if site_ids:
844                 self.api.SetPersonPrimarySite(person_id, site_ids[0])
845
846             if self.check:
847                 person = self.api.GetPersons([person_id])[0]
848                 assert set(site_ids) == set(person['site_ids'])
849
850             if self.verbose:
851                 print("Updated person", person_id, "to sites", site_ids)
852
853     def DeletePersons(self):
854         """
855         Delete any random users we may have added.
856         """
857
858         for person_id in self.person_ids:
859             # Remove from site
860             person = self.api.GetPersons([person_id])[0]
861             for site_id in person['site_ids']:
862                 self.api.DeletePersonFromSite(person_id, site_id)
863
864             if self.check:
865                 person = self.api.GetPersons([person_id])[0]
866                 assert not person['site_ids']
867
868             # Revoke roles
869             for role_id in person['role_ids']:
870                 self.api.DeleteRoleFromPerson(role_id, person_id)
871
872             if self.check:
873                 person = self.api.GetPersons([person_id])[0]
874                 assert not person['role_ids']
875
876             # Disable account
877             self.api.UpdatePerson(person_id, {'enabled': False})
878
879             if self.check:
880                 person = self.api.GetPersons([person_id])[0]
881                 assert not person['enabled']
882
883             # Delete account
884             self.api.DeletePerson(person_id)
885
886             if self.check:
887                 assert not self.api.GetPersons([person_id])
888
889             if self.verbose:
890                 print("Deleted user", person_id)
891
892         if self.check:
893             assert not self.api.GetPersons(self.person_ids)
894
895         self.person_ids = []
896
897     def AddKeys(self, per_person=2):
898         """
899         Add a number of random keys to each user.
900         """
901
902         key_types = self.api.GetKeyTypes()
903         if not key_types:
904             raise Exception("No key types")
905
906         for person_id in self.person_ids:
907             for i in range(per_person):
908                 # Add key
909                 key_fields = random_key(key_types, self.namelengths)
910                 key_id = self.api.AddPersonKey(person_id, key_fields)
911
912                 # Should return a unique key_id
913                 assert key_id not in self.key_ids
914                 self.key_ids.append(key_id)
915
916                 if self.check:
917                     # Check key
918                     key = self.api.GetKeys([key_id])[0]
919                     for field in key_fields:
920                         assert key[field] == key_fields[field]
921
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)
925
926                     self.api.BlacklistKey(key_id)
927
928                     # Is effectively deleted
929                     assert not self.api.GetKeys([key_id])
930
931                     # Cannot be added again
932                     try:
933                         key_id = self.api.AddPersonKey(person_id, key_fields)
934                         assert False
935                     except Exception as e:
936                         pass
937
938                 if self.verbose:
939                     print("Added key", key_id, "to user", person_id)
940
941     def UpdateKeys(self):
942         """
943         Make random changes to any keys we may have added.
944         """
945
946         key_types = self.api.GetKeyTypes()
947         if not key_types:
948             raise Exception("No key types")
949
950         for key_id in self.key_ids:
951             # Update key
952             key_fields = random_key(key_types, self.namelengths)
953             self.api.UpdateKey(key_id, key_fields)
954
955             if self.check:
956                 # Check key
957                 key = self.api.GetKeys([key_id])[0]
958                 for field in key_fields:
959                     assert key[field] == key_fields[field]
960
961             if self.verbose:
962                 print("Updated key", key_id)
963
964     def DeleteKeys(self):
965         """
966         Delete any random keys we may have added.
967         """
968
969         for key_id in self.key_ids:
970             self.api.DeleteKey(key_id)
971
972             if self.check:
973                 assert not self.api.GetKeys([key_id])
974
975             if self.verbose:
976                 print("Deleted key", key_id)
977
978         if self.check:
979             assert not self.api.GetKeys(self.key_ids)
980
981         self.key_ids = []
982
983     def AddNodeGroups(self, n=10):
984         """
985         Add a number of random node groups.
986         """
987
988         for i in range(n):
989             # locate tag type
990             tag_type_id = self.nodegroup_type_ids[i]
991             tagname = self.api.GetTagTypes([tag_type_id])[0]['tagname']
992
993             # Add node group
994             groupname = random_nodegroup()['groupname']
995             value = 'yes'
996             nodegroup_id = self.api.AddNodeGroup(groupname, tagname, value)
997
998             # Should return a unique nodegroup_id
999             assert nodegroup_id not in self.nodegroup_ids
1000             self.nodegroup_ids.append(nodegroup_id)
1001
1002             if self.check:
1003                 # Check node group
1004                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1005                 assert nodegroup['groupname'] == groupname
1006                 assert nodegroup['tagname'] == tagname
1007                 assert nodegroup['value'] == value
1008
1009             if self.verbose:
1010                 print("Added node group", nodegroup_id)
1011
1012     def UpdateNodeGroups(self):
1013         """
1014         Make random changes to any node groups we may have added.
1015         """
1016
1017         for nodegroup_id in self.nodegroup_ids:
1018             # Update nodegroup
1019             groupname = random_nodegroup()['groupname']
1020             # cannot change tagname
1021             nodegroup_fields = {'groupname': groupname}
1022             self.api.UpdateNodeGroup(nodegroup_id, nodegroup_fields)
1023
1024             if self.check:
1025                 # Check nodegroup
1026                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1027                 for field in nodegroup_fields:
1028                     assert nodegroup[field] == nodegroup_fields[field]
1029
1030             if self.verbose:
1031                 print("Updated node group", nodegroup_id)
1032
1033     def DeleteNodeGroups(self):
1034         """
1035         Delete any random node groups we may have added.
1036         """
1037
1038         for nodegroup_id in self.nodegroup_ids:
1039             self.api.DeleteNodeGroup(nodegroup_id)
1040
1041             if self.check:
1042                 assert not self.api.GetNodeGroups([nodegroup_id])
1043
1044             if self.verbose:
1045                 print("Deleted node group", nodegroup_id)
1046
1047         if self.check:
1048             assert not self.api.GetNodeGroups(self.nodegroup_ids)
1049
1050         self.nodegroup_ids = []
1051
1052     def AddNodes(self, per_site=2):
1053         """
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
1056         previously run.
1057         """
1058
1059         node_types = self.api.GetNodeTypes()
1060         if not node_types:
1061             raise Exception("No node types")
1062         boot_states = self.api.GetBootStates()
1063         if not boot_states:
1064             raise Exception("No boot states")
1065
1066         for site_id in self.site_ids:
1067             for i in range(per_site):
1068                 # Add node
1069                 node_fields = random_node(
1070                     node_types, boot_states, self.namelengths)
1071                 node_id = self.api.AddNode(site_id, node_fields)
1072
1073                 # Should return a unique node_id
1074                 assert node_id not in self.node_ids
1075                 self.node_ids.append(node_id)
1076
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')
1084
1085                 if self.check:
1086                     # Check node
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]
1091
1092                 if self.verbose:
1093                     print("Added node", node_id)
1094
1095     def UpdateNodes(self):
1096         """
1097         Make random changes to any nodes we may have added.
1098         """
1099
1100         node_types = self.api.GetNodeTypes()
1101         if not node_types:
1102             raise Exception("No node types")
1103         boot_states = self.api.GetBootStates()
1104         if not boot_states:
1105             raise Exception("No boot states")
1106
1107         for node_id in self.node_ids:
1108             # Update node
1109             node_fields = random_node(
1110                 node_types, boot_states, self.namelengths)
1111             self.api.UpdateNode(node_id, node_fields)
1112
1113             node = self.api.GetNodes([node_id])[0]
1114
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})
1123                 if not node_tags:
1124                     self.api.AddNodeTag(node_id, tagname, 'yes')
1125                 else:
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})
1133                 if not node_tags:
1134                     self.api.AddNodeTag(node_id, tagname, 'no')
1135                 else:
1136                     node_tag = node_tags[0]
1137                     self.api.UpdateNodeTag(node_tag['node_tag_id'], 'no')
1138
1139             if self.check:
1140                 # Check node
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]:
1145                             raise Exception(
1146                                 "Unexpected field %s in node after GetNodes()" % field)
1147                 assert set(nodegroup_ids) == set(node['nodegroup_ids'])
1148
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]:
1154                         raise Exception(
1155                             "Unexpected field %s in node after GetNodes(tags)" % field)
1156
1157             if self.verbose:
1158                 print("Updated node", node_id)
1159
1160     def DeleteNodes(self):
1161         """
1162         Delete any random nodes we may have added.
1163         """
1164
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'], '')
1170
1171             if self.check:
1172                 node = self.api.GetNodes([node_id])[0]
1173                 assert not node['nodegroup_ids']
1174
1175             self.api.DeleteNode(node_id)
1176
1177             if self.check:
1178                 assert not self.api.GetNodes([node_id])
1179
1180             if self.verbose:
1181                 print("Deleted node", node_id)
1182
1183         if self.check:
1184             assert not self.api.GetNodes(self.node_ids)
1185
1186         self.node_ids = []
1187
1188     def AddInterfaces(self, per_node=1):
1189         """
1190         Add a number of random network interfaces to each node.
1191         """
1192
1193         network_methods = self.api.GetNetworkMethods()
1194         if not network_methods:
1195             raise Exception("No network methods")
1196
1197         network_types = self.api.GetNetworkTypes()
1198         if not network_types:
1199             raise Exception("No network types")
1200
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]
1205
1206                 # Add interface
1207                 interface_fields = random_interface(
1208                     method, type, self.namelengths)
1209                 interface_id = self.api.AddInterface(node_id, interface_fields)
1210
1211                 # Should return a unique interface_id
1212                 assert interface_id not in self.interface_ids
1213                 self.interface_ids.append(interface_id)
1214
1215                 if self.check:
1216                     # Check interface
1217                     interface = self.api.GetInterfaces([interface_id])[0]
1218                     for field in interface_fields:
1219                         assert interface[field] == interface_fields[field]
1220
1221                 if self.verbose:
1222                     print("Added interface", interface_id, "to node", node_id)
1223
1224     def UpdateInterfaces(self):
1225         """
1226         Make random changes to any network interfaces we may have added.
1227         """
1228
1229         network_methods = self.api.GetNetworkMethods()
1230         if not network_methods:
1231             raise Exception("No network methods")
1232
1233         network_types = self.api.GetNetworkTypes()
1234         if not network_types:
1235             raise Exception("No network types")
1236
1237         for interface_id in self.interface_ids:
1238             method = random.sample(network_methods, 1)[0]
1239             type = random.sample(network_types, 1)[0]
1240
1241             # Update interface
1242             interface_fields = random_interface(method, type, self.namelengths)
1243             self.api.UpdateInterface(interface_id, interface_fields)
1244
1245             if self.check:
1246                 # Check interface
1247                 interface = self.api.GetInterfaces([interface_id])[0]
1248                 for field in interface_fields:
1249                     assert interface[field] == interface_fields[field]
1250
1251             if self.verbose:
1252                 print("Updated interface", interface_id)
1253
1254     def DeleteInterfaces(self):
1255         """
1256         Delete any random network interfaces we may have added.
1257         """
1258
1259         for interface_id in self.interface_ids:
1260             self.api.DeleteInterface(interface_id)
1261
1262             if self.check:
1263                 assert not self.api.GetInterfaces([interface_id])
1264
1265             if self.verbose:
1266                 print("Deleted interface", interface_id)
1267
1268         if self.check:
1269             assert not self.api.GetInterfaces(self.interface_ids)
1270
1271         self.interface_ids = []
1272
1273     def AddIlinks(self, n):
1274         """
1275         Add random links between interfaces.
1276         """
1277
1278         for i in range(n):
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],
1283                                          random_ilink())
1284
1285             assert ilink_id not in self.ilink_ids
1286             self.ilink_ids.append(ilink_id)
1287
1288             if self.verbose:
1289                 print('Added Ilink', ilink_id,
1290                       ' - attached interface', src, 'to', dst)
1291
1292             if self.check:
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']
1296
1297     def UpdateIlinks(self):
1298
1299         for ilink_id in self.ilink_ids:
1300             new_value = random_ilink()
1301             self.api.UpdateIlink(ilink_id, new_value)
1302
1303             if self.check:
1304                 ilink = self.api.GetIlinks([ilink_id])[0]
1305                 assert ilink['value'] == new_value
1306
1307             if self.verbose:
1308                 print('Updated Ilink', ilink_id)
1309
1310     def DeleteIlinks(self):
1311         for ilink_id in self.ilink_ids:
1312             self.api.DeleteIlink(ilink_id)
1313
1314             if self.check:
1315                 assert not self.api.GetIlinks({'ilink_id': ilink_id})
1316
1317             if self.verbose:
1318                 print('Deleted Ilink', ilink_id)
1319
1320         if self.check:
1321             assert not self.api.GetIlinks(self.ilink_ids)
1322
1323         self.ilink_ids = []
1324
1325     def AddPCUs(self, per_site=1):
1326         """
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
1329         previously run.
1330         """
1331
1332         for site_id in self.site_ids:
1333             for i in range(per_site):
1334                 # Add PCU
1335                 pcu_fields = random_pcu(self.namelengths)
1336                 pcu_id = self.api.AddPCU(site_id, pcu_fields)
1337
1338                 # Should return a unique pcu_id
1339                 assert pcu_id not in self.pcu_ids
1340                 self.pcu_ids.append(pcu_id)
1341
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)
1347                     port += 1
1348
1349                 if self.check:
1350                     # Check PCU
1351                     pcu = self.api.GetPCUs([pcu_id])[0]
1352                     for field in pcu_fields:
1353                         assert pcu[field] == pcu_fields[field]
1354
1355                 if self.verbose:
1356                     print("Added PCU", pcu_id, "to site", site_id)
1357
1358     def UpdatePCUs(self):
1359         """
1360         Make random changes to any PCUs we may have added.
1361         """
1362
1363         for pcu_id in self.pcu_ids:
1364             # Update PCU
1365             pcu_fields = random_pcu(self.namelengths)
1366             self.api.UpdatePCU(pcu_id, pcu_fields)
1367
1368             if self.check:
1369                 # Check PCU
1370                 pcu = self.api.GetPCUs([pcu_id])[0]
1371                 for field in pcu_fields:
1372                     assert pcu[field] == pcu_fields[field]
1373
1374             if self.verbose:
1375                 print("Updated PCU", pcu_id)
1376
1377     def DeletePCUs(self):
1378         """
1379         Delete any random nodes we may have added.
1380         """
1381
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)
1387
1388             if self.check:
1389                 pcu = self.api.GetPCUs([pcu_id])[0]
1390                 assert not pcu['node_ids']
1391
1392             self.api.DeletePCU(pcu_id)
1393
1394             if self.check:
1395                 assert not self.api.GetPCUs([pcu_id])
1396
1397             if self.verbose:
1398                 print("Deleted PCU", pcu_id)
1399
1400         if self.check:
1401             assert not self.api.GetPCUs(self.pcu_ids)
1402
1403         self.pcu_ids = []
1404
1405     def AddConfFiles(self, n=10):
1406         """
1407         Add a number of random global configuration files.
1408         """
1409
1410         conf_files = []
1411
1412         for i in range(n):
1413             # Add a random configuration file
1414             conf_files.append(random_conf_file())
1415
1416         if n:
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)
1421
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)
1426
1427         for conf_file_fields in conf_files:
1428             conf_file_id = self.api.AddConfFile(conf_file_fields)
1429
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)
1433
1434             # Add to nodegroup
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)
1438             else:
1439                 nodegroup_id = None
1440
1441             # Add to node
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)
1445             else:
1446                 node_id = None
1447
1448             if self.check:
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]
1453
1454             if self.verbose:
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=' ')
1460                 print()
1461
1462     def UpdateConfFiles(self):
1463         """
1464         Make random changes to any configuration files we may have added.
1465         """
1466
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)
1474
1475             if self.check:
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]
1480
1481             if self.verbose:
1482                 print("Updated configuration file", conf_file_id)
1483
1484     def DeleteConfFiles(self):
1485         """
1486         Delete any random configuration files we may have added.
1487         """
1488
1489         for conf_file_id in self.conf_file_ids:
1490             self.api.DeleteConfFile(conf_file_id)
1491
1492             if self.check:
1493                 assert not self.api.GetConfFiles([conf_file_id])
1494
1495             if self.verbose:
1496                 print("Deleted configuration file", conf_file_id)
1497
1498         if self.check:
1499             assert not self.api.GetConfFiles(self.conf_file_ids)
1500
1501         self.conf_file_ids = []
1502
1503     def AddTagTypes(self, n_sa, n_ng, n_il):
1504         """
1505         Add as many tag types as there are nodegroups,
1506         will use value=yes for each nodegroup
1507         """
1508
1509         roles = self.api.GetRoles()
1510         if not roles:
1511             raise Exception("No roles")
1512         role_ids = [role['role_id'] for role in roles]
1513
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)
1517
1518             assert tag_type_id not in \
1519                 self.slice_type_ids + \
1520                 self.nodegroup_type_ids + \
1521                 self.ilink_type_ids
1522
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)
1526
1527             if i < n_sa:
1528                 self.slice_type_ids.append(tag_type_id)
1529             elif i < n_sa+n_ng:
1530                 self.nodegroup_type_ids.append(tag_type_id)
1531             else:
1532                 self.ilink_type_ids.append(tag_type_id)
1533
1534             if self.check:
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']
1540             if self.verbose:
1541                 print("Created tag type", tag_type_id)
1542
1543     def UpdateTagTypes(self):
1544         """
1545         Make random changes to any slice attribute types we may have added.
1546         """
1547
1548         roles = self.api.GetRoles()
1549         if not roles:
1550             raise Exception("No roles")
1551         role_ids = [role['role_id'] for role in roles]
1552
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)
1557
1558             if self.check:
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]
1563             if self.verbose:
1564                 print("Updated tag type", tag_type_id)
1565
1566     def DeleteTagTypes(self):
1567         """
1568         Delete any random slice attribute types we may have added.
1569         """
1570
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)
1573
1574             if self.check:
1575                 assert not self.api.GetTagTypes([tag_type_id])
1576
1577             if self.verbose:
1578                 print("Deleted tag type", tag_type_id)
1579
1580         if self.check:
1581             assert not self.api.GetTagTypes(
1582                 self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1583
1584         self.slice_type_ids = []
1585         self.nodegroup_type_ids = []
1586
1587     def AddSlices(self, per_site=10):
1588         """
1589         Add a number of random slices per site.
1590         """
1591
1592         for site in self.api.GetSites(self.site_ids):
1593             for i in range(min(per_site, site['max_slices'])):
1594                 # Add slice
1595                 slice_fields = random_slice(
1596                     site['login_base'], self.namelengths)
1597                 slice_id = self.api.AddSlice(slice_fields)
1598
1599                 # Should return a unique slice_id
1600                 assert slice_id not in self.slice_ids
1601                 self.slice_ids.append(slice_id)
1602
1603                 # Add slice to a random set of nodes
1604                 node_ids = random.sample(
1605                     self.node_ids, randint(0, len(self.node_ids)))
1606                 if node_ids:
1607                     self.api.AddSliceToNodes(slice_id, node_ids)
1608
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)
1614
1615                 if self.check:
1616                     # Check slice
1617                     slice = self.api.GetSlices([slice_id])[0]
1618                     for field in slice_fields:
1619                         assert slice[field] == slice_fields[field]
1620
1621                     assert set(node_ids) == set(slice['node_ids'])
1622                     assert set(person_ids) == set(slice['person_ids'])
1623
1624                 if self.verbose:
1625                     print("Added slice", slice_id, "to site",
1626                           site['site_id'], end=' ')
1627                     if node_ids:
1628                         print("and nodes", node_ids, end=' ')
1629                     print()
1630                     if person_ids:
1631                         print("Added users",
1632                               site['person_ids'], "to slice", slice_id)
1633
1634     def UpdateSlices(self):
1635         """
1636         Make random changes to any slices we may have added.
1637         """
1638
1639         for slice_id in self.slice_ids:
1640             # Update slice
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)
1646
1647             slice = self.api.GetSlices([slice_id])[0]
1648
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)))
1656
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)
1664
1665             if self.check:
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'])
1671
1672             if self.verbose:
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)
1676
1677     def DeleteSlices(self):
1678         """
1679         Delete any random slices we may have added.
1680         """
1681
1682         for slice_id in self.slice_ids:
1683             self.api.DeleteSlice(slice_id)
1684
1685             if self.check:
1686                 assert not self.api.GetSlices([slice_id])
1687
1688             if self.verbose:
1689                 print("Deleted slice", slice_id)
1690
1691         if self.check:
1692             assert not self.api.GetSlices(self.slice_ids)
1693
1694         self.slice_ids = []
1695
1696     def AddSliceTags(self, per_slice=2):
1697         """
1698         Add a number of random slices per site.
1699         """
1700
1701         if not self.slice_type_ids:
1702             return
1703
1704         for slice_id in self.slice_ids:
1705             slice = self.api.GetSlices([slice_id])[0]
1706
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]
1715                     else:
1716                         node_id = None
1717
1718                     # Add slice attribute
1719                     if node_id is None:
1720                         slice_tag_id = self.api.AddSliceTag(
1721                             slice_id, tag_type_id, value)
1722                     else:
1723                         slice_tag_id = self.api.AddSliceTag(
1724                             slice_id, tag_type_id, value, node_id)
1725
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)
1729
1730                     if self.check:
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]
1735
1736                     if self.verbose:
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=' ')
1741                         print()
1742
1743     def UpdateSliceTags(self):
1744         """
1745         Make random changes to any slice attributes we may have added.
1746         """
1747
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)
1752
1753             # Check slice attribute again
1754             slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1755             assert slice_tag['value'] == value
1756
1757             if self.verbose:
1758                 print("Updated slice attribute", slice_tag_id)
1759
1760     def DeleteSliceTags(self):
1761         """
1762         Delete any random slice attributes we may have added.
1763         """
1764
1765         for slice_tag_id in self.slice_tag_ids:
1766             self.api.DeleteSliceTag(slice_tag_id)
1767
1768             if self.check:
1769                 assert not self.api.GetSliceTags([slice_tag_id])
1770
1771             if self.verbose:
1772                 print("Deleted slice attribute", slice_tag_id)
1773
1774         if self.check:
1775             assert not self.api.GetSliceTags(self.slice_tag_ids)
1776
1777         self.slice_tag_ids = []
1778
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'])
1789
1790
1791 def main():
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()
1812
1813     test = Test(api=Shell(),
1814                 check=options.check,
1815                 verbose=not options.quiet,
1816                 preserve=options.preserve,
1817                 federating=options.federating)
1818
1819     if options.short_names:
1820         test.namelengths = Test.namelengths_short
1821     else:
1822         test.namelengths = Test.namelengths_default
1823
1824     if options.wipe:
1825         test.WipeSitesFromLength()
1826         return
1827
1828     if options.tiny:
1829         sizes = Test.sizes_tiny
1830     elif options.large:
1831         sizes = Test.sizes_large
1832     elif options.xlarge:
1833         sizes = Test.sizes_xlarge
1834     else:
1835         sizes = Test.sizes_default
1836     test.Run(**sizes)
1837
1838
1839 if __name__ == "__main__":
1840     main()