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