Merge branch 'master' of ssh://git.onelab.eu/git/tests
[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.lower()
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                 # again but we are now fetching 'arch' explicitly
1088                 node2 = self.api.GetNodes([node_id],node_fields.keys())[0]
1089                 for field in node_fields:
1090                     if node2[field] != node_fields[field]:
1091                         raise Exception, "Unexpected field %s in node after GetNodes(tags)"%field
1092
1093             if self.verbose:
1094                 print "Updated node", node_id
1095
1096     def DeleteNodes(self):
1097         """
1098         Delete any random nodes we may have added.
1099         """
1100
1101         for node_id in self.node_ids:
1102             # Remove from node groups
1103             node = self.api.GetNodes([node_id])[0]
1104             for node_tag in self.api.GetNodeTags ( {'node_id': node_id} ):
1105                 self.api.UpdateNodeTag(node_tag['node_tag_id'],'')
1106
1107             if self.check:
1108                 node = self.api.GetNodes([node_id])[0]
1109                 assert not node['nodegroup_ids']
1110
1111             self.api.DeleteNode(node_id)
1112
1113             if self.check:
1114                 assert not self.api.GetNodes([node_id])
1115
1116             if self.verbose:
1117                 print "Deleted node", node_id
1118
1119         if self.check:
1120             assert not self.api.GetNodes(self.node_ids)
1121
1122         self.node_ids = []
1123
1124     def AddInterfaces(self, per_node = 1):
1125         """
1126         Add a number of random network interfaces to each node.
1127         """
1128
1129         network_methods = self.api.GetNetworkMethods()
1130         if not network_methods:
1131             raise Exception, "No network methods"
1132         
1133         network_types = self.api.GetNetworkTypes()
1134         if not network_types:
1135             raise Exception, "No network types"
1136
1137         for node_id in self.node_ids:
1138             for i in range(per_node):
1139                 method = random.sample(network_methods, 1)[0]
1140                 type = random.sample(network_types, 1)[0]
1141
1142                 # Add interface
1143                 interface_fields = random_interface(method, type,self.namelengths)
1144                 interface_id = self.api.AddInterface(node_id, interface_fields)
1145
1146                 # Should return a unique interface_id
1147                 assert interface_id not in self.interface_ids
1148                 self.interface_ids.append(interface_id)
1149
1150                 if self.check:
1151                     # Check interface
1152                     interface = self.api.GetInterfaces([interface_id])[0]
1153                     for field in interface_fields:
1154                         assert interface[field] == interface_fields[field]
1155
1156                 if self.verbose:
1157                     print "Added interface", interface_id, "to node", node_id
1158
1159     def UpdateInterfaces(self):
1160         """
1161         Make random changes to any network interfaces we may have added.
1162         """
1163
1164         network_methods = self.api.GetNetworkMethods()
1165         if not network_methods:
1166             raise Exception, "No network methods"
1167         
1168         network_types = self.api.GetNetworkTypes()
1169         if not network_types:
1170             raise Exception, "No network types"
1171
1172         for interface_id in self.interface_ids:
1173             method = random.sample(network_methods, 1)[0]
1174             type = random.sample(network_types, 1)[0]
1175
1176             # Update interface
1177             interface_fields = random_interface(method, type,self.namelengths)
1178             self.api.UpdateInterface(interface_id, interface_fields)
1179
1180             if self.check:
1181                 # Check interface
1182                 interface = self.api.GetInterfaces([interface_id])[0]
1183                 for field in interface_fields:
1184                     assert interface[field] == interface_fields[field]
1185
1186             if self.verbose:
1187                 print "Updated interface", interface_id
1188
1189     def DeleteInterfaces(self):
1190         """
1191         Delete any random network interfaces we may have added.
1192         """
1193
1194         for interface_id in self.interface_ids:
1195             self.api.DeleteInterface(interface_id)
1196
1197             if self.check:
1198                 assert not self.api.GetInterfaces([interface_id])
1199
1200             if self.verbose:
1201                 print "Deleted interface", interface_id
1202
1203         if self.check:
1204             assert not self.api.GetInterfaces(self.interface_ids)
1205
1206         self.interface_ids = []
1207         
1208     def AddIlinks (self, n):
1209         """
1210         Add random links between interfaces.
1211         """
1212
1213         for i in range (n):
1214             src = random.sample(self.interface_ids,1)[0]
1215             dst = random.sample(self.interface_ids,1)[0]
1216             ilink_id = self.api.AddIlink (src,dst,
1217                                           self.ilink_type_ids[i],
1218                                           random_ilink())
1219
1220             assert ilink_id not in self.ilink_ids
1221             self.ilink_ids.append(ilink_id)
1222
1223             if self.verbose:
1224                 print 'Added Ilink',ilink_id,' - attached interface',src,'to',dst
1225
1226             if self.check:
1227                 retrieve=GetIlinks({'src_interface_id':src,'dst_interface_id':dst,
1228                                     'tag_type_id':self.ilink_type_ids[i]})
1229                 assert ilink_id==retrieve[0]['ilink_id']
1230
1231
1232     def UpdateIlinks (self):
1233
1234         for ilink_id in self.ilink_ids:
1235             new_value=random_ilink()
1236             self.api.UpdateIlink(ilink_id,new_value)
1237
1238             if self.check:
1239                 ilink=self.api.GetIlinks([ilink_id])[0]
1240                 assert ilink['value'] == new_value
1241
1242             if self.verbose:
1243                 print 'Updated Ilink',ilink_id
1244
1245     def DeleteIlinks (self):
1246         for ilink_id in self.ilink_ids:
1247             self.api.DeleteIlink(ilink_id)
1248
1249             if self.check:
1250                 assert not self.api.GetIlinks({'ilink_id':ilink_id})
1251
1252             if self.verbose:
1253                 print 'Deleted Ilink',ilink_id
1254
1255         if self.check:
1256             assert not self.api.GetIlinks(self.ilink_ids)
1257
1258         self.ilink_ids = []
1259
1260
1261     def AddPCUs(self, per_site = 1):
1262         """
1263         Add a number of random PCUs to each site. Each node at the
1264         site will be added to a port on the PCU if AddNodes() was
1265         previously run.
1266         """
1267
1268         for site_id in self.site_ids:
1269             for i in range(per_site):
1270                 # Add PCU
1271                 pcu_fields = random_pcu(self.namelengths)
1272                 pcu_id = self.api.AddPCU(site_id, pcu_fields)
1273
1274                 # Should return a unique pcu_id
1275                 assert pcu_id not in self.pcu_ids
1276                 self.pcu_ids.append(pcu_id)
1277
1278                 # Add each node at this site to a different port on this PCU
1279                 site = self.api.GetSites([site_id])[0]
1280                 port = randint(1, 10)
1281                 for node_id in site['node_ids']:
1282                     self.api.AddNodeToPCU(node_id, pcu_id, port)
1283                     port += 1
1284
1285                 if self.check:
1286                     # Check PCU
1287                     pcu = self.api.GetPCUs([pcu_id])[0]
1288                     for field in pcu_fields:
1289                         assert pcu[field] == pcu_fields[field]
1290
1291                 if self.verbose:
1292                     print "Added PCU", pcu_id, "to site", site_id
1293
1294     def UpdatePCUs(self):
1295         """
1296         Make random changes to any PCUs we may have added.
1297         """
1298
1299         for pcu_id in self.pcu_ids:
1300             # Update PCU
1301             pcu_fields = random_pcu(self.namelengths)
1302             self.api.UpdatePCU(pcu_id, pcu_fields)
1303
1304             if self.check:
1305                 # Check PCU
1306                 pcu = self.api.GetPCUs([pcu_id])[0]
1307                 for field in pcu_fields:
1308                     assert pcu[field] == pcu_fields[field]
1309
1310             if self.verbose:
1311                 print "Updated PCU", pcu_id
1312
1313     def DeletePCUs(self):
1314         """
1315         Delete any random nodes we may have added.
1316         """
1317
1318         for pcu_id in self.pcu_ids:
1319             # Remove nodes from PCU
1320             pcu = self.api.GetPCUs([pcu_id])[0]
1321             for node_id in pcu['node_ids']:
1322                 self.api.DeleteNodeFromPCU(node_id, pcu_id)
1323
1324             if self.check:
1325                 pcu = self.api.GetPCUs([pcu_id])[0]
1326                 assert not pcu['node_ids']
1327
1328             self.api.DeletePCU(pcu_id)
1329
1330             if self.check:
1331                 assert not self.api.GetPCUs([pcu_id])
1332
1333             if self.verbose:
1334                 print "Deleted PCU", pcu_id
1335
1336         if self.check:
1337             assert not self.api.GetPCUs(self.pcu_ids)
1338
1339         self.pcu_ids = []
1340
1341     def AddConfFiles(self, n = 10):
1342         """
1343         Add a number of random global configuration files.
1344         """
1345
1346         conf_files = []
1347
1348         for i in range(n):
1349             # Add a random configuration file
1350             conf_files.append(random_conf_file())
1351
1352         if n:
1353             # Add a nodegroup override file
1354             nodegroup_conf_file = conf_files[0].copy()
1355             nodegroup_conf_file['source'] = randpath(255)
1356             conf_files.append(nodegroup_conf_file)
1357
1358             # Add a node override file
1359             node_conf_file = conf_files[0].copy()
1360             node_conf_file['source'] = randpath(255)
1361             conf_files.append(node_conf_file)
1362
1363         for conf_file_fields in conf_files:
1364             conf_file_id = self.api.AddConfFile(conf_file_fields)
1365
1366             # Should return a unique conf_file_id
1367             assert conf_file_id not in self.conf_file_ids
1368             self.conf_file_ids.append(conf_file_id)
1369
1370             # Add to nodegroup
1371             if conf_file_fields == nodegroup_conf_file and self.nodegroup_ids:
1372                 nodegroup_id = random.sample(self.nodegroup_ids, 1)[0]
1373                 self.api.AddConfFileToNodeGroup(conf_file_id, nodegroup_id)
1374             else:
1375                 nodegroup_id = None
1376
1377             # Add to node
1378             if conf_file_fields == node_conf_file and self.node_ids:
1379                 node_id = random.sample(self.node_ids, 1)[0]
1380                 self.api.AddConfFileToNode(conf_file_id, node_id)
1381             else:
1382                 node_id = None
1383
1384             if self.check:
1385                 # Check configuration file
1386                 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1387                 for field in conf_file_fields:
1388                     assert conf_file[field] == conf_file_fields[field]
1389
1390             if self.verbose:
1391                 print "Added configuration file", conf_file_id,
1392                 if nodegroup_id is not None:
1393                     print "to node group", nodegroup_id,
1394                 elif node_id is not None:
1395                     print "to node", node_id,
1396                 print
1397
1398     def UpdateConfFiles(self):
1399         """
1400         Make random changes to any configuration files we may have added.
1401         """
1402
1403         for conf_file_id in self.conf_file_ids:
1404             # Update configuration file
1405             conf_file_fields = random_conf_file()
1406             # Do not update dest so that it remains an override if set
1407             if 'dest' in conf_file_fields:
1408                 del conf_file_fields['dest']
1409             self.api.UpdateConfFile(conf_file_id, conf_file_fields)
1410
1411             if self.check:
1412                 # Check configuration file
1413                 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1414                 for field in conf_file_fields:
1415                     assert conf_file[field] == conf_file_fields[field]
1416
1417             if self.verbose:
1418                 print "Updated configuration file", conf_file_id
1419
1420     def DeleteConfFiles(self):
1421         """
1422         Delete any random configuration files we may have added.
1423         """
1424
1425         for conf_file_id in self.conf_file_ids:
1426             self.api.DeleteConfFile(conf_file_id)
1427
1428             if self.check:
1429                 assert not self.api.GetConfFiles([conf_file_id])
1430
1431             if self.verbose:
1432                 print "Deleted configuration file", conf_file_id
1433
1434         if self.check:
1435             assert not self.api.GetConfFiles(self.conf_file_ids)
1436
1437         self.conf_file_ids = []
1438
1439     def AddTagTypes(self,n_sa,n_ng,n_il):
1440         """
1441         Add as many tag types as there are nodegroups, 
1442         will use value=yes for each nodegroup
1443         """
1444
1445         roles = self.api.GetRoles()
1446         if not roles:
1447             raise Exception, "No roles"
1448         role_ids = [role['role_id'] for role in roles]
1449
1450         for i in range (n_sa + n_ng + n_il):
1451             tag_type_fields = random_tag_type (role_ids)
1452             tag_type_id = self.api.AddTagType (tag_type_fields)
1453
1454             assert tag_type_id not in \
1455                 self.slice_type_ids + \
1456                 self.nodegroup_type_ids + \
1457                 self.ilink_type_ids
1458             
1459             if i < n_sa:
1460                 self.slice_type_ids.append(tag_type_id)
1461             elif i < n_sa+n_ng :
1462                 self.nodegroup_type_ids.append(tag_type_id)
1463             else:
1464                 self.ilink_type_ids.append(tag_type_id)
1465
1466             if self.check:
1467                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1468                 for field in tag_type_fields:
1469                     assert tag_type[field] == tag_type_fields[field]
1470             if self.verbose:
1471                 print "Updated slice attribute type", tag_type_id
1472
1473     def UpdateTagTypes(self):
1474         """
1475         Make random changes to any slice attribute types we may have added.
1476         """
1477
1478         roles = self.api.GetRoles()
1479         if not roles:
1480             raise Exception, "No roles"
1481         role_ids = [role['role_id'] for role in roles]
1482
1483         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1484             # Update slice attribute type
1485             tag_type_fields = random_tag_type(role_ids)
1486             self.api.UpdateTagType(tag_type_id, tag_type_fields)
1487
1488             if self.check:
1489                 # Check slice attribute type
1490                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1491                 for field in tag_type_fields:
1492                     assert tag_type[field] == tag_type_fields[field]
1493             if self.verbose:
1494                 print "Updated slice attribute type", tag_type_id
1495
1496     def DeleteTagTypes(self):
1497         """
1498         Delete any random slice attribute types we may have added.
1499         """
1500
1501         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1502             self.api.DeleteTagType(tag_type_id)
1503
1504             if self.check:
1505                 assert not self.api.GetTagTypes([tag_type_id])
1506
1507             if self.verbose:
1508                 print "Deleted slice attribute type", tag_type_id
1509
1510         if self.check:
1511             assert not self.api.GetTagTypes(self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1512
1513         self.slice_type_ids = []
1514         self.nodegroup_type_ids = []
1515
1516     def AddSlices(self, per_site = 10):
1517         """
1518         Add a number of random slices per site.
1519         """
1520
1521         for site in self.api.GetSites(self.site_ids):
1522             for i in range(min(per_site, site['max_slices'])):
1523                 # Add slice
1524                 slice_fields = random_slice(site['login_base'],self.namelengths)
1525                 slice_id = self.api.AddSlice(slice_fields)
1526
1527                 # Should return a unique slice_id
1528                 assert slice_id not in self.slice_ids
1529                 self.slice_ids.append(slice_id)
1530
1531                 # Add slice to a random set of nodes
1532                 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1533                 if node_ids:
1534                     self.api.AddSliceToNodes(slice_id, node_ids)
1535
1536                 # Add random set of site users to slice
1537                 person_ids = random.sample(site['person_ids'], randint(0, len(site['person_ids'])))
1538                 for person_id in person_ids:
1539                     self.api.AddPersonToSlice(person_id, slice_id)
1540
1541                 if self.check:
1542                     # Check slice
1543                     slice = self.api.GetSlices([slice_id])[0]
1544                     for field in slice_fields:
1545                         assert slice[field] == slice_fields[field]
1546
1547                     assert set(node_ids) == set(slice['node_ids'])
1548                     assert set(person_ids) == set(slice['person_ids'])
1549
1550                 if self.verbose:
1551                     print "Added slice", slice_id, "to site", site['site_id'],
1552                     if node_ids:
1553                         print "and nodes", node_ids,
1554                     print
1555                     if person_ids:
1556                         print "Added users", site['person_ids'], "to slice", slice_id
1557
1558     def UpdateSlices(self):
1559         """
1560         Make random changes to any slices we may have added.
1561         """
1562
1563         for slice_id in self.slice_ids:
1564             # Update slice
1565             slice_fields = random_slice("unused",self.namelengths)
1566             # Cannot change slice name
1567             if 'name' in slice_fields:
1568                 del slice_fields['name']
1569             self.api.UpdateSlice(slice_id, slice_fields)
1570
1571             slice = self.api.GetSlices([slice_id])[0]
1572
1573             # Add slice to a random set of nodes
1574             node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1575             self.api.AddSliceToNodes(slice_id, list(set(node_ids) - set(slice['node_ids'])))
1576             self.api.DeleteSliceFromNodes(slice_id, list(set(slice['node_ids']) - set(node_ids)))
1577
1578             # Add random set of users to slice
1579             person_ids = random.sample(self.person_ids, randint(0, len(self.person_ids)))
1580             for person_id in (set(person_ids) - set(slice['person_ids'])):
1581                 self.api.AddPersonToSlice(person_id, slice_id)
1582             for person_id in (set(slice['person_ids']) - set(person_ids)):
1583                 self.api.DeletePersonFromSlice(person_id, slice_id)
1584
1585             if self.check:
1586                 slice = self.api.GetSlices([slice_id])[0]
1587                 for field in slice_fields:
1588                     assert slice[field] == slice_fields[field]
1589                 assert set(node_ids) == set(slice['node_ids'])
1590                 assert set(person_ids) == set(slice['person_ids'])
1591
1592             if self.verbose:
1593                 print "Updated slice", slice_id
1594                 print "Added nodes", node_ids, "to slice", slice_id
1595                 print "Added persons", person_ids, "to slice", slice_id
1596
1597     def DeleteSlices(self):
1598         """
1599         Delete any random slices we may have added.
1600         """
1601
1602         for slice_id in self.slice_ids:
1603             self.api.DeleteSlice(slice_id)
1604
1605             if self.check:
1606                 assert not self.api.GetSlices([slice_id])
1607
1608             if self.verbose:
1609                 print "Deleted slice", slice_id
1610
1611         if self.check:
1612             assert not self.api.GetSlices(self.slice_ids)
1613
1614         self.slice_ids = []
1615
1616     def AddSliceTags(self, per_slice = 2):
1617         """
1618         Add a number of random slices per site.
1619         """
1620
1621         if not self.slice_type_ids:
1622             return
1623
1624         for slice_id in self.slice_ids:
1625             slice = self.api.GetSlices([slice_id])[0]
1626
1627             for i in range(per_slice):
1628                 # Set a random slice/sliver attribute
1629                 for tag_type_id in random.sample(self.slice_type_ids, 1):
1630                     value = randstr(16, letters + '_' + digits)
1631                     # Make it a sliver attribute with 50% probability
1632                     if slice['node_ids']:
1633                         node_id = random.sample(slice['node_ids'] + [None] * len(slice['node_ids']), 1)[0]
1634                     else:
1635                         node_id = None
1636
1637                     # Add slice attribute
1638                     if node_id is None:
1639                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value)
1640                     else:
1641                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value, node_id)
1642
1643                     # Should return a unique slice_tag_id
1644                     assert slice_tag_id not in self.slice_tag_ids
1645                     self.slice_tag_ids.append(slice_tag_id)
1646
1647                     if self.check:
1648                         # Check slice attribute
1649                         slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1650                         for field in 'tag_type_id', 'slice_id', 'node_id', 'slice_tag_id', 'value':
1651                             assert slice_tag[field] == locals()[field]
1652
1653                     if self.verbose:
1654                         print "Added slice attribute", slice_tag_id, "of type", tag_type_id,
1655                         if node_id is not None:
1656                             print "to node", node_id,
1657                         print
1658                         
1659     def UpdateSliceTags(self):
1660         """
1661         Make random changes to any slice attributes we may have added.
1662         """
1663
1664         for slice_tag_id in self.slice_tag_ids:
1665             # Update slice attribute
1666             value = randstr(16, letters + '_' + digits)
1667             self.api.UpdateSliceTag(slice_tag_id, value)
1668
1669             # Check slice attribute again
1670             slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1671             assert slice_tag['value'] == value
1672
1673             if self.verbose:
1674                 print "Updated slice attribute", slice_tag_id
1675
1676     def DeleteSliceTags(self):
1677         """
1678         Delete any random slice attributes we may have added.
1679         """
1680
1681         for slice_tag_id in self.slice_tag_ids:
1682             self.api.DeleteSliceTag(slice_tag_id)
1683
1684             if self.check:
1685                 assert not self.api.GetSliceTags([slice_tag_id])
1686
1687             if self.verbose:
1688                 print "Deleted slice attribute", slice_tag_id
1689
1690         if self.check:
1691             assert not self.api.GetSliceTags(self.slice_tag_ids)
1692
1693         self.slice_tag_ids = []
1694
1695 def main():
1696     parser = OptionParser()
1697     parser.add_option("-c", "--check", action = "store_true", default = False, 
1698                       help = "Check most actions (default: %default)")
1699     parser.add_option("-q", "--quiet", action = "store_true", default = False, 
1700                       help = "Be quiet (default: %default)")
1701     parser.add_option("-p","--preserve", action="store_true", default =False,
1702                       help = "Do not delete created objects")
1703     parser.add_option("-t", "--tiny", action = "store_true", default = False, 
1704                       help = "Run a tiny test (default: %default)")
1705     parser.add_option("-l", "--large", action = "store_true", default = False, 
1706                       help = "Run a large test (default: %default)")
1707     parser.add_option("-x", "--xlarge", action = "store_true", default = False, 
1708                       help = "Run an XL test (default: %default)")
1709     parser.add_option("-s", "--short-names", action="store_true", dest="short_names", default = False, 
1710                       help = "Generate smaller names for checking UI rendering")
1711     parser.add_option ("-f", "--foreign", action="store_true", dest="federating", default = False,
1712                        help = "Create a fake peer and add items in it (no update, no delete)")
1713     (options, args) = parser.parse_args()
1714
1715     test = Test(api = Shell(),
1716                 check = options.check,
1717                 verbose = not options.quiet,
1718                 preserve = options.preserve,
1719                 federating = options.federating)
1720
1721     if options.short_names:
1722         test.namelengths = Test.namelengths_short
1723     else:
1724         test.namelengths = Test.namelengths_default
1725
1726     if options.tiny:
1727         sizes = Test.sizes_tiny
1728     elif options.large:
1729         sizes = Test.sizes_large
1730     elif options.xlarge:
1731         sizes = Test.sizes_xlarge
1732     else:
1733         sizes = Test.sizes_default
1734
1735     test.Run(**sizes)
1736
1737 if __name__ == "__main__":
1738     main()