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