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