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 TunIface(object):
87 def __init__(self, api=None):
98 self.device_name = None
101 self.txqueuelen = None
106 # These get initialized when the iface is connected to its node
109 # These get initialized when the iface is configured
110 self.external_iface = None
112 # These get initialized when the iface is configured
113 # They're part of the TUN standard attribute set
117 # These get initialized when the iface is connected to its peer
118 self.peer_iface = None
119 self.peer_proto = None
120 self.peer_proto_impl = None
122 # same as peer proto, but for execute-time standard attribute lookups
123 self.tun_proto = None
126 return "%s<ip:%s/%s %s%s>" % (
127 self.__class__.__name__,
128 self.address, self.netprefix,
129 " up" if self.up else " down",
130 " snat" if self.snat else "",
133 def add_address(self, address, netprefix, broadcast):
134 if (self.address or self.netprefix or self.netmask) is not None:
135 raise RuntimeError, "Cannot add more than one address to TUN interfaces"
137 raise ValueError, "TUN interfaces cannot broadcast in PlanetLab"
139 self.address = address
140 self.netprefix = netprefix
141 self.netmask = ipaddr2.ipv4_mask2dot(netprefix)
145 raise RuntimeError, "Unconnected TUN iface - missing node"
146 if self.peer_iface and self.peer_proto not in tunproto.PROTO_MAP:
147 raise RuntimeError, "Unsupported tunnelling protocol: %s" % (self.peer_proto,)
148 if not self.address or not self.netprefix or not self.netmask:
149 raise RuntimeError, "Misconfigured TUN iface - missing address"
151 def prepare(self, home_path, listening):
153 if not self.peer_proto_impl:
154 self.peer_proto_impl = tunproto.PROTO_MAP[self.peer_proto](
155 self, self.peer_iface, home_path, listening)
156 self.peer_proto_impl.port = self.tun_port
157 self.peer_proto_impl.prepare()
161 self.peer_proto_impl.setup()
164 if self.peer_proto_impl:
165 self.peer_proto_impl.shutdown()
166 self.peer_proto_impl = None
168 def sync_trace(self, local_dir, whichtrace):
169 if self.peer_proto_impl:
170 return self.peer_proto_impl.sync_trace(local_dir, whichtrace)
174 # Yep, it does nothing - yet
175 class Internet(object):
176 def __init__(self, api=None):
178 api = plcapi.PLCAPI()
181 class NetPipe(object):
182 def __init__(self, api=None):
184 api = plcapi.PLCAPI()
200 # These get initialized when the pipe is connected to its node
202 self.configured = False
206 raise RuntimeError, "Undefined NetPipe mode"
207 if not self.portList:
208 raise RuntimeError, "Undefined NetPipe port list - must always define the scope"
209 if not (self.plrIn or self.bwIn or self.delayIn):
210 raise RuntimeError, "Undefined NetPipe inbound characteristics"
211 if not (self.plrOut or self.bwOut or self.delayOut):
212 raise RuntimeError, "Undefined NetPipe outbound characteristics"
214 raise RuntimeError, "Unconnected NetPipe"
216 def _add_pipedef(self, bw, plr, delay, options):
218 options.extend(("delay","%dms" % (delay,)))
220 options.extend(("bw","%.8fMbit/s" % (bw,)))
222 options.extend(("plr","%.8f" % (plr,)))
224 def _get_ruledef(self):
227 "@" if self.addrList else "",
232 if self.bwIn or self.plrIn or self.delayIn:
234 self._add_pipedef(self.bwIn, self.plrIn, self.delayIn, options)
235 if self.bwOut or self.plrOut or self.delayOut:
236 options.append("OUT")
237 self._add_pipedef(self.bwOut, self.plrOut, self.delayOut, options)
238 options = ' '.join(options)
240 return (scope,options)
244 scope, options = self._get_ruledef()
245 command = "sudo -S netconfig config %s %s %s" % (self.mode, scope, options)
247 (out,err),proc = server.popen_ssh_command(
249 host = self.node.hostname,
251 user = self.node.slicename,
253 ident_key = self.node.ident_path,
254 server_key = self.node.server_key
258 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
260 # we have to clean up afterwards
261 self.configured = True
266 scope, options = self._get_ruledef()
267 command = "sudo -S netconfig refresh %s %s %s" % (self.mode, scope, options)
269 (out,err),proc = server.popen_ssh_command(
271 host = self.node.hostname,
273 user = self.node.slicename,
275 ident_key = self.node.ident_path,
276 server_key = self.node.server_key
280 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
285 scope, options = self._get_ruledef()
286 command = "sudo -S netconfig delete %s %s" % (self.mode, scope)
288 (out,err),proc = server.popen_ssh_command(
290 host = self.node.hostname,
292 user = self.node.slicename,
294 ident_key = self.node.ident_path,
295 server_key = self.node.server_key
299 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
301 self.configured = False
303 def sync_trace(self, local_dir, whichtrace):
304 if whichtrace != 'netpipeStats':
305 raise ValueError, "Unsupported trace %s" % (whichtrace,)
307 local_path = os.path.join(local_dir, "netpipe_stats_%s" % (self.mode,))
309 # create parent local folders
310 proc = subprocess.Popen(
311 ["mkdir", "-p", os.path.dirname(local_path)],
312 stdout = open("/dev/null","w"),
313 stdin = open("/dev/null","r"))
316 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
318 (out,err),proc = server.popen_ssh_command(
319 "echo 'Rules:' ; sudo -S netconfig show rules ; echo 'Pipes:' ; sudo -S netconfig show pipes",
320 host = self.node.hostname,
322 user = self.node.slicename,
324 ident_key = self.node.ident_path,
325 server_key = self.node.server_key
329 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
331 # dump results to file
332 f = open(local_path, "wb")