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