Make integration tests also use the new environment variables
[nepi.git] / src / nepi / testbeds / planetlab / execute.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from constants import TESTBED_ID
5 from nepi.core import testbed_impl
6 from nepi.util.constants import TIME_NOW
7 import os
8 import time
9
10 class TestbedController(testbed_impl.TestbedController):
11     def __init__(self, testbed_version):
12         super(TestbedController, self).__init__(TESTBED_ID, testbed_version)
13         self._home_directory = None
14         self.slicename = None
15         self._traces = dict()
16
17         import node, interfaces, application
18         self._node = node
19         self._interfaces = interfaces
20         self._app = application
21
22     @property
23     def home_directory(self):
24         return self._home_directory
25
26     @property
27     def plapi(self):
28         if not hasattr(self, '_plapi'):
29             import plcapi
30
31             if self.authUser:
32                 self._plapi = plcapi.PLCAPI(
33                     username = self.authUser,
34                     password = self.authString)
35             else:
36                 # anonymous access - may not be enough for much
37                 self._plapi = plcapi.PLCAPI()
38         return self._plapi
39
40     @property
41     def slice_id(self):
42         if not hasattr(self, '_slice_id'):
43             slices = self.plapi.GetSlices(self.slicename, fields=('slice_id',))
44             if slices:
45                 self._slice_id = slices[0]['slice_id']
46             else:
47                 # If it wasn't found, don't remember this failure, keep trying
48                 return None
49         return self._slice_id
50
51     def do_setup(self):
52         self._home_directory = self._attributes.\
53             get_attribute_value("homeDirectory")
54         self.slicename = self._attributes.\
55             get_attribute_value("slice")
56         self.authUser = self._attributes.\
57             get_attribute_value("authUser")
58         self.authString = self._attributes.\
59             get_attribute_value("authPass")
60         self.sliceSSHKey = self._attributes.\
61             get_attribute_value("sliceSSHKey")
62         super(TestbedController, self).do_setup()
63
64     def do_preconfigure(self):
65         # Perform resource discovery if we don't have
66         # specific resources assigned yet
67         self.do_resource_discovery()
68
69         # Create PlanetLab slivers
70         self.do_provisioning()
71
72         # Configure elements per XML data
73         super(TestbedController, self).do_preconfigure()
74
75     def do_resource_discovery(self):
76         # Do what?
77
78         # Provisional algo:
79         #   look for perfectly defined nodes
80         #   (ie: those with only one candidate)
81         to_provision = self._to_provision = set()
82         for guid, node in self._elements.iteritems():
83             if isinstance(node, self._node.Node) and node._node_id is None:
84                 # Try existing nodes first
85                 # If we have only one candidate, simply use it
86                 candidates = node.find_candidates(
87                     filter_slice_id = self.slice_id)
88                 if len(candidates) == 1:
89                     node.assign_node_id(iter(candidates).next())
90                 else:
91                     # Try again including unassigned nodes
92                     candidates = node.find_candidates()
93                     if len(candidates) > 1:
94                         raise RuntimeError, "Cannot assign resources for node %s, too many candidates" % (guid,)
95                     if len(candidates) == 1:
96                         node_id = iter(candidates).next()
97                         node.assign_node_id(node_id)
98                         to_provision.add(node_id)
99                     elif not candidates:
100                         raise RuntimeError, "Cannot assign resources for node %s, no candidates" % (guid,)
101
102     def do_provisioning(self):
103         if self._to_provision:
104             # Add new nodes to the slice
105             cur_nodes = self.plapi.GetSlices(self.slicename, ['node_ids'])[0]['node_ids']
106             new_nodes = list(set(cur_nodes) | self._to_provision)
107             self.plapi.UpdateSlice(self.slicename, nodes=new_nodes)
108
109         # cleanup
110         del self._to_provision
111
112     def set(self, guid, name, value, time = TIME_NOW):
113         super(TestbedController, self).set(guid, name, value, time)
114         # TODO: take on account schedule time for the task
115         element = self._elements[guid]
116         if element:
117             setattr(element, name, value)
118
119             if hasattr(element, 'refresh'):
120                 # invoke attribute refresh hook
121                 element.refresh()
122
123     def get(self, guid, name, time = TIME_NOW):
124         value = super(TestbedController, self).get(guid, name, time)
125         # TODO: take on account schedule time for the task
126         factory_id = self._create[guid]
127         factory = self._factories[factory_id]
128         if factory.box_attributes.is_attribute_design_only(name):
129             return value
130         element = self._elements.get(guid)
131         try:
132             return getattr(element, name)
133         except KeyError, AttributeError:
134             return value
135
136     def get_address(self, guid, index, attribute='Address'):
137         index = int(index)
138
139         # try the real stuff
140         iface = self._elements.get(guid)
141         if iface and index == 0:
142             if attribute == 'Address':
143                 return iface.address
144             elif attribute == 'NetPrefix':
145                 return iface.netprefix
146             elif attribute == 'Broadcast':
147                 return iface.broadcast
148
149         # if all else fails, query box
150         return self.get_address(guid, index, attribute)
151
152     def action(self, time, guid, action):
153         raise NotImplementedError
154
155     def shutdown(self):
156         for trace in self._traces.values():
157             trace.close()
158         for element in self._elements.values():
159             # invoke cleanup hooks
160             if hasattr(element, 'cleanup'):
161                 element.cleanup()
162
163     def trace(self, guid, trace_id, attribute='value'):
164         app = self._elements[guid]
165
166         if attribute == 'value':
167             path = app.sync_trace(self.home_directory, trace_id)
168             if path:
169                 fd = open(path, "r")
170                 content = fd.read()
171                 fd.close()
172             else:
173                 content = None
174         elif attribute == 'path':
175             content = app.remote_trace_path(trace_id)
176         else:
177             content = None
178         return content
179
180     def follow_trace(self, trace_id, trace):
181         self._traces[trace_id] = trace
182     
183     def _make_generic(self, parameters, kind):
184         app = kind(self.plapi)
185
186         # Note: there is 1-to-1 correspondence between attribute names
187         #   If that changes, this has to change as well
188         for attr,val in parameters.iteritems():
189             setattr(app, attr, val)
190
191         return app
192
193     def _make_node(self, parameters):
194         node = self._make_generic(parameters, self._node.Node)
195
196         # If emulation is enabled, we automatically need
197         # some vsys interfaces and packages
198         if node.emulation:
199             node.required_vsys.add('ipfw-be')
200             node.required_packages.add('ipfwslice')
201
202         return node
203
204     def _make_node_iface(self, parameters):
205         return self._make_generic(parameters, self._interfaces.NodeIface)
206
207     def _make_tun_iface(self, parameters):
208         return self._make_generic(parameters, self._interfaces.TunIface)
209
210     def _make_netpipe(self, parameters):
211         return self._make_generic(parameters, self._interfaces.NetPipe)
212
213     def _make_internet(self, parameters):
214         return self._make_generic(parameters, self._interfaces.Internet)
215
216     def _make_application(self, parameters):
217         return self._make_generic(parameters, self._app.Application)
218
219     def _make_dependency(self, parameters):
220         return self._make_generic(parameters, self._app.Dependency)
221
222     def _make_nepi_dependency(self, parameters):
223         return self._make_generic(parameters, self._app.NepiDependency)
224
225