checking tags: did it again
[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             node = self.api.GetNodes([node_id])[0]
930
931             # Add to a random set of node groups
932             nodegroup_ids = random.sample(self.nodegroup_ids, randint(0, len(self.nodegroup_ids)))
933             for nodegroup_id in (set(nodegroup_ids) - set(node['nodegroup_ids'])):
934                 nodegroup = self.api.GetNodeGroups([nodegroup_id])[0]
935                 tagname = nodegroup['tagname']
936                 node_tags = self.api.GetNodeTags({'node_id':node_id,'tagname':tagname})
937                 if not node_tags:
938                     self.api.AddNodeTag(node_id,tagname,'yes')
939                 else:
940                     node_tag=node_tags[0]
941                     self.api.UpdateNodeTag(node_tag['node_tag_id'],'yes')
942             for nodegroup_id in (set(node['nodegroup_ids']) - set(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,'no')
948                 else:
949                     node_tag=node_tags[0]
950                     self.api.UpdateNodeTag(node_tag['node_tag_id'],'no')
951
952             if self.check:
953                 # Check node
954                 node = self.api.GetNodes([node_id])[0]
955                 for field in node_fields:
956                     if field not in tag_fields:
957                         if node[field] != node_fields[field]:
958                             raise Exception, "Unexpected field %s in node after GetNodes()"%field
959                 assert set(nodegroup_ids) == set(node['nodegroup_ids'])
960
961                 print 'WARNING: skipping updatenode with tags as this is not implemented yet'
962                 # again when fetching 'arch' explicitly
963                 node2 = self.api.GetNodes([node_id],node_fields.keys())[0]
964                 for field in node_fields:
965                     if node2[field] != node_fields[field]:
966                         raise Exception, "Unexpected field %s in node after GetNodes(tags)"%field
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()