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