initial effort to remove swallowing exceptions within internal code
This commit is contained in:
parent
a3356127d2
commit
43554cbb62
10 changed files with 423 additions and 399 deletions
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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("/", ".")
|
||||||
|
|
|
@ -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"):
|
||||||
|
|
Loading…
Add table
Reference in a new issue