mock cross_connect test added to test/core/integration.py
[nepi.git] / src / nepi / testbeds / netns / metadata_v01.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from constants import TESTBED_ID
5 from nepi.core import metadata
6 from nepi.core.attributes import Attribute
7 from nepi.util import validation
8 from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \
9         STATUS_FINISHED
10
11 NODE = "Node"
12 P2PIFACE = "P2PNodeInterface"
13 TAPIFACE = "TapNodeInterface"
14 NODEIFACE = "NodeInterface"
15 SWITCH = "Switch"
16 APPLICATION = "Application"
17
18 NS3_TESTBED_ID = "ns3"
19 FDNETDEV = "ns3::FileDescriptorNetDevice"
20
21 ### Connection functions ####
22
23 def connect_switch(testbed_instance, switch_guid, interface_guid):
24     switch = testbed_instance._elements[switch_guid]
25     interface = testbed_instance._elements[interface_guid]
26     switch.connect(interface)
27    
28 def connect_fd(testbed_instance, tap_guid, cross_data):
29     import passfd
30     import socket
31     tap = testbed_instance._elements[tap_guid]
32     fd = tap.file_descriptor
33     address = cross_data["LinuxSocketAddress"]
34     sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
35     sock.connect(address)
36     passfd.sendfd(sock, fd, '0')
37     # TODO: after succesful transfer, the tap device should close the fd
38
39 ### Creation functions ###
40
41 def create_node(testbed_instance, guid):
42     parameters = testbed_instance._get_parameters(guid)
43     forward_X11 = False
44     if "forward_X11" in parameters:
45         forward_X11 = parameters["forward_X11"]
46         del parameters["forward_X11"]
47     element = testbed_instance.netns.Node(forward_X11 = forward_X11)
48     testbed_instance.elements[guid] = element
49
50 def create_p2piface(testbed_instance, guid):
51     if guid in testbed_instance.elements:
52         # The interface pair was already instantiated
53         return
54     # search for the node asociated with the p2piface
55     node1_guid = testbed_instance.get_connected(guid, "node", "devs")
56     if len(node1_guid) == 0:
57         raise RuntimeError("Can't instantiate interface %d outside netns \
58                 node" % guid)
59     node1 = testbed_instance.elements[node1_guid[0]]
60     # search for the pair p2piface
61     p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
62     if len(p2p_guid) == 0:
63         raise RuntimeError("Can't instantiate p2p interface %d. \
64                 Missing interface pair" % guid)
65     guid2 = p2p_guid[0]
66     node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
67     if len(node2_guid) == 0:
68         raise RuntimeError("Can't instantiate interface %d outside netns \
69                 node" % guid2)
70     node2 = testbed_instance.elements[node2_guid[0]]
71     element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
72         node1, node2)
73     testbed_instance.elements[guid] = element1
74     testbed_instance.elements[guid2] = element2
75
76 def create_tapiface(testbed_instance, guid):
77     node_guid = testbed_instance.get_connected(guid, "node", "devs")
78     if len(node_guid) == 0:
79         raise RuntimeError("Can't instantiate interface %d outside netns \
80                 node" % guid)
81     node = testbed_instance.elements[node_guid[0]]
82     element = node.add_tap()
83     testbed_instance.elements[guid] = element
84
85 def create_nodeiface(testbed_instance, guid):
86     node_guid = testbed_instance.get_connected(guid, "node", "devs")
87     if len(node_guid) == 0:
88         raise RuntimeError("Can't instantiate interface %d outside netns \
89                 node" % guid)
90     node = testbed_instance.elements[node_guid[0]]
91     element = node.add_if()
92     testbed_instance.elements[guid] = element
93
94 def create_switch(testbed_instance, guid):
95     element = testbed_instance.netns.Switch()
96     testbed_instance.elements[guid] = element
97
98 def create_application(testbed_instance, guid):
99     testbed_instance.elements[guid] = None # Delayed construction 
100
101 ### Start/Stop functions ###
102
103 def start_application(testbed_instance, guid):
104     parameters = testbed_instance._get_parameters(guid)
105     traces = testbed_instance._get_traces(guid)
106     user = parameters["user"]
107     command = parameters["command"]
108     stdout = stderr = None
109     if "stdout" in traces:
110         filename = testbed_instance.trace_filename(guid, "stdout")
111         stdout = open(filename, "wb")
112         testbed_instance.follow_trace("stdout", stdout)
113     if "stderr" in traces:
114         filename = testbed_instance.trace_filename(guid, "stderr")
115         stderr = open(filename, "wb")
116         testbed_instance.follow_trace("stderr", stderr)
117
118     node_guid = testbed_instance.get_connected(guid, "node", "apps")
119     if len(node_guid) == 0:
120         raise RuntimeError("Can't instantiate interface %d outside netns \
121                 node" % guid)
122     node = testbed_instance.elements[node_guid[0]]
123     element  = node.Popen(command, shell = True, stdout = stdout, 
124             stderr = stderr, user = user)
125     testbed_instance.elements[guid] = element
126
127 ### Status functions ###
128
129 def status_application(testbed_instance, guid):
130     if guid not in testbed_instance.elements.keys():
131         return STATUS_NOT_STARTED
132     app = testbed_instance.elements[guid]
133     if app.poll() == None:
134         return STATUS_RUNNING
135     return STATUS_FINISHED
136
137 ### Configure functions ###
138
139 def configure_device(testbed_instance, guid):
140     element = testbed_instance._elements[guid]
141     if not guid in testbed_instance._add_address:
142         return
143     addresses = testbed_instance._add_address[guid]
144     for address in addresses:
145         (address, netprefix, broadcast) = address
146         # TODO: Decide if we should add a ipv4 or ipv6 address
147         element.add_v4_address(address, netprefix)
148
149 def configure_node(testbed_instance, guid):
150     element = testbed_instance._elements[guid]
151     if not guid in testbed_instance._add_route:
152         return
153     routes = testbed_instance._add_route[guid]
154     for route in routes:
155         (destination, netprefix, nexthop) = route
156         element.add_route(prefix = destination, prefix_len = netprefix,
157             nexthop = nexthop)
158
159 ### Factory information ###
160
161 connector_types = dict({
162     "apps": dict({
163                 "help": "Connector from node to applications", 
164                 "name": "apps",
165                 "max": -1, 
166                 "min": 0
167             }),
168     "devs": dict({
169                 "help": "Connector from node to network interfaces", 
170                 "name": "devs",
171                 "max": -1, 
172                 "min": 0
173             }),
174     "node": dict({
175                 "help": "Connector to a Node", 
176                 "name": "node",
177                 "max": 1, 
178                 "min": 1
179             }),
180     "p2p": dict({
181                 "help": "Connector to a P2PInterface", 
182                 "name": "p2p",
183                 "max": 1, 
184                 "min": 0
185             }),
186     "fd": dict({
187                 "help": "Connector to a network interface that can receive a file descriptor", 
188                 "name": "fd",
189                 "max": 1, 
190                 "min": 0
191             }),
192     "switch": dict({
193                 "help": "Connector to a switch", 
194                 "name": "switch",
195                 "max": 1, 
196                 "min": 0
197             })
198    })
199
200 connections = [
201     dict({
202         "from": (TESTBED_ID, NODE, "devs"),
203         "to":   (TESTBED_ID, P2PIFACE, "node"),
204         "can_cross": False
205     }),
206     dict({
207         "from": (TESTBED_ID, NODE, "devs"),
208         "to":   (TESTBED_ID, TAPIFACE, "node"),
209         "can_cross": False
210     }),
211     dict({
212         "from": (TESTBED_ID, NODE, "devs"),
213         "to":   (TESTBED_ID, NODEIFACE, "node"),
214         "can_cross": False
215     }),
216     dict({
217         "from": (TESTBED_ID, P2PIFACE, "p2p"),
218         "to":   (TESTBED_ID, P2PIFACE, "p2p"),
219         "can_cross": False
220     }),
221     dict({
222         "from": (TESTBED_ID, TAPIFACE, "fd"),
223         "to":   (NS3_TESTBED_ID, FDNETDEV, "fd"),
224         "compl_code": connect_fd,
225         "can_cross": True
226     }),
227      dict({
228         "from": (TESTBED_ID, SWITCH, "devs"),
229         "to":   (TESTBED_ID, NODEIFACE, "switch"),
230         "init_code": connect_switch,
231         "can_cross": False
232     }),
233     dict({
234         "from": (TESTBED_ID, NODE, "apps"),
235         "to":   (TESTBED_ID, APPLICATION, "node"),
236         "can_cross": False
237     })
238 ]
239
240 attributes = dict({
241     "forward_X11": dict({      
242                 "name": "forward_X11",
243                 "help": "Forward x11 from main namespace to the node",
244                 "type": Attribute.BOOL, 
245                 "value": False,
246                 "flags": Attribute.DesignOnly,
247                 "validation_function": validation.is_bool
248             }),
249     "lladdr": dict({      
250                 "name": "lladdr", 
251                 "help": "Mac address", 
252                 "type": Attribute.STRING,
253                 "flags": Attribute.DesignOnly,
254                 "validation_function": validation.is_mac_address
255             }),
256     "up": dict({
257                 "name": "up",
258                 "help": "Link up",
259                 "type": Attribute.BOOL,
260                 "value": False,
261                 "validation_function": validation.is_bool
262             }),
263     "device_name": dict({
264                 "name": "name",
265                 "help": "Device name",
266                 "type": Attribute.STRING,
267                 "flags": Attribute.DesignOnly,
268                 "validation_function": validation.is_string
269             }),
270     "mtu":  dict({
271                 "name": "mtu", 
272                 "help": "Maximum transmition unit for device",
273                 "type": Attribute.INTEGER,
274                 "validation_function": validation.is_integer
275             }),
276     "broadcast": dict({ 
277                 "name": "broadcast",
278                 "help": "Broadcast address",
279                 "type": Attribute.STRING,
280                 "validation_function": validation.is_string # TODO: should be is address!
281             }),
282     "multicast": dict({      
283                 "name": "multicast",
284                 "help": "Multicast enabled",
285                 "type": Attribute.BOOL,
286                 "value": False,
287                 "validation_function": validation.is_bool
288             }),
289     "arp": dict({
290                 "name": "arp",
291                 "help": "ARP enabled",
292                 "type": Attribute.BOOL,
293                 "value": False,
294                 "validation_function": validation.is_bool
295             }),
296     "command": dict({
297                 "name": "command",
298                 "help": "Command line string",
299                 "type": Attribute.STRING,
300                 "flags": Attribute.DesignOnly,
301                 "validation_function": validation.is_string
302             }),
303     "user": dict({
304                 "name": "user",
305                 "help": "System user",
306                 "type": Attribute.STRING,
307                 "flags": Attribute.DesignOnly,
308                 "validation_function": validation.is_string
309             }),
310     "stdin": dict({
311                 "name": "stdin",
312                 "help": "Standard input",
313                 "type": Attribute.STRING,
314                 "flags": Attribute.DesignOnly,
315                 "validation_function": validation.is_string
316             }),
317     })
318
319 traces = dict({
320     "stdout": dict({
321                 "name": "stdout",
322                 "help": "Standard output stream"
323               }),
324     "stderr": dict({
325                 "name": "stderr",
326                 "help": "Application standard error",
327         }) 
328     })
329
330 create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
331         APPLICATION ]
332
333 configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE, SWITCH, NODE,
334         APPLICATION ]
335
336 factories_info = dict({
337     NODE: dict({
338             "allow_routes": True,
339             "help": "Emulated Node with virtualized network stack",
340             "category": "topology",
341             "create_function": create_node,
342             "configure_function": configure_node,
343             "box_attributes": ["forward_X11"],
344             "connector_types": ["devs", "apps"]
345        }),
346     P2PIFACE: dict({
347             "allow_addresses": True,
348             "help": "Point to point network interface",
349             "category": "devices",
350             "create_function": create_p2piface,
351             "configure_function": configure_device,
352             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
353                 "multicast", "broadcast", "arp"],
354             "connector_types": ["node", "p2p"]
355        }),
356     TAPIFACE: dict({
357             "allow_addresses": True,
358             "help": "Tap device network interface",
359             "category": "devices",
360             "create_function": create_tapiface,
361             "configure_function": configure_device,
362             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
363                 "multicast", "broadcast", "arp"],
364             "connector_types": ["node", "fd"]
365         }),
366     NODEIFACE: dict({
367             "allow_addresses": True,
368             "help": "Node network interface",
369             "category": "devices",
370             "create_function": create_nodeiface,
371             "configure_function": configure_device,
372             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
373                 "multicast", "broadcast", "arp"],
374             "connector_types": ["node", "switch"]
375         }),
376     SWITCH: dict({
377             "display_name": "Switch",
378             "help": "Switch interface",
379             "category": "devices",
380             "create_function": create_switch,
381             "box_attributes": ["up", "device_name", "mtu", "multicast"],
382              #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
383              #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
384              #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
385              #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
386              #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
387            "connector_types": ["devs"]
388         }),
389     APPLICATION: dict({
390             "help": "Generic executable command line application",
391             "category": "applications",
392             "create_function": create_application,
393             "start_function": start_application,
394             "status_function": status_application,
395             "box_attributes": ["command", "user"],
396             "connector_types": ["node"],
397             "traces": ["stdout", "stderr"]
398         }),
399 })
400
401 testbed_attributes = dict({
402         "enable_debug": dict({
403                 "name": "enableDebug",
404                 "help": "Enable netns debug output",
405                 "type": Attribute.BOOL,
406                 "value": False,
407                 "validation_function": validation.is_bool
408             }),
409     })
410
411 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
412     @property
413     def connector_types(self):
414         return connector_types
415
416     @property
417     def connections(self):
418         return connections
419
420     @property
421     def attributes(self):
422         return attributes
423
424     @property
425     def traces(self):
426         return traces
427
428     @property
429     def create_order(self):
430         return create_order
431
432     @property
433     def configure_order(self):
434         return configure_order
435
436     @property
437     def factories_info(self):
438         return factories_info
439
440     @property
441     def testbed_attributes(self):
442         return testbed_attributes
443