2to3'ed plcsh-stress-test, which is required as we run on top of plcsh
[tests.git] / system / plcsh_stress_test.py
1 #!/usr/bin/env plcsh
2 #
3 # WARNING: as opposed to the rest of the python code in this repo
4 # the current script runs on top of plcsh and so it is for now
5 # pinned as python2 code
6 #
7 #
8 # Test script utility class
9 #
10 # Mark Huang <mlhuang@cs.princeton.edu>
11 # Copyright (C) 2006 The Trustees of Princeton University
12 #
13
14 # NOTE on porting to python3
15 #
16 # this file gets fed to plcsh on the tested myplc, so
17 # it needs to remain python2 for now
18 #
19
20 from pprint import pprint
21 from string import letters, digits, punctuation, whitespace
22 from traceback import print_exc
23 from optparse import OptionParser
24 import socket
25 import base64
26 import struct
27 import os
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 def randfloat(min = 0.0, max = 1.0):
41     return float(min) + (random.random() * (float(max) - float(min)))
42
43 def randint(min = 0, max = 1):
44     return int(randfloat(min, max + 1))
45
46 # See "2.2 Characters" in the XML specification:
47 #
48 # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
49 # avoiding
50 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
51 #
52
53 ascii_xml_chars = list(map(chr, [0x9, 0xA]))
54 # xmlrpclib uses xml.parsers.expat, which always converts either '\r'
55 # (#xD) or '\n' (#xA) to '\n'. So avoid using '\r', too, if this is
56 # still the case.
57 if xmlrpc.client.loads(xmlrpc.client.dumps(('\r',)))[0][0] == '\r':
58     ascii_xml_chars.append('\r')
59 ascii_xml_chars += list(map(chr, range(0x20, 0x7F - 1)))
60 low_xml_chars = list(ascii_xml_chars)
61 low_xml_chars += list(map(chr, range(0x84 + 1, 0x86 - 1)))
62 low_xml_chars += list(map(chr, range(0x9F + 1, 0xFF)))
63 valid_xml_chars = list(low_xml_chars)
64 valid_xml_chars += list(map(chr, range(0xFF + 1, 0xD7FF)))
65 valid_xml_chars += list(map(chr, range(0xE000, 0xFDD0 - 1)))
66 valid_xml_chars += list(map(chr, range(0xFDDF + 1, 0xFFFD)))
67
68 def randstr(length, pool = valid_xml_chars, encoding = "utf-8"):
69     sample = random.sample(pool, min(length, len(pool)))
70     while True:
71         s = ''.join(sample)
72         bytes = len(s.encode(encoding))
73         if bytes > length:
74             sample.pop()
75         elif bytes < length:
76             sample += random.sample(pool, min(length - bytes, len(pool)))
77             random.shuffle(sample)
78         else:
79             break
80     return s
81
82 def randhostname(namelengths):
83     # 1. Each part begins and ends with a letter or number.
84     # 2. Each part except the last can contain letters, numbers, or hyphens.
85     # 3. Each part is between 1 and 64 characters, including the trailing dot.
86     # 4. At least two parts.
87     # 5. Last part can only contain between 2 and 6 letters.
88     hostname = 'a' + randstr(namelengths['hostname1'], letters + digits + '-') + '1.' + \
89                'b' + randstr(namelengths['hostname1'], letters + digits + '-') + '2.' + \
90                'c' + randstr(namelengths['hostname2'], letters)
91     return hostname.lower()
92
93 def randpath(length):
94     parts = []
95     for i in range(randint(1, 10)):
96         parts.append(randstr(randint(1, 30), ascii_xml_chars))
97     return '/'.join(parts)[0:length]
98
99 def randemail(namelengths):
100     return (randstr(namelengths['email'], letters + digits) + "@" + randhostname(namelengths)).lower()
101
102 def randkey(namelengths,bits = 2048):
103     ssh_key_types = ["ssh-dss", "ssh-rsa"]
104     key_type = random.sample(ssh_key_types, 1)[0]
105     return ' '.join([key_type,
106                      base64.b64encode(''.join(randstr(bits / 8).encode("utf-8"))),
107                      randemail(namelengths)])
108
109 def random_peer():
110     return {
111         'peername': randstr(24,letters + ' ' + digits),
112         'peer_url': "https://" + randhostname ({'hostname1':8,'hostname2':3}) + ":443/PLCAPI/",
113         'key' : randstr(1024,letters+digits),
114         'cacert' : randstr(1024,letters+digits),
115         'shortname' : randstr(1,letters) + 'LAB',
116         'hrn_root' : 'planetlab.' + randstr (3,letters),
117         }
118
119 def random_site(namelengths):
120     sitename=randstr(namelengths['sitename'],namelengths['sitename_contents'])
121     abbreviated_name=randstr(namelengths['abbreviated_name'],namelengths['abbreviated_name_contents'])
122
123     print('nl[a] in random_site',namelengths['abbreviated_name'],'actual',len(abbreviated_name))
124     return {
125         'name': sitename,
126         'abbreviated_name': abbreviated_name,
127         'login_base': randstr(namelengths['login_base'], letters).lower(),
128         'latitude': int(randfloat(-90.0, 90.0) * 1000) / 1000.0,
129         'longitude': int(randfloat(-180.0, 180.0) * 1000) / 1000.0,
130         }
131
132 def random_address_type():
133     return {
134         'name': randstr(20),
135         'description': randstr(254),
136         }
137
138 def random_address():
139     return {
140         'line1': randstr(254),
141         'line2': randstr(254),
142         'line3': randstr(254),
143         'city': randstr(254),
144         'state': randstr(254),
145         'postalcode': randstr(64),
146         'country': randstr(128),
147         }
148
149 def random_person(namelengths):
150     return {
151         'first_name': randstr(namelengths['first_name']),
152         'last_name': randstr(namelengths['last_name']),
153         'email': randemail(namelengths),
154         'bio': randstr(254),
155         # Accounts are disabled by default
156         'enabled': False,
157         'password': randstr(254),
158         }
159
160 def random_key(key_types,namelengths):
161     return {
162         'key_type': random.sample(key_types, 1)[0],
163         'key': randkey(namelengths)
164         }
165
166 def random_tag_type (role_ids):
167     return  {'tagname': randstr(12,letters+digits),
168              'category':randstr(4,letters+digits)+'/'+randstr(6,letters+digits),
169              'description' : randstr(128,letters+digits+whitespace+punctuation),
170              }
171
172 def random_nodegroup():
173     return {'groupname' : randstr(30, letters+digits+whitespace) }
174
175 def random_roles(role_ids):
176     nb_roles=len(role_ids)
177     return random.sample(role_ids,random.choice(list(range(1,nb_roles+1))))
178
179 tag_fields=['arch']
180 def random_node(node_types,boot_states,namelengths):
181     return {
182         'hostname': randhostname(namelengths),
183         'node_type': random.sample(node_types,1)[0],
184         'boot_state': random.sample(boot_states, 1)[0],
185         'model': randstr(namelengths['model']),
186         'version': randstr(64),
187         # for testing node tags
188         'arch':randstr(10),
189         }
190
191 def random_interface(method, type,namelengths):
192     interface_fields = {
193         'method': method,
194         'type': type,
195         'bwlimit': randint(500000, 10000000),
196         }
197
198     if method != 'dhcp':
199         ip = randint(0, 0xffffffff)
200         netmask = (0xffffffff << randint(2, 31)) & 0xffffffff
201         network = ip & netmask
202         broadcast = ((ip & netmask) | ~netmask) & 0xffffffff
203         gateway = randint(network + 1, broadcast - 1)
204         dns1 = randint(0, 0xffffffff)
205
206         for field in 'ip', 'netmask', 'network', 'broadcast', 'gateway', 'dns1':
207             interface_fields[field] = socket.inet_ntoa(struct.pack('>L', locals()[field]))
208         if randint(0,1):
209             interface_fields['hostname']=randhostname(namelengths);
210
211     return interface_fields
212
213 def random_ilink ():
214     return randstr (12)
215
216 def random_pcu(namelengths):
217     return {
218         'hostname': randhostname(namelengths),
219         'ip': socket.inet_ntoa(struct.pack('>L', randint(0, 0xffffffff))),
220         'protocol': randstr(16),
221         'username': randstr(254),
222         'password': randstr(254),
223         'notes': randstr(254),
224         'model': randstr(32),
225         }
226
227 def random_conf_file():
228     return {
229         'enabled': bool(randint()),
230         'source': randpath(255),
231         'dest': randpath(255),
232         'file_permissions': "%#o" % randint(0, 512),
233         'file_owner': randstr(32, letters + '_' + digits),
234         'file_group': randstr(32, letters + '_' + digits),
235         'preinstall_cmd': randpath(100),
236         'postinstall_cmd': randpath(100),
237         'error_cmd': randpath(100),
238         'ignore_cmd_errors': bool(randint()),
239         'always_update': bool(randint()),
240         }
241
242 def random_slice(login_base,namelengths):
243     return {
244         'name': login_base + "_" + randstr(11, letters).lower(),
245         'url': "http://" + randhostname(namelengths) + "/",
246         'description': randstr(2048),
247         }
248
249 class Test:
250     sizes_tiny = {
251         'sites': 1,
252         'address_types': 1,
253         'addresses_per_site': 1,
254         'persons_per_site': 1,
255         'keys_per_person': 1,
256         'slice_tags': 1,
257         'nodegroups': 1,
258         'nodes_per_site': 1,
259         'interfaces_per_node': 1,
260         'ilinks':1,
261         'pcus_per_site': 1,
262         'conf_files': 1,
263         'slices_per_site': 1,
264         'attributes_per_slice': 1,
265         }
266
267     sizes_default = {
268         'sites': 10,
269         'address_types': 2,
270         'addresses_per_site': 2,
271         'persons_per_site': 4,
272         'keys_per_person': 2,
273         'slice_tags': 10,
274         'nodegroups': 10,
275         'nodes_per_site': 2,
276         'interfaces_per_node': 1,
277         'ilinks': 20,
278         'pcus_per_site': 1,
279         'conf_files': 10,
280         'slices_per_site': 4,
281         'attributes_per_slice': 2,
282         }
283
284     sizes_large = {
285         'sites': 200,
286         'address_types': 4,
287         'addresses_per_site': 2,
288         'persons_per_site': 5,
289         'keys_per_person': 2,
290         'slice_tags': 4,
291         'nodegroups': 20,
292         'nodes_per_site': 2,
293         'interfaces_per_node': 2,
294         'ilinks':100,
295         'pcus_per_site': 1,
296         'conf_files': 50,
297         'slices_per_site': 10,
298         'attributes_per_slice': 4,
299         }
300
301     sizes_xlarge = {
302         'sites': 1000,
303         'address_types': 4,
304         'addresses_per_site': 2,
305         'persons_per_site': 5,
306         'keys_per_person': 2,
307         'slice_tags': 4,
308         'nodegroups': 20,
309         'nodes_per_site': 2,
310         'interfaces_per_node': 2,
311         'ilinks':100,
312         'pcus_per_site': 1,
313         'conf_files': 50,
314         'slices_per_site': 10,
315         'attributes_per_slice': 4,
316         }
317
318     namelengths_default = {
319         'hostname1': 61,
320         'hostname2':5,
321         'login_base':20,
322         'sitename':254,
323         'sitename_contents':letters+digits,
324         'abbreviated_name':50,
325         'abbreviated_name_contents':letters+digits+whitespace+punctuation,
326         'model':255,
327         'first_name':128,
328         'last_name':128,
329         'email':100,
330         }
331
332     namelengths_short = {
333         'hostname1': 8,
334         'hostname2':3,
335         'login_base':8,
336         'sitename':64,
337         'sitename_contents':letters+digits,
338         'abbreviated_name':24,
339         'abbreviated_name_contents':letters+digits+whitespace+punctuation,
340         'model':40,
341         'first_name':12,
342         'last_name':20,
343         'email':24,
344         }
345
346     def __init__(self, api, check, verbose, preserve, federating):
347         self.api = api
348         self.check = check
349         self.verbose = verbose
350         self.preserve = preserve
351         self.federating = federating
352
353         self.site_ids = []
354         self.address_type_ids = []
355         self.address_ids = []
356         self.person_ids = []
357         self.key_ids = []
358         self.slice_type_ids = []
359         self.nodegroup_type_ids = []
360         self.ilink_type_ids = []
361         self.nodegroup_ids = []
362         self.node_ids = []
363         self.interface_ids = []
364         self.ilink_ids = []
365         self.pcu_ids = []
366         self.conf_file_ids = []
367         self.slice_ids = []
368         self.slice_tag_ids = []
369
370     def Cardinals (self):
371         return [len(x) for x in (
372                 self.api.GetNodes({},['node_id']),
373                 self.api.GetSites({},['site_id']),
374                 self.api.GetPersons({},['person_id']),
375                 self.api.GetSlices({},['slice_id']),
376             )]
377
378     def Run(self, **kwds):
379         """
380         Run a complete database and API consistency test. Populates
381         the database with a set of random entities, updates them, then
382         deletes them. Examples:
383
384         test.Run() # Defaults
385         test.Run(**Test.sizes_default) # Defaults
386         test.Run(**Test.sizes_tiny) # Tiny set
387         test.Run(sites = 123, slices_per_site = 4) # Defaults with overrides
388         """
389
390         cardinals_before=self.Cardinals()
391         print('Cardinals before test (n,s,p,sl)',cardinals_before)
392
393         self.Add(**kwds)
394         # if federating : we're done
395
396         if self.federating or self.preserve:
397             print('Preserving - update & delete skipped')
398         else:
399             self.Update()
400             self.Delete()
401
402             cardinals_after=self.Cardinals()
403             print('Cardinals after test (n,s,p,sl)',cardinals_after)
404
405             if cardinals_before != cardinals_after:
406                 raise Exception('cardinals before and after differ - check deletion mechanisms')
407
408     def Add(self, **kwds):
409         """
410         Populate the database with a set of random entities. Examples:
411
412         same args as Run()
413         """
414
415         sizes = self.sizes_default.copy()
416         sizes.update(kwds)
417
418         if not self.federating:
419             self.AddSites(sizes['sites'])
420             self.AddAddressTypes(sizes['address_types'])
421             self.AddAddresses(sizes['addresses_per_site'])
422             self.AddPersons(sizes['persons_per_site'])
423             self.AddKeys(sizes['keys_per_person'])
424             self.AddTagTypes(sizes['slice_tags'],sizes['nodegroups'],sizes['ilinks'])
425             self.AddNodeGroups(sizes['nodegroups'])
426             self.AddNodes(sizes['nodes_per_site'])
427             self.AddInterfaces(sizes['interfaces_per_node'])
428             self.AddIlinks (sizes['ilinks'])
429             self.AddPCUs(sizes['pcus_per_site'])
430             self.AddConfFiles(sizes['conf_files'])
431             self.AddSlices(sizes['slices_per_site'])
432             self.AddSliceTags(sizes['attributes_per_slice'])
433
434         else:
435             self.RecordStatus()
436             self.AddSites(sizes['sites'])
437             self.AddPersons(sizes['persons_per_site'])
438             self.AddKeys(sizes['keys_per_person'])
439             self.AddNodes(sizes['nodes_per_site'])
440             self.AddSlices(sizes['slices_per_site'])
441             # create peer and add newly created entities
442             self.AddPeer()
443
444
445     def Update(self):
446         self.UpdateSites()
447         self.UpdateAddressTypes()
448         self.UpdateAddresses()
449         self.UpdatePersons()
450         self.UpdateKeys()
451         self.UpdateTagTypes()
452         self.UpdateNodeGroups()
453         self.UpdateNodes()
454         self.UpdateInterfaces()
455         self.UpdateIlinks()
456         self.UpdatePCUs()
457         self.UpdateConfFiles()
458         self.UpdateSlices()
459         self.UpdateSliceTags()
460
461     def Delete(self):
462         self.DeleteSliceTags()
463         self.DeleteSlices()
464         self.DeleteKeys()
465         self.DeleteConfFiles()
466         self.DeletePCUs()
467         self.DeleteIlinks()
468         self.DeleteInterfaces()
469         self.DeleteNodes()
470         self.DeletePersons()
471         self.DeleteNodeGroups()
472         self.DeleteTagTypes()
473         self.DeleteAddresses()
474         self.DeleteAddressTypes()
475         self.DeleteSites()
476
477     # record current (old) objects
478     def RecordStatus (self):
479         self.old_site_ids = [ s['site_id'] for s in self.api.GetSites({},['site_id']) ]
480         self.old_person_ids = [ s['person_id'] for s in self.api.GetPersons({},['person_id']) ]
481         self.old_key_ids = [ s['key_id'] for s in self.api.GetKeys({},['key_id']) ]
482         self.old_node_ids = [ s['node_id'] for s in self.api.GetNodes({},['node_id']) ]
483         self.old_slice_ids = [ s['slice_id'] for s in self.api.GetSlices({},['slice_id']) ]
484
485     def AddPeer (self):
486         peer_id=self.api.AddPeer (random_peer())
487         peer = GetPeers([peer_id])[0]
488         if self.verbose:
489             print("Added peer",peer_id)
490
491         # add new sites (the ones not in self.site_ids) in the peer
492         # cheating a bit
493         for site in self.api.GetSites ({'~site_id':self.old_site_ids}):
494             peer.add_site(site,site['site_id'])
495         for person in self.api.GetPersons ({'~person_id':self.old_person_ids}):
496             peer.add_person(person,person['person_id'])
497         for key in self.api.GetKeys ({'~key_id':self.old_key_ids}):
498             peer.add_key(key,key['key_id'])
499         for node in self.api.GetNodes ({'~node_id':self.old_node_ids}):
500             peer.add_node(node,node['node_id'])
501         for slice in self.api.GetSlices ({'~slice_id':self.old_slice_ids}):
502             peer.add_slice(slice,slice['slice_id'])
503
504     def AddSites(self, n = 10):
505         """
506         Add a number of random sites.
507         """
508
509         for i in range(n):
510             # Add site
511             site_fields = random_site(self.namelengths)
512             site_id = self.api.AddSite(site_fields)
513
514             # Should return a unique site_id
515             assert site_id not in self.site_ids
516             self.site_ids.append(site_id)
517
518             # Enable slice creation
519             site_fields['max_slices'] = randint(1, 10)
520             self.api.UpdateSite(site_id, site_fields)
521
522             if self.check:
523                 # Check site
524                 site = self.api.GetSites([site_id])[0]
525                 for field in site_fields:
526                     assert site[field] == site_fields[field]
527
528             if self.verbose:
529                 print("Added site", site_id)
530
531     def UpdateSites(self):
532         """
533         Make random changes to any sites we may have added.
534         """
535
536         for site_id in self.site_ids:
537             # Update site
538             site_fields = random_site(self.namelengths)
539             # Do not change login_base
540             if 'login_base' in site_fields:
541                 del site_fields['login_base']
542             self.api.UpdateSite(site_id, site_fields)
543
544             if self.check:
545                 # Check site
546                 site = self.api.GetSites([site_id])[0]
547                 for field in site_fields:
548                     assert site[field] == site_fields[field]
549
550             if self.verbose:
551                 print("Updated site", site_id)
552
553     def DeleteSites(self):
554         """
555         Delete any random sites we may have added.
556         """
557
558         for site_id in self.site_ids:
559             self.api.DeleteSite(site_id)
560
561             if self.check:
562                 assert not self.api.GetSites([site_id])
563
564             if self.verbose:
565                 print("Deleted site", site_id)
566
567         if self.check:
568             assert not self.api.GetSites(self.site_ids)
569
570         self.site_ids = []
571
572     def AddAddressTypes(self, n = 2):
573         """
574         Add a number of random address types.
575         """
576
577         for i in range(n):
578             address_type_fields = random_address_type()
579             address_type_id = self.api.AddAddressType(address_type_fields)
580
581             # Should return a unique address_type_id
582             assert address_type_id not in self.address_type_ids
583             self.address_type_ids.append(address_type_id)
584
585             if self.check:
586                 # Check address type
587                 address_type = self.api.GetAddressTypes([address_type_id])[0]
588                 for field in address_type_fields:
589                     assert address_type[field] == address_type_fields[field]
590
591             if self.verbose:
592                 print("Added address type", address_type_id)
593
594     def UpdateAddressTypes(self):
595         """
596         Make random changes to any address types we may have added.
597         """
598
599         for address_type_id in self.address_type_ids:
600             # Update address_type
601             address_type_fields = random_address_type()
602             self.api.UpdateAddressType(address_type_id, address_type_fields)
603
604             if self.check:
605                 # Check address type
606                 address_type = self.api.GetAddressTypes([address_type_id])[0]
607                 for field in address_type_fields:
608                     assert address_type[field] == address_type_fields[field]
609
610             if self.verbose:
611                 print("Updated address_type", address_type_id)
612
613     def DeleteAddressTypes(self):
614         """
615         Delete any random address types we may have added.
616         """
617
618         for address_type_id in self.address_type_ids:
619             self.api.DeleteAddressType(address_type_id)
620
621             if self.check:
622                 assert not self.api.GetAddressTypes([address_type_id])
623
624             if self.verbose:
625                 print("Deleted address type", address_type_id)
626
627         if self.check:
628             assert not self.api.GetAddressTypes(self.address_type_ids)
629
630         self.address_type_ids = []
631
632     def AddAddresses(self, per_site = 2):
633         """
634         Add a number of random addresses to each site.
635         """
636
637         for site_id in self.site_ids:
638             for i in range(per_site):
639                 address_fields = random_address()
640                 address_id = self.api.AddSiteAddress(site_id, address_fields)
641
642                 # Should return a unique address_id
643                 assert address_id not in self.address_ids
644                 self.address_ids.append(address_id)
645
646                 # Add random address type
647                 if self.address_type_ids:
648                     for address_type_id in random.sample(self.address_type_ids, 1):
649                         self.api.AddAddressTypeToAddress(address_type_id, address_id)
650
651                 if self.check:
652                     # Check address
653                     address = self.api.GetAddresses([address_id])[0]
654                     for field in address_fields:
655                         assert address[field] == address_fields[field]
656
657                 if self.verbose:
658                     print("Added address", address_id, "to site", site_id)
659
660     def UpdateAddresses(self):
661         """
662         Make random changes to any addresses we may have added.
663         """
664
665         for address_id in self.address_ids:
666             # Update address
667             address_fields = random_address()
668             self.api.UpdateAddress(address_id, address_fields)
669
670             if self.check:
671                 # Check address
672                 address = self.api.GetAddresses([address_id])[0]
673                 for field in address_fields:
674                     assert address[field] == address_fields[field]
675
676             if self.verbose:
677                 print("Updated address", address_id)
678
679     def DeleteAddresses(self):
680         """
681         Delete any random addresses we may have added.
682         """
683
684         for address_id in self.address_ids:
685             # Remove address types
686             address = self.api.GetAddresses([address_id])[0]
687             for address_type_id in address['address_type_ids']:
688                 self.api.DeleteAddressTypeFromAddress(address_type_id, address_id)
689
690             if self.check:
691                 address = self.api.GetAddresses([address_id])[0]
692                 assert not address['address_type_ids']
693
694             self.api.DeleteAddress(address_id)
695
696             if self.check:
697                 assert not self.api.GetAddresses([address_id])
698
699             if self.verbose:
700                 print("Deleted address", address_id)
701
702         if self.check:
703             assert not self.api.GetAddresses(self.address_ids)
704
705         self.address_ids = []
706
707     def AddPersons(self, per_site = 10):
708         """
709         Add a number of random users to each site.
710         """
711
712         for site_id in self.site_ids:
713             for i in range(per_site):
714                 # Add user
715                 person_fields = random_person(self.namelengths)
716                 person_id = self.api.AddPerson(person_fields)
717
718                 # Should return a unique person_id
719                 assert person_id not in self.person_ids
720                 self.person_ids.append(person_id)
721
722                 if self.check:
723                     # Check user
724                     person = self.api.GetPersons([person_id])[0]
725                     for field in person_fields:
726                         if field != 'password':
727                             assert person[field] == person_fields[field]
728
729                 auth = {'AuthMethod': "password",
730                         'Username': person_fields['email'],
731                         'AuthString': person_fields['password']}
732
733                 if self.check:
734                     # Check that user is disabled
735                     try:
736                         assert not self.api.AuthCheck(auth)
737                     except:
738                         pass
739
740                 # Add random set of roles
741                 role_ids = random.sample([20, 30, 40], randint(1, 3))
742                 for role_id in role_ids:
743                     self.api.AddRoleToPerson(role_id, person_id)
744
745                 if self.check:
746                     person = self.api.GetPersons([person_id])[0]
747                     assert set(role_ids) == set(person['role_ids'])
748
749                 # Enable user
750                 self.api.UpdatePerson(person_id, {'enabled': True})
751
752                 if self.check:
753                     # Check that user is enabled
754                     assert self.api.AuthCheck(auth)
755
756                 # Associate user with site
757                 self.api.AddPersonToSite(person_id, site_id)
758                 self.api.SetPersonPrimarySite(person_id, site_id)
759
760                 if self.check:
761                     person = self.api.GetPersons([person_id])[0]
762                     assert person['site_ids'][0] == site_id
763
764                 if self.verbose:
765                     print("Added user", person_id, "to site", site_id)
766
767     def UpdatePersons(self):
768         """
769         Make random changes to any users we may have added.
770         """
771
772         for person_id in self.person_ids:
773             # Update user
774             person_fields = random_person(self.namelengths)
775             # Keep them enabled
776             person_fields['enabled'] = True
777             self.api.UpdatePerson(person_id, person_fields)
778
779             if self.check:
780                 # Check user
781                 person = self.api.GetPersons([person_id])[0]
782                 for field in person_fields:
783                     if field != 'password':
784                         assert person[field] == person_fields[field]
785
786             if self.verbose:
787                 print("Updated person", person_id)
788
789             person = self.api.GetPersons([person_id])[0]
790
791             # Associate user with a random set of sites
792             site_ids = random.sample(self.site_ids, randint(0, len(self.site_ids)))
793             for site_id in (set(site_ids) - set(person['site_ids'])):
794                 self.api.AddPersonToSite(person_id, site_id)
795             for site_id in (set(person['site_ids']) - set(site_ids)):
796                 self.api.DeletePersonFromSite(person_id, site_id)
797
798             if site_ids:
799                 self.api.SetPersonPrimarySite(person_id, site_ids[0])
800
801             if self.check:
802                 person = self.api.GetPersons([person_id])[0]
803                 assert set(site_ids) == set(person['site_ids'])
804
805             if self.verbose:
806                 print("Updated person", person_id, "to sites", site_ids)
807
808     def DeletePersons(self):
809         """
810         Delete any random users we may have added.
811         """
812
813         for person_id in self.person_ids:
814             # Remove from site
815             person = self.api.GetPersons([person_id])[0]
816             for site_id in person['site_ids']:
817                 self.api.DeletePersonFromSite(person_id, site_id)
818
819             if self.check:
820                 person = self.api.GetPersons([person_id])[0]
821                 assert not person['site_ids']
822
823             # Revoke roles
824             for role_id in person['role_ids']:
825                 self.api.DeleteRoleFromPerson(role_id, person_id)
826
827             if self.check:
828                 person = self.api.GetPersons([person_id])[0]
829                 assert not person['role_ids']
830
831             # Disable account
832             self.api.UpdatePerson(person_id, {'enabled': False})
833
834             if self.check:
835                 person = self.api.GetPersons([person_id])[0]
836                 assert not person['enabled']
837
838             # Delete account
839             self.api.DeletePerson(person_id)
840
841             if self.check:
842                 assert not self.api.GetPersons([person_id])
843
844             if self.verbose:
845                 print("Deleted user", person_id)
846
847         if self.check:
848             assert not self.api.GetPersons(self.person_ids)
849
850         self.person_ids = []
851
852     def AddKeys(self, per_person = 2):
853         """
854         Add a number of random keys to each user.
855         """
856
857         key_types = self.api.GetKeyTypes()
858         if not key_types:
859             raise Exception("No key types")
860
861         for person_id in self.person_ids:
862             for i in range(per_person):
863                 # Add key
864                 key_fields = random_key(key_types,self.namelengths)
865                 key_id = self.api.AddPersonKey(person_id, key_fields)
866
867                 # Should return a unique key_id
868                 assert key_id not in self.key_ids
869                 self.key_ids.append(key_id)
870
871                 if self.check:
872                     # Check key
873                     key = self.api.GetKeys([key_id])[0]
874                     for field in key_fields:
875                         assert key[field] == key_fields[field]
876
877                     # Add and immediately blacklist a key
878                     key_fields = random_key(key_types,self.namelengths)
879                     key_id = self.api.AddPersonKey(person_id, key_fields)
880
881                     self.api.BlacklistKey(key_id)
882
883                     # Is effectively deleted
884                     assert not self.api.GetKeys([key_id])
885
886                     # Cannot be added again
887                     try:
888                         key_id = self.api.AddPersonKey(person_id, key_fields)
889                         assert False
890                     except Exception as e:
891                         pass
892
893                 if self.verbose:
894                     print("Added key", key_id, "to user", person_id)
895
896     def UpdateKeys(self):
897         """
898         Make random changes to any keys we may have added.
899         """
900
901         key_types = self.api.GetKeyTypes()
902         if not key_types:
903             raise Exception("No key types")
904
905         for key_id in self.key_ids:
906             # Update key
907             key_fields = random_key(key_types,self.namelengths)
908             self.api.UpdateKey(key_id, key_fields)
909
910             if self.check:
911                 # Check key
912                 key = self.api.GetKeys([key_id])[0]
913                 for field in key_fields:
914                     assert key[field] == key_fields[field]
915
916             if self.verbose:
917                 print("Updated key", key_id)
918
919     def DeleteKeys(self):
920         """
921         Delete any random keys we may have added.
922         """
923
924         for key_id in self.key_ids:
925             self.api.DeleteKey(key_id)
926
927             if self.check:
928                 assert not self.api.GetKeys([key_id])
929
930             if self.verbose:
931                 print("Deleted key", key_id)
932
933         if self.check:
934             assert not self.api.GetKeys(self.key_ids)
935
936         self.key_ids = []
937
938     def AddNodeGroups(self, n = 10):
939         """
940         Add a number of random node groups.
941         """
942
943         for i in range(n):
944             # locate tag type
945             tag_type_id = self.nodegroup_type_ids[i]
946             tagname=self.api.GetTagTypes([tag_type_id])[0]['tagname']
947
948             # Add node group
949             groupname = random_nodegroup() ['groupname']
950             value = 'yes'
951             nodegroup_id = self.api.AddNodeGroup(groupname, tagname, value)
952
953             # Should return a unique nodegroup_id
954             assert nodegroup_id not in self.nodegroup_ids
955             self.nodegroup_ids.append(nodegroup_id)
956
957             if self.check:
958                 # Check node group
959                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
960                 assert nodegroup['groupname'] == groupname
961                 assert nodegroup['tagname'] == tagname
962                 assert nodegroup['value'] == value
963
964             if self.verbose:
965                 print("Added node group", nodegroup_id)
966
967     def UpdateNodeGroups(self):
968         """
969         Make random changes to any node groups we may have added.
970         """
971
972         for nodegroup_id in self.nodegroup_ids:
973             # Update nodegroup
974             groupname = random_nodegroup()['groupname']
975             # cannot change tagname
976             nodegroup_fields = { 'groupname':groupname }
977             self.api.UpdateNodeGroup(nodegroup_id, nodegroup_fields)
978
979             if self.check:
980                 # Check nodegroup
981                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
982                 for field in nodegroup_fields:
983                     assert nodegroup[field] == nodegroup_fields[field]
984
985             if self.verbose:
986                 print("Updated node group", nodegroup_id)
987
988     def DeleteNodeGroups(self):
989         """
990         Delete any random node groups we may have added.
991         """
992
993         for nodegroup_id in self.nodegroup_ids:
994             self.api.DeleteNodeGroup(nodegroup_id)
995
996             if self.check:
997                 assert not self.api.GetNodeGroups([nodegroup_id])
998
999             if self.verbose:
1000                 print("Deleted node group", nodegroup_id)
1001
1002         if self.check:
1003             assert not self.api.GetNodeGroups(self.nodegroup_ids)
1004
1005         self.nodegroup_ids = []
1006
1007     def AddNodes(self, per_site = 2):
1008         """
1009         Add a number of random nodes to each site. Each node will also
1010         be added to a random node group if AddNodeGroups() was
1011         previously run.
1012         """
1013
1014         node_types = self.api.GetNodeTypes()
1015         if not node_types:
1016             raise Exception("No node types")
1017         boot_states = self.api.GetBootStates()
1018         if not boot_states:
1019             raise Exception("No boot states")
1020
1021         for site_id in self.site_ids:
1022             for i in range(per_site):
1023                 # Add node
1024                 node_fields = random_node(node_types,boot_states,self.namelengths)
1025                 node_id = self.api.AddNode(site_id, node_fields)
1026
1027                 # Should return a unique node_id
1028                 assert node_id not in self.node_ids
1029                 self.node_ids.append(node_id)
1030
1031                 # Add to a random set of node groups
1032                 nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
1033                 for nodegroup_id in nodegroup_ids:
1034                     tagname = self.api.GetNodeGroups([nodegroup_id])[0]['tagname']
1035                     self.api.AddNodeTag( node_id, tagname, 'yes' )
1036
1037                 if self.check:
1038                     # Check node
1039                     node = self.api.GetNodes([node_id])[0]
1040                     for field in node_fields:
1041                         if field not in tag_fields:
1042                             assert node[field] == node_fields[field]
1043
1044                 if self.verbose:
1045                     print("Added node", node_id)
1046
1047     def UpdateNodes(self):
1048         """
1049         Make random changes to any nodes we may have added.
1050         """
1051
1052         node_types = self.api.GetNodeTypes()
1053         if not node_types:
1054             raise Exception("No node types")
1055         boot_states = self.api.GetBootStates()
1056         if not boot_states:
1057             raise Exception("No boot states")
1058
1059         for node_id in self.node_ids:
1060             # Update node
1061             node_fields = random_node(node_types,boot_states,self.namelengths)
1062             self.api.UpdateNode(node_id, node_fields)
1063
1064             node = self.api.GetNodes([node_id])[0]
1065
1066             # Add to a random set of node groups
1067             nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
1068             for nodegroup_id in (set(nodegroup_ids) - set(node['nodegroup_ids'])):
1069                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1070                 tagname = nodegroup['tagname']
1071                 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
1072                 if not node_tags:
1073                     self.api.AddNodeTag(node_id,tagname,'yes')
1074                 else:
1075                     node_tag=node_tags[0]
1076                     self.api.UpdateNodeTag(node_tag['node_tag_id'],'yes')
1077             for nodegroup_id in (set(node['nodegroup_ids']) - set(nodegroup_ids)):
1078                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
1079                 tagname = nodegroup['tagname']
1080                 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
1081                 if not node_tags:
1082                     self.api.AddNodeTag(node_id,tagname,'no')
1083                 else:
1084                     node_tag=node_tags[0]
1085                     self.api.UpdateNodeTag(node_tag['node_tag_id'],'no')
1086
1087             if self.check:
1088                 # Check node
1089                 node = self.api.GetNodes([node_id])[0]
1090                 for field in node_fields:
1091                     if field not in tag_fields:
1092                         if node[field] != node_fields[field]:
1093                             raise Exception("Unexpected field %s in node after GetNodes()"%field)
1094                 assert set(nodegroup_ids) == set(node['nodegroup_ids'])
1095
1096                 # again but we are now fetching 'arch' explicitly
1097                 node2 = self.api.GetNodes([node_id],list(node_fields.keys()))[0]
1098                 for field in node_fields:
1099                     if node2[field] != node_fields[field]:
1100                         raise Exception("Unexpected field %s in node after GetNodes(tags)"%field)
1101
1102             if self.verbose:
1103                 print("Updated node", node_id)
1104
1105     def DeleteNodes(self):
1106         """
1107         Delete any random nodes we may have added.
1108         """
1109
1110         for node_id in self.node_ids:
1111             # Remove from node groups
1112             node = self.api.GetNodes([node_id])[0]
1113             for node_tag in self.api.GetNodeTags ( {'node_id': node_id} ):
1114                 self.api.UpdateNodeTag(node_tag['node_tag_id'],'')
1115
1116             if self.check:
1117                 node = self.api.GetNodes([node_id])[0]
1118                 assert not node['nodegroup_ids']
1119
1120             self.api.DeleteNode(node_id)
1121
1122             if self.check:
1123                 assert not self.api.GetNodes([node_id])
1124
1125             if self.verbose:
1126                 print("Deleted node", node_id)
1127
1128         if self.check:
1129             assert not self.api.GetNodes(self.node_ids)
1130
1131         self.node_ids = []
1132
1133     def AddInterfaces(self, per_node = 1):
1134         """
1135         Add a number of random network interfaces to each node.
1136         """
1137
1138         network_methods = self.api.GetNetworkMethods()
1139         if not network_methods:
1140             raise Exception("No network methods")
1141
1142         network_types = self.api.GetNetworkTypes()
1143         if not network_types:
1144             raise Exception("No network types")
1145
1146         for node_id in self.node_ids:
1147             for i in range(per_node):
1148                 method = random.sample(network_methods, 1)[0]
1149                 type = random.sample(network_types, 1)[0]
1150
1151                 # Add interface
1152                 interface_fields = random_interface(method, type,self.namelengths)
1153                 interface_id = self.api.AddInterface(node_id, interface_fields)
1154
1155                 # Should return a unique interface_id
1156                 assert interface_id not in self.interface_ids
1157                 self.interface_ids.append(interface_id)
1158
1159                 if self.check:
1160                     # Check interface
1161                     interface = self.api.GetInterfaces([interface_id])[0]
1162                     for field in interface_fields:
1163                         assert interface[field] == interface_fields[field]
1164
1165                 if self.verbose:
1166                     print("Added interface", interface_id, "to node", node_id)
1167
1168     def UpdateInterfaces(self):
1169         """
1170         Make random changes to any network interfaces we may have added.
1171         """
1172
1173         network_methods = self.api.GetNetworkMethods()
1174         if not network_methods:
1175             raise Exception("No network methods")
1176
1177         network_types = self.api.GetNetworkTypes()
1178         if not network_types:
1179             raise Exception("No network types")
1180
1181         for interface_id in self.interface_ids:
1182             method = random.sample(network_methods, 1)[0]
1183             type = random.sample(network_types, 1)[0]
1184
1185             # Update interface
1186             interface_fields = random_interface(method, type,self.namelengths)
1187             self.api.UpdateInterface(interface_id, interface_fields)
1188
1189             if self.check:
1190                 # Check interface
1191                 interface = self.api.GetInterfaces([interface_id])[0]
1192                 for field in interface_fields:
1193                     assert interface[field] == interface_fields[field]
1194
1195             if self.verbose:
1196                 print("Updated interface", interface_id)
1197
1198     def DeleteInterfaces(self):
1199         """
1200         Delete any random network interfaces we may have added.
1201         """
1202
1203         for interface_id in self.interface_ids:
1204             self.api.DeleteInterface(interface_id)
1205
1206             if self.check:
1207                 assert not self.api.GetInterfaces([interface_id])
1208
1209             if self.verbose:
1210                 print("Deleted interface", interface_id)
1211
1212         if self.check:
1213             assert not self.api.GetInterfaces(self.interface_ids)
1214
1215         self.interface_ids = []
1216
1217     def AddIlinks (self, n):
1218         """
1219         Add random links between interfaces.
1220         """
1221
1222         for i in range (n):
1223             src = random.sample(self.interface_ids,1)[0]
1224             dst = random.sample(self.interface_ids,1)[0]
1225             ilink_id = self.api.AddIlink (src,dst,
1226                                           self.ilink_type_ids[i],
1227                                           random_ilink())
1228
1229             assert ilink_id not in self.ilink_ids
1230             self.ilink_ids.append(ilink_id)
1231
1232             if self.verbose:
1233                 print('Added Ilink',ilink_id,' - attached interface',src,'to',dst)
1234
1235             if self.check:
1236                 retrieve=GetIlinks({'src_interface_id':src,'dst_interface_id':dst,
1237                                     'tag_type_id':self.ilink_type_ids[i]})
1238                 assert ilink_id==retrieve[0]['ilink_id']
1239
1240
1241     def UpdateIlinks (self):
1242
1243         for ilink_id in self.ilink_ids:
1244             new_value=random_ilink()
1245             self.api.UpdateIlink(ilink_id,new_value)
1246
1247             if self.check:
1248                 ilink=self.api.GetIlinks([ilink_id])[0]
1249                 assert ilink['value'] == new_value
1250
1251             if self.verbose:
1252                 print('Updated Ilink',ilink_id)
1253
1254     def DeleteIlinks (self):
1255         for ilink_id in self.ilink_ids:
1256             self.api.DeleteIlink(ilink_id)
1257
1258             if self.check:
1259                 assert not self.api.GetIlinks({'ilink_id':ilink_id})
1260
1261             if self.verbose:
1262                 print('Deleted Ilink',ilink_id)
1263
1264         if self.check:
1265             assert not self.api.GetIlinks(self.ilink_ids)
1266
1267         self.ilink_ids = []
1268
1269
1270     def AddPCUs(self, per_site = 1):
1271         """
1272         Add a number of random PCUs to each site. Each node at the
1273         site will be added to a port on the PCU if AddNodes() was
1274         previously run.
1275         """
1276
1277         for site_id in self.site_ids:
1278             for i in range(per_site):
1279                 # Add PCU
1280                 pcu_fields = random_pcu(self.namelengths)
1281                 pcu_id = self.api.AddPCU(site_id, pcu_fields)
1282
1283                 # Should return a unique pcu_id
1284                 assert pcu_id not in self.pcu_ids
1285                 self.pcu_ids.append(pcu_id)
1286
1287                 # Add each node at this site to a different port on this PCU
1288                 site = self.api.GetSites([site_id])[0]
1289                 port = randint(1, 10)
1290                 for node_id in site['node_ids']:
1291                     self.api.AddNodeToPCU(node_id, pcu_id, port)
1292                     port += 1
1293
1294                 if self.check:
1295                     # Check PCU
1296                     pcu = self.api.GetPCUs([pcu_id])[0]
1297                     for field in pcu_fields:
1298                         assert pcu[field] == pcu_fields[field]
1299
1300                 if self.verbose:
1301                     print("Added PCU", pcu_id, "to site", site_id)
1302
1303     def UpdatePCUs(self):
1304         """
1305         Make random changes to any PCUs we may have added.
1306         """
1307
1308         for pcu_id in self.pcu_ids:
1309             # Update PCU
1310             pcu_fields = random_pcu(self.namelengths)
1311             self.api.UpdatePCU(pcu_id, pcu_fields)
1312
1313             if self.check:
1314                 # Check PCU
1315                 pcu = self.api.GetPCUs([pcu_id])[0]
1316                 for field in pcu_fields:
1317                     assert pcu[field] == pcu_fields[field]
1318
1319             if self.verbose:
1320                 print("Updated PCU", pcu_id)
1321
1322     def DeletePCUs(self):
1323         """
1324         Delete any random nodes we may have added.
1325         """
1326
1327         for pcu_id in self.pcu_ids:
1328             # Remove nodes from PCU
1329             pcu = self.api.GetPCUs([pcu_id])[0]
1330             for node_id in pcu['node_ids']:
1331                 self.api.DeleteNodeFromPCU(node_id, pcu_id)
1332
1333             if self.check:
1334                 pcu = self.api.GetPCUs([pcu_id])[0]
1335                 assert not pcu['node_ids']
1336
1337             self.api.DeletePCU(pcu_id)
1338
1339             if self.check:
1340                 assert not self.api.GetPCUs([pcu_id])
1341
1342             if self.verbose:
1343                 print("Deleted PCU", pcu_id)
1344
1345         if self.check:
1346             assert not self.api.GetPCUs(self.pcu_ids)
1347
1348         self.pcu_ids = []
1349
1350     def AddConfFiles(self, n = 10):
1351         """
1352         Add a number of random global configuration files.
1353         """
1354
1355         conf_files = []
1356
1357         for i in range(n):
1358             # Add a random configuration file
1359             conf_files.append(random_conf_file())
1360
1361         if n:
1362             # Add a nodegroup override file
1363             nodegroup_conf_file = conf_files[0].copy()
1364             nodegroup_conf_file['source'] = randpath(255)
1365             conf_files.append(nodegroup_conf_file)
1366
1367             # Add a node override file
1368             node_conf_file = conf_files[0].copy()
1369             node_conf_file['source'] = randpath(255)
1370             conf_files.append(node_conf_file)
1371
1372         for conf_file_fields in conf_files:
1373             conf_file_id = self.api.AddConfFile(conf_file_fields)
1374
1375             # Should return a unique conf_file_id
1376             assert conf_file_id not in self.conf_file_ids
1377             self.conf_file_ids.append(conf_file_id)
1378
1379             # Add to nodegroup
1380             if conf_file_fields == nodegroup_conf_file and self.nodegroup_ids:
1381                 nodegroup_id = random.sample(self.nodegroup_ids, 1)[0]
1382                 self.api.AddConfFileToNodeGroup(conf_file_id, nodegroup_id)
1383             else:
1384                 nodegroup_id = None
1385
1386             # Add to node
1387             if conf_file_fields == node_conf_file and self.node_ids:
1388                 node_id = random.sample(self.node_ids, 1)[0]
1389                 self.api.AddConfFileToNode(conf_file_id, node_id)
1390             else:
1391                 node_id = None
1392
1393             if self.check:
1394                 # Check configuration file
1395                 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1396                 for field in conf_file_fields:
1397                     assert conf_file[field] == conf_file_fields[field]
1398
1399             if self.verbose:
1400                 print("Added configuration file", conf_file_id, end=' ')
1401                 if nodegroup_id is not None:
1402                     print("to node group", nodegroup_id, end=' ')
1403                 elif node_id is not None:
1404                     print("to node", node_id, end=' ')
1405                 print()
1406
1407     def UpdateConfFiles(self):
1408         """
1409         Make random changes to any configuration files we may have added.
1410         """
1411
1412         for conf_file_id in self.conf_file_ids:
1413             # Update configuration file
1414             conf_file_fields = random_conf_file()
1415             # Do not update dest so that it remains an override if set
1416             if 'dest' in conf_file_fields:
1417                 del conf_file_fields['dest']
1418             self.api.UpdateConfFile(conf_file_id, conf_file_fields)
1419
1420             if self.check:
1421                 # Check configuration file
1422                 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1423                 for field in conf_file_fields:
1424                     assert conf_file[field] == conf_file_fields[field]
1425
1426             if self.verbose:
1427                 print("Updated configuration file", conf_file_id)
1428
1429     def DeleteConfFiles(self):
1430         """
1431         Delete any random configuration files we may have added.
1432         """
1433
1434         for conf_file_id in self.conf_file_ids:
1435             self.api.DeleteConfFile(conf_file_id)
1436
1437             if self.check:
1438                 assert not self.api.GetConfFiles([conf_file_id])
1439
1440             if self.verbose:
1441                 print("Deleted configuration file", conf_file_id)
1442
1443         if self.check:
1444             assert not self.api.GetConfFiles(self.conf_file_ids)
1445
1446         self.conf_file_ids = []
1447
1448     def AddTagTypes(self,n_sa,n_ng,n_il):
1449         """
1450         Add as many tag types as there are nodegroups,
1451         will use value=yes for each nodegroup
1452         """
1453
1454         roles = self.api.GetRoles()
1455         if not roles:
1456             raise Exception("No roles")
1457         role_ids = [role['role_id'] for role in roles]
1458
1459         for i in range (n_sa + n_ng + n_il):
1460             tag_type_fields = random_tag_type (role_ids)
1461             tag_type_id = self.api.AddTagType (tag_type_fields)
1462
1463             assert tag_type_id not in \
1464                 self.slice_type_ids + \
1465                 self.nodegroup_type_ids + \
1466                 self.ilink_type_ids
1467
1468             tt_role_ids=random_roles(role_ids)
1469             for tt_role_id in tt_role_ids:
1470                 self.api.AddRoleToTagType(tt_role_id,tag_type_id)
1471
1472             if i < n_sa:
1473                 self.slice_type_ids.append(tag_type_id)
1474             elif i < n_sa+n_ng :
1475                 self.nodegroup_type_ids.append(tag_type_id)
1476             else:
1477                 self.ilink_type_ids.append(tag_type_id)
1478
1479             if self.check:
1480                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1481                 for field in tag_type_fields:
1482                     assert tag_type[field] == tag_type_fields[field]
1483                 for tt_role_id in tt_role_ids:
1484                     assert tt_role_id in tag_type['role_ids']
1485             if self.verbose:
1486                 print("Created tag type", tag_type_id)
1487
1488     def UpdateTagTypes(self):
1489         """
1490         Make random changes to any slice attribute types we may have added.
1491         """
1492
1493         roles = self.api.GetRoles()
1494         if not roles:
1495             raise Exception("No roles")
1496         role_ids = [role['role_id'] for role in roles]
1497
1498         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1499             # Update slice attribute type
1500             tag_type_fields = random_tag_type(role_ids)
1501             self.api.UpdateTagType(tag_type_id, tag_type_fields)
1502
1503             if self.check:
1504                 # Check slice attribute type
1505                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1506                 for field in tag_type_fields:
1507                     assert tag_type[field] == tag_type_fields[field]
1508             if self.verbose:
1509                 print("Updated tag type", tag_type_id)
1510
1511     def DeleteTagTypes(self):
1512         """
1513         Delete any random slice attribute types we may have added.
1514         """
1515
1516         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1517             self.api.DeleteTagType(tag_type_id)
1518
1519             if self.check:
1520                 assert not self.api.GetTagTypes([tag_type_id])
1521
1522             if self.verbose:
1523                 print("Deleted tag type", tag_type_id)
1524
1525         if self.check:
1526             assert not self.api.GetTagTypes(self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1527
1528         self.slice_type_ids = []
1529         self.nodegroup_type_ids = []
1530
1531     def AddSlices(self, per_site = 10):
1532         """
1533         Add a number of random slices per site.
1534         """
1535
1536         for site in self.api.GetSites(self.site_ids):
1537             for i in range(min(per_site, site['max_slices'])):
1538                 # Add slice
1539                 slice_fields = random_slice(site['login_base'],self.namelengths)
1540                 slice_id = self.api.AddSlice(slice_fields)
1541
1542                 # Should return a unique slice_id
1543                 assert slice_id not in self.slice_ids
1544                 self.slice_ids.append(slice_id)
1545
1546                 # Add slice to a random set of nodes
1547                 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1548                 if node_ids:
1549                     self.api.AddSliceToNodes(slice_id, node_ids)
1550
1551                 # Add random set of site users to slice
1552                 person_ids = random.sample(site['person_ids'], randint(0, len(site['person_ids'])))
1553                 for person_id in person_ids:
1554                     self.api.AddPersonToSlice(person_id, slice_id)
1555
1556                 if self.check:
1557                     # Check slice
1558                     slice = self.api.GetSlices([slice_id])[0]
1559                     for field in slice_fields:
1560                         assert slice[field] == slice_fields[field]
1561
1562                     assert set(node_ids) == set(slice['node_ids'])
1563                     assert set(person_ids) == set(slice['person_ids'])
1564
1565                 if self.verbose:
1566                     print("Added slice", slice_id, "to site", site['site_id'], end=' ')
1567                     if node_ids:
1568                         print("and nodes", node_ids, end=' ')
1569                     print()
1570                     if person_ids:
1571                         print("Added users", site['person_ids'], "to slice", slice_id)
1572
1573     def UpdateSlices(self):
1574         """
1575         Make random changes to any slices we may have added.
1576         """
1577
1578         for slice_id in self.slice_ids:
1579             # Update slice
1580             slice_fields = random_slice("unused",self.namelengths)
1581             # Cannot change slice name
1582             if 'name' in slice_fields:
1583                 del slice_fields['name']
1584             self.api.UpdateSlice(slice_id, slice_fields)
1585
1586             slice = self.api.GetSlices([slice_id])[0]
1587
1588             # Add slice to a random set of nodes
1589             node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1590             self.api.AddSliceToNodes(slice_id, list(set(node_ids) - set(slice['node_ids'])))
1591             self.api.DeleteSliceFromNodes(slice_id, list(set(slice['node_ids']) - set(node_ids)))
1592
1593             # Add random set of users to slice
1594             person_ids = random.sample(self.person_ids, randint(0, len(self.person_ids)))
1595             for person_id in (set(person_ids) - set(slice['person_ids'])):
1596                 self.api.AddPersonToSlice(person_id, slice_id)
1597             for person_id in (set(slice['person_ids']) - set(person_ids)):
1598                 self.api.DeletePersonFromSlice(person_id, slice_id)
1599
1600             if self.check:
1601                 slice = self.api.GetSlices([slice_id])[0]
1602                 for field in slice_fields:
1603                     assert slice[field] == slice_fields[field]
1604                 assert set(node_ids) == set(slice['node_ids'])
1605                 assert set(person_ids) == set(slice['person_ids'])
1606
1607             if self.verbose:
1608                 print("Updated slice", slice_id)
1609                 print("Added nodes", node_ids, "to slice", slice_id)
1610                 print("Added persons", person_ids, "to slice", slice_id)
1611
1612     def DeleteSlices(self):
1613         """
1614         Delete any random slices we may have added.
1615         """
1616
1617         for slice_id in self.slice_ids:
1618             self.api.DeleteSlice(slice_id)
1619
1620             if self.check:
1621                 assert not self.api.GetSlices([slice_id])
1622
1623             if self.verbose:
1624                 print("Deleted slice", slice_id)
1625
1626         if self.check:
1627             assert not self.api.GetSlices(self.slice_ids)
1628
1629         self.slice_ids = []
1630
1631     def AddSliceTags(self, per_slice = 2):
1632         """
1633         Add a number of random slices per site.
1634         """
1635
1636         if not self.slice_type_ids:
1637             return
1638
1639         for slice_id in self.slice_ids:
1640             slice = self.api.GetSlices([slice_id])[0]
1641
1642             for i in range(per_slice):
1643                 # Set a random slice/sliver attribute
1644                 for tag_type_id in random.sample(self.slice_type_ids, 1):
1645                     value = randstr(16, letters + '_' + digits)
1646                     # Make it a sliver attribute with 50% probability
1647                     if slice['node_ids']:
1648                         node_id = random.sample(slice['node_ids'] + [None] * len(slice['node_ids']), 1)[0]
1649                     else:
1650                         node_id = None
1651
1652                     # Add slice attribute
1653                     if node_id is None:
1654                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value)
1655                     else:
1656                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value, node_id)
1657
1658                     # Should return a unique slice_tag_id
1659                     assert slice_tag_id not in self.slice_tag_ids
1660                     self.slice_tag_ids.append(slice_tag_id)
1661
1662                     if self.check:
1663                         # Check slice attribute
1664                         slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1665                         for field in 'tag_type_id', 'slice_id', 'node_id', 'slice_tag_id', 'value':
1666                             assert slice_tag[field] == locals()[field]
1667
1668                     if self.verbose:
1669                         print("Added slice attribute", slice_tag_id, "of type", tag_type_id, end=' ')
1670                         if node_id is not None:
1671                             print("to node", node_id, end=' ')
1672                         print()
1673
1674     def UpdateSliceTags(self):
1675         """
1676         Make random changes to any slice attributes we may have added.
1677         """
1678
1679         for slice_tag_id in self.slice_tag_ids:
1680             # Update slice attribute
1681             value = randstr(16, letters + '_' + digits)
1682             self.api.UpdateSliceTag(slice_tag_id, value)
1683
1684             # Check slice attribute again
1685             slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1686             assert slice_tag['value'] == value
1687
1688             if self.verbose:
1689                 print("Updated slice attribute", slice_tag_id)
1690
1691     def DeleteSliceTags(self):
1692         """
1693         Delete any random slice attributes we may have added.
1694         """
1695
1696         for slice_tag_id in self.slice_tag_ids:
1697             self.api.DeleteSliceTag(slice_tag_id)
1698
1699             if self.check:
1700                 assert not self.api.GetSliceTags([slice_tag_id])
1701
1702             if self.verbose:
1703                 print("Deleted slice attribute", slice_tag_id)
1704
1705         if self.check:
1706             assert not self.api.GetSliceTags(self.slice_tag_ids)
1707
1708         self.slice_tag_ids = []
1709
1710     # convenience for cleaning up
1711     # not exactly accurate -- use on test plcs only
1712     def WipeSitesFromLength(self):
1713         for site in self.api.GetSites():
1714             abbrev=site['abbreviated_name']
1715 #            print 'matching',len(abbrev),'against',self.namelengths['abbreviated_name']
1716             if len(abbrev)==self.namelengths['abbreviated_name']:
1717 #            if len(abbrev)==17:
1718                 print('wiping site %d (%s)'%(site['site_id'],site['name']))
1719                 self.api.DeleteSite(site['site_id'])
1720
1721 def main():
1722     parser = OptionParser()
1723     parser.add_option("-c", "--check", action = "store_true", default = False,
1724                       help = "Check most actions (default: %default)")
1725     parser.add_option("-q", "--quiet", action = "store_true", default = False,
1726                       help = "Be quiet (default: %default)")
1727     parser.add_option("-p","--preserve", action="store_true", default =False,
1728                       help = "Do not delete created objects")
1729     parser.add_option("-t", "--tiny", action = "store_true", default = False,
1730                       help = "Run a tiny test (default: %default)")
1731     parser.add_option("-l", "--large", action = "store_true", default = False,
1732                       help = "Run a large test (default: %default)")
1733     parser.add_option("-x", "--xlarge", action = "store_true", default = False,
1734                       help = "Run an XL test (default: %default)")
1735     parser.add_option("-s", "--short-names", action="store_true", dest="short_names", default = False,
1736                       help = "Generate smaller names for checking UI rendering")
1737     parser.add_option ("-f", "--foreign", action="store_true", dest="federating", default = False,
1738                        help = "Create a fake peer and add items in it (no update, no delete)")
1739     parser.add_option ("-w", "--wipe", action="store_true", dest="wipe", default = False,
1740                        help = "Wipe sites whose abbrev matches what the tests created")
1741     (options, args) = parser.parse_args()
1742
1743     test = Test(api = Shell(),
1744                 check = options.check,
1745                 verbose = not options.quiet,
1746                 preserve = options.preserve,
1747                 federating = options.federating)
1748
1749     if options.short_names:
1750         test.namelengths = Test.namelengths_short
1751     else:
1752         test.namelengths = Test.namelengths_default
1753
1754     if options.wipe:
1755         test.WipeSitesFromLength()
1756         return
1757
1758     if options.tiny:
1759         sizes = Test.sizes_tiny
1760     elif options.large:
1761         sizes = Test.sizes_large
1762     elif options.xlarge:
1763         sizes = Test.sizes_xlarge
1764     else:
1765         sizes = Test.sizes_default
1766     test.Run(**sizes)
1767
1768 if __name__ == "__main__":
1769     main()