Commit 385fbe6c authored by Martín Ferrari's avatar Martín Ferrari

be able to get/set bridge parameters too

parent 57a7a20f
...@@ -23,6 +23,7 @@ class Interface(object): ...@@ -23,6 +23,7 @@ class Interface(object):
return "NETNSif-%.4x%.3x" % (os.getpid(), n) return "NETNSif-%.4x%.3x" % (os.getpid(), n)
def __init__(self, index): def __init__(self, index):
self._changeable_attributes = interface.changeable_attributes
self._idx = index self._idx = index
def __del__(self): def __del__(self):
...@@ -51,7 +52,11 @@ class NSInterface(Interface): ...@@ -51,7 +52,11 @@ class NSInterface(Interface):
# some black magic to automatically get/set interface attributes # some black magic to automatically get/set interface attributes
def __getattr__(self, name): def __getattr__(self, name):
if (name not in interface.changeable_attributes): try:
ca = object.__getattr__(self, '_changeable_attributes')
except:
ca = []
if (name not in ca):
raise AttributeError("'%s' object has no attribute '%s'" % raise AttributeError("'%s' object has no attribute '%s'" %
(self.__class__.__name__, name)) (self.__class__.__name__, name))
# I can use attributes now, as long as they are not in # I can use attributes now, as long as they are not in
...@@ -60,7 +65,11 @@ class NSInterface(Interface): ...@@ -60,7 +65,11 @@ class NSInterface(Interface):
return getattr(iface, name) return getattr(iface, name)
def __setattr__(self, name, value): def __setattr__(self, name, value):
if (name not in interface.changeable_attributes): try:
ca = object.__getattr__(self, '_changeable_attributes')
except:
ca = []
if (name not in ca):
if name[0] != '_': # forbid anything that doesn't start with a _ if name[0] != '_': # forbid anything that doesn't start with a _
raise AttributeError("'%s' object has no attribute '%s'" % raise AttributeError("'%s' object has no attribute '%s'" %
(self.__class__.__name__, name)) (self.__class__.__name__, name))
...@@ -199,7 +208,11 @@ class ExternalInterface(Interface): ...@@ -199,7 +208,11 @@ class ExternalInterface(Interface):
# some black magic to automatically get/set interface attributes # some black magic to automatically get/set interface attributes
def __getattr__(self, name): def __getattr__(self, name):
if (name not in interface.changeable_attributes): try:
ca = object.__getattr__(self, '_changeable_attributes')
except:
ca = []
if (name not in ca):
raise AttributeError("'%s' object has no attribute '%s'" % raise AttributeError("'%s' object has no attribute '%s'" %
(self.__class__.__name__, name)) (self.__class__.__name__, name))
# I can use attributes now, as long as they are not in # I can use attributes now, as long as they are not in
...@@ -208,7 +221,11 @@ class ExternalInterface(Interface): ...@@ -208,7 +221,11 @@ class ExternalInterface(Interface):
return getattr(iface, name) return getattr(iface, name)
def __setattr__(self, name, value): def __setattr__(self, name, value):
if (name not in interface.changeable_attributes): try:
ca = object.__getattr__(self, '_changeable_attributes')
except:
ca = []
if (name not in ca):
if name[0] != '_': # forbid anything that doesn't start with a _ if name[0] != '_': # forbid anything that doesn't start with a _
raise AttributeError("'%s' object has no attribute '%s'" % raise AttributeError("'%s' object has no attribute '%s'" %
(self.__class__.__name__, name)) (self.__class__.__name__, name))
...@@ -302,6 +319,7 @@ class Link(ExternalInterface): ...@@ -302,6 +319,7 @@ class Link(ExternalInterface):
iface = netns.iproute.create_bridge(self._gen_br_name()) iface = netns.iproute.create_bridge(self._gen_br_name())
super(Link, self).__init__(iface.index) super(Link, self).__init__(iface.index)
self._changeable_attributes = bridge.changeable_attributes
self._ports = set() self._ports = set()
# register somewhere # register somewhere
...@@ -391,22 +409,6 @@ def _make_setter(attr, conv = lambda x: x): ...@@ -391,22 +409,6 @@ def _make_setter(attr, conv = lambda x: x):
class interface(object): class interface(object):
"""Class for internal use. It is mostly a data container used to easily """Class for internal use. It is mostly a data container used to easily
pass information around; with some convenience methods.""" pass information around; with some convenience methods."""
@classmethod
def parse_ip(cls, line):
"""Parse a line of ouput from `ip -o link list' and construct and
return a new object with the data."""
match = re.search(r'^(\d+): (\S+): <(\S+)> mtu (\d+) qdisc \S+' +
r'.*link/\S+ ([0-9a-f:]+) brd ([0-9a-f:]+)', line)
flags = match.group(3).split(",")
return cls(
index = match.group(1),
name = match.group(2),
up = "UP" in flags,
mtu = match.group(4),
lladdr = match.group(5),
arp = not ("NOARP" in flags),
broadcast = match.group(6),
multicast = "MULTICAST" in flags)
# information for other parts of the code # information for other parts of the code
changeable_attributes = ["name", "mtu", "lladdr", "broadcast", "up", changeable_attributes = ["name", "mtu", "lladdr", "broadcast", "up",
...@@ -423,14 +425,14 @@ class interface(object): ...@@ -423,14 +425,14 @@ class interface(object):
def __init__(self, index = None, name = None, up = None, mtu = None, def __init__(self, index = None, name = None, up = None, mtu = None,
lladdr = None, broadcast = None, multicast = None, arp = None): lladdr = None, broadcast = None, multicast = None, arp = None):
self._index = _positive(index) if index is not None else None self._index = _positive(index) if index is not None else None
self.name = name self.name = name
self.up = up self.up = up
self.mtu = mtu self.mtu = mtu
self.lladdr = lladdr self.lladdr = lladdr
self.broadcast = broadcast self.broadcast = broadcast
self.multicast = multicast self.multicast = multicast
self.arp = arp self.arp = arp
def __repr__(self): def __repr__(self):
s = "%s.%s(index = %s, name = %s, up = %s, mtu = %s, lladdr = %s, " s = "%s.%s(index = %s, name = %s, up = %s, mtu = %s, lladdr = %s, "
...@@ -445,40 +447,81 @@ class interface(object): ...@@ -445,40 +447,81 @@ class interface(object):
"""Compare attributes and return a new object with just the attributes """Compare attributes and return a new object with just the attributes
that differ set (with the value they have in the first operand). The that differ set (with the value they have in the first operand). The
index remains equal to the first operand.""" index remains equal to the first operand."""
name = None if self.name == o.name else self.name name = None if self.name == o.name else self.name
up = None if self.up == o.up else self.up up = None if self.up == o.up else self.up
mtu = None if self.mtu == o.mtu else self.mtu mtu = None if self.mtu == o.mtu else self.mtu
lladdr = None if self.lladdr == o.lladdr else self.lladdr lladdr = None if self.lladdr == o.lladdr else self.lladdr
broadcast = None if self.broadcast == o.broadcast else self.broadcast broadcast = None if self.broadcast == o.broadcast else self.broadcast
multicast = None if self.multicast == o.multicast else self.multicast multicast = None if self.multicast == o.multicast else self.multicast
arp = None if self.arp == o.arp else self.arp arp = None if self.arp == o.arp else self.arp
return self.__class__(self.index, name, up, mtu, lladdr, broadcast, return self.__class__(self.index, name, up, mtu, lladdr, broadcast,
multicast, arp) multicast, arp)
class bridge(interface):
changeable_attributes = interface.changeable_attributes + ["stp",
"forward_delay", "hello_time", "ageing_time", "max_age"]
# Index should be read-only
stp = property(_make_getter("_stp"), _make_setter("_stp", _any_to_bool))
forward_delay = property(_make_getter("_forward_delay"),
_make_setter("_forward_delay", float))
hello_time = property(_make_getter("_hello_time"),
_make_setter("_hello_time", float))
ageing_time = property(_make_getter("_ageing_time"),
_make_setter("_ageing_time", float))
max_age = property(_make_getter("_max_age"),
_make_setter("_max_age", float))
@classmethod
def upgrade(cls, iface, *kargs, **kwargs):
"""Upgrade a interface to a bridge."""
return cls(iface.index, iface.name, iface.up, iface.mtu, iface.lladdr,
iface.broadcast, iface.multicast, iface.arp, *kargs, **kwargs)
def __init__(self, index = None, name = None, up = None, mtu = None,
lladdr = None, broadcast = None, multicast = None, arp = None,
stp = None, forward_delay = None, hello_time = None,
ageing_time = None, max_age = None):
super(bridge, self).__init__(index, name, up, mtu, lladdr, broadcast,
multicast, arp)
self.stp = stp
self.forward_delay = forward_delay
self.hello_time = hello_time
self.ageing_time = ageing_time
self.max_age = max_age
def __repr__(self):
s = "%s.%s(index = %s, name = %s, up = %s, mtu = %s, lladdr = %s, "
s += "broadcast = %s, multicast = %s, arp = %s, stp = %s, "
s += "forward_delay = %s, hello_time = %s, ageing_time = %s, "
s += "max_age = %s)"
return s % (self.__module__, self.__class__.__name__,
self.index.__repr__(), self.name.__repr__(),
self.up.__repr__(), self.mtu.__repr__(),
self.lladdr.__repr__(), self.broadcast.__repr__(),
self.multicast.__repr__(), self.arp.__repr__(),
self.stp.__repr__(), self.forward_delay.__repr__(),
self.hello_time.__repr__(), self.ageing_time.__repr__(),
self.max_age.__repr__())
def __sub__(self, o):
r = super(bridge, self).__sub__(o)
if type(o) == interface:
return r
r.stp = None if self.stp == o.stp else self.stp
r.hello_time = None if self.hello_time == o.hello_time else \
self.hello_time
r.forward_delay = None if self.forward_delay == o.forward_delay else \
self.forward_delay
r.ageing_time = None if self.ageing_time == o.ageing_time else \
self.ageing_time
r.max_age = None if self.max_age == o.max_age else self.max_age
return r
class address(object): class address(object):
"""Class for internal use. It is mostly a data container used to easily """Class for internal use. It is mostly a data container used to easily
pass information around; with some convenience methods. __eq__ and __hash__ pass information around; with some convenience methods. __eq__ and __hash__
are defined just to be able to easily find duplicated addresses.""" are defined just to be able to easily find duplicated addresses."""
@classmethod
def parse_ip(cls, line):
"""Parse a line of ouput from `ip -o addr list' (after trimming the
index and interface name) and construct and return a new object with
the data."""
match = re.search(r'^inet ([0-9.]+)/(\d+)(?: brd ([0-9.]+))?', line)
if match != None:
return ipv4address(
address = match.group(1),
prefix_len = match.group(2),
broadcast = match.group(3))
match = re.search(r'^inet6 ([0-9a-f:]+)/(\d+)', line)
if match != None:
return ipv6address(
address = match.group(1),
prefix_len = match.group(2))
raise RuntimeError("Problems parsing ip command output")
# broadcast is not taken into account for differentiating addresses # broadcast is not taken into account for differentiating addresses
def __eq__(self, o): def __eq__(self, o):
if not isinstance(o, address): if not isinstance(o, address):
......
# vim:ts=4:sw=4:et:ai:sts=4 # vim:ts=4:sw=4:et:ai:sts=4
import re, subprocess, sys import os, re, subprocess, sys
import netns.interface import netns.interface
# XXX: ideally this should be replaced by netlink communication # XXX: ideally this should be replaced by netlink communication
...@@ -25,7 +25,18 @@ def get_if_data(): ...@@ -25,7 +25,18 @@ def get_if_data():
continue continue
match = re.search(r'^(\d+):\s+(.*)', line) match = re.search(r'^(\d+):\s+(.*)', line)
idx = int(match.group(1)) idx = int(match.group(1))
i = netns.interface.interface.parse_ip(line) match = re.search(r'^(\d+): (\S+): <(\S+)> mtu (\d+) qdisc \S+' +
r'.*link/\S+ ([0-9a-f:]+) brd ([0-9a-f:]+)', line)
flags = match.group(3).split(",")
i = netns.interface.interface(
index = match.group(1),
name = match.group(2),
up = "UP" in flags,
mtu = match.group(4),
lladdr = match.group(5),
arp = not ("NOARP" in flags),
broadcast = match.group(6),
multicast = "MULTICAST" in flags)
byidx[idx] = bynam[i.name] = i byidx[idx] = bynam[i.name] = i
return byidx, bynam return byidx, bynam
...@@ -146,9 +157,25 @@ def get_addr_data(): ...@@ -146,9 +157,25 @@ def get_addr_data():
if match.group(3): if match.group(3):
bynam[name] = byidx[idx] = [] bynam[name] = byidx[idx] = []
continue # link info continue # link info
bynam[name].append(netns.interface.address.parse_ip(match.group(4))) bynam[name].append(_parse_ip_addr(match.group(4)))
return byidx, bynam return byidx, bynam
def _parse_ip_addr(line):
match = re.search(r'^inet ([0-9.]+)/(\d+)(?: brd ([0-9.]+))?', line)
if match != None:
return ipv4address(
address = match.group(1),
prefix_len = match.group(2),
broadcast = match.group(3))
match = re.search(r'^inet6 ([0-9a-f:]+)/(\d+)', line)
if match != None:
return ipv6address(
address = match.group(1),
prefix_len = match.group(2))
raise RuntimeError("Problems parsing ip command output")
def add_addr(iface, address): def add_addr(iface, address):
ifname = _get_if_name(iface) ifname = _get_if_name(iface)
addresses = get_addr_data()[1][ifname] addresses = get_addr_data()[1][ifname]
...@@ -194,32 +221,34 @@ def set_addr(iface, addresses, recover = True): ...@@ -194,32 +221,34 @@ def set_addr(iface, addresses, recover = True):
# Bridge handling # Bridge handling
def get_br_data(): def get_br_data():
ifdata = get_if_data() # brctl stinks too much; it is better to directly use sysfs, it is probably
ipcmd = subprocess.Popen(["brctl", "show"], stdout = subprocess.PIPE) # stable by now
ipdata = ipcmd.communicate()[0] def readval(fname):
assert ipcmd.wait() == 0 f = file(fname)
return f.readline().strip()
byidx = {} byidx = {}
bynam = {} bynam = {}
ports = {} ports = {}
last = None ifdata = get_if_data()
for line in ipdata.split("\n")[1:]: # skip header for i in ifdata[1]: # by name
if line == "": p = '/sys/class/net/%s/bridge/' % i
p2 = '/sys/class/net/%s/brif/' % i
try:
os.stat(p)
except:
continue continue
if last: params = dict(
match = re.search(r'^\s+(\S+)$', line) stp = readval(p + 'stp_state'),
if match: forward_delay = float(readval(p + 'forward_delay')) / 100,
ports[last].append(ifdata[1][match.group(1)]) hello_time = float(readval(p + 'hello_time')) / 100,
continue ageing_time = float(readval(p + 'ageing_time')) / 100,
max_age = float(readval(p + 'max_age')) / 100)
iface = ifdata[1][i]
bynam[i] = byidx[iface.index] = netns.interface.bridge.upgrade(
iface, **params)
ports[iface.index] = [ifdata[1][x].index for x in os.listdir(p2)]
match = re.search(r'^(\S+)\s+\S+\s+\S+\s*(\S+)?', line)
if not match:
raise RuntimeError("Invalid `brctl' command output")
name = match.group(1)
last = idx = ifdata[1][name].index
bynam[name] = byidx[idx] = ifdata[1][name]
ports[idx] = []
return byidx, bynam, ports return byidx, bynam, ports
def create_bridge(br): def create_bridge(br):
...@@ -242,6 +271,39 @@ def del_bridge(br): ...@@ -242,6 +271,39 @@ def del_bridge(br):
brname = _get_if_name(br) brname = _get_if_name(br)
_execute(["brctl", "delbr", brname]) _execute(["brctl", "delbr", brname])
def set_bridge(br, recover = True):
def saveval(fname, val):
f = file(fname, 'w')
f.write(str(val))
f.close()
def do_cmds(basename, cmds, orig_br):
for n, v in cmds:
try:
saveval(basename + n, v)
except:
if recover:
set_bridge(orig_br, recover = False) # rollback
set_if(orig_br, recover = False) # rollback
raise
orig_br = get_br_data()[1][_get_if_name(br)]
diff = br - orig_br # Only set what's needed
cmds = []
if diff.stp != None:
cmds.append(('stp_state', int(diff.stp)))
if diff.forward_delay != None:
cmds.append(('forward_delay', int(diff.forward_delay)))
if diff.hello_time != None:
cmds.append(('hello_time', int(diff.hello_time)))
if diff.ageing_time != None:
cmds.append(('ageing_time', int(diff.ageing_time)))
if diff.max_age != None:
cmds.append(('max_age', int(diff.max_age)))
set_if(diff)
do_cmds('/sys/class/net/%s/bridge/' % br.name, cmds, orig_br)
def add_bridge_port(br, iface): def add_bridge_port(br, iface):
ifname = _get_if_name(iface) ifname = _get_if_name(iface)
brname = _get_if_name(br) brname = _get_if_name(br)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment