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