2 # -*- coding: utf-8 -*-
4 from constants import TESTBED_ID
5 from nepi.core import testbed_impl
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
16 import node, interfaces, application
18 self._interfaces = interfaces
19 self._app = application
22 def home_directory(self):
23 return self._home_directory
27 if not hasattr(self, '_plapi'):
31 self._plapi = plcapi.PLCAPI(
32 username = self.authUser,
33 password = self.authString)
35 # anonymous access - may not be enough for much
36 self._plapi = plcapi.PLCAPI()
41 if not hasattr(self, '_slice_id'):
42 slices = self.plapi.GetSlices(self.slicename, fields=('slice_id',))
44 self._slice_id = slices[0]['slice_id']
46 # If it wasn't found, don't remember this failure, keep trying
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")
62 def do_preconfigure(self):
63 # Perform resource discovery if we don't have
64 # specific resources assigned yet
65 self.do_resource_discovery()
67 # Create PlanetLab slivers
68 self.do_provisioning()
70 # Configure elements per XML data
71 super(TestbedController, self).do_preconfigure()
73 def do_resource_discovery(self):
77 # look for perfectly defined nodes
78 # (ie: those with only one candidate)
79 to_provision = self._to_provision = set()
80 for guid, node in self._elements.iteritems():
81 if isinstance(node, self._node.Node) and node._node_id is None:
82 # Try existing nodes first
83 # If we have only one candidate, simply use it
84 candidates = node.find_candidates(
85 filter_slice_id = self.slice_id)
86 if len(candidates) == 1:
87 node.assign_node_id(iter(candidates).next())
89 # Try again including unassigned nodes
90 candidates = node.find_candidates()
91 if len(candidates) > 1:
92 raise RuntimeError, "Cannot assign resources for node %s, too many candidates" % (guid,)
93 if len(candidates) == 1:
94 node_id = iter(candidates).next()
95 node.assign_node_id(node_id)
96 to_provision.add(node_id)
98 raise RuntimeError, "Cannot assign resources for node %s, no candidates" % (guid,)
100 def do_provisioning(self):
101 if self._to_provision:
102 # Add new nodes to the slice
103 cur_nodes = self.plapi.GetSlices(self.slicename, ['node_ids'])[0]['node_ids']
104 new_nodes = list(set(cur_nodes) | self._to_provision)
105 self.plapi.UpdateSlice(self.slicename, nodes=new_nodes)
108 del self._to_provision
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]
116 setattr(element, name, value)
118 if hasattr(element, 'refresh'):
119 # invoke attribute refresh hook
122 def get(self, time, guid, name):
123 # TODO: take on account schedule time for the task
124 element = self._elements.get(guid)
127 if hasattr(element, name):
129 return getattr(element, name)
131 # Try design-time attributes
132 return self.box_get(time, guid, name)
133 except KeyError, AttributeError:
136 def get_route(self, guid, index, attribute):
137 # TODO: fetch real data from planetlab
139 return self.box_get_route(guid, int(index), attribute)
140 except KeyError, AttributeError:
143 def get_address(self, guid, index, attribute='Address'):
147 iface = self._elements.get(guid)
148 if iface and index == 0:
149 if attribute == 'Address':
151 elif attribute == 'NetPrefix':
152 return iface.netprefix
153 elif attribute == 'Broadcast':
154 return iface.broadcast
156 # if all else fails, query box
158 return self.box_get_address(guid, index, attribute)
159 except KeyError, AttributeError:
163 def action(self, time, guid, action):
164 raise NotImplementedError
167 for trace in self._traces.values():
169 for element in self._elements.values():
170 # invoke cleanup hooks
171 if hasattr(element, 'cleanup'):
174 def trace(self, guid, trace_id, attribute='value'):
175 app = self._elements[guid]
177 if attribute == 'value':
178 path = app.sync_trace(self.home_directory, trace_id)
185 elif attribute == 'path':
186 content = app.remote_trace_path(trace_id)
191 def follow_trace(self, trace_id, trace):
192 self._traces[trace_id] = trace
194 def _make_generic(self, parameters, kind):
195 app = kind(self.plapi)
197 # Note: there is 1-to-1 correspondence between attribute names
198 # If that changes, this has to change as well
199 for attr,val in parameters.iteritems():
200 setattr(app, attr, val)
204 def _make_node(self, parameters):
205 node = self._make_generic(parameters, self._node.Node)
207 # If emulation is enabled, we automatically need
208 # some vsys interfaces and packages
210 node.required_vsys.add('ipfw-be')
211 node.required_packages.add('ipfwslice')
215 def _make_node_iface(self, parameters):
216 return self._make_generic(parameters, self._interfaces.NodeIface)
218 def _make_tun_iface(self, parameters):
219 return self._make_generic(parameters, self._interfaces.TunIface)
221 def _make_netpipe(self, parameters):
222 return self._make_generic(parameters, self._interfaces.NetPipe)
224 def _make_internet(self, parameters):
225 return self._make_generic(parameters, self._interfaces.Internet)
227 def _make_application(self, parameters):
228 return self._make_generic(parameters, self._app.Application)
230 def _make_dependency(self, parameters):
231 return self._make_generic(parameters, self._app.Dependency)
233 def _make_nepi_dependency(self, parameters):
234 return self._make_generic(parameters, self._app.NepiDependency)