2 # -*- coding: utf-8 -*-
4 from constants import TESTBED_ID
5 from nepi.core import testbed_impl
6 from nepi.util.constants import TIME_NOW
11 class TestbedController(testbed_impl.TestbedController):
12 def __init__(self, testbed_version):
13 super(TestbedController, self).__init__(TESTBED_ID, testbed_version)
14 self._home_directory = None
18 import node, interfaces, application
20 self._interfaces = interfaces
21 self._app = application
24 def home_directory(self):
25 return self._home_directory
29 if not hasattr(self, '_plapi'):
33 self._plapi = plcapi.PLCAPI(
34 username = self.authUser,
35 password = self.authString,
36 hostname = self.plcHost,
37 urlpattern = self.plcUrl
40 # anonymous access - may not be enough for much
41 self._plapi = plcapi.PLCAPI()
46 if not hasattr(self, '_slice_id'):
47 slices = self.plapi.GetSlices(self.slicename, fields=('slice_id',))
49 self._slice_id = slices[0]['slice_id']
51 # If it wasn't found, don't remember this failure, keep trying
56 self._home_directory = self._attributes.\
57 get_attribute_value("homeDirectory")
58 self.slicename = self._attributes.\
59 get_attribute_value("slice")
60 self.authUser = self._attributes.\
61 get_attribute_value("authUser")
62 self.authString = self._attributes.\
63 get_attribute_value("authPass")
64 self.sliceSSHKey = self._attributes.\
65 get_attribute_value("sliceSSHKey")
66 self.plcHost = self._attributes.\
67 get_attribute_value("plcHost")
68 self.plcUrl = self._attributes.\
69 get_attribute_value("plcUrl")
70 super(TestbedController, self).do_setup()
72 def do_preconfigure(self):
73 # Perform resource discovery if we don't have
74 # specific resources assigned yet
75 self.do_resource_discovery()
77 # Create PlanetLab slivers
78 self.do_provisioning()
80 # Configure elements per XML data
81 super(TestbedController, self).do_preconfigure()
83 def do_resource_discovery(self):
84 to_provision = self._to_provision = set()
87 # look for perfectly defined nodes
88 # (ie: those with only one candidate)
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())
98 # Try again including unassigned nodes
99 candidates = node.find_candidates()
100 if len(candidates) > 1:
102 if len(candidates) == 1:
103 node_id = iter(candidates).next()
104 node.assign_node_id(node_id)
105 to_provision.add(node_id)
107 raise RuntimeError, "Cannot assign resources for node %s, no candidates sith %s" % (guid,
108 node.make_filter_description())
110 # Now do the backtracking search for a suitable solution
111 # First with existing slice nodes
114 for guid, node in self._elements.iteritems():
115 if isinstance(node, self._node.Node) and node._node_id is None:
116 # Try existing nodes first
117 # If we have only one candidate, simply use it
118 candidates = node.find_candidates(
119 filter_slice_id = self.slice_id)
120 reqs.append(candidates)
125 solution = resourcealloc.alloc(reqs)
126 except resourcealloc.ResourceAllocationError:
127 # Failed, try again with all nodes
130 candidates = node.find_candidates()
131 reqs.append(candidates)
133 solution = resourcealloc.alloc(reqs)
134 to_provision.update(solution)
137 for node, node_id in zip(nodes, solution):
138 node.assign_node_id(node_id)
140 def do_provisioning(self):
141 if self._to_provision:
142 # Add new nodes to the slice
143 cur_nodes = self.plapi.GetSlices(self.slicename, ['node_ids'])[0]['node_ids']
144 new_nodes = list(set(cur_nodes) | self._to_provision)
145 self.plapi.UpdateSlice(self.slicename, nodes=new_nodes)
148 del self._to_provision
150 def set(self, guid, name, value, time = TIME_NOW):
151 super(TestbedController, self).set(guid, name, value, time)
152 # TODO: take on account schedule time for the task
153 element = self._elements[guid]
155 setattr(element, name, value)
157 if hasattr(element, 'refresh'):
158 # invoke attribute refresh hook
161 def get(self, guid, name, time = TIME_NOW):
162 value = super(TestbedController, self).get(guid, name, time)
163 # TODO: take on account schedule time for the task
164 factory_id = self._create[guid]
165 factory = self._factories[factory_id]
166 if factory.box_attributes.is_attribute_design_only(name):
168 element = self._elements.get(guid)
170 return getattr(element, name)
171 except KeyError, AttributeError:
174 def get_address(self, guid, index, attribute='Address'):
178 iface = self._elements.get(guid)
179 if iface and index == 0:
180 if attribute == 'Address':
182 elif attribute == 'NetPrefix':
183 return iface.netprefix
184 elif attribute == 'Broadcast':
185 return iface.broadcast
187 # if all else fails, query box
188 return super(TestbedController, self).get_address(guid, index, attribute)
190 def action(self, time, guid, action):
191 raise NotImplementedError
194 for trace in self._traces.values():
196 for element in self._elements.values():
197 # invoke cleanup hooks
198 if hasattr(element, 'cleanup'):
201 def trace(self, guid, trace_id, attribute='value'):
202 app = self._elements[guid]
204 if attribute == 'value':
205 path = app.sync_trace(self.home_directory, trace_id)
212 elif attribute == 'path':
213 content = app.remote_trace_path(trace_id)
218 def follow_trace(self, trace_id, trace):
219 self._traces[trace_id] = trace
221 def _make_generic(self, parameters, kind):
222 app = kind(self.plapi)
224 # Note: there is 1-to-1 correspondence between attribute names
225 # If that changes, this has to change as well
226 for attr,val in parameters.iteritems():
227 setattr(app, attr, val)
231 def _make_node(self, parameters):
232 node = self._make_generic(parameters, self._node.Node)
234 # If emulation is enabled, we automatically need
235 # some vsys interfaces and packages
237 node.required_vsys.add('ipfw-be')
238 node.required_packages.add('ipfwslice')
242 def _make_node_iface(self, parameters):
243 return self._make_generic(parameters, self._interfaces.NodeIface)
245 def _make_tun_iface(self, parameters):
246 return self._make_generic(parameters, self._interfaces.TunIface)
248 def _make_tap_iface(self, parameters):
249 return self._make_generic(parameters, self._interfaces.TapIface)
251 def _make_netpipe(self, parameters):
252 return self._make_generic(parameters, self._interfaces.NetPipe)
254 def _make_internet(self, parameters):
255 return self._make_generic(parameters, self._interfaces.Internet)
257 def _make_application(self, parameters):
258 return self._make_generic(parameters, self._app.Application)
260 def _make_dependency(self, parameters):
261 return self._make_generic(parameters, self._app.Dependency)
263 def _make_nepi_dependency(self, parameters):
264 return self._make_generic(parameters, self._app.NepiDependency)
266 def _make_ns3_dependency(self, parameters):
267 return self._make_generic(parameters, self._app.NS3Dependency)