take tags into account
[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             # for testing node arch
930             check=node_fields.copy()
931             for tagname in tag_fields:
932                 check.pop(tagname)
933             node = self.api.GetNodes(node_id)[0]
934             if node != check:
935                 raise Exception,'Unexpected result in GetNodes()'
936             
937             # again when fetching 'arch' explicitly
938             node = self.api.GetNodes(node_id,Node.fields.keys()+tag_fields)[0]
939             if node != node_fields:
940                 raise Exception,"Unexpected result in GetNodes() with tags"
941
942
943             # Add to a random set of node groups
944             nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
945             for nodegroup_id in (set(nodegroup_ids) - set(node['nodegroup_ids'])):
946                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
947                 tagname = nodegroup['tagname']
948                 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
949                 if not node_tags:
950                     self.api.AddNodeTag(node_id,tagname,'yes')
951                 else:
952                     node_tag=node_tags[0]
953                     self.api.UpdateNodeTag(node_tag['node_tag_id'],'yes')
954             for nodegroup_id in (set(node['nodegroup_ids']) - set(nodegroup_ids)):
955                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
956                 tagname = nodegroup['tagname']
957                 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
958                 if not node_tags:
959                     self.api.AddNodeTag(node_id,tagname,'no')
960                 else:
961                     node_tag=node_tags[0]
962                     self.api.UpdateNodeTag(node_tag['node_tag_id'],'no')
963
964             if self.check:
965                 # Check node
966                 node = self.api.GetNodes([node_id])[0]
967                 for field in node_fields:
968                     assert node[field] == node_fields[field]
969                 assert set(nodegroup_ids) == set(node['nodegroup_ids'])
970
971             if self.verbose:
972                 print "Updated node", node_id
973
974     def DeleteNodes(self):
975         """
976         Delete any random nodes we may have added.
977         """
978
979         for node_id in self.node_ids:
980             # Remove from node groups
981             node = self.api.GetNodes([node_id])[0]
982             for node_tag in GetNodeTags ( {'node_id': node_id} ):
983                 self.api.UpdateNodeTag(node_tag['node_tag_id'],'')
984
985             if self.check:
986                 node = self.api.GetNodes([node_id])[0]
987                 assert not node['nodegroup_ids']
988
989             self.api.DeleteNode(node_id)
990
991             if self.check:
992                 assert not self.api.GetNodes([node_id])
993
994             if self.verbose:
995                 print "Deleted node", node_id
996
997         if self.check:
998             assert not self.api.GetNodes(self.node_ids)
999
1000         self.node_ids = []
1001
1002     def AddInterfaces(self, per_node = 1):
1003         """
1004         Add a number of random network interfaces to each node.
1005         """
1006
1007         network_methods = self.api.GetNetworkMethods()
1008         if not network_methods:
1009             raise Exception, "No network methods"
1010         
1011         network_types = self.api.GetNetworkTypes()
1012         if not network_types:
1013             raise Exception, "No network types"
1014
1015         for node_id in self.node_ids:
1016             for i in range(per_node):
1017                 method = random.sample(network_methods, 1)[0]
1018                 type = random.sample(network_types, 1)[0]
1019
1020                 # Add interface
1021                 interface_fields = random_interface(method, type)
1022                 interface_id = self.api.AddInterface(node_id, interface_fields)
1023
1024                 # Should return a unique interface_id
1025                 assert interface_id not in self.interface_ids
1026                 self.interface_ids.append(interface_id)
1027
1028                 if self.check:
1029                     # Check interface
1030                     interface = self.api.GetInterfaces([interface_id])[0]
1031                     for field in interface_fields:
1032                         assert interface[field] == interface_fields[field]
1033
1034                 if self.verbose:
1035                     print "Added interface", interface_id, "to node", node_id
1036
1037     def UpdateInterfaces(self):
1038         """
1039         Make random changes to any network interfaces we may have added.
1040         """
1041
1042         network_methods = self.api.GetNetworkMethods()
1043         if not network_methods:
1044             raise Exception, "No network methods"
1045         
1046         network_types = self.api.GetNetworkTypes()
1047         if not network_types:
1048             raise Exception, "No network types"
1049
1050         for interface_id in self.interface_ids:
1051             method = random.sample(network_methods, 1)[0]
1052             type = random.sample(network_types, 1)[0]
1053
1054             # Update interface
1055             interface_fields = random_interface(method, type)
1056             self.api.UpdateInterface(interface_id, interface_fields)
1057
1058             if self.check:
1059                 # Check interface
1060                 interface = self.api.GetInterfaces([interface_id])[0]
1061                 for field in interface_fields:
1062                     assert interface[field] == interface_fields[field]
1063
1064             if self.verbose:
1065                 print "Updated interface", interface_id
1066
1067     def DeleteInterfaces(self):
1068         """
1069         Delete any random network interfaces we may have added.
1070         """
1071
1072         for interface_id in self.interface_ids:
1073             self.api.DeleteInterface(interface_id)
1074
1075             if self.check:
1076                 assert not self.api.GetInterfaces([interface_id])
1077
1078             if self.verbose:
1079                 print "Deleted interface", interface_id
1080
1081         if self.check:
1082             assert not self.api.GetInterfaces(self.interface_ids)
1083
1084         self.interface_ids = []
1085         
1086     def AddIlinks (self, n):
1087         """
1088         Add random links between interfaces.
1089         """
1090
1091         for i in range (n):
1092             src = random.sample(self.interface_ids,1)[0]
1093             dst = random.sample(self.interface_ids,1)[0]
1094             ilink_id = self.api.AddIlink (src,dst,
1095                                           self.ilink_type_ids[i],
1096                                           random_ilink())
1097
1098             assert ilink_id not in self.ilink_ids
1099             self.ilink_ids.append(ilink_id)
1100
1101             if self.verbose:
1102                 print 'Added Ilink',ilink_id,' - attached interface',src,'to',dst
1103
1104             if self.check:
1105                 retrieve=GetIlinks({'src_interface_id':src,'dst_interface_id':dst,
1106                                     'tag_type_id':self.ilink_type_ids[i]})
1107                 assert ilink_id==retrieve[0]['ilink_id']
1108
1109
1110     def UpdateIlinks (self):
1111
1112         for ilink_id in self.ilink_ids:
1113             new_value=random_ilink()
1114             self.api.UpdateIlink(ilink_id,new_value)
1115
1116             if self.check:
1117                 ilink=self.api.GetIlinks([ilink_id])[0]
1118                 assert ilink['value'] == new_value
1119
1120             if self.verbose:
1121                 print 'Updated Ilink',ilink_id
1122
1123     def DeleteIlinks (self):
1124         for ilink_id in self.ilink_ids:
1125             self.api.DeleteIlink(ilink_id)
1126
1127             if self.check:
1128                 assert not self.api.GetIlinks({'ilink_id':ilink_id})
1129
1130             if self.verbose:
1131                 print 'Deleted Ilink',ilink_id
1132
1133         if self.check:
1134             assert not self.api.GetIlinks(self.ilink_ids)
1135
1136         self.ilink_ids = []
1137
1138
1139     def AddPCUs(self, per_site = 1):
1140         """
1141         Add a number of random PCUs to each site. Each node at the
1142         site will be added to a port on the PCU if AddNodes() was
1143         previously run.
1144         """
1145
1146         for site_id in self.site_ids:
1147             for i in range(per_site):
1148                 # Add PCU
1149                 pcu_fields = random_pcu()
1150                 pcu_id = self.api.AddPCU(site_id, pcu_fields)
1151
1152                 # Should return a unique pcu_id
1153                 assert pcu_id not in self.pcu_ids
1154                 self.pcu_ids.append(pcu_id)
1155
1156                 # Add each node at this site to a different port on this PCU
1157                 site = self.api.GetSites([site_id])[0]
1158                 port = randint(1, 10)
1159                 for node_id in site['node_ids']:
1160                     self.api.AddNodeToPCU(node_id, pcu_id, port)
1161                     port += 1
1162
1163                 if self.check:
1164                     # Check PCU
1165                     pcu = self.api.GetPCUs([pcu_id])[0]
1166                     for field in pcu_fields:
1167                         assert pcu[field] == pcu_fields[field]
1168
1169                 if self.verbose:
1170                     print "Added PCU", pcu_id, "to site", site_id
1171
1172     def UpdatePCUs(self):
1173         """
1174         Make random changes to any PCUs we may have added.
1175         """
1176
1177         for pcu_id in self.pcu_ids:
1178             # Update PCU
1179             pcu_fields = random_pcu()
1180             self.api.UpdatePCU(pcu_id, pcu_fields)
1181
1182             if self.check:
1183                 # Check PCU
1184                 pcu = self.api.GetPCUs([pcu_id])[0]
1185                 for field in pcu_fields:
1186                     assert pcu[field] == pcu_fields[field]
1187
1188             if self.verbose:
1189                 print "Updated PCU", pcu_id
1190
1191     def DeletePCUs(self):
1192         """
1193         Delete any random nodes we may have added.
1194         """
1195
1196         for pcu_id in self.pcu_ids:
1197             # Remove nodes from PCU
1198             pcu = self.api.GetPCUs([pcu_id])[0]
1199             for node_id in pcu['node_ids']:
1200                 self.api.DeleteNodeFromPCU(node_id, pcu_id)
1201
1202             if self.check:
1203                 pcu = self.api.GetPCUs([pcu_id])[0]
1204                 assert not pcu['node_ids']
1205
1206             self.api.DeletePCU(pcu_id)
1207
1208             if self.check:
1209                 assert not self.api.GetPCUs([pcu_id])
1210
1211             if self.verbose:
1212                 print "Deleted PCU", pcu_id
1213
1214         if self.check:
1215             assert not self.api.GetPCUs(self.pcu_ids)
1216
1217         self.pcu_ids = []
1218
1219     def AddConfFiles(self, n = 10):
1220         """
1221         Add a number of random global configuration files.
1222         """
1223
1224         conf_files = []
1225
1226         for i in range(n):
1227             # Add a random configuration file
1228             conf_files.append(random_conf_file())
1229
1230         if n:
1231             # Add a nodegroup override file
1232             nodegroup_conf_file = conf_files[0].copy()
1233             nodegroup_conf_file['source'] = randpath(255)
1234             conf_files.append(nodegroup_conf_file)
1235
1236             # Add a node override file
1237             node_conf_file = conf_files[0].copy()
1238             node_conf_file['source'] = randpath(255)
1239             conf_files.append(node_conf_file)
1240
1241         for conf_file_fields in conf_files:
1242             conf_file_id = self.api.AddConfFile(conf_file_fields)
1243
1244             # Should return a unique conf_file_id
1245             assert conf_file_id not in self.conf_file_ids
1246             self.conf_file_ids.append(conf_file_id)
1247
1248             # Add to nodegroup
1249             if conf_file_fields == nodegroup_conf_file and self.nodegroup_ids:
1250                 nodegroup_id = random.sample(self.nodegroup_ids, 1)[0]
1251                 self.api.AddConfFileToNodeGroup(conf_file_id, nodegroup_id)
1252             else:
1253                 nodegroup_id = None
1254
1255             # Add to node
1256             if conf_file_fields == node_conf_file and self.node_ids:
1257                 node_id = random.sample(self.node_ids, 1)[0]
1258                 self.api.AddConfFileToNode(conf_file_id, node_id)
1259             else:
1260                 node_id = None
1261
1262             if self.check:
1263                 # Check configuration file
1264                 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1265                 for field in conf_file_fields:
1266                     assert conf_file[field] == conf_file_fields[field]
1267
1268             if self.verbose:
1269                 print "Added configuration file", conf_file_id,
1270                 if nodegroup_id is not None:
1271                     print "to node group", nodegroup_id,
1272                 elif node_id is not None:
1273                     print "to node", node_id,
1274                 print
1275
1276     def UpdateConfFiles(self):
1277         """
1278         Make random changes to any configuration files we may have added.
1279         """
1280
1281         for conf_file_id in self.conf_file_ids:
1282             # Update configuration file
1283             conf_file_fields = random_conf_file()
1284             # Do not update dest so that it remains an override if set
1285             if 'dest' in conf_file_fields:
1286                 del conf_file_fields['dest']
1287             self.api.UpdateConfFile(conf_file_id, conf_file_fields)
1288
1289             if self.check:
1290                 # Check configuration file
1291                 conf_file = self.api.GetConfFiles([conf_file_id])[0]
1292                 for field in conf_file_fields:
1293                     assert conf_file[field] == conf_file_fields[field]
1294
1295             if self.verbose:
1296                 print "Updated configuration file", conf_file_id
1297
1298     def DeleteConfFiles(self):
1299         """
1300         Delete any random configuration files we may have added.
1301         """
1302
1303         for conf_file_id in self.conf_file_ids:
1304             self.api.DeleteConfFile(conf_file_id)
1305
1306             if self.check:
1307                 assert not self.api.GetConfFiles([conf_file_id])
1308
1309             if self.verbose:
1310                 print "Deleted configuration file", conf_file_id
1311
1312         if self.check:
1313             assert not self.api.GetConfFiles(self.conf_file_ids)
1314
1315         self.conf_file_ids = []
1316
1317     def AddTagTypes(self,n_sa,n_ng,n_il):
1318         """
1319         Add as many tag types as there are nodegroups, 
1320         will use tagvalue=yes for each nodegroup
1321         """
1322
1323         roles = self.api.GetRoles()
1324         if not roles:
1325             raise Exception, "No roles"
1326         role_ids = [role['role_id'] for role in roles]
1327
1328         for i in range (n_sa + n_ng + n_il):
1329             tag_type_fields = random_tag_type (role_ids)
1330             tag_type_id = self.api.AddTagType (tag_type_fields)
1331
1332             assert tag_type_id not in \
1333                 self.slice_type_ids + \
1334                 self.nodegroup_type_ids + \
1335                 self.ilink_type_ids
1336             
1337             if i < n_sa:
1338                 self.slice_type_ids.append(tag_type_id)
1339             elif i < n_sa+n_ng :
1340                 self.nodegroup_type_ids.append(tag_type_id)
1341             else:
1342                 self.ilink_type_ids.append(tag_type_id)
1343
1344             if self.check:
1345                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1346                 for field in tag_type_fields:
1347                     assert tag_type[field] == tag_type_fields[field]
1348             if self.verbose:
1349                 print "Updated slice attribute type", tag_type_id
1350
1351     def UpdateTagTypes(self):
1352         """
1353         Make random changes to any slice attribute types we may have added.
1354         """
1355
1356         roles = self.api.GetRoles()
1357         if not roles:
1358             raise Exception, "No roles"
1359         role_ids = [role['role_id'] for role in roles]
1360
1361         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1362             # Update slice attribute type
1363             tag_type_fields = random_tag_type(role_ids)
1364             self.api.UpdateTagType(tag_type_id, tag_type_fields)
1365
1366             if self.check:
1367                 # Check slice attribute type
1368                 tag_type = self.api.GetTagTypes([tag_type_id])[0]
1369                 for field in tag_type_fields:
1370                     assert tag_type[field] == tag_type_fields[field]
1371             if self.verbose:
1372                 print "Updated slice attribute type", tag_type_id
1373
1374     def DeleteTagTypes(self):
1375         """
1376         Delete any random slice attribute types we may have added.
1377         """
1378
1379         for tag_type_id in self.slice_type_ids + self.nodegroup_type_ids + self.ilink_type_ids:
1380             self.api.DeleteTagType(tag_type_id)
1381
1382             if self.check:
1383                 assert not self.api.GetTagTypes([tag_type_id])
1384
1385             if self.verbose:
1386                 print "Deleted slice attribute type", tag_type_id
1387
1388         if self.check:
1389             assert not self.api.GetTagTypes(self.slice_type_ids+self.nodegroup_type_ids+self.ilink_type_ids)
1390
1391         self.slice_type_ids = []
1392         self.nodegroup_type_ids = []
1393
1394     def AddSlices(self, per_site = 10):
1395         """
1396         Add a number of random slices per site.
1397         """
1398
1399         for site in self.api.GetSites(self.site_ids):
1400             for i in range(min(per_site, site['max_slices'])):
1401                 # Add slice
1402                 slice_fields = random_slice(site['login_base'])
1403                 slice_id = self.api.AddSlice(slice_fields)
1404
1405                 # Should return a unique slice_id
1406                 assert slice_id not in self.slice_ids
1407                 self.slice_ids.append(slice_id)
1408
1409                 # Add slice to a random set of nodes
1410                 node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1411                 if node_ids:
1412                     self.api.AddSliceToNodes(slice_id, node_ids)
1413
1414                 # Add random set of site users to slice
1415                 person_ids = random.sample(site['person_ids'], randint(0, len(site['person_ids'])))
1416                 for person_id in person_ids:
1417                     self.api.AddPersonToSlice(person_id, slice_id)
1418
1419                 if self.check:
1420                     # Check slice
1421                     slice = self.api.GetSlices([slice_id])[0]
1422                     for field in slice_fields:
1423                         assert slice[field] == slice_fields[field]
1424
1425                     assert set(node_ids) == set(slice['node_ids'])
1426                     assert set(person_ids) == set(slice['person_ids'])
1427
1428                 if self.verbose:
1429                     print "Added slice", slice_id, "to site", site['site_id'],
1430                     if node_ids:
1431                         print "and nodes", node_ids,
1432                     print
1433                     if person_ids:
1434                         print "Added users", site['person_ids'], "to slice", slice_id
1435
1436     def UpdateSlices(self):
1437         """
1438         Make random changes to any slices we may have added.
1439         """
1440
1441         for slice_id in self.slice_ids:
1442             # Update slice
1443             slice_fields = random_slice("unused")
1444             # Cannot change slice name
1445             if 'name' in slice_fields:
1446                 del slice_fields['name']
1447             self.api.UpdateSlice(slice_id, slice_fields)
1448
1449             slice = self.api.GetSlices([slice_id])[0]
1450
1451             # Add slice to a random set of nodes
1452             node_ids = random.sample(self.node_ids, randint(0, len(self.node_ids)))
1453             self.api.AddSliceToNodes(slice_id, list(set(node_ids) - set(slice['node_ids'])))
1454             self.api.DeleteSliceFromNodes(slice_id, list(set(slice['node_ids']) - set(node_ids)))
1455
1456             # Add random set of users to slice
1457             person_ids = random.sample(self.person_ids, randint(0, len(self.person_ids)))
1458             for person_id in (set(person_ids) - set(slice['person_ids'])):
1459                 self.api.AddPersonToSlice(person_id, slice_id)
1460             for person_id in (set(slice['person_ids']) - set(person_ids)):
1461                 self.api.DeletePersonFromSlice(person_id, slice_id)
1462
1463             if self.check:
1464                 slice = self.api.GetSlices([slice_id])[0]
1465                 for field in slice_fields:
1466                     assert slice[field] == slice_fields[field]
1467                 assert set(node_ids) == set(slice['node_ids'])
1468                 assert set(person_ids) == set(slice['person_ids'])
1469
1470             if self.verbose:
1471                 print "Updated slice", slice_id
1472                 print "Added nodes", node_ids, "to slice", slice_id
1473                 print "Added persons", person_ids, "to slice", slice_id
1474
1475     def DeleteSlices(self):
1476         """
1477         Delete any random slices we may have added.
1478         """
1479
1480         for slice_id in self.slice_ids:
1481             self.api.DeleteSlice(slice_id)
1482
1483             if self.check:
1484                 assert not self.api.GetSlices([slice_id])
1485
1486             if self.verbose:
1487                 print "Deleted slice", slice_id
1488
1489         if self.check:
1490             assert not self.api.GetSlices(self.slice_ids)
1491
1492         self.slice_ids = []
1493
1494     def AddSliceTags(self, per_slice = 2):
1495         """
1496         Add a number of random slices per site.
1497         """
1498
1499         if not self.slice_type_ids:
1500             return
1501
1502         for slice_id in self.slice_ids:
1503             slice = self.api.GetSlices([slice_id])[0]
1504
1505             for i in range(per_slice):
1506                 # Set a random slice/sliver attribute
1507                 for tag_type_id in random.sample(self.slice_type_ids, 1):
1508                     value = randstr(16, letters + '_' + digits)
1509                     # Make it a sliver attribute with 50% probability
1510                     if slice['node_ids']:
1511                         node_id = random.sample(slice['node_ids'] + [None] * len(slice['node_ids']), 1)[0]
1512                     else:
1513                         node_id = None
1514
1515                     # Add slice attribute
1516                     if node_id is None:
1517                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value)
1518                     else:
1519                         slice_tag_id = self.api.AddSliceTag(slice_id, tag_type_id, value, node_id)
1520
1521                     # Should return a unique slice_tag_id
1522                     assert slice_tag_id not in self.slice_tag_ids
1523                     self.slice_tag_ids.append(slice_tag_id)
1524
1525                     if self.check:
1526                         # Check slice attribute
1527                         slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1528                         for field in 'tag_type_id', 'slice_id', 'node_id', 'slice_tag_id', 'value':
1529                             assert slice_tag[field] == locals()[field]
1530
1531                     if self.verbose:
1532                         print "Added slice attribute", slice_tag_id, "of type", tag_type_id,
1533                         if node_id is not None:
1534                             print "to node", node_id,
1535                         print
1536                         
1537     def UpdateSliceTags(self):
1538         """
1539         Make random changes to any slice attributes we may have added.
1540         """
1541
1542         for slice_tag_id in self.slice_tag_ids:
1543             # Update slice attribute
1544             value = randstr(16, letters + '_' + digits)
1545             self.api.UpdateSliceTag(slice_tag_id, value)
1546
1547             # Check slice attribute again
1548             slice_tag = self.api.GetSliceTags([slice_tag_id])[0]
1549             assert slice_tag['value'] == value
1550
1551             if self.verbose:
1552                 print "Updated slice attribute", slice_tag_id
1553
1554     def DeleteSliceTags(self):
1555         """
1556         Delete any random slice attributes we may have added.
1557         """
1558
1559         for slice_tag_id in self.slice_tag_ids:
1560             self.api.DeleteSliceTag(slice_tag_id)
1561
1562             if self.check:
1563                 assert not self.api.GetSliceTags([slice_tag_id])
1564
1565             if self.verbose:
1566                 print "Deleted slice attribute", slice_tag_id
1567
1568         if self.check:
1569             assert not self.api.GetSliceTags(self.slice_tag_ids)
1570
1571         self.slice_tag_ids = []
1572
1573 def main():
1574     parser = OptionParser()
1575     parser.add_option("-c", "--check", action = "store_true", default = False, 
1576                       help = "Check most actions (default: %default)")
1577     parser.add_option("-q", "--quiet", action = "store_true", default = False, 
1578                       help = "Be quiet (default: %default)")
1579     parser.add_option("-p","--preserve", action="store_true", default =False,
1580                       help = "Do not delete created objects")
1581     parser.add_option("-t", "--tiny", action = "store_true", default = False, 
1582                       help = "Run a tiny test (default: %default)")
1583     (options, args) = parser.parse_args()
1584
1585     test = Test(api = Shell(),
1586                 check = options.check,
1587                 verbose = not options.quiet,
1588                 preserve = options.preserve)
1589
1590     if options.tiny:
1591         params = Test.tiny
1592     else:
1593         params = Test.default
1594
1595     test.Run(**params)
1596
1597 if __name__ == "__main__":
1598     main()