added step sfa-describe that at the very least does a rain check on sfi describe...
[tests.git] / system / TestSliceSfa.py
1 # Thierry Parmentelat <thierry.parmentelat@inria.fr>
2 # Copyright (C) 2010 INRIA 
3 #
4
5 import os.path
6 import time
7 from datetime import datetime, timedelta
8 import json
9 import traceback
10
11 import utils
12 from TestNode import TestNode
13 from TestUser import TestUser
14 from TestBoxQemu import TestBoxQemu
15
16 from Completer import Completer, CompleterTask
17 from TestSlice import CompleterTaskSliceSsh
18
19 class TestSliceSfa:
20
21     def __init__ (self, test_auth_sfa, slice_spec):
22         self.test_auth_sfa = test_auth_sfa
23         self.slice_spec = slice_spec
24         # shortcuts
25         self.test_plc = self.test_auth_sfa.test_plc
26
27     def hrn (self): 
28         return self.test_auth_sfa.obj_hrn(self.slice_spec['name'])
29     def sfi_path (self):
30         return self.test_auth_sfa.sfi_path()
31
32     # send back up to the TestAuthSfa
33     def sfi_path (self):
34         return self.test_auth_sfa.sfi_path()
35     def sfi_pi(self, *args, **kwds):
36         return self.test_auth_sfa.sfi_pi(*args, **kwds)
37     def sfi_user(self, *args, **kwds):
38         return self.test_auth_sfa.sfi_user(*args, **kwds)
39
40     def discover_option(self):
41         return "-r GENI"
42
43     # those are step names exposed as methods of TestPlc, hence the _sfa
44
45     # needs to be run as pi
46     def sfa_register_slice(self, options):
47         "run sfi register (on Registry)"
48         sfi_command  = "register"
49         sfi_command += " --type slice"
50         sfi_command += " --xrn {}".format(self.hrn())
51         for opt in self.slice_spec['register_options']:
52             sfi_command += " {}".format(opt)
53         return self.test_plc.run_in_guest(self.sfi_pi(sfi_command))==0
54
55     def sfa_renew_slice(self, options):
56         "run sfi renew (on Aggregates)"
57 #        too_late =  (datetime.now() + timedelta(weeks=52)).strftime("%Y-%m-%d")
58         one_month = (datetime.now() + timedelta(weeks=4)).strftime("%Y-%m-%d")
59         too_late =  "+12m"
60 #        one_month = "+4w"
61         # we expect this to fail on too long term attemps, but to succeed otherwise
62         overall = True
63         for ( renew_until, expected) in [ (too_late, False), (one_month, True) ] :
64             sfi_command = "renew"
65             sfi_command += " {}".format(self.hrn())
66             sfi_command += " {}".format(renew_until)
67             succeeded = self.test_plc.run_in_guest(self.sfi_user(sfi_command))==0
68             if succeeded != expected:
69                 utils.header ("Expecting success={}, got {}".format(expected, succeeded))
70                 # however it turns out sfi renew always returns fine....
71                 #overall=False
72             # so for helping manual checks:
73             # xxx this should use sfa_get_expires below and actually check the expected result
74             sfi_command = "show -k hrn -k expires {}".format(self.hrn())
75             self.test_plc.run_in_guest(self.sfi_user(sfi_command))
76         return overall
77
78     def sfa_get_expires (self, options):
79         filename = "{}.json".format(self.hrn())
80         # /root/sfi/pg/<>
81         inplc_filename = os.path.join(self.sfi_path(), filename)
82         # /vservers/<>/root/sfi/... - cannot use os.path 
83         inbox_filename = "{}{}".format(self.test_plc.vm_root_in_host(), inplc_filename)
84         sfi_command  = ""
85         sfi_command += "-R {} --rawformat json".format(inplc_filename)
86         sfi_command += " status"
87         sfi_command += " {}".format(self.hrn())
88         # cannot find it if sfi status returns an error
89         if self.test_plc.run_in_guest (self.sfi_user(sfi_command)) !=0: return
90         if self.test_plc.test_ssh.fetch(inbox_filename, filename)!=0: return 
91         try:
92             with open(filename) as f:
93                 status = json.loads(f.read())
94             value = status['value']
95             sliver = value['geni_slivers'][0]
96             expires = sliver['geni_expires']
97             print(" * expiration for {} (first sliver) -> {}".format(self.hrn(), expires))
98             return expires
99         except:
100             traceback.print_exc()
101
102     # helper - filename to store a given result
103     def _resname (self, name, ext): return "{}.{}".format(name, ext)
104     def adfile (self): return self._resname("ad", "rspec")
105     def reqfile (self): return self._resname("req", "rspec")
106     def empty_reqfile (self): return "empty-rspec.xml"
107     def nodefile (self): return self._resname("nodes", "txt")
108     def describfile (self): return self._resname("describ", "rspec")
109     
110     def sfa_describe(self, options):
111         "run sfi describe into described.rspec"
112         return self.test_plc.run_in_guest(self.sfi_user(
113             "describe {} -o {}/{}"
114             .format(self.hrn(), self.sfi_path(), self.describfile()))) == 0
115
116     # run as user
117     def sfa_discover(self, options):
118         "discover resources into ad.rspec"
119         return self.test_plc.run_in_guest(self.sfi_user(
120                 "resources {} -o {}/{}"\
121                     .format(self.discover_option(),self.sfi_path(),self.adfile()))) == 0
122
123     def sfa_rspec(self, options):
124         "invoke sfiListNodes and sfiAddSlivers to prepare a rspec"
125         commands = [
126             "sfiListNodes.py -i {}/{} -o {}/{}".format(self.sfi_path(), self.adfile(),
127                                                        self.sfi_path(), self.nodefile()),
128             "sfiAddSliver.py -i {}/{} -n {}/{} -o {}/{}".format(self.sfi_path(), self.adfile(),
129                                                                 self.sfi_path(), self.nodefile(),
130                                                                 self.sfi_path(), self.reqfile()),
131             ]
132         for command in commands:
133             if self.test_plc.run_in_guest(command) != 0: return False
134         return True
135
136     def _sfa_allocate(self, file, options):
137         command = self.sfi_user("allocate {} {}".format(self.hrn(), file))
138         return self.test_plc.run_in_guest(command) == 0
139
140     def sfa_allocate(self, options):
141         "invoke run sfi allocate (on SM)"
142         return self._sfa_allocate(self.reqfile(), options)
143     def sfa_allocate_empty(self, options):
144         "invoke run sfi allocate (on SM) with an empty rspec"
145         return self._sfa_allocate(self.empty_reqfile(), options)
146
147     def sfa_provision(self, options):
148         "invoke run sfi provision (on SM)"
149         command = self.sfi_user("provision {}".format(self.hrn()))
150         return self.test_plc.run_in_guest(command) == 0
151     # just a synonym
152     sfa_provision_empty = sfa_provision
153
154     def plc_name (self):
155         return "{}_{}".format(self.test_auth_sfa.login_base, self.slice_spec['name'])
156
157     # all local nodes in slice ?
158     def sfa_check_slice_plc (self, options):
159         "check the slice has been created at the plc - all local nodes should be in slice"
160         slice = self.test_plc.apiserver.GetSlices(self.test_plc.auth_root(), self.plc_name())[0]
161         nodes = self.test_plc.apiserver.GetNodes(self.test_plc.auth_root(), {'peer_id':None})
162         result = True
163         for node in nodes: 
164             if node['node_id'] in slice['node_ids']:
165                 utils.header("local node {} found in slice {}".format(node['hostname'], slice['name']))
166             else:
167                 utils.header("ERROR - local node {} NOT FOUND in slice {}"\
168                              .format(node['hostname'], slice['name']))
169                 result = False
170         return result
171
172     # no node left in slice ?
173     def sfa_check_slice_plc_empty (self, options):
174         "check the slice have been emptied at the plcs - no node should be in slice"
175         slices = self.test_plc.apiserver.GetSlices(self.test_plc.auth_root(), 
176                                                    self.plc_name(),
177                                                    ['node_ids'])
178         return not slices[0]['node_ids']
179
180     # xxx historically this used to do the same as sfa-create-slice
181     # which was later on split into 3 distinct steps, 
182     # and we can ignore the first that is about setting up the rspec
183     def sfa_update_slice(self, options):
184         "re-run sfi allocate and provision (on SM) on existing object"
185         return self.sfa_allocate(options) and self.sfa_provision(options)
186
187     # run as pi
188     def sfa_delete_slice(self, options):
189         "run sfi delete"
190         self.test_plc.run_in_guest(self.sfi_pi("delete {}".format(self.hrn())))
191         return self.test_plc.run_in_guest(self.sfi_pi("remove -t slice {}".format(self.hrn()))) == 0
192
193     def locate_private_key(self):
194         return self.test_plc.locate_private_key_from_key_names ( [ self.slice_spec['key_name'] ] )
195
196     # check the resulting sliver
197     def ssh_slice_sfa(self, options, timeout_minutes=40, silent_minutes=0, period_seconds=15):
198         "tries to ssh-enter the SFA slice"
199         timeout  = timedelta(minutes=timeout_minutes)
200         graceout = timedelta(minutes=silent_minutes)
201         period   = timedelta(seconds=period_seconds)
202         # locate a key
203         private_key=self.locate_private_key()
204         if not private_key :
205             utils.header("WARNING: Cannot find a valid key for slice {}".format(self.hrn()))
206             return False
207         command="echo hostname ; hostname; echo id; id; echo uname -a ; uname -a"
208         
209         tasks = []
210         slicename = self.plc_name()
211         dry_run = getattr(options,'dry_run',False)
212         for nodename in self.slice_spec['nodenames']:
213             (site_spec,node_spec) = self.test_plc.locate_node(nodename)
214             tasks.append( CompleterTaskSliceSsh(self.test_plc, node_spec['node_fields']['hostname'],
215                                                 slicename, private_key, command,
216                                                 expected=True, dry_run=dry_run))
217         return Completer (tasks, message='ssh_slice_sfa').run(timeout, graceout, period)