initial effort to remove swallowing exceptions within internal code

This commit is contained in:
Blake J. Harnden 2018-03-02 13:39:44 -08:00
parent a3356127d2
commit 43554cbb62
10 changed files with 423 additions and 399 deletions

View file

@ -37,9 +37,9 @@ class Position(object):
""" """
Returns True if the position has actually changed. Returns True if the position has actually changed.
:param x: x position :param float x: x position
:param y: y position :param float y: y position
:param z: z position :param float z: z position
:return: True if position changed, False otherwise :return: True if position changed, False otherwise
:rtype: bool :rtype: bool
""" """
@ -113,9 +113,9 @@ class PyCoreObj(object):
""" """
Set the (x,y,z) position of the object. Set the (x,y,z) position of the object.
:param x: x position :param float x: x position
:param y: y position :param float y: y position
:param z: z position :param float z: z position
:return: True if position changed, False otherwise :return: True if position changed, False otherwise
:rtype: bool :rtype: bool
""" """
@ -460,6 +460,22 @@ class PyCoreNet(PyCoreObj):
""" """
linktype = LinkTypes.WIRED.value linktype = LinkTypes.WIRED.value
def startup(self):
"""
Each object implements its own startup method.
:return: nothing
"""
raise NotImplementedError
def shutdown(self):
"""
Each object implements its own shutdown method.
:return: nothing
"""
raise NotImplementedError
def __init__(self, session, objid, name, start=True): def __init__(self, session, objid, name, start=True):
""" """
Create a PyCoreNet instance. Create a PyCoreNet instance.
@ -597,7 +613,7 @@ class PyCoreNetIf(object):
""" """
Creates a PyCoreNetIf instance. Creates a PyCoreNetIf instance.
:param core.netns.vnode.SimpleLxcNode node: node for interface :param core.coreobj.PyCoreNode node: node for interface
:param str name: interface name :param str name: interface name
:param mtu: mtu value :param mtu: mtu value
""" """

View file

@ -21,31 +21,30 @@ def emane_version():
""" """
global VERSION global VERSION
global VERSIONSTR global VERSIONSTR
args = ("emane", "--version") args = ["emane", "--version"]
try:
status, result = utils.check_cmd(args)
except subprocess.CalledProcessError:
logger.exception("error checking emane version")
status = -1
result = ""
VERSION = EMANEUNK VERSION = EMANEUNK
if status == 0:
if result.startswith("0.7.4"):
VERSION = EMANE074
elif result.startswith("0.8.1"):
VERSION = EMANE081
elif result.startswith("0.9.1"):
VERSION = EMANE091
elif result.startswith("0.9.2"):
VERSION = EMANE092
elif result.startswith("0.9.3"):
VERSION = EMANE093
elif result.startswith("1.0.1"):
VERSION = EMANE101
VERSIONSTR = result.strip() try:
status, output = utils.check_cmd(args)
if status == 0:
if output.startswith("0.7.4"):
VERSION = EMANE074
elif output.startswith("0.8.1"):
VERSION = EMANE081
elif output.startswith("0.9.1"):
VERSION = EMANE091
elif output.startswith("0.9.2"):
VERSION = EMANE092
elif output.startswith("0.9.3"):
VERSION = EMANE093
elif output.startswith("1.0.1"):
VERSION = EMANE101
except subprocess.CalledProcessError:
logger.exception("error checking emane version")
output = ""
VERSIONSTR = output.strip()
# set version variables for the Emane class # set version variables for the Emane class

View file

@ -925,7 +925,8 @@ class EmaneManager(ConfigurableManager):
logger.exception("error adding route for event data") logger.exception("error adding route for event data")
try: try:
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n), os.path.join(path, "platform%d.xml" % n)] args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n),
os.path.join(path, "platform%d.xml" % n)]
logger.info("Emane.startdaemons2() running %s" % str(args)) logger.info("Emane.startdaemons2() running %s" % str(args))
status, output = node.check_cmd(args) status, output = node.check_cmd(args)
logger.info("Emane.startdaemons2() return code %d" % status) logger.info("Emane.startdaemons2() return code %d" % status)
@ -1163,14 +1164,15 @@ class EmaneManager(ConfigurableManager):
Return True if an EMANE process associated with the given node Return True if an EMANE process associated with the given node
is running, False otherwise. is running, False otherwise.
""" """
status = -1
args = ["pkill", "-0", "-x", "emane"] args = ["pkill", "-0", "-x", "emane"]
status = -1
try: try:
if emane.VERSION < emane.EMANE092: if emane.VERSION < emane.EMANE092:
status = utils.check_cmd(args) utils.check_cmd(args)
else: else:
status, _ = node.check_cmd(args) node.check_cmd(args)
status = 0
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
logger.exception("error checking if emane is running") logger.exception("error checking if emane is running")

View file

@ -199,7 +199,7 @@ def cmd_output(args):
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, _ = p.communicate() stdout, _ = p.communicate()
status = p.wait() status = p.wait()
return status, stdout return status, stdout.strip()
except OSError: except OSError:
raise subprocess.CalledProcessError(-1, args) raise subprocess.CalledProcessError(-1, args)
@ -222,9 +222,9 @@ def check_cmd(args, **kwargs):
p = subprocess.Popen(args, **kwargs) p = subprocess.Popen(args, **kwargs)
stdout, _ = p.communicate() stdout, _ = p.communicate()
status = p.wait() status = p.wait()
if status: if status != 0:
raise subprocess.CalledProcessError(status, args, stdout) raise subprocess.CalledProcessError(status, args, stdout)
return status, stdout return status, stdout.strip()
except OSError: except OSError:
raise subprocess.CalledProcessError(-1, args) raise subprocess.CalledProcessError(-1, args)

View file

@ -68,6 +68,7 @@ class CtrlNet(LxBrNet):
Startup functionality for the control network. Startup functionality for the control network.
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
if self.detectoldbridge(): if self.detectoldbridge():
return return
@ -79,26 +80,23 @@ class CtrlNet(LxBrNet):
else: else:
addr = self.prefix.max_addr() addr = self.prefix.max_addr()
msg = "Added control network bridge: %s %s" % (self.brname, self.prefix) logger.info("added control network bridge: %s %s", self.brname, self.prefix)
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
if self.assign_address: if self.assign_address:
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
self.addrconfig(addrlist=addrlist) self.addrconfig(addrlist=addrlist)
msg += " address %s" % addr logger.info("address %s", addr)
logger.info(msg)
if self.updown_script is not None: if self.updown_script:
logger.info("interface %s updown script (%s startup) called", logger.info("interface %s updown script (%s startup) called", self.brname, self.updown_script)
self.brname, self.updown_script)
utils.check_cmd([self.updown_script, self.brname, "startup"]) utils.check_cmd([self.updown_script, self.brname, "startup"])
if self.serverintf is not None: if self.serverintf:
try: # sets the interface as a port of the bridge
utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, self.serverintf]) utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, self.serverintf])
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
except subprocess.CalledProcessError: # bring interface up
logger.exception("error joining server interface %s to controlnet bridge %s", utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
self.serverintf, self.brname)
def detectoldbridge(self): def detectoldbridge(self):
""" """
@ -120,10 +118,9 @@ class CtrlNet(LxBrNet):
if len(flds) == 3: if len(flds) == 3:
if flds[0] == "b" and flds[1] == self.objid: if flds[0] == "b" and flds[1] == self.objid:
logger.error( logger.error(
"Error: An active control net bridge (%s) found. " "error: An active control net bridge (%s) found. "
"An older session might still be running. " "An older session might still be running. "
"Stop all sessions and, if needed, delete %s to continue." % "Stop all sessions and, if needed, delete %s to continue.", oldbr, oldbr
(oldbr, oldbr)
) )
return True return True
return False return False
@ -137,20 +134,26 @@ class CtrlNet(LxBrNet):
if self.serverintf is not None: if self.serverintf is not None:
try: try:
utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, self.serverintf]) utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, self.serverintf])
except subprocess.CalledProcessError: except subprocess.CalledProcessError as e:
logger.exception("error deleting server interface %s to controlnet bridge %s", logger.exception("error deleting server interface %s from bridge %s: %s",
self.serverintf, self.brname) self.serverintf, self.brname, e.output)
if self.updown_script is not None: if self.updown_script is not None:
logger.info("interface %s updown script (%s shutdown) called" % (self.brname, self.updown_script)) try:
utils.check_cmd([self.updown_script, self.brname, "shutdown"]) logger.info("interface %s updown script (%s shutdown) called", self.brname, self.updown_script)
utils.check_cmd([self.updown_script, self.brname, "shutdown"])
except subprocess.CalledProcessError as e:
logger.exception("error issuing shutdown script shutdown: %s", e.output)
LxBrNet.shutdown(self) LxBrNet.shutdown(self)
def all_link_data(self, flags): def all_link_data(self, flags):
""" """
Do not include CtrlNet in link messages describing this session. Do not include CtrlNet in link messages describing this session.
:return: nothing :param flags: message flags
:return: list of link data
:rtype: list[core.data.LinkData]
""" """
return [] return []
@ -172,29 +175,36 @@ class PtpNet(LxBrNet):
""" """
Attach a network interface, but limit attachment to two interfaces. Attach a network interface, but limit attachment to two interfaces.
:param core.coreobj.PyCoreNetIf netif: network interface :param core.netns.vif.VEth netif: network interface
:return: nothing :return: nothing
""" """
if len(self._netif) >= 2: if len(self._netif) >= 2:
raise ValueError("Point-to-point links support at most 2 network interfaces") raise ValueError("Point-to-point links support at most 2 network interfaces")
LxBrNet.attach(self, netif) LxBrNet.attach(self, netif)
def data(self, message_type): def data(self, message_type, lat=None, lon=None, alt=None):
""" """
Do not generate a Node Message for point-to-point links. They are Do not generate a Node Message for point-to-point links. They are
built using a link message instead. built using a link message instead.
:return: nothing :param message_type: purpose for the data object we are creating
:param float lat: latitude
:param float lon: longitude
:param float alt: altitude
:return: node data object
:rtype: core.data.NodeData
""" """
pass return None
def all_link_data(self, flags): def all_link_data(self, flags):
""" """
Build CORE API TLVs for a point-to-point link. One Link message Build CORE API TLVs for a point-to-point link. One Link message
describes this network. describes this network.
:return: all link data :param flags: message flags
:rtype: list[LinkData] :return: list of link data
:rtype: list[core.data.LinkData]
""" """
all_links = [] all_links = []
@ -318,8 +328,11 @@ class HubNode(LxBrNet):
:param int objid: node id :param int objid: node id
:param str name: node namee :param str name: node namee
:param bool start: start flag :param bool start: start flag
:raises subprocess.CalledProcessError: when there is a command exception
""" """
LxBrNet.__init__(self, session, objid, name, start) LxBrNet.__init__(self, session, objid, name, start)
# TODO: move to startup method
if start: if start:
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"]) utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
@ -353,7 +366,7 @@ class WlanNode(LxBrNet):
""" """
Attach a network interface. Attach a network interface.
:param core.coreobj.PyCoreNetIf netif: network interface :param core.netns.vif.VEth netif: network interface
:return: nothing :return: nothing
""" """
LxBrNet.attach(self, netif) LxBrNet.attach(self, netif)
@ -364,7 +377,6 @@ class WlanNode(LxBrNet):
x, y, z = netif.node.position.get() x, y, z = netif.node.position.get()
# invokes any netif.poshook # invokes any netif.poshook
netif.setposition(x, y, z) netif.setposition(x, y, z)
# self.model.setlinkparams()
def setmodel(self, model, config): def setmodel(self, model, config):
""" """
@ -398,25 +410,28 @@ class WlanNode(LxBrNet):
logger.info("updating model %s" % model_name) logger.info("updating model %s" % model_name)
if self.model is None or self.model.name != model_name: if self.model is None or self.model.name != model_name:
return return
model = self.model model = self.model
if model.config_type == RegisterTlvs.WIRELESS.value: if model.config_type == RegisterTlvs.WIRELESS.value:
if not model.updateconfig(values): if not model.updateconfig(values):
return return
if self.model.position_callback: if self.model.position_callback:
for netif in self.netifs(): for netif in self.netifs():
netif.poshook = self.model.position_callback netif.poshook = self.model.position_callback
if netif.node is not None: if netif.node is not None:
(x, y, z) = netif.node.position.get() (x, y, z) = netif.node.position.get()
netif.poshook(netif, x, y, z) netif.poshook(netif, x, y, z)
self.model.setlinkparams() self.model.setlinkparams()
def all_link_data(self, flags): def all_link_data(self, flags):
""" """
Retrieve all link data. Retrieve all link data.
:param flags: link flags :param flags: message flags
:return: all link data :return: list of link data
:rtype: list[LinkData] :rtype: list[core.data.LinkData]
""" """
all_links = LxBrNet.all_link_data(self, flags) all_links = LxBrNet.all_link_data(self, flags)
@ -446,7 +461,6 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
:return: :return:
""" """
PyCoreNode.__init__(self, session, objid, name, start=start) PyCoreNode.__init__(self, session, objid, name, start=start)
# this initializes net, params, poshook
PyCoreNetIf.__init__(self, node=self, name=name, mtu=mtu) PyCoreNetIf.__init__(self, node=self, name=name, mtu=mtu)
self.up = False self.up = False
self.lock = threading.RLock() self.lock = threading.RLock()
@ -456,6 +470,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
self.localname = name self.localname = name
self.old_up = False self.old_up = False
self.old_addrs = [] self.old_addrs = []
if start: if start:
self.startup() self.startup()
@ -464,15 +479,12 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
Set the interface in the up state. Set the interface in the up state.
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
# interface will also be marked up during net.attach() # interface will also be marked up during net.attach()
self.savestate() self.savestate()
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
try: self.up = True
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
self.up = True
except subprocess.CalledProcessError:
logger.exception("failed to run command: %s link set %s up", constants.IP_BIN, self.localname)
def shutdown(self): def shutdown(self):
""" """
@ -504,6 +516,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
""" """
PyCoreNetIf.attachnet(self, net) PyCoreNetIf.attachnet(self, net)
# TODO: issue in that both classes inherited from provide the same method with different signatures
def detachnet(self): def detachnet(self):
""" """
Detach a network. Detach a network.
@ -523,7 +536,9 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
:param str hwaddr: hardware address :param str hwaddr: hardware address
:param int ifindex: interface index :param int ifindex: interface index
:param str ifname: interface name :param str ifname: interface name
:return: :return: interface index
:rtype: int
:raises ValueError: when an interface has already been created, one max
""" """
with self.lock: with self.lock:
if ifindex is None: if ifindex is None:
@ -556,14 +571,12 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
if ifindex is None: if ifindex is None:
ifindex = 0 ifindex = 0
if ifindex not in self._netif:
raise ValueError, "ifindex %s does not exist" % ifindex
self._netif.pop(ifindex) self._netif.pop(ifindex)
if ifindex == self.ifindex: if ifindex == self.ifindex:
self.shutdown() self.shutdown()
else: else:
raise ValueError, "ifindex %s does not exist" % ifindex raise ValueError("ifindex %s does not exist" % ifindex)
def netif(self, ifindex, net=None): def netif(self, ifindex, net=None):
""" """
@ -606,9 +619,11 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
:param str addr: address to add :param str addr: address to add
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
if self.up: if self.up:
utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]) utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
PyCoreNetIf.addaddr(self, addr) PyCoreNetIf.addaddr(self, addr)
def deladdr(self, addr): def deladdr(self, addr):
@ -617,9 +632,11 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
:param str addr: address to delete :param str addr: address to delete
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
if self.up: if self.up:
utils.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.name]) utils.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.name])
PyCoreNetIf.deladdr(self, addr) PyCoreNetIf.deladdr(self, addr)
def savestate(self): def savestate(self):
@ -628,35 +645,34 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
interface for emulation purposes. TODO: save/restore the PROMISC flag interface for emulation purposes. TODO: save/restore the PROMISC flag
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
self.old_up = False self.old_up = False
self.old_addrs = [] self.old_addrs = []
args = [constants.IP_BIN, "addr", "show", "dev", self.localname] args = [constants.IP_BIN, "addr", "show", "dev", self.localname]
try: _, output = utils.check_cmd(args)
_, output = utils.check_cmd(args) for line in output.split("\n"):
for line in output.split("\n"): items = line.split()
items = line.split() if len(items) < 2:
if len(items) < 2: continue
continue
if items[1] == "%s:" % self.localname: if items[1] == "%s:" % self.localname:
flags = items[2][1:-1].split(",") flags = items[2][1:-1].split(",")
if "UP" in flags: if "UP" in flags:
self.old_up = True self.old_up = True
elif items[0] == "inet": elif items[0] == "inet":
self.old_addrs.append((items[1], items[3])) self.old_addrs.append((items[1], items[3]))
elif items[0] == "inet6": elif items[0] == "inet6":
if items[1][:4] == "fe80": if items[1][:4] == "fe80":
continue continue
self.old_addrs.append((items[1], None)) self.old_addrs.append((items[1], None))
except subprocess.CalledProcessError:
logger.exception("error during save state")
def restorestate(self): def restorestate(self):
""" """
Restore the addresses and other interface state after using it. Restore the addresses and other interface state after using it.
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
for addr in self.old_addrs: for addr in self.old_addrs:
if addr[1] is None: if addr[1] is None:
@ -669,13 +685,58 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
def setposition(self, x=None, y=None, z=None): def setposition(self, x=None, y=None, z=None):
""" """
Use setposition() from both parent classes. Uses setposition from both parent classes.
:return: nothing :param float x: x position
:param float y: y position
:param float z: z position
:return: True if position changed, False otherwise
:rtype: bool
""" """
PyCoreObj.setposition(self, x, y, z) result = PyCoreObj.setposition(self, x, y, z)
# invoke any poshook
PyCoreNetIf.setposition(self, x, y, z) PyCoreNetIf.setposition(self, x, y, z)
return result
def check_cmd(self, args):
"""
Runs shell command on node.
:param list[str]|str args: command to run
:return: exist status and combined stdout and stderr
:rtype: tuple[int, str]
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
"""
raise NotImplementedError
def cmd(self, args, wait=True):
"""
Runs shell command on node, with option to not wait for a result.
:param list[str]|str args: command to run
:param bool wait: wait for command to exit, defaults to True
:return: exit status for command
:rtype: int
"""
raise NotImplementedError
def cmd_output(self, args):
"""
Runs shell command on node and get exit status and output.
:param list[str]|str args: command to run
:return: exit status and combined stdout and stderr
:rtype: tuple[int, str]
"""
raise NotImplementedError
def termcmdstring(self, sh):
"""
Create a terminal command string.
:param str sh: shell to execute command in
:return: str
"""
raise NotImplementedError
class TunnelNode(GreTapBridge): class TunnelNode(GreTapBridge):

View file

@ -78,24 +78,23 @@ class OvsNet(PyCoreNet):
ebtables_queue.startupdateloop(self) ebtables_queue.startupdateloop(self)
def startup(self): def startup(self):
try: """
utils.check_cmd([constants.OVS_BIN, "add-br", self.bridge_name])
except subprocess.CalledProcessError:
logger.exception("error adding bridge")
try: :return:
# turn off spanning tree protocol and forwarding delay :raises subprocess.CalledProcessError: when there is a command exception
# TODO: appears stp and rstp are off by default, make sure this always holds true """
# TODO: apears ovs only supports rstp forward delay and again it's off by default utils.check_cmd([constants.OVS_BIN, "add-br", self.bridge_name])
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
# create a new ebtables chain for this bridge # turn off spanning tree protocol and forwarding delay
ebtables_commands(utils.check_cmd, [ # TODO: appears stp and rstp are off by default, make sure this always holds true
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy], # TODO: apears ovs only supports rstp forward delay and again it's off by default
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name] utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
])
except subprocess.CalledProcessError: # create a new ebtables chain for this bridge
logger.exception("Error setting bridge parameters") ebtables_commands(utils.check_cmd, [
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.bridge_name, "-j", self.bridge_name]
])
self.up = True self.up = True
@ -127,22 +126,14 @@ class OvsNet(PyCoreNet):
def attach(self, interface): def attach(self, interface):
if self.up: if self.up:
try: utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, interface.localname])
utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, interface.localname]) utils.check_cmd([constants.IP_BIN, "link", "set", interface.localname, "up"])
utils.check_cmd([constants.IP_BIN, "link", "set", interface.localname, "up"])
except subprocess.CalledProcessError:
logger.exception("error joining interface %s to bridge %s", interface.localname, self.bridge_name)
return
PyCoreNet.attach(self, interface) PyCoreNet.attach(self, interface)
def detach(self, interface): def detach(self, interface):
if self.up: if self.up:
try: utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, interface.localname])
utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, interface.localname])
except subprocess.CalledProcessError:
logger.exception("error removing interface %s from bridge %s", interface.localname, self.bridge_name)
return
PyCoreNet.detach(self, interface) PyCoreNet.detach(self, interface)
@ -345,10 +336,7 @@ class OvsNet(PyCoreNet):
return return
for address in addresses: for address in addresses:
try: utils.check_cmd([constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name])
utils.check_cmd([constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name])
except subprocess.CalledProcessError:
logger.exception("error adding IP address")
class OvsCtrlNet(OvsNet): class OvsCtrlNet(OvsNet):
@ -392,20 +380,16 @@ class OvsCtrlNet(OvsNet):
utils.check_cmd([self.updown_script, self.bridge_name, "startup"]) utils.check_cmd([self.updown_script, self.bridge_name, "startup"])
if self.serverintf: if self.serverintf:
try: utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf])
utils.check_cmd([constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf]) utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
except subprocess.CalledProcessError:
logger.exception("error joining server interface %s to controlnet bridge %s",
self.serverintf, self.bridge_name)
def detectoldbridge(self): def detectoldbridge(self):
""" """
Occassionally, control net bridges from previously closed sessions are not cleaned up. Occasionally, control net bridges from previously closed sessions are not cleaned up.
Check if there are old control net bridges and delete them Check if there are old control net bridges and delete them
""" """
status, output = utils.cmd_output([constants.OVS_BIN, "list-br"]) _, output = utils.check_cmd([constants.OVS_BIN, "list-br"])
output = output.strip() output = output.strip()
if output: if output:
for line in output.split("\n"): for line in output.split("\n"):
@ -420,13 +404,16 @@ class OvsCtrlNet(OvsNet):
if self.serverintf: if self.serverintf:
try: try:
utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf]) utils.check_cmd([constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf])
except subprocess.CalledProcessError: except subprocess.CalledProcessError as e:
logger.exception("Error deleting server interface %s to controlnet bridge %s", logger.exception("error deleting server interface %s to controlnet bridge %s: %s",
self.serverintf, self.bridge_name) self.serverintf, self.bridge_name, e.output)
if self.updown_script: if self.updown_script:
logger.info("interface %s updown script (%s shutdown) called", self.bridge_name, self.updown_script) try:
utils.check_cmd([self.updown_script, self.bridge_name, "shutdown"]) logger.info("interface %s updown script (%s shutdown) called", self.bridge_name, self.updown_script)
utils.check_cmd([self.updown_script, self.bridge_name, "shutdown"])
except subprocess.CalledProcessError as e:
logger.exception("error during updown script shutdown: %s", e.output)
OvsNet.shutdown(self) OvsNet.shutdown(self)
@ -445,12 +432,12 @@ class OvsPtpNet(OvsNet):
raise ValueError("point-to-point links support at most 2 network interfaces") raise ValueError("point-to-point links support at most 2 network interfaces")
OvsNet.attach(self, interface) OvsNet.attach(self, interface)
def data(self, message_type): def data(self, message_type, lat=None, lon=None, alt=None):
""" """
Do not generate a Node Message for point-to-point links. They are Do not generate a Node Message for point-to-point links. They are
built using a link message instead. built using a link message instead.
""" """
pass return None
def all_link_data(self, flags): def all_link_data(self, flags):
""" """
@ -685,8 +672,8 @@ class OvsGreTapBridge(OvsNet):
if remoteip is None: if remoteip is None:
self.gretap = None self.gretap = None
else: else:
self.gretap = GreTap(node=self, name=None, session=session, remoteip=remoteip, self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
objid=None, localip=localip, ttl=ttl, key=self.grekey) localip=localip, ttl=ttl, key=self.grekey)
if start: if start:
self.startup() self.startup()
@ -726,7 +713,7 @@ class OvsGreTapBridge(OvsNet):
if len(addresses) > 1: if len(addresses) > 1:
localip = addresses[1].split("/")[0] localip = addresses[1].split("/")[0]
self.gretap = GreTap(session=self.session, remoteip=remoteip, objid=None, name=None, self.gretap = GreTap(session=self.session, remoteip=remoteip,
localip=localip, ttl=self.ttl, key=self.grekey) localip=localip, ttl=self.ttl, key=self.grekey)
self.attach(self.gretap) self.attach(self.gretap)

View file

@ -31,7 +31,7 @@ class VEth(PyCoreNetIf):
:param mtu: interface mtu :param mtu: interface mtu
:param net: network :param net: network
:param bool start: start flag :param bool start: start flag
:return: :raises subprocess.CalledProcessError: when there is a command exception
""" """
# note that net arg is ignored # note that net arg is ignored
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu) PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
@ -45,6 +45,7 @@ class VEth(PyCoreNetIf):
Interface startup logic. Interface startup logic.
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
utils.check_cmd([constants.IP_BIN, "link", "add", "name", self.localname, utils.check_cmd([constants.IP_BIN, "link", "add", "name", self.localname,
"type", "veth", "peer", "name", self.name]) "type", "veth", "peer", "name", self.name])
@ -63,11 +64,14 @@ class VEth(PyCoreNetIf):
if self.node: if self.node:
try: try:
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]) self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
except subprocess.CalledProcessError: except subprocess.CalledProcessError as e:
logger.exception("error shutting down interface") logger.exception("error shutting down interface: %s", e.output)
if self.localname: if self.localname:
utils.mute_detach([constants.IP_BIN, "link", "delete", self.localname]) try:
utils.check_cmd([constants.IP_BIN, "link", "delete", self.localname])
except subprocess.CalledProcessError as e:
logger.exception("error deleting link: %s", e.output)
self.up = False self.up = False
@ -121,8 +125,8 @@ class TunTap(PyCoreNetIf):
try: try:
self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]) self.node.check_cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
except subprocess.CalledProcessError: except subprocess.CalledProcessError as e:
logger.exception("error shutting down tunnel tap") logger.exception("error shutting down tunnel tap: %s", e.output)
self.up = False self.up = False
@ -133,13 +137,16 @@ class TunTap(PyCoreNetIf):
:param func: function to wait for a result of zero :param func: function to wait for a result of zero
:param int attempts: number of attempts to wait for a zero result :param int attempts: number of attempts to wait for a zero result
:param float maxretrydelay: maximum retry delay :param float maxretrydelay: maximum retry delay
:return: nothing :return: True if wait succeeded, False otherwise
:rtype: bool
""" """
delay = 0.01 delay = 0.01
result = False
for i in xrange(1, attempts + 1): for i in xrange(1, attempts + 1):
r = func() r = func()
if r == 0: if r == 0:
return result = True
break
msg = "attempt %s failed with nonzero exit status %s" % (i, r) msg = "attempt %s failed with nonzero exit status %s" % (i, r)
if i < attempts + 1: if i < attempts + 1:
msg += ", retrying..." msg += ", retrying..."
@ -152,7 +159,7 @@ class TunTap(PyCoreNetIf):
msg += ", giving up" msg += ", giving up"
logger.info(msg) logger.info(msg)
raise RuntimeError("command failed after %s attempts" % attempts) return result
def waitfordevicelocal(self): def waitfordevicelocal(self):
""" """
@ -182,18 +189,20 @@ class TunTap(PyCoreNetIf):
count = 0 count = 0
while True: while True:
try: result = self.waitfor(nodedevexists)
self.waitfor(nodedevexists) if result:
break break
except RuntimeError as e:
# check if this is an EMANE interface; if so, continue # check if this is an EMANE interface; if so, continue
# waiting if EMANE is still running # waiting if EMANE is still running
# TODO: remove emane code # TODO: remove emane code
if count < 5 and nodeutils.is_node(self.net, NodeTypes.EMANE) and self.node.session.emane.emanerunning( should_retry = count < 5
self.node): is_emane_node = nodeutils.is_node(self.net, NodeTypes.EMANE)
count += 1 is_emane_running = self.node.session.emane.emanerunning(self.node)
else: if all([should_retry, is_emane_node, is_emane_running]):
raise e count += 1
else:
raise RuntimeError("node device failed to exist")
def install(self): def install(self):
""" """
@ -203,16 +212,13 @@ class TunTap(PyCoreNetIf):
end of the TAP. end of the TAP.
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
self.waitfordevicelocal() self.waitfordevicelocal()
netns = str(self.node.pid) netns = str(self.node.pid)
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
try: self.node.check_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "netns", netns]) self.node.check_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
self.node.check_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
except subprocess.CalledProcessError:
logger.exception("error installing TAP interface")
def setaddrs(self): def setaddrs(self):
""" """
@ -222,10 +228,7 @@ class TunTap(PyCoreNetIf):
""" """
self.waitfordevicenode() self.waitfordevicenode()
for addr in self.addrlist: for addr in self.addrlist:
try: self.node.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
self.node.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
except subprocess.CalledProcessError:
logger.exception("failure setting interface address")
class GreTap(PyCoreNetIf): class GreTap(PyCoreNetIf):
@ -251,6 +254,7 @@ class GreTap(PyCoreNetIf):
:param ttl: ttl value :param ttl: ttl value
:param key: gre tap key :param key: gre tap key
:param bool start: start flag :param bool start: start flag
:raises subprocess.CalledProcessError: when there is a command exception
""" """
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu) PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
self.session = session self.session = session
@ -268,16 +272,16 @@ class GreTap(PyCoreNetIf):
if remoteip is None: if remoteip is None:
raise ValueError, "missing remote IP required for GRE TAP device" raise ValueError, "missing remote IP required for GRE TAP device"
args = ("ip", "link", "add", self.localname, "type", "gretap", args = ["ip", "link", "add", self.localname, "type", "gretap",
"remote", str(remoteip)) "remote", str(remoteip)]
if localip: if localip:
args += ("local", str(localip)) args += ["local", str(localip)]
if ttl: if ttl:
args += ("ttl", str(ttl)) args += ["ttl", str(ttl)]
if key: if key:
args += ("key", str(key)) args += ["key", str(key)]
utils.check_cmd(args) utils.check_cmd(args)
args = ("ip", "link", "set", self.localname, "up") args = ["ip", "link", "set", self.localname, "up"]
utils.check_cmd(args) utils.check_cmd(args)
self.up = True self.up = True
@ -288,10 +292,14 @@ class GreTap(PyCoreNetIf):
:return: nothing :return: nothing
""" """
if self.localname: if self.localname:
args = ("ip", "link", "set", self.localname, "down") try:
utils.check_cmd(args) args = ["ip", "link", "set", self.localname, "down"]
args = ("ip", "link", "del", self.localname) utils.check_cmd(args)
utils.check_cmd(args) args = ["ip", "link", "del", self.localname]
utils.check_cmd(args)
except subprocess.CalledProcessError as e:
logger.exception("error during shutdown: %s", e.output)
self.localname = None self.localname = None
def data(self, message_type): def data(self, message_type):

View file

@ -59,11 +59,12 @@ class EbtablesQueue(object):
:return: nothing :return: nothing
""" """
self.updatelock.acquire() with self.updatelock:
self.last_update_time[wlan] = time.time() self.last_update_time[wlan] = time.time()
self.updatelock.release()
if self.doupdateloop: if self.doupdateloop:
return return
self.doupdateloop = True self.doupdateloop = True
self.updatethread = threading.Thread(target=self.updateloop) self.updatethread = threading.Thread(target=self.updateloop)
self.updatethread.daemon = True self.updatethread.daemon = True
@ -75,15 +76,15 @@ class EbtablesQueue(object):
:return: nothing :return: nothing
""" """
self.updatelock.acquire() with self.updatelock:
try: try:
del self.last_update_time[wlan] del self.last_update_time[wlan]
except KeyError: except KeyError:
logger.exception("error deleting last update time for wlan, ignored before: %s", wlan) logger.exception("error deleting last update time for wlan, ignored before: %s", wlan)
self.updatelock.release()
if len(self.last_update_time) > 0: if len(self.last_update_time) > 0:
return return
self.doupdateloop = False self.doupdateloop = False
if self.updatethread: if self.updatethread:
self.updatethread.join() self.updatethread.join()
@ -137,25 +138,26 @@ class EbtablesQueue(object):
:return: nothing :return: nothing
""" """
while self.doupdateloop: while self.doupdateloop:
self.updatelock.acquire() with self.updatelock:
for wlan in self.updates: for wlan in self.updates:
""" """
Check if wlan is from a previously closed session. Because of the Check if wlan is from a previously closed session. Because of the
rate limiting scheme employed here, this may happen if a new session rate limiting scheme employed here, this may happen if a new session
is started soon after closing a previous session. is started soon after closing a previous session.
""" """
try: # TODO: if these are WlanNodes, this will never throw an exception
wlan.session try:
except: wlan.session
# Just mark as updated to remove from self.updates. except:
self.updated(wlan) # Just mark as updated to remove from self.updates.
continue self.updated(wlan)
if self.lastupdate(wlan) > self.rate: continue
self.buildcmds(wlan)
# print "ebtables commit %d rules" % len(self.cmds) if self.lastupdate(wlan) > self.rate:
self.ebcommit(wlan) self.buildcmds(wlan)
self.updated(wlan) self.ebcommit(wlan)
self.updatelock.release() self.updated(wlan)
time.sleep(self.rate) time.sleep(self.rate)
def ebcommit(self, wlan): def ebcommit(self, wlan):
@ -166,29 +168,22 @@ class EbtablesQueue(object):
""" """
# save kernel ebtables snapshot to a file # save kernel ebtables snapshot to a file
args = self.ebatomiccmd(["--atomic-save", ]) args = self.ebatomiccmd(["--atomic-save", ])
try: utils.check_cmd(args)
utils.check_cmd(args)
except subprocess.CalledProcessError:
logger.exception("atomic-save (%s)", args)
# no atomic file, exit
return
# modify the table file using queued ebtables commands # modify the table file using queued ebtables commands
for c in self.cmds: for c in self.cmds:
args = self.ebatomiccmd(c) args = self.ebatomiccmd(c)
try: utils.check_cmd(args)
utils.check_cmd(args)
except subprocess.CalledProcessError:
logger.exception("cmd=%s", args)
self.cmds = [] self.cmds = []
# commit the table file to the kernel # commit the table file to the kernel
args = self.ebatomiccmd(["--atomic-commit", ]) args = self.ebatomiccmd(["--atomic-commit", ])
utils.check_cmd(args)
try: try:
utils.check_cmd(args)
os.unlink(self.atomic_file) os.unlink(self.atomic_file)
except OSError: except OSError:
logger.exception("atomic-commit (%s)", args) logger.exception("error removing atomic file: %s", self.atomic_file)
def ebchange(self, wlan): def ebchange(self, wlan):
""" """
@ -197,10 +192,9 @@ class EbtablesQueue(object):
:return: nothing :return: nothing
""" """
self.updatelock.acquire() with self.updatelock:
if wlan not in self.updates: if wlan not in self.updates:
self.updates.append(wlan) self.updates.append(wlan)
self.updatelock.release()
def buildcmds(self, wlan): def buildcmds(self, wlan):
""" """
@ -208,23 +202,22 @@ class EbtablesQueue(object):
:return: nothing :return: nothing
""" """
wlan._linked_lock.acquire() with wlan._linked_lock:
# flush the chain # flush the chain
self.cmds.extend([["-F", wlan.brname], ]) self.cmds.extend([["-F", wlan.brname], ])
# rebuild the chain # rebuild the chain
for netif1, v in wlan._linked.items(): for netif1, v in wlan._linked.items():
for netif2, linked in v.items(): for netif2, linked in v.items():
if wlan.policy == "DROP" and linked: if wlan.policy == "DROP" and linked:
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname, self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
"-o", netif2.localname, "-j", "ACCEPT"], "-o", netif2.localname, "-j", "ACCEPT"],
["-A", wlan.brname, "-o", netif1.localname, ["-A", wlan.brname, "-o", netif1.localname,
"-i", netif2.localname, "-j", "ACCEPT"]]) "-i", netif2.localname, "-j", "ACCEPT"]])
elif wlan.policy == "ACCEPT" and not linked: elif wlan.policy == "ACCEPT" and not linked:
self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname, self.cmds.extend([["-A", wlan.brname, "-i", netif1.localname,
"-o", netif2.localname, "-j", "DROP"], "-o", netif2.localname, "-j", "DROP"],
["-A", wlan.brname, "-o", netif1.localname, ["-A", wlan.brname, "-o", netif1.localname,
"-i", netif2.localname, "-j", "DROP"]]) "-i", netif2.localname, "-j", "DROP"]])
wlan._linked_lock.release()
# a global object because all WLANs share the same queue # a global object because all WLANs share the same queue
@ -279,28 +272,24 @@ class LxBrNet(PyCoreNet):
Linux bridge starup logic. Linux bridge starup logic.
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when there is a command exception
""" """
try: utils.check_cmd([constants.BRCTL_BIN, "addbr", self.brname])
utils.check_cmd([constants.BRCTL_BIN, "addbr", self.brname])
except subprocess.CalledProcessError:
logger.exception("Error adding bridge")
try: # turn off spanning tree protocol and forwarding delay
# turn off spanning tree protocol and forwarding delay utils.check_cmd([constants.BRCTL_BIN, "stp", self.brname, "off"])
utils.check_cmd([constants.BRCTL_BIN, "stp", self.brname, "off"]) utils.check_cmd([constants.BRCTL_BIN, "setfd", self.brname, "0"])
utils.check_cmd([constants.BRCTL_BIN, "setfd", self.brname, "0"]) utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "up"])
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "up"]) # create a new ebtables chain for this bridge
# create a new ebtables chain for this bridge ebtablescmds(utils.check_cmd, [
ebtablescmds(utils.check_cmd, [ [constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy],
[constants.EBTABLES_BIN, "-N", self.brname, "-P", self.policy], [constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.brname, "-j", self.brname]
[constants.EBTABLES_BIN, "-A", "FORWARD", "--logical-in", self.brname, "-j", self.brname] ])
]) # turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname if os.path.exists(snoop):
if os.path.exists(snoop): with open(snoop, "w") as snoop_file:
open(snoop, "w").write("0") snoop_file.write("0")
except subprocess.CalledProcessError:
logger.exception("Error setting bridge parameters")
self.up = True self.up = True
@ -312,6 +301,7 @@ class LxBrNet(PyCoreNet):
""" """
if not self.up: if not self.up:
return return
ebq.stopupdateloop(self) ebq.stopupdateloop(self)
try: try:
@ -333,20 +323,18 @@ class LxBrNet(PyCoreNet):
del self.session del self.session
self.up = False self.up = False
# TODO: this depends on a subtype with localname defined, seems like the wrong place for this to live
def attach(self, netif): def attach(self, netif):
""" """
Attach a network interface. Attach a network interface.
:param core.netns.vif.VEth netif: network interface to attach :param core.netns.vnode.VEth netif: network interface to attach
:return: nothing :return: nothing
""" """
if self.up: if self.up:
try: utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, netif.localname])
utils.check_cmd([constants.BRCTL_BIN, "addif", self.brname, netif.localname]) utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
except subprocess.CalledProcessError:
logger.exception("Error joining interface %s to bridge %s", netif.localname, self.brname)
return
PyCoreNet.attach(self, netif) PyCoreNet.attach(self, netif)
def detach(self, netif): def detach(self, netif):
@ -357,11 +345,8 @@ class LxBrNet(PyCoreNet):
:return: nothing :return: nothing
""" """
if self.up: if self.up:
try: utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, netif.localname])
utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, netif.localname])
except subprocess.CalledProcessError:
logger.exception("Error removing interface %s from bridge %s", netif.localname, self.brname)
return
PyCoreNet.detach(self, netif) PyCoreNet.detach(self, netif)
def linked(self, netif1, netif2): def linked(self, netif1, netif2):
@ -402,12 +387,11 @@ class LxBrNet(PyCoreNet):
:param core.netns.vif.Veth netif2: interface two :param core.netns.vif.Veth netif2: interface two
:return: nothing :return: nothing
""" """
self._linked_lock.acquire() with self._linked_lock:
if not self.linked(netif1, netif2): if not self.linked(netif1, netif2):
self._linked_lock.release() return
return self._linked[netif1][netif2] = False
self._linked[netif1][netif2] = False
self._linked_lock.release()
ebq.ebchange(self) ebq.ebchange(self)
def link(self, netif1, netif2): def link(self, netif1, netif2):
@ -419,12 +403,11 @@ class LxBrNet(PyCoreNet):
:param core.netns.vif.Veth netif2: interface two :param core.netns.vif.Veth netif2: interface two
:return: nothing :return: nothing
""" """
self._linked_lock.acquire() with self._linked_lock:
if self.linked(netif1, netif2): if self.linked(netif1, netif2):
self._linked_lock.release() return
return self._linked[netif1][netif2] = True
self._linked[netif1][netif2] = True
self._linked_lock.release()
ebq.ebchange(self) ebq.ebchange(self)
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
@ -525,18 +508,21 @@ class LxBrNet(PyCoreNet):
self_objid = "%x" % self.objid self_objid = "%x" % self.objid
except TypeError: except TypeError:
self_objid = "%s" % self.objid self_objid = "%s" % self.objid
try: try:
net_objid = "%x" % net.objid net_objid = "%x" % net.objid
except TypeError: except TypeError:
net_objid = "%s" % net.objid net_objid = "%s" % net.objid
localname = "veth%s.%s.%s" % (self_objid, net_objid, sessionid) localname = "veth%s.%s.%s" % (self_objid, net_objid, sessionid)
if len(localname) >= 16: if len(localname) >= 16:
raise ValueError("interface local name %s too long" % localname) raise ValueError("interface local name %s too long" % localname)
name = "veth%s.%s.%s" % (net_objid, self_objid, sessionid) name = "veth%s.%s.%s" % (net_objid, self_objid, sessionid)
if len(name) >= 16: if len(name) >= 16:
raise ValueError("interface name %s too long" % name) raise ValueError("interface name %s too long" % name)
netif = VEth(node=None, name=name, localname=localname,
mtu=1500, net=self, start=self.up) netif = VEth(node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up)
self.attach(netif) self.attach(netif)
if net.up: if net.up:
# this is similar to net.attach() but uses netif.name instead # this is similar to net.attach() but uses netif.name instead
@ -563,6 +549,7 @@ class LxBrNet(PyCoreNet):
for netif in self.netifs(): for netif in self.netifs():
if hasattr(netif, "othernet") and netif.othernet == net: if hasattr(netif, "othernet") and netif.othernet == net:
return netif return netif
return None return None
def addrconfig(self, addrlist): def addrconfig(self, addrlist):
@ -574,11 +561,9 @@ class LxBrNet(PyCoreNet):
""" """
if not self.up: if not self.up:
return return
for addr in addrlist: for addr in addrlist:
try: utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname])
utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname])
except subprocess.CalledProcessError:
logger.exception("Error adding IP address")
class GreTapBridge(LxBrNet): class GreTapBridge(LxBrNet):
@ -615,8 +600,8 @@ class GreTapBridge(LxBrNet):
if remoteip is None: if remoteip is None:
self.gretap = None self.gretap = None
else: else:
self.gretap = GreTap(node=self, name=None, session=session, remoteip=remoteip, self.gretap = GreTap(node=self, session=session, remoteip=remoteip,
objid=None, localip=localip, ttl=ttl, key=self.grekey) localip=localip, ttl=ttl, key=self.grekey)
if start: if start:
self.startup() self.startup()
@ -658,7 +643,7 @@ class GreTapBridge(LxBrNet):
localip = None localip = None
if len(addrlist) > 1: if len(addrlist) > 1:
localip = addrlist[1].split("/")[0] localip = addrlist[1].split("/")[0]
self.gretap = GreTap(session=self.session, remoteip=remoteip, objid=None, name=None, self.gretap = GreTap(session=self.session, remoteip=remoteip,
localip=localip, ttl=self.ttl, key=self.grekey) localip=localip, ttl=self.ttl, key=self.grekey)
self.attach(self.gretap) self.attach(self.gretap)

View file

@ -98,25 +98,19 @@ class SimpleLxcNode(PyCoreNode):
env["NODE_NUMBER"] = str(self.objid) env["NODE_NUMBER"] = str(self.objid)
env["NODE_NAME"] = str(self.name) env["NODE_NAME"] = str(self.name)
try: _, output = utils.check_cmd(vnoded, env=env)
_, output = utils.check_cmd(vnoded, env=env) self.pid = int(output)
self.pid = int(output)
except subprocess.CalledProcessError:
logger.exception("vnoded failed to create a namespace; check kernel support and user privileges")
# create vnode client # create vnode client
self.client = vnodeclient.VnodeClient(self.name, self.ctrlchnlname) self.client = vnodeclient.VnodeClient(self.name, self.ctrlchnlname)
try: # bring up the loopback interface
# bring up the loopback interface logger.info("bringing up loopback interface")
logger.info("bringing up loopback interface") self.check_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
self.check_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
# set hostname for node # set hostname for node
logger.info("setting hostname: %s" % self.name) logger.info("setting hostname: %s" % self.name)
self.check_cmd(["hostname", self.name]) self.check_cmd(["hostname", self.name])
except subprocess.CalledProcessError:
logger.exception("error setting up loopback and hostname: %s")
# mark node as up # mark node as up
self.up = True self.up = True
@ -165,7 +159,7 @@ class SimpleLxcNode(PyCoreNode):
:return: nothing :return: nothing
""" """
pass return None
def cmd(self, args, wait=True): def cmd(self, args, wait=True):
""" """
@ -208,7 +202,6 @@ class SimpleLxcNode(PyCoreNode):
""" """
return self.client.termcmdstring(sh) return self.client.termcmdstring(sh)
# TODO: should change how this exception is just swallowed up
def mount(self, source, target): def mount(self, source, target):
""" """
Create and mount a directory. Create and mount a directory.
@ -216,17 +209,15 @@ class SimpleLxcNode(PyCoreNode):
:param str source: source directory to mount :param str source: source directory to mount
:param str target: target directory to create :param str target: target directory to create
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
""" """
source = os.path.abspath(source) source = os.path.abspath(source)
logger.info("mounting %s at %s" % (source, target)) logger.info("mounting %s at %s" % (source, target))
try: cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (target, constants.MOUNT_BIN, source, target)
cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (target, constants.MOUNT_BIN, source, target) status, output = self.client.shcmd_result(cmd)
status, output = self.client.shcmd_result(cmd) if status:
if status: raise subprocess.CalledProcessError(status, cmd, output)
raise IOError("error during mount: %s" % output) self._mounts.append((source, target))
self._mounts.append((source, target))
except IOError:
logger.exception("mounting failed for %s at %s", source, target)
def umount(self, target): def umount(self, target):
""" """
@ -238,8 +229,8 @@ class SimpleLxcNode(PyCoreNode):
logger.info("unmounting: %s", target) logger.info("unmounting: %s", target)
try: try:
self.check_cmd([constants.UMOUNT_BIN, "-n", "-l", target]) self.check_cmd([constants.UMOUNT_BIN, "-n", "-l", target])
except subprocess.CalledProcessError: except subprocess.CalledProcessError as e:
logger.exception("error during unmount") logger.exception("error during unmount: %s", e.output)
def newifindex(self): def newifindex(self):
""" """
@ -277,33 +268,29 @@ class SimpleLxcNode(PyCoreNode):
localname = "veth" + suffix localname = "veth" + suffix
if len(localname) >= 16: if len(localname) >= 16:
raise ValueError("interface local name (%s) too long" % localname) raise ValueError("interface local name (%s) too long" % localname)
name = localname + "p" name = localname + "p"
if len(name) >= 16: if len(name) >= 16:
raise ValueError("interface name (%s) too long" % name) raise ValueError("interface name (%s) too long" % name)
veth = VEth(node=self, name=name, localname=localname, net=net, start=self.up) veth = VEth(node=self, name=name, localname=localname, net=net, start=self.up)
if self.up: if self.up:
try: utils.check_cmd([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)])
utils.check_cmd([constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)]) self.check_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
self.check_cmd([constants.IP_BIN, "link", "set", veth.name, "name", ifname])
except subprocess.CalledProcessError:
logger.exception("failure setting eth name")
veth.name = ifname veth.name = ifname
if self.up: if self.up:
# TODO: potentially find better way to query interface ID # TODO: potentially find better way to query interface ID
# retrieve interface information # retrieve interface information
try: _, output = self.check_cmd(["ip", "link", "show", veth.name])
_, output = self.check_cmd(["ip", "link", "show", veth.name]) logger.info("interface command output: %s", output)
logger.info("interface command output: %s", output) output = output.split("\n")
output = output.split("\n") veth.flow_id = int(output[0].strip().split(":")[0]) + 1
veth.flow_id = int(output[0].strip().split(":")[0]) + 1 logger.info("interface flow index: %s - %s", veth.name, veth.flow_id)
logger.info("interface flow index: %s - %s", veth.name, veth.flow_id) veth.hwaddr = output[1].strip().split()[1]
veth.hwaddr = output[1].strip().split()[1] logger.info("interface mac: %s - %s", veth.name, veth.hwaddr)
logger.info("interface mac: %s - %s", veth.name, veth.hwaddr)
except subprocess.CalledProcessError:
logger.exception("failure getting flow id and mac address")
try: try:
self.addnetif(veth, ifindex) self.addnetif(veth, ifindex)
@ -351,15 +338,13 @@ class SimpleLxcNode(PyCoreNode):
:param int ifindex: index of interface to set hardware address for :param int ifindex: index of interface to set hardware address for
:param core.misc.ipaddress.MacAddress addr: hardware address to set :param core.misc.ipaddress.MacAddress addr: hardware address to set
:return: mothing :return: nothing
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
""" """
self._netif[ifindex].sethwaddr(addr) self._netif[ifindex].sethwaddr(addr)
if self.up: if self.up:
args = [constants.IP_BIN, "link", "set", "dev", self.ifname(ifindex), "address", str(addr)] args = [constants.IP_BIN, "link", "set", "dev", self.ifname(ifindex), "address", str(addr)]
try: self.check_cmd(args)
self.check_cmd(args)
except subprocess.CalledProcessError:
logger.exception("error setting MAC address %s: %s", addr)
def addaddr(self, ifindex, addr): def addaddr(self, ifindex, addr):
""" """
@ -370,16 +355,13 @@ class SimpleLxcNode(PyCoreNode):
:return: nothing :return: nothing
""" """
if self.up: if self.up:
try: # check if addr is ipv6
# check if addr is ipv6 if ":" in str(addr):
if ":" in str(addr): args = [constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)]
args = [constants.IP_BIN, "addr", "add", str(addr), "dev", self.ifname(ifindex)] self.check_cmd(args)
self.check_cmd(args) else:
else: args = [constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", "dev", self.ifname(ifindex)]
args = [constants.IP_BIN, "addr", "add", str(addr), "broadcast", "+", "dev", self.ifname(ifindex)] self.check_cmd(args)
self.check_cmd(args)
except subprocess.CalledProcessError:
logger.exception("failure adding interface address")
self._netif[ifindex].addaddr(addr) self._netif[ifindex].addaddr(addr)
@ -390,6 +372,7 @@ class SimpleLxcNode(PyCoreNode):
:param int ifindex: index of interface to delete address from :param int ifindex: index of interface to delete address from
:param str addr: address to delete from interface :param str addr: address to delete from interface
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
""" """
try: try:
self._netif[ifindex].deladdr(addr) self._netif[ifindex].deladdr(addr)
@ -397,10 +380,7 @@ class SimpleLxcNode(PyCoreNode):
logger.exception("trying to delete unknown address: %s" % addr) logger.exception("trying to delete unknown address: %s" % addr)
if self.up: if self.up:
try: self.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
self.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.ifname(ifindex)])
except subprocess.CalledProcessError:
logger.exception("failure deleting address")
def delalladdr(self, ifindex, address_types=valid_address_types): def delalladdr(self, ifindex, address_types=valid_address_types):
""" """
@ -409,6 +389,7 @@ class SimpleLxcNode(PyCoreNode):
:param int ifindex: index of interface to delete address types from :param int ifindex: index of interface to delete address types from
:param tuple[str] address_types: address types to delete :param tuple[str] address_types: address types to delete
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
""" """
interface_name = self.ifname(ifindex) interface_name = self.ifname(ifindex)
addresses = self.client.getaddr(interface_name, rescan=True) addresses = self.client.getaddr(interface_name, rescan=True)
@ -430,10 +411,7 @@ class SimpleLxcNode(PyCoreNode):
:return: nothing :return: nothing
""" """
if self.up: if self.up:
try: self.check_cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
self.check_cmd([constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"])
except subprocess.CalledProcessError:
logger.exception("failure bringing interface up")
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None): def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
""" """
@ -510,17 +488,15 @@ class SimpleLxcNode(PyCoreNode):
:param str srcname: source file name :param str srcname: source file name
:param str filename: file name to add :param str filename: file name to add
:return: nothing :return: nothing
:raises subprocess.CalledProcessError: when a non-zero exit status occurs
""" """
logger.info("adding file from %s to %s", srcname, filename) logger.info("adding file from %s to %s", srcname, filename)
directory = os.path.dirname(filename) directory = os.path.dirname(filename)
try: cmd = 'mkdir -p "%s" && mv "%s" "%s" && sync' % (directory, srcname, filename)
cmd = 'mkdir -p "%s" && mv "%s" "%s" && sync' % (directory, srcname, filename) status, output = self.client.shcmd_result(cmd)
status, output = self.client.shcmd_result(cmd) if status:
if status: raise subprocess.CalledProcessError(status, cmd, output)
raise IOError("error adding file: %s" % output)
except IOError:
logger.exception("error during addfile")
class LxcNode(SimpleLxcNode): class LxcNode(SimpleLxcNode):
@ -567,13 +543,10 @@ class LxcNode(SimpleLxcNode):
:return: nothing :return: nothing
""" """
with self.lock: with self.lock:
try: self.makenodedir()
self.makenodedir() super(LxcNode, self).startup()
super(LxcNode, self).startup() self.privatedir("/var/run")
self.privatedir("/var/run") self.privatedir("/var/log")
self.privatedir("/var/log")
except OSError:
logger.exception("error during startup")
def shutdown(self): def shutdown(self):
""" """
@ -585,8 +558,6 @@ class LxcNode(SimpleLxcNode):
return return
with self.lock: with self.lock:
# services are instead stopped when session enters datacollect state
# self.session.services.stopnodeservices(self)
try: try:
super(LxcNode, self).shutdown() super(LxcNode, self).shutdown()
except OSError: except OSError:
@ -605,12 +576,7 @@ class LxcNode(SimpleLxcNode):
if path[0] != "/": if path[0] != "/":
raise ValueError("path not fully qualified: %s" % path) raise ValueError("path not fully qualified: %s" % path)
hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip("/").replace("/", ".")) hostpath = os.path.join(self.nodedir, os.path.normpath(path).strip("/").replace("/", "."))
os.mkdir(hostpath)
try:
os.mkdir(hostpath)
except OSError:
logger.exception("error creating directory: %s", hostpath)
self.mount(hostpath, path) self.mount(hostpath, path)
def hostfilename(self, filename): def hostfilename(self, filename):
@ -622,7 +588,7 @@ class LxcNode(SimpleLxcNode):
""" """
dirname, basename = os.path.split(filename) dirname, basename = os.path.split(filename)
if not basename: if not basename:
raise ValueError("no basename for filename: " + filename) raise ValueError("no basename for filename: %s" % filename)
if dirname and dirname[0] == "/": if dirname and dirname[0] == "/":
dirname = dirname[1:] dirname = dirname[1:]
dirname = dirname.replace("/", ".") dirname = dirname.replace("/", ".")

View file

@ -109,7 +109,7 @@ class VnodeClient(object):
:raises subprocess.CalledProcessError: when there is a non-zero exit status :raises subprocess.CalledProcessError: when there is a non-zero exit status
""" """
status, output = self.cmd_output(args) status, output = self.cmd_output(args)
if status: if status != 0:
raise subprocess.CalledProcessError(status, args, output) raise subprocess.CalledProcessError(status, args, output)
return status, output.strip() return status, output.strip()
@ -174,8 +174,8 @@ class VnodeClient(object):
args = ("xterm", "-ut", "-title", self.name, "-e", VCMD, "-c", self.ctrlchnlname, "--", sh) args = ("xterm", "-ut", "-title", self.name, "-e", VCMD, "-c", self.ctrlchnlname, "--", sh)
if "SUDO_USER" in os.environ: if "SUDO_USER" in os.environ:
args = ("su", "-s", "/bin/sh", "-c", args = ("su", "-s", "/bin/sh", "-c",
"exec " + " ".join(map(lambda x: "'%s'" % x, args)), "exec " + " ".join(map(lambda x: "'%s'" % x, args)),
os.environ["SUDO_USER"]) os.environ["SUDO_USER"])
return os.spawnvp(os.P_NOWAIT, args[0], args) return os.spawnvp(os.P_NOWAIT, args[0], args)
def termcmdstring(self, sh="/bin/sh"): def termcmdstring(self, sh="/bin/sh"):