2 # -*- coding: utf-8 -*-
4 from nepi.core import testbed
13 class TestbedConfiguration(testbed.TestbedConfiguration):
16 class TestbedInstance(testbed.TestbedInstance):
17 def __init__(self, configuration):
18 self._netns = self._load_netns_module(configuration)
19 self._elements = dict()
20 self._configuration = configuration
21 self._instructions = dict({
29 self._factories = dict({
31 "P2PInterface": list(),
32 "TapNodeInterface": list(),
33 "NodeInterface": list(),
37 self._connections = list()
39 def create(self, guid, factory_id):
40 if guid in self._instructions[CREATE]:
42 raise RuntimeError("cannot add two elements with the same guid")
43 if factory_id not in self._factories:
45 raise RuntimeError("%s is not an allowed factory_id" % factory_id)
46 self._instructions[CREATE][guid] = factory_id
47 self._factories[factory_id].append(guid)
49 def create_set(self, guid, name, value):
50 if guid not in self._instructions[SET]:
51 self._instructions[SET][guid] = dict()
52 self._instructions[SET][guid][name] = value
54 def connect(self, guid1, connector_type_name1, guid2,
55 connector_type_name2):
56 if not guid1 in self._instructions[CONNECT]:
57 self._instructions[CONNECT] = dict()
58 if not connector_type_name1 in self._instructions[CONNECT][guid1]:
59 self._instructions[CONNECT][guid1][connector_type_name1] = dict()
60 self._instructions[CONNECT][guid1][connector_type_name1][guid2] = \
62 self._connections.append((guid1, connector_type_name1, guid2,
63 connector_type_name2))
65 def add_trace(self, guid, trace_id):
66 if not guid in self._instructions[ADD_TRACE]:
67 self._instructions[ADD_TRACE][guid] = list()
68 self._instructions[ADD_TRACE][guid].append(trace_id)
70 def add_adddress(self, guid, family, address, netprefix, broadcast):
71 if not guid in self._instructions[ADD_ADDRESS]:
72 self._instructions[ADD_ADDRESS][guid] = list()
73 self._instructions[ADD_ADDRESS][guid].append((guid, family, address,
74 netprefix, broadcast))
76 def add_route(self, guid, family, destination, netprefix, nexthop,
78 if not guid in self._instructions[ADD_ROUTE]:
79 self._instructions[ADD_ROUTE][guid] = list()
80 self._instructions[ADD_ROUTE][guid].append((family, destination,
81 netprefix, nexthop, interface))
84 # nodes need to be created first
85 factories_order = ["Node", "P2PInterface", "TapNodeInterface",
86 "NodeInterface", "Switch", "Application"]
87 for guid in self._factories[factory_id] \
88 for factory_id in factories_order:
89 self._create_element(guid, factory_id)
90 # free self._factories as it is not going to be used further
91 # TODO: Check if this methods frees everithing...
92 # maybe there are still some references!
93 self._factories = None
96 cross_connections = list()
97 for (guid1, connector_type_name1, guid2, connector_type_name2) \
99 if guid1 not in self._elements or guid2 not in self._elements:
100 # at least one of the elements does not belong to this
101 # TestbedIntsance and so it needs to be treated as a cross
103 cross_connections.append(guid1, connector_type_name1, guid2,
104 connector_type_name2)
106 # TODO: do Whatever is needed to connect
108 self._connections = cross_connections
110 def do_configure(self):
111 raise NotImplementedError
112 #self._object.add_route(
113 # prefix = destination,
114 # prefix_len = netprefix,
117 def do_cross_connect(self):
118 for (guid1, connector_type_name1, guid2, connector_type_name2) \
119 in self._connections:
120 # TODO: do Whatever is needed to connect
122 # free connections list as is not going to be used further
123 self._connections = None
125 def set(self, time, guid, name, value):
126 raise NotImplementedError
128 def get(self, time, guid, name):
129 raise NotImplementedError
131 def start(self, time):
132 raise NotImplementedError
134 def action(self, time, guid, action):
135 raise NotImplementedError
137 def stop(self, time):
138 raise NotImplementedError
140 def status(self, guid):
141 raise NotImplementedError
143 def trace(self, guid, trace_id):
144 raise NotImplementedError
147 raise NotImplementedError
149 def _netns_module(self, configuration):
150 # TODO: Do something with the configuration!!!
153 return sys.modules["netns"]
155 def _create_element(self, guid, factory_id):
157 if guid in self._instructions[SET]:
158 parameters = self._instructions[SET][guid]
159 if guid not in self._elements.keys():
160 if factory_id == "Node":
161 self._create_node_element(guid, parameters)
162 elif factory_id == "P2PInterface":
163 self._create_p2piface_element(guid, parameters)
164 self._set_attributes(guid, parameters)
166 def _create_node_element(self, guid, parameters):
168 if "forward_X11" in paremeters:
169 forward_X11 = parameters["forward_X11"]
170 del parameters["forward_X11"]
171 element = self._netns.Node(forward_X11 = forward_X11)
172 self._elements[guid] = element
174 def _create_p2piface_element(self, guid, parameters):
175 # search in the connections the node asociated with this
176 # P2PInterface and the other P2PInterface to which it is
181 element1, element2 = self._netns.P2PInterface.create_pair(
183 self._elements[guid] = element1
184 self._elements[guid2] = element2
186 def _set_attributes(self, guid, paramenters):
187 for name, value in parameters:
188 # TODO: Validate attribute does not exist!!!
189 setattr(element, name, value)
191 def connect_switch(switch_connector, interface_connector):
192 switch = switch_connector.container_element
193 interface = interface_connector.container_element
194 if switch.is_installed() and interface.is_installed():
195 switch._object.connect(interface._object)
199 #XXX: This connection function cannot be use to transfer a file descriptor
200 # to a remote tap device
201 def connect_fd_local(tap_connector, fdnd_connector):
202 tap = tap_connector.container_element
203 fdnd = fdnd_connector.container_element
204 if fdnd.is_installed() and tap.is_installed():
205 socket = tap.server.modules.socket
206 passfd = tap.server.modules.passfd
207 fd = tap.file_descriptor
208 address = fdnd.socket_address
209 sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
210 sock.connect(address)
211 passfd.sendfd(sock, fd, '0')
212 # TODO: after succesful transfer, the tap device should close the fd
218 'from' : [ "netns", "Node", "devs" ],
219 'to' : [ "netns", "P2PInterface", "node" ],
224 'from' : [ "netns", "Node", "devs" ],
225 'to' : [ "netns", "TapNodeInterface", "node" ],
230 'from' : [ "netns", "Node", "devs" ],
231 'to' : [ "netns", "NodeInterface", "node" ],
236 'from' : [ "netns", "P2PInterface", "p2p" ],
237 'to' : [ "netns", "P2PInterface", "p2p" ],
242 'from' : [ "netns", "TapNodeInterface", "fd" ],
243 'to' : [ "ns3", "ns3::FileDescriptorNetDevice", "fd" ],
244 'code' : connect_fd_local,
248 'from' : [ "netns", "Switch", "devs" ],
249 'to' : [ "netns", "NodeInterface", "switch" ],
250 'code' : connect_switch,
254 'from' : [ "netns", "Node", "apps" ],
255 'to' : [ "netns", "Application", "node" ],
261 class TapNodeInterface(object):
264 self._object = node._object.add_tap()
266 class NodeInterface(object):
269 self._object = n.add_if()
271 class Switch(Topo_ChannelMixin, NetnsElement):
272 def __init__(self, factory, server, container = None):
273 self._object = self.server.modules.netns.Switch()
275 class NetnsApplication(NetnsElement):
276 def __init__(self, factory, server, container = None):
277 super(NetnsApplication, self).__init__(factory, server, container)
279 self.add_string_attribute(name = "command",
280 help = "Command name")
281 self.add_string_attribute(name = "user",
282 help = "system user")
283 self.add_string_attribute(name = "stdin",
284 help = "Standard input")
288 help='Application standard output',
289 filename='stdout.txt',
293 help='Application standard error',
294 filename='stderr.txt',
296 self._add_trace(stdout)
297 self._add_trace(stderr)
300 user = self.get_attribute("user").value
302 stdin = stdout = stderr = None
303 if self.get_attribute('stdin').value:
304 filename = self.get_attribute('stdin')
305 stdin = self.server.modules.__builtin__.open(filename, 'rb')
307 trace = self.get_trace("StdoutTrace")
309 filename = trace.real_filepath
310 stdout = self.server.modules.__builtin__.open(filename, 'wb')
312 trace = self.get_trace("StderrTrace")
314 filename = trace.real_filepath
315 stderr = self.server.modules.__builtin__.open(filename, 'wb')
317 cnctr = self.connector("node")
318 node = iter(cnctr.connections).next().get_other(cnctr)\
322 command = self.get_attribute('command').value
323 targets = re.findall(r"%target:(.*?)%", command)
324 for target in targets:
326 (family, address, port) = resolve_netref(target, AF_INET,
327 self.server.experiment )
328 command = command.replace("%%target:%s%%" % target, address.address)
332 self._object = n.Popen(command, shell = True, stdout = stdout, stdin = stdin,
333 stderr = stderr, user = user)
335 def is_completed(self):
336 if self._object is not None:
337 returncode = self._object.poll()
338 if returncode is not None:
342 class StdTrace(Trace):
347 def real_filepath(self):
348 filename = self.get_attribute("Filename").value
349 return self.element.server.get_trace_filepath(filename)
351 def read_trace(self):
352 filename = self.get_attribute("Filename").value
353 return self.element.server.read_trace(filename)
355 def force_install(object):
356 if not object.is_installed():