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