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