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