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