2 # -*- coding: utf-8 -*-
4 from constants import TESTBED_ID
5 import nepi.util.ipaddr2 as ipaddr2
6 import nepi.util.server as server
14 class NodeIface(object):
15 def __init__(self, api=None):
23 # These get initialized at configuration time
29 self._interface_id = None
31 # These get initialized when the iface is connected to its node
34 # These get initialized when the iface is connected to the internet
35 self.has_internet = False
38 return "%s<ip:%s/%s up mac:%s>" % (
39 self.__class__.__name__,
40 self.address, self.netmask,
44 def add_address(self, address, netprefix, broadcast):
45 raise RuntimeError, "Cannot add explicit addresses to public interface"
47 def pick_iface(self, siblings):
49 Picks an interface using the PLCAPI to query information about the node.
51 Needs an assigned node.
54 siblings: other NodeIface elements attached to the same node
57 if self.node is None or self.node._node_id is None:
58 raise RuntimeError, "Cannot pick interface without an assigned node"
60 avail = self._api.GetInterfaces(
61 node_id=self.node._node_id,
62 is_primary=self.primary,
63 fields=('interface_id','mac','netmask','ip') )
65 used = set([sibling._interface_id for sibling in siblings
66 if sibling._interface_id is not None])
68 for candidate in avail:
69 candidate_id = candidate['interface_id']
70 if candidate_id not in used:
72 self._interface_id = candidate_id
73 self.address = candidate['ip']
74 self.lladdr = candidate['mac']
75 self.netprefix = candidate['netmask']
76 self.netmask = ipaddr2.ipv4_dot2mask(self.netprefix) if self.netprefix else None
79 raise RuntimeError, "Cannot configure interface: cannot find suitable interface in PlanetLab node"
82 if not self.has_internet:
83 raise RuntimeError, "All external interface devices must be connected to the Internet"
86 class _CrossIface(object):
87 def __init__(self, proto, addr, port):
88 self.tun_proto = proto
92 class TunIface(object):
93 def __init__(self, api=None):
100 self.netprefix = None
104 self.device_name = None
107 self.txqueuelen = None
112 # These get initialized when the iface is connected to its node
115 # These get initialized when the iface is configured
116 self.external_iface = None
118 # These get initialized when the iface is configured
119 # They're part of the TUN standard attribute set
123 # These get initialized when the iface is connected to its peer
124 self.peer_iface = None
125 self.peer_proto = None
126 self.peer_proto_impl = None
128 # same as peer proto, but for execute-time standard attribute lookups
129 self.tun_proto = None
132 return "%s<ip:%s/%s %s%s>" % (
133 self.__class__.__name__,
134 self.address, self.netprefix,
135 " up" if self.up else " down",
136 " snat" if self.snat else "",
139 def add_address(self, address, netprefix, broadcast):
140 if (self.address or self.netprefix or self.netmask) is not None:
141 raise RuntimeError, "Cannot add more than one address to TUN interfaces"
143 raise ValueError, "TUN interfaces cannot broadcast in PlanetLab"
145 self.address = address
146 self.netprefix = netprefix
147 self.netmask = ipaddr2.ipv4_mask2dot(netprefix)
151 raise RuntimeError, "Unconnected TUN iface - missing node"
152 if self.peer_iface and self.peer_proto not in tunproto.PROTO_MAP:
153 raise RuntimeError, "Unsupported tunnelling protocol: %s" % (self.peer_proto,)
154 if not self.address or not self.netprefix or not self.netmask:
155 raise RuntimeError, "Misconfigured TUN iface - missing address"
157 def prepare(self, home_path, listening):
158 if not self.peer_iface and (self.peer_proto and (listening or (self.peer_addr and self.peer_port))):
160 self.peer_iface = CrossIface(
165 if not self.peer_proto_impl:
166 self.peer_proto_impl = tunproto.PROTO_MAP[self.peer_proto](
167 self, self.peer_iface, home_path, listening)
168 self.peer_proto_impl.port = self.tun_port
169 self.peer_proto_impl.prepare()
172 if self.peer_proto_impl:
173 self.peer_proto_impl.setup()
176 if self.peer_proto_impl:
177 self.peer_proto_impl.shutdown()
178 self.peer_proto_impl = None
180 def sync_trace(self, local_dir, whichtrace):
181 if self.peer_proto_impl:
182 return self.peer_proto_impl.sync_trace(local_dir, whichtrace)
186 # Yep, it does nothing - yet
187 class Internet(object):
188 def __init__(self, api=None):
190 api = plcapi.PLCAPI()
193 class NetPipe(object):
194 def __init__(self, api=None):
196 api = plcapi.PLCAPI()
212 # These get initialized when the pipe is connected to its node
214 self.configured = False
218 raise RuntimeError, "Undefined NetPipe mode"
219 if not self.portList:
220 raise RuntimeError, "Undefined NetPipe port list - must always define the scope"
221 if not (self.plrIn or self.bwIn or self.delayIn):
222 raise RuntimeError, "Undefined NetPipe inbound characteristics"
223 if not (self.plrOut or self.bwOut or self.delayOut):
224 raise RuntimeError, "Undefined NetPipe outbound characteristics"
226 raise RuntimeError, "Unconnected NetPipe"
228 def _add_pipedef(self, bw, plr, delay, options):
230 options.extend(("delay","%dms" % (delay,)))
232 options.extend(("bw","%.8fMbit/s" % (bw,)))
234 options.extend(("plr","%.8f" % (plr,)))
236 def _get_ruledef(self):
239 "@" if self.addrList else "",
244 if self.bwIn or self.plrIn or self.delayIn:
246 self._add_pipedef(self.bwIn, self.plrIn, self.delayIn, options)
247 if self.bwOut or self.plrOut or self.delayOut:
248 options.append("OUT")
249 self._add_pipedef(self.bwOut, self.plrOut, self.delayOut, options)
250 options = ' '.join(options)
252 return (scope,options)
256 scope, options = self._get_ruledef()
257 command = "sudo -S netconfig config %s %s %s" % (self.mode, scope, options)
259 (out,err),proc = server.popen_ssh_command(
261 host = self.node.hostname,
263 user = self.node.slicename,
265 ident_key = self.node.ident_path,
266 server_key = self.node.server_key
270 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
272 # we have to clean up afterwards
273 self.configured = True
278 scope, options = self._get_ruledef()
279 command = "sudo -S netconfig refresh %s %s %s" % (self.mode, scope, options)
281 (out,err),proc = server.popen_ssh_command(
283 host = self.node.hostname,
285 user = self.node.slicename,
287 ident_key = self.node.ident_path,
288 server_key = self.node.server_key
292 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
297 scope, options = self._get_ruledef()
298 command = "sudo -S netconfig delete %s %s" % (self.mode, scope)
300 (out,err),proc = server.popen_ssh_command(
302 host = self.node.hostname,
304 user = self.node.slicename,
306 ident_key = self.node.ident_path,
307 server_key = self.node.server_key
311 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
313 self.configured = False
315 def sync_trace(self, local_dir, whichtrace):
316 if whichtrace != 'netpipeStats':
317 raise ValueError, "Unsupported trace %s" % (whichtrace,)
319 local_path = os.path.join(local_dir, "netpipe_stats_%s" % (self.mode,))
321 # create parent local folders
322 proc = subprocess.Popen(
323 ["mkdir", "-p", os.path.dirname(local_path)],
324 stdout = open("/dev/null","w"),
325 stdin = open("/dev/null","r"))
328 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
330 (out,err),proc = server.popen_ssh_command(
331 "echo 'Rules:' ; sudo -S netconfig show rules ; echo 'Pipes:' ; sudo -S netconfig show pipes",
332 host = self.node.hostname,
334 user = self.node.slicename,
336 ident_key = self.node.ident_path,
337 server_key = self.node.server_key
341 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
343 # dump results to file
344 f = open(local_path, "wb")