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