another bugfix
[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     sitename=randstr(namelengths['sitename'],namelengths['sitename_contents'])
110     abbreviated_name=randstr(namelengths['abbreviated_name'],namelengths['abbreviated_name_contents'])
111
112     print 'nl[a] in random_site',namelengths['abbreviated_name'],'actual',len(abbreviated_name)
113     return {
114         'name': sitename,
115         'abbreviated_name': abbreviated_name,
116         'login_base': randstr(namelengths['login_base'], letters).lower(),
117         'latitude': int(randfloat(-90.0, 90.0) * 1000) / 1000.0,
118         'longitude': int(randfloat(-180.0, 180.0) * 1000) / 1000.0,
119         }
120             
121 def random_address_type():
122     return {
123         'name': randstr(20),
124         'description': randstr(254),
125         }
126
127 def random_address():
128     return {
129         'line1': randstr(254),
130         'line2': randstr(254),
131         'line3': randstr(254),
132         'city': randstr(254),
133         'state': randstr(254),
134         'postalcode': randstr(64),
135         'country': randstr(128),
136         }
137
138 def random_person(namelengths):
139     return {
140         'first_name': randstr(namelengths['first_name']),
141         'last_name': randstr(namelengths['last_name']),
142         'email': randemail(namelengths),
143         'bio': randstr(254),
144         # Accounts are disabled by default
145         'enabled': False,
146         'password': randstr(254),
147         }
148
149 def random_key(key_types,namelengths):
150     return {
151         'key_type': random.sample(key_types, 1)[0],
152         'key': randkey(namelengths)
153         }
154
155 def random_tag_type (role_ids):
156     return  {'tagname': randstr(12,letters+digits),
157              'category':randstr(4,letters+digits)+'/'+randstr(6,letters+digits),
158              'description' : randstr(128,letters+digits+whitespace+punctuation),
159              }
160
161 def random_nodegroup():
162     return {'groupname' : randstr(30, letters+digits+whitespace) }
163
164 def random_roles(role_ids):
165     nb_roles=len(role_ids)
166     return random.sample(role_ids,random.choice(range(1,nb_roles+1)))
167
168 tag_fields=['arch']
169 def random_node(node_types,boot_states,namelengths):
170     return {
171         'hostname': randhostname(namelengths),
172         'node_type': random.sample(node_types,1)[0],
173         'boot_state': random.sample(boot_states, 1)[0],
174         'model': randstr(namelengths['model']),
175         'version': randstr(64),
176         # for testing node tags
177         'arch':randstr(10),
178         }
179
180 def random_interface(method, type,namelengths):
181     interface_fields = {
182         'method': method,
183         'type': type,
184         'bwlimit': randint(500000, 10000000),
185         }
186
187     if method != 'dhcp':
188         ip = randint(0, 0xffffffff)
189         netmask = (0xffffffff << randint(2, 31)) & 0xffffffff
190         network = ip & netmask
191         broadcast = ((ip & netmask) | ~netmask) & 0xffffffff
192         gateway = randint(network + 1, broadcast - 1)
193         dns1 = randint(0, 0xffffffff)
194
195         for field in 'ip', 'netmask', 'network', 'broadcast', 'gateway', 'dns1':
196             interface_fields[field] = socket.inet_ntoa(struct.pack('>L', locals()[field]))
197         if randint(0,1):
198             interface_fields['hostname']=randhostname(namelengths);
199
200     return interface_fields
201
202 def random_ilink ():
203     return randstr (12)
204
205 def random_pcu(namelengths):
206     return {
207         'hostname': randhostname(namelengths),
208         'ip': socket.inet_ntoa(struct.pack('>L', randint(0, 0xffffffff))),
209         'protocol': randstr(16),
210         'username': randstr(254),
211         'password': randstr(254),
212         'notes': randstr(254),
213         'model': randstr(32),
214         }
215
216 def random_conf_file():
217     return {
218         'enabled': bool(randint()),
219         'source': randpath(255),
220         'dest': randpath(255),
221         'file_permissions': "%#o" % randint(0, 512),
222         'file_owner': randstr(32, letters + '_' + digits),
223         'file_group': randstr(32, letters + '_' + digits),
224         'preinstall_cmd': randpath(100),
225         'postinstall_cmd': randpath(100),
226         'error_cmd': randpath(100),
227         'ignore_cmd_errors': bool(randint()),
228         'always_update': bool(randint()),
229         }
230
231 def random_slice(login_base,namelengths):
232     return {
233         'name': login_base + "_" + randstr(11, letters).lower(),
234         'url': "http://" + randhostname(namelengths) + "/",
235         'description': randstr(2048),
236         }
237
238 class Test:
239     sizes_tiny = {
240         'sites': 1,
241         'address_types': 1,
242         'addresses_per_site': 1,
243         'persons_per_site': 1,
244         'keys_per_person': 1,
245         'slice_tags': 1,
246         'nodegroups': 1,
247         'nodes_per_site': 1,
248         'interfaces_per_node': 1,
249         'ilinks':1,
250         'pcus_per_site': 1,
251         'conf_files': 1,
252         'slices_per_site': 1,
253         'attributes_per_slice': 1,
254         }
255
256     sizes_default = {
257         'sites': 10,
258         'address_types': 2,
259         'addresses_per_site': 2,
260         'persons_per_site': 4,
261         'keys_per_person': 2,
262         'slice_tags': 10,
263         'nodegroups': 10,
264         'nodes_per_site': 2,
265         'interfaces_per_node': 1,
266         'ilinks': 20,
267         'pcus_per_site': 1,
268         'conf_files': 10,
269         'slices_per_site': 4,
270         'attributes_per_slice': 2,
271         }
272
273     sizes_large = {
274         'sites': 200,
275         'address_types': 4,
276         'addresses_per_site': 2,
277         'persons_per_site': 5,
278         'keys_per_person': 2,
279         'slice_tags': 4,
280         'nodegroups': 20,
281         'nodes_per_site': 2,
282         'interfaces_per_node': 2,
283         'ilinks':100,
284         'pcus_per_site': 1,
285         'conf_files': 50,
286         'slices_per_site': 10,
287         'attributes_per_slice': 4,
288         }
289
290     sizes_xlarge = {
291         'sites': 1000,
292         'address_types': 4,
293         'addresses_per_site': 2,
294         'persons_per_site': 5,
295         'keys_per_person': 2,
296         'slice_tags': 4,
297         'nodegroups': 20,
298         'nodes_per_site': 2,
299         'interfaces_per_node': 2,
300         'ilinks':100,
301         'pcus_per_site': 1,
302         'conf_files': 50,
303         'slices_per_site': 10,
304         'attributes_per_slice': 4,
305         }
306
307     namelengths_default = {
308         'hostname1': 61,
309         'hostname2':5,
310         'login_base':20,
311         'sitename':254,
312         'sitename_contents':letters+digits,
313         'abbreviated_name':50,
314         'abbreviated_name_contents':letters+digits+whitespace+punctuation,
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,
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             tt_role_ids=random_roles(role_ids)
1458             for tt_role_id in tt_role_ids:
1459                 self.api.AddRoleToTagType(tt_role_id,tag_type_id)
1460
1461             if i < n_sa:
1462                 self.slice_type_ids.append(tag_type_id)
1463             elif i < n_sa+n_ng :
1464                 self.nodegroup_type_ids.append(tag_type_id)
1465             else:
1466                 self.ilink_type_ids.append(tag_type_id)
1467
1468             if self.check:
1469                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1470                 for field in tag_type_fields:
1471                     assert tag_type[field] == tag_type_fields[field]
1472                 for tt_role_id in tt_role_ids:
1473                     assert tt_role_id in tag_type['role_ids']
1474             if self.verbose:
1475                 print "Created tag type", tag_type_id
1476
1477     def UpdateTagTypes(self):
1478         """
1479         Make random changes to any slice attribute types we may have added.
1480         """
1481
1482         roles = self.api.GetRoles()
1483         if not roles:
1484             raise Exception, "No roles"
1485         role_ids = [role['role_id'] for role in roles]
1486
1487         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1488             # Update slice attribute type
1489             tag_type_fields = random_tag_type(role_ids)
1490             self.api.UpdateTagType(tag_type_id, tag_type_fields)
1491
1492             if self.check:
1493                 # Check slice attribute type
1494                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1495                 for field in tag_type_fields:
1496                     assert tag_type[field] == tag_type_fields[field]
1497             if self.verbose:
1498                 print "Updated tag type", tag_type_id
1499
1500     def DeleteTagTypes(self):
1501         """
1502         Delete any random slice attribute types we may have added.
1503         """
1504
1505         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1506             self.api.DeleteTagType(tag_type_id)
1507
1508             if self.check:
1509                 assert not self.api.GetTagTypes([tag_type_id])
1510
1511             if self.verbose:
1512                 print "Deleted tag type", tag_type_id
1513
1514         if self.check:
1515             assert not self.api.GetTagTypes(self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1516
1517         self.slice_type_ids = []
1518         self.nodegroup_type_ids = []
1519
1520     def AddSlices(self, per_site = 10):
1521         """
1522         Add a number of random slices per site.
1523         """
1524
1525         for site in self.api.GetSites(self.site_ids):
1526             for i in range(min(per_site, site['max_slices'])):
1527                 # Add slice
1528                 slice_fields = random_slice(site['login_base'],self.namelengths)
1529                 slice_id = self.api.AddSlice(slice_fields)
1530
1531                 # Should return a unique slice_id
1532                 assert slice_id not in self.slice_ids
1533                 self.slice_ids.append(slice_id)
1534
1535                 # Add slice to a random set of nodes
1536                 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1537                 if node_ids:
1538                     self.api.AddSliceToNodes(slice_id, node_ids)
1539
1540                 # Add random set of site users to slice
1541                 person_ids = random.sample(site['person_ids'], randint(0, len(site['person_ids'])))
1542                 for person_id in person_ids:
1543                     self.api.AddPersonToSlice(person_id, slice_id)
1544
1545                 if self.check:
1546                     # Check slice
1547                     slice = self.api.GetSlices([slice_id])[0]
1548                     for field in slice_fields:
1549                         assert slice[field] == slice_fields[field]
1550
1551                     assert set(node_ids) == set(slice['node_ids'])
1552                     assert set(person_ids) == set(slice['person_ids'])
1553
1554                 if self.verbose:
1555                     print "Added slice", slice_id, "to site", site['site_id'],
1556                     if node_ids:
1557                         print "and nodes", node_ids,
1558                     print
1559                     if person_ids:
1560                         print "Added users", site['person_ids'], "to slice", slice_id
1561
1562     def UpdateSlices(self):
1563         """
1564         Make random changes to any slices we may have added.
1565         """
1566
1567         for slice_id in self.slice_ids:
1568             # Update slice
1569             slice_fields = random_slice("unused",self.namelengths)
1570             # Cannot change slice name
1571             if 'name' in slice_fields:
1572                 del slice_fields['name']
1573             self.api.UpdateSlice(slice_id, slice_fields)
1574
1575             slice = self.api.GetSlices([slice_id])[0]
1576
1577             # Add slice to a random set of nodes
1578             node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1579             self.api.AddSliceToNodes(slice_id, list(set(node_ids) - set(slice['node_ids'])))
1580             self.api.DeleteSliceFromNodes(slice_id, list(set(slice['node_ids']) - set(node_ids)))
1581
1582             # Add random set of users to slice
1583             person_ids = random.sample(self.person_ids, randint(0, len(self.person_ids)))
1584             for person_id in (set(person_ids) - set(slice['person_ids'])):
1585                 self.api.AddPersonToSlice(person_id, slice_id)
1586             for person_id in (set(slice['person_ids']) - set(person_ids)):
1587                 self.api.DeletePersonFromSlice(person_id, slice_id)
1588
1589             if self.check:
1590                 slice = self.api.GetSlices([slice_id])[0]
1591                 for field in slice_fields:
1592                     assert slice[field] == slice_fields[field]
1593                 assert set(node_ids) == set(slice['node_ids'])
1594                 assert set(person_ids) == set(slice['person_ids'])
1595
1596             if self.verbose:
1597                 print "Updated slice", slice_id
1598                 print "Added nodes", node_ids, "to slice", slice_id
1599                 print "Added persons", person_ids, "to slice", slice_id
1600
1601     def DeleteSlices(self):
1602         """
1603         Delete any random slices we may have added.
1604         """
1605
1606         for slice_id in self.slice_ids:
1607             self.api.DeleteSlice(slice_id)
1608
1609             if self.check:
1610                 assert not self.api.GetSlices([slice_id])
1611
1612             if self.verbose:
1613                 print "Deleted slice", slice_id
1614
1615         if self.check:
1616             assert not self.api.GetSlices(self.slice_ids)
1617
1618         self.slice_ids = []
1619
1620     def AddSliceTags(self, per_slice = 2):
1621         """
1622         Add a number of random slices per site.
1623         """
1624
1625         if not self.slice_type_ids:
1626             return
1627
1628         for slice_id in self.slice_ids:
1629             slice = self.api.GetSlices([slice_id])[0]
1630
1631             for i in range(per_slice):
1632                 # Set a random slice/sliver attribute
1633                 for tag_type_id in random.sample(self.slice_type_ids, 1):
1634                     value = randstr(16, letters + '_' + digits)
1635                     # Make it a sliver attribute with 50% probability
1636                     if slice['node_ids']:
1637                         node_id = random.sample(slice['node_ids'] + [None] * len(slice['node_ids']), 1)[0]
1638                     else:
1639                         node_id = None
1640
1641                     # Add slice attribute
1642                     if node_id is None:
1643                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value)
1644                     else:
1645                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value, node_id)
1646
1647                     # Should return a unique slice_tag_id
1648                     assert slice_tag_id not in self.slice_tag_ids
1649                     self.slice_tag_ids.append(slice_tag_id)
1650
1651                     if self.check:
1652                         # Check slice attribute
1653                         slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1654                         for field in 'tag_type_id', 'slice_id', 'node_id', 'slice_tag_id', 'value':
1655                             assert slice_tag[field] == locals()[field]
1656
1657                     if self.verbose:
1658                         print "Added slice attribute", slice_tag_id, "of type", tag_type_id,
1659                         if node_id is not None:
1660                             print "to node", node_id,
1661                         print
1662                         
1663     def UpdateSliceTags(self):
1664         """
1665         Make random changes to any slice attributes we may have added.
1666         """
1667
1668         for slice_tag_id in self.slice_tag_ids:
1669             # Update slice attribute
1670             value = randstr(16, letters + '_' + digits)
1671             self.api.UpdateSliceTag(slice_tag_id, value)
1672
1673             # Check slice attribute again
1674             slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1675             assert slice_tag['value'] == value
1676
1677             if self.verbose:
1678                 print "Updated slice attribute", slice_tag_id
1679
1680     def DeleteSliceTags(self):
1681         """
1682         Delete any random slice attributes we may have added.
1683         """
1684
1685         for slice_tag_id in self.slice_tag_ids:
1686             self.api.DeleteSliceTag(slice_tag_id)
1687
1688             if self.check:
1689                 assert not self.api.GetSliceTags([slice_tag_id])
1690
1691             if self.verbose:
1692                 print "Deleted slice attribute", slice_tag_id
1693
1694         if self.check:
1695             assert not self.api.GetSliceTags(self.slice_tag_ids)
1696
1697         self.slice_tag_ids = []
1698
1699     # convenience for cleaning up
1700     # not exactly accurate -- use on test plcs only
1701     def WipeSitesFromLength(self):
1702         for site in self.api.GetSites():
1703             abbrev=site['abbreviated_name']
1704 #            print 'matching',len(abbrev),'against',self.namelengths['abbreviated_name']
1705             if len(abbrev)==self.namelengths['abbreviated_name']:
1706 #            if len(abbrev)==17:
1707                 print 'wiping site %d (%s)'%(site['site_id'],site['name'])
1708                 self.api.DeleteSite(site['site_id'])
1709
1710 def main():
1711     parser = OptionParser()
1712     parser.add_option("-c", "--check", action = "store_true", default = False, 
1713                       help = "Check most actions (default: %default)")
1714     parser.add_option("-q", "--quiet", action = "store_true", default = False, 
1715                       help = "Be quiet (default: %default)")
1716     parser.add_option("-p","--preserve", action="store_true", default =False,
1717                       help = "Do not delete created objects")
1718     parser.add_option("-t", "--tiny", action = "store_true", default = False, 
1719                       help = "Run a tiny test (default: %default)")
1720     parser.add_option("-l", "--large", action = "store_true", default = False, 
1721                       help = "Run a large test (default: %default)")
1722     parser.add_option("-x", "--xlarge", action = "store_true", default = False, 
1723                       help = "Run an XL test (default: %default)")
1724     parser.add_option("-s", "--short-names", action="store_true", dest="short_names", default = False, 
1725                       help = "Generate smaller names for checking UI rendering")
1726     parser.add_option ("-f", "--foreign", action="store_true", dest="federating", default = False,
1727                        help = "Create a fake peer and add items in it (no update, no delete)")
1728     parser.add_option ("-w", "--wipe", action="store_true", dest="wipe", default = False,
1729                        help = "Wipe sites whose abbrev matches what the tests created")
1730     (options, args) = parser.parse_args()
1731
1732     test = Test(api = Shell(),
1733                 check = options.check,
1734                 verbose = not options.quiet,
1735                 preserve = options.preserve,
1736                 federating = options.federating)
1737
1738     if options.short_names:
1739         test.namelengths = Test.namelengths_short
1740     else:
1741         test.namelengths = Test.namelengths_default
1742
1743     if options.wipe:
1744         test.WipeSitesFromLength()
1745         return
1746
1747     if options.tiny:
1748         sizes = Test.sizes_tiny
1749     elif options.large:
1750         sizes = Test.sizes_large
1751     elif options.xlarge:
1752         sizes = Test.sizes_xlarge
1753     else:
1754         sizes = Test.sizes_default
1755     test.Run(**sizes)
1756
1757 if __name__ == "__main__":
1758     main()