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