Merge pull request #298 from coreemu/cleanup/node-client

Cleanup/node client
This commit is contained in:
bharnden 2019-10-03 12:02:24 -07:00 committed by GitHub
commit 478eb84786
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 349 additions and 589 deletions

View file

@ -7,7 +7,7 @@ import logging
import os
import threading
from core import constants, utils
from core import utils
from core.api.tlv import coreapi, dataconversion
from core.config import ConfigGroup, ConfigShim, Configuration, ModelManager
from core.emane import emanemanifest
@ -733,13 +733,11 @@ class EmaneManager(ModelManager):
)
# multicast route is needed for OTA data
args = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev]
node.network_cmd(args)
node.node_net_client.create_route(otagroup, otadev)
# multicast route is also needed for event data if on control network
if eventservicenetidx >= 0 and eventgroup != otagroup:
args = [constants.IP_BIN, "route", "add", eventgroup, "dev", eventdev]
node.network_cmd(args)
node.node_net_client.create_route(eventgroup, eventdev)
# start emane
args = emanecmd + [

View file

@ -17,7 +17,6 @@ from socket import AF_INET, AF_INET6
from core import constants, utils
from core.emulator.data import LinkData, NodeData
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.errors import CoreCommandError
from core.nodes import client, ipaddress
from core.nodes.interface import CoreInterface, TunTap, Veth
from core.nodes.netclient import LinuxNetClient, OvsNetClient
@ -54,7 +53,7 @@ class NodeBase(object):
self.type = None
self.server = None
self.services = None
# ifindex is key, PyCoreNetIf instance is value
# ifindex is key, CoreInterface instance is value
self._netif = {}
self.ifindex = 0
self.canvas = None
@ -62,6 +61,11 @@ class NodeBase(object):
self.opaque = None
self.position = Position()
if session.options.get_config("ovs") == "True":
self.net_client = OvsNetClient(self.net_cmd)
else:
self.net_client = LinuxNetClient(self.net_cmd)
def startup(self):
"""
Each object implements its own startup method.
@ -78,6 +82,18 @@ class NodeBase(object):
"""
raise NotImplementedError
def net_cmd(self, args):
"""
Runs a command that is used to configure and setup the network on the host
system.
:param list[str]|str args: command to run
:return: combined stdout and stderr
:rtype: str
:raises CoreCommandError: when a non-zero exit status occurs
"""
return utils.check_cmd(args)
def setposition(self, x=None, y=None, z=None):
"""
Set the (x,y,z) position of the object.
@ -360,6 +376,18 @@ class CoreNodeBase(NodeBase):
return common
def node_net_cmd(self, args):
"""
Runs a command that is used to configure and setup the network within a
node.
:param list[str]|str args: command to run
:return: combined stdout and stderr
:rtype: str
:raises CoreCommandError: when a non-zero exit status occurs
"""
raise NotImplementedError
def check_cmd(self, args):
"""
Runs shell command on node.
@ -434,6 +462,12 @@ class CoreNode(CoreNodeBase):
self.lock = threading.RLock()
self._mounts = []
self.bootsh = bootsh
if session.options.get_config("ovs") == "True":
self.node_net_client = OvsNetClient(self.node_net_cmd)
else:
self.node_net_client = LinuxNetClient(self.node_net_cmd)
if start:
self.startup()
@ -489,11 +523,11 @@ class CoreNode(CoreNodeBase):
# bring up the loopback interface
logging.debug("bringing up loopback interface")
self.network_cmd([constants.IP_BIN, "link", "set", "lo", "up"])
self.node_net_client.device_up("lo")
# set hostname for node
logging.debug("setting hostname: %s", self.name)
self.network_cmd(["hostname", self.name])
self.node_net_client.set_hostname(self.name)
# mark node as up
self.up = True
@ -568,9 +602,10 @@ class CoreNode(CoreNodeBase):
"""
return self.client.cmd_output(args)
def network_cmd(self, args):
def node_net_cmd(self, args):
"""
Runs a command for a node that is used to configure and setup network interfaces.
Runs a command that is used to configure and setup the network within a
node.
:param list[str]|str args: command to run
:return: combined stdout and stderr
@ -625,15 +660,8 @@ class CoreNode(CoreNodeBase):
"""
source = os.path.abspath(source)
logging.debug("node(%s) mounting: %s at %s", self.name, source, target)
cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (
target,
constants.MOUNT_BIN,
source,
target,
)
status, output = self.client.shcmd_result(cmd)
if status:
raise CoreCommandError(status, cmd, output)
self.client.check_cmd(["mkdir", "-p", target])
self.client.check_cmd([constants.MOUNT_BIN, "-n", "--bind", source, target])
self._mounts.append((source, target))
def newifindex(self):
@ -682,22 +710,16 @@ class CoreNode(CoreNodeBase):
)
if self.up:
utils.check_cmd(
[constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)]
)
self.network_cmd(
[constants.IP_BIN, "link", "set", veth.name, "name", ifname]
)
self.network_cmd(
[constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"]
)
self.net_client.device_ns(veth.name, str(self.pid))
self.node_net_client.device_name(veth.name, ifname)
self.node_net_client.checksums_off(ifname)
veth.name = ifname
if self.up:
# TODO: potentially find better way to query interface ID
# retrieve interface information
output = self.network_cmd([constants.IP_BIN, "link", "show", veth.name])
output = self.node_net_client.device_show(veth.name)
logging.debug("interface command output: %s", output)
output = output.split("\n")
veth.flow_id = int(output[0].strip().split(":")[0]) + 1
@ -707,7 +729,8 @@ class CoreNode(CoreNodeBase):
logging.debug("interface mac: %s - %s", veth.name, veth.hwaddr)
try:
# add network interface to the node. If unsuccessful, destroy the network interface and raise exception.
# add network interface to the node. If unsuccessful, destroy the
# network interface and raise exception.
self.addnetif(veth, ifindex)
except ValueError as e:
veth.shutdown()
@ -758,105 +781,47 @@ class CoreNode(CoreNodeBase):
:return: nothing
:raises CoreCommandError: when a non-zero exit status occurs
"""
self._netif[ifindex].sethwaddr(addr)
interface = self._netif[ifindex]
interface.sethwaddr(addr)
if self.up:
args = [
constants.IP_BIN,
"link",
"set",
"dev",
self.ifname(ifindex),
"address",
str(addr),
]
self.network_cmd(args)
self.node_net_client.device_mac(interface.name, str(addr))
def addaddr(self, ifindex, addr):
"""
Add interface address.
:param int ifindex: index of interface to add address to
:param str addr: address to add to interface
:param core.nodes.ipaddress.IpAddress addr: address to add to interface
:return: nothing
"""
interface = self._netif[ifindex]
interface.addaddr(addr)
if self.up:
# check if addr is ipv6
if ":" in str(addr):
args = [
constants.IP_BIN,
"addr",
"add",
str(addr),
"dev",
self.ifname(ifindex),
]
self.network_cmd(args)
else:
args = [
constants.IP_BIN,
"addr",
"add",
str(addr),
"broadcast",
"+",
"dev",
self.ifname(ifindex),
]
self.network_cmd(args)
self._netif[ifindex].addaddr(addr)
address = str(addr)
# ipv6 check
broadcast = None
if ":" not in address:
broadcast = "+"
self.node_net_client.create_address(interface.name, address, broadcast)
def deladdr(self, ifindex, addr):
"""
Delete address from an interface.
:param int ifindex: index of interface to delete address from
:param str addr: address to delete from interface
:param core.nodes.ipaddress.IpAddress addr: address to delete from interface
:return: nothing
:raises CoreCommandError: when a non-zero exit status occurs
"""
interface = self._netif[ifindex]
try:
self._netif[ifindex].deladdr(addr)
interface.deladdr(addr)
except ValueError:
logging.exception("trying to delete unknown address: %s" % addr)
if self.up:
self.network_cmd(
[
constants.IP_BIN,
"addr",
"del",
str(addr),
"dev",
self.ifname(ifindex),
]
)
def delalladdr(self, ifindex, address_types=None):
"""
Delete all addresses from an interface.
:param int ifindex: index of interface to delete address types from
:param tuple[str] address_types: address types to delete
:return: nothing
:raises CoreCommandError: when a non-zero exit status occurs
"""
if not address_types:
address_types = self.valid_address_types
interface_name = self.ifname(ifindex)
addresses = self.client.getaddr(interface_name, rescan=True)
for address_type in address_types:
if address_type not in self.valid_address_types:
raise ValueError(
"addr type must be in: %s" % " ".join(self.valid_address_types)
)
for address in addresses[address_type]:
self.deladdr(ifindex, address)
# update cached information
self.client.getaddr(interface_name, rescan=True)
self.node_net_client.delete_address(interface.name, str(addr))
def ifup(self, ifindex):
"""
@ -866,9 +831,8 @@ class CoreNode(CoreNodeBase):
:return: nothing
"""
if self.up:
self.network_cmd(
[constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"]
)
interface_name = self.ifname(ifindex)
self.node_net_client.device_up(interface_name)
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
"""
@ -919,7 +883,7 @@ class CoreNode(CoreNodeBase):
Connect a node.
:param str ifname: name of interface to connect
:param core.nodes.CoreNodeBase othernode: node to connect to
:param core.nodes.base.CoreNode othernode: node to connect to
:param str otherifname: interface name to connect to
:return: nothing
"""
@ -930,32 +894,14 @@ class CoreNode(CoreNodeBase):
tmp2 = "tmp." + "".join(
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
)
utils.check_cmd(
[
constants.IP_BIN,
"link",
"add",
"name",
tmp1,
"type",
"veth",
"peer",
"name",
tmp2,
]
)
utils.check_cmd([constants.IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
self.network_cmd([constants.IP_BIN, "link", "set", tmp1, "name", ifname])
self.net_client.create_veth(tmp1, tmp2)
self.net_client.device_ns(tmp1, str(self.pid))
self.node_net_client.device_name(tmp1, ifname)
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
self.addnetif(interface, self.newifindex())
utils.check_cmd(
[constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)]
)
othernode.network_cmd(
[constants.IP_BIN, "link", "set", tmp2, "name", otherifname]
)
self.net_client.device_ns(tmp2, str(othernode.pid))
othernode.node_net_client.device_name(tmp2, otherifname)
other_interface = CoreInterface(
node=othernode, name=otherifname, mtu=_DEFAULT_MTU
)
@ -972,11 +918,9 @@ class CoreNode(CoreNodeBase):
"""
logging.info("adding file from %s to %s", srcname, filename)
directory = os.path.dirname(filename)
cmd = 'mkdir -p "%s" && mv "%s" "%s" && sync' % (directory, srcname, filename)
status, output = self.client.shcmd_result(cmd)
if status:
raise CoreCommandError(status, cmd, output)
self.client.check_cmd(["mkdir", "-p", directory])
self.client.check_cmd(["mv", srcname, filename])
self.client.check_cmd(["sync"])
def hostfilename(self, filename):
"""
@ -1064,10 +1008,6 @@ class CoreNetworkBase(NodeBase):
super(CoreNetworkBase, self).__init__(session, _id, name, start=start)
self._linked = {}
self._linked_lock = threading.Lock()
if session.options.get_config("ovs") == "True":
self.net_client = OvsNetClient()
else:
self.net_client = LinuxNetClient()
def startup(self):
"""

View file

@ -26,7 +26,6 @@ class VnodeClient(object):
"""
self.name = name
self.ctrlchnlname = ctrlchnlname
self._addr = {}
def _verify_connection(self):
"""
@ -146,36 +145,6 @@ class VnodeClient(object):
*args
)
def redircmd(self, infd, outfd, errfd, args, wait=True):
"""
Execute a command on a node with standard input, output, and
error redirected according to the given file descriptors.
:param infd: stdin file descriptor
:param outfd: stdout file descriptor
:param errfd: stderr file descriptor
:param list[str]|str args: command arguments
:param bool wait: wait flag
:return: command status
:rtype: int
"""
self._verify_connection()
# run command, return process when not waiting
args = utils.split_args(args)
cmd = self._cmd_args() + args
logging.debug("redircmd: %s", cmd)
p = Popen(cmd, stdin=infd, stdout=outfd, stderr=errfd)
if not wait:
return p
# wait for and return exit status
status = p.wait()
if status:
logging.warning("cmd exited with status %s: %s", status, args)
return status
def term(self, sh="/bin/sh"):
"""
Open a terminal on a node.
@ -215,111 +184,3 @@ class VnodeClient(object):
:return: str
"""
return "%s -c %s -- %s" % (constants.VCMD_BIN, self.ctrlchnlname, sh)
def shcmd(self, cmd, sh="/bin/sh"):
"""
Execute a shell command.
:param str cmd: command string
:param str sh: shell to run command in
:return: command result
:rtype: int
"""
return self.cmd([sh, "-c", cmd])
def shcmd_result(self, cmd, sh="/bin/sh"):
"""
Execute a shell command and return the exist status and combined output.
:param str cmd: shell command to run
:param str sh: shell to run command in
:return: exist status and combined output
:rtype: tuple[int, str]
"""
return self.cmd_output([sh, "-c", cmd])
def getaddr(self, ifname, rescan=False):
"""
Get address for interface on node.
:param str ifname: interface name to get address for
:param bool rescan: rescan flag
:return: interface information
:rtype: dict
"""
if ifname in self._addr and not rescan:
return self._addr[ifname]
interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
args = [constants.IP_BIN, "addr", "show", "dev", ifname]
p, stdin, stdout, stderr = self.popen(args)
stdin.close()
for line in stdout:
line = line.strip().split()
if line[0] == "link/ether":
interface["ether"].append(line[1])
elif line[0] == "inet":
interface["inet"].append(line[1])
elif line[0] == "inet6":
if line[3] == "global":
interface["inet6"].append(line[1])
elif line[3] == "link":
interface["inet6link"].append(line[1])
else:
logging.warning("unknown scope: %s" % line[3])
err = stderr.read()
stdout.close()
stderr.close()
status = p.wait()
if status:
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
if err:
logging.warning("error output: %s", err)
self._addr[ifname] = interface
return interface
def netifstats(self, ifname=None):
"""
Retrieve network interface state.
:param str ifname: name of interface to get state for
:return: interface state information
:rtype: dict
"""
stats = {}
args = ["cat", "/proc/net/dev"]
p, stdin, stdout, stderr = self.popen(args)
stdin.close()
# ignore first line
stdout.readline()
# second line has count names
tmp = stdout.readline().decode("utf-8").strip().split("|")
rxkeys = tmp[1].split()
txkeys = tmp[2].split()
for line in stdout:
line = line.decode("utf-8").strip().split()
devname, tmp = line[0].split(":")
if tmp:
line.insert(1, tmp)
stats[devname] = {"rx": {}, "tx": {}}
field = 1
for count in rxkeys:
stats[devname]["rx"][count] = int(line[field])
field += 1
for count in txkeys:
stats[devname]["tx"][count] = int(line[field])
field += 1
err = stderr.read()
stdout.close()
stderr.close()
status = p.wait()
if status:
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
if err:
logging.warning("error output: %s", err)
if ifname is not None:
return stats[ifname]
else:
return stats

View file

@ -13,7 +13,6 @@ class DockerClient(object):
self.name = name
self.image = image
self.pid = None
self._addr = {}
def create_container(self):
utils.check_cmd(
@ -95,40 +94,6 @@ class DockerClient(object):
if status:
raise CoreCommandError(status, args, output)
def getaddr(self, ifname, rescan=False):
"""
Get address for interface on node.
:param str ifname: interface name to get address for
:param bool rescan: rescan flag
:return: interface information
:rtype: dict
"""
if ifname in self._addr and not rescan:
return self._addr[ifname]
interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
args = ["ip", "addr", "show", "dev", ifname]
status, output = self.ns_cmd(args)
for line in output:
line = line.strip().split()
if line[0] == "link/ether":
interface["ether"].append(line[1])
elif line[0] == "inet":
interface["inet"].append(line[1])
elif line[0] == "inet6":
if line[3] == "global":
interface["inet6"].append(line[1])
elif line[3] == "link":
interface["inet6link"].append(line[1])
else:
logging.warning("unknown scope: %s" % line[3])
if status:
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
self._addr[ifname] = interface
return interface
class DockerNode(CoreNode):
apitype = NodeTypes.DOCKER.value
@ -225,7 +190,7 @@ class DockerNode(CoreNode):
raise CoreCommandError(status, args, output)
return output
def network_cmd(self, args):
def node_net_cmd(self, args):
if not self.up:
logging.debug("node down, not running network command: %s", args)
return 0

View file

@ -6,8 +6,9 @@ import logging
import time
from builtins import int, range
from core import constants, utils
from core import utils
from core.errors import CoreCommandError
from core.nodes.netclient import LinuxNetClient
class CoreInterface(object):
@ -41,6 +42,7 @@ class CoreInterface(object):
self.netindex = None
# index used to find flow data
self.flow_id = None
self.net_client = LinuxNetClient(utils.check_cmd)
def startup(self):
"""
@ -216,21 +218,8 @@ class Veth(CoreInterface):
:return: nothing
:raises CoreCommandError: when there is a command exception
"""
utils.check_cmd(
[
constants.IP_BIN,
"link",
"add",
"name",
self.localname,
"type",
"veth",
"peer",
"name",
self.name,
]
)
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
self.net_client.create_veth(self.localname, self.name)
self.net_client.device_up(self.localname)
self.up = True
def shutdown(self):
@ -244,15 +233,13 @@ class Veth(CoreInterface):
if self.node:
try:
self.node.network_cmd(
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
)
self.node.node_net_client.device_flush(self.name)
except CoreCommandError:
logging.exception("error shutting down interface")
if self.localname:
try:
utils.check_cmd([constants.IP_BIN, "link", "delete", self.localname])
self.net_client.delete_device(self.localname)
except CoreCommandError:
logging.info("link already removed: %s", self.localname)
@ -307,9 +294,7 @@ class TunTap(CoreInterface):
return
try:
self.node.network_cmd(
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
)
self.node.node_net_client.device_flush(self.name)
except CoreCommandError:
logging.exception("error shutting down tunnel tap")
@ -357,8 +342,11 @@ class TunTap(CoreInterface):
logging.debug("waiting for device local: %s", self.localname)
def localdevexists():
args = [constants.IP_BIN, "link", "show", self.localname]
return utils.cmd(args)
try:
self.net_client.device_show(self.localname)
return 0
except CoreCommandError:
return 1
self.waitfor(localdevexists)
@ -371,9 +359,8 @@ class TunTap(CoreInterface):
logging.debug("waiting for device node: %s", self.name)
def nodedevexists():
args = [constants.IP_BIN, "link", "show", self.name]
try:
self.node.network_cmd(args)
self.node.node_net_client.device_show(self.name)
return 0
except CoreCommandError:
return 1
@ -406,13 +393,9 @@ class TunTap(CoreInterface):
"""
self.waitfordevicelocal()
netns = str(self.node.pid)
utils.check_cmd(
[constants.IP_BIN, "link", "set", self.localname, "netns", netns]
)
self.node.network_cmd(
[constants.IP_BIN, "link", "set", self.localname, "name", self.name]
)
self.node.network_cmd([constants.IP_BIN, "link", "set", self.name, "up"])
self.net_client.device_ns(self.localname, netns)
self.node.node_net_client.device_name(self.localname, self.name)
self.node.node_net_client.device_up(self.name)
def setaddrs(self):
"""
@ -422,9 +405,7 @@ class TunTap(CoreInterface):
"""
self.waitfordevicenode()
for addr in self.addrlist:
self.node.network_cmd(
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
)
self.node.node_net_client.create_address(self.name, str(addr))
class GreTap(CoreInterface):
@ -478,25 +459,11 @@ class GreTap(CoreInterface):
if remoteip is None:
raise ValueError("missing remote IP required for GRE TAP device")
args = [
constants.IP_BIN,
"link",
"add",
self.localname,
"type",
"gretap",
"remote",
str(remoteip),
]
if localip:
args += ["local", str(localip)]
if ttl:
args += ["ttl", str(ttl)]
if key:
args += ["key", str(key)]
utils.check_cmd(args)
args = [constants.IP_BIN, "link", "set", self.localname, "up"]
utils.check_cmd(args)
self.net_client.create_gretap(
self.localname, str(remoteip), str(localip), str(ttl), str(key)
)
self.net_client.device_up(self.localname)
self.up = True
def shutdown(self):
@ -507,10 +474,8 @@ class GreTap(CoreInterface):
"""
if self.localname:
try:
args = [constants.IP_BIN, "link", "set", self.localname, "down"]
utils.check_cmd(args)
args = [constants.IP_BIN, "link", "del", self.localname]
utils.check_cmd(args)
self.net_client.device_down(self.localname)
self.net_client.delete_device(self.localname)
except CoreCommandError:
logging.exception("error during shutdown")

View file

@ -14,7 +14,6 @@ class LxdClient(object):
self.name = name
self.image = image
self.pid = None
self._addr = {}
def create_container(self):
utils.check_cmd(
@ -91,40 +90,6 @@ class LxdClient(object):
if status:
raise CoreCommandError(status, args, output)
def getaddr(self, ifname, rescan=False):
"""
Get address for interface on node.
:param str ifname: interface name to get address for
:param bool rescan: rescan flag
:return: interface information
:rtype: dict
"""
if ifname in self._addr and not rescan:
return self._addr[ifname]
interface = {"ether": [], "inet": [], "inet6": [], "inet6link": []}
args = ["ip", "addr", "show", "dev", ifname]
status, output = self.ns_cmd_output(args)
for line in output:
line = line.strip().split()
if line[0] == "link/ether":
interface["ether"].append(line[1])
elif line[0] == "inet":
interface["inet"].append(line[1])
elif line[0] == "inet6":
if line[3] == "global":
interface["inet6"].append(line[1])
elif line[3] == "link":
interface["inet6link"].append(line[1])
else:
logging.warning("unknown scope: %s" % line[3])
if status:
logging.warning("nonzero exist status (%s) for cmd: %s", status, args)
self._addr[ifname] = interface
return interface
class LxcNode(CoreNode):
apitype = NodeTypes.LXC.value
@ -228,7 +193,7 @@ class LxcNode(CoreNode):
raise CoreCommandError(status, args, output)
return output
def network_cmd(self, args):
def node_net_cmd(self, args):
if not self.up:
logging.debug("node down, not running network command: %s", args)
return 0

View file

@ -2,87 +2,205 @@
Clients for dealing with bridge/interface commands.
"""
import abc
import os
from future.utils import with_metaclass
from core.constants import BRCTL_BIN, IP_BIN, OVS_BIN
from core.constants import BRCTL_BIN, ETHTOOL_BIN, IP_BIN, OVS_BIN, TC_BIN
from core.utils import check_cmd
class NetClientBase(with_metaclass(abc.ABCMeta)):
"""
Base client for running command line bridge/interface commands.
"""
@abc.abstractmethod
def create_bridge(self, name):
"""
Create a network bridge to connect interfaces to.
:param str name: bridge name
:return: nothing
"""
pass
@abc.abstractmethod
def delete_bridge(self, name):
"""
Delete a network bridge.
:param str name: bridge name
:return: nothing
"""
pass
@abc.abstractmethod
def create_interface(self, bridge_name, interface_name):
"""
Create an interface associated with a network bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
pass
@abc.abstractmethod
def delete_interface(self, bridge_name, interface_name):
"""
Delete an interface associated with a network bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
pass
@abc.abstractmethod
def existing_bridges(self, _id):
"""
Checks if there are any existing bridges for a node.
:param _id: node id to check bridges for
"""
pass
@abc.abstractmethod
def disable_mac_learning(self, name):
"""
Disable mac learning for a bridge.
:param str name: bridge name
:return: nothing
"""
pass
class LinuxNetClient(NetClientBase):
class LinuxNetClient(object):
"""
Client for creating Linux bridges and ip interfaces for nodes.
"""
def __init__(self, run):
"""
Create LinuxNetClient instance.
:param run: function to run commands with
"""
self.run = run
def set_hostname(self, name):
"""
Set network hostname.
:param str name: name for hostname
:return: nothing
"""
self.run(["hostname", name])
def create_route(self, route, device):
"""
Create a new route for a device.
:param str route: route to create
:param str device: device to add route to
:return: nothing
"""
self.run([IP_BIN, "route", "add", route, "dev", device])
def device_up(self, device):
"""
Bring a device up.
:param str device: device to bring up
:return: nothing
"""
self.run([IP_BIN, "link", "set", device, "up"])
def device_down(self, device):
"""
Bring a device down.
:param str device: device to bring down
:return: nothing
"""
self.run([IP_BIN, "link", "set", device, "down"])
def device_name(self, device, name):
"""
Set a device name.
:param str device: device to set name for
:param str name: name to set
:return: nothing
"""
self.run([IP_BIN, "link", "set", device, "name", name])
def device_show(self, device):
"""
Show information for a device.
:param str device: device to get information for
:return: device information
:rtype: str
"""
return self.run([IP_BIN, "link", "show", device])
def device_ns(self, device, namespace):
"""
Set netns for a device.
:param str device: device to setns for
:param str namespace: namespace to set device to
:return: nothing
"""
self.run([IP_BIN, "link", "set", device, "netns", namespace])
def device_flush(self, device):
"""
Flush device addresses.
:param str device: device to flush
:return: nothing
"""
self.run([IP_BIN, "-6", "address", "flush", "dev", device])
def device_mac(self, device, mac):
"""
Set MAC address for a device.
:param str device: device to set mac for
:param str mac: mac to set
:return: nothing
"""
self.run([IP_BIN, "link", "set", "dev", device, "address", mac])
def delete_device(self, device):
"""
Delete device.
:param str device: device to delete
:return: nothing
"""
self.run([IP_BIN, "link", "delete", device])
def delete_tc(self, device):
"""
Remove traffic control settings for a device.
:param str device: device to remove tc
:return: nothing
"""
self.run([TC_BIN, "qdisc", "del", "dev", device, "root"])
def checksums_off(self, interface_name):
"""
Turns interface checksums off.
:param str interface_name: interface to update
:return: nothing
"""
self.run([ETHTOOL_BIN, "-K", interface_name, "rx", "off", "tx", "off"])
def create_address(self, device, address, broadcast=None):
"""
Create address for a device.
:param str device: device to add address to
:param str address: address to add
:param str broadcast: broadcast address to use, default is None
:return: nothing
"""
if broadcast is not None:
self.run(
[
IP_BIN,
"address",
"add",
address,
"broadcast",
broadcast,
"dev",
device,
]
)
else:
self.run([IP_BIN, "address", "add", address, "dev", device])
def delete_address(self, device, address):
"""
Delete an address from a device.
:param str device: targeted device
:param str address: address to remove
:return: nothing
"""
self.run([IP_BIN, "address", "delete", address, "dev", device])
def create_veth(self, name, peer):
"""
Create a veth pair.
:param str name: veth name
:param str peer: peer name
:return: nothing
"""
self.run(
[IP_BIN, "link", "add", "name", name, "type", "veth", "peer", "name", peer]
)
def create_gretap(self, device, address, local, ttl, key):
"""
Create a GRE tap on a device.
:param str device: device to add tap to
:param str address: address to add tap for
:param str local: local address to tie to
:param str ttl: time to live value
:param str key: key for tap
:return: nothing
"""
cmd = [IP_BIN, "link", "add", device, "type", "gretap", "remote", address]
if local is not None:
cmd.extend(["local", local])
if ttl is not None:
cmd.extend(["ttl", ttl])
if key is not None:
cmd.extend(["key", key])
self.run(cmd)
def create_bridge(self, name):
"""
Create a Linux bridge and bring it up.
@ -90,10 +208,10 @@ class LinuxNetClient(NetClientBase):
:param str name: bridge name
:return: nothing
"""
check_cmd([BRCTL_BIN, "addbr", name])
check_cmd([BRCTL_BIN, "stp", name, "off"])
check_cmd([BRCTL_BIN, "setfd", name, "0"])
check_cmd([IP_BIN, "link", "set", name, "up"])
self.run([BRCTL_BIN, "addbr", name])
self.run([BRCTL_BIN, "stp", name, "off"])
self.run([BRCTL_BIN, "setfd", name, "0"])
self.device_up(name)
# turn off multicast snooping so forwarding occurs w/o IGMP joins
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % name
@ -108,8 +226,8 @@ class LinuxNetClient(NetClientBase):
:param str name: bridge name
:return: nothing
"""
check_cmd([IP_BIN, "link", "set", name, "down"])
check_cmd([BRCTL_BIN, "delbr", name])
self.device_down(name)
self.run([BRCTL_BIN, "delbr", name])
def create_interface(self, bridge_name, interface_name):
"""
@ -119,8 +237,8 @@ class LinuxNetClient(NetClientBase):
:param str interface_name: interface name
:return: nothing
"""
check_cmd([BRCTL_BIN, "addif", bridge_name, interface_name])
check_cmd([IP_BIN, "link", "set", interface_name, "up"])
self.run([BRCTL_BIN, "addif", bridge_name, interface_name])
self.device_up(interface_name)
def delete_interface(self, bridge_name, interface_name):
"""
@ -130,7 +248,7 @@ class LinuxNetClient(NetClientBase):
:param str interface_name: interface name
:return: nothing
"""
check_cmd([BRCTL_BIN, "delif", bridge_name, interface_name])
self.run([BRCTL_BIN, "delif", bridge_name, interface_name])
def existing_bridges(self, _id):
"""
@ -138,7 +256,7 @@ class LinuxNetClient(NetClientBase):
:param _id: node id to check bridges for
"""
output = check_cmd([BRCTL_BIN, "show"])
output = self.run([BRCTL_BIN, "show"])
lines = output.split("\n")
for line in lines[1:]:
columns = line.split()
@ -160,7 +278,7 @@ class LinuxNetClient(NetClientBase):
check_cmd([BRCTL_BIN, "setageing", name, "0"])
class OvsNetClient(NetClientBase):
class OvsNetClient(LinuxNetClient):
"""
Client for creating OVS bridges and ip interfaces for nodes.
"""
@ -172,11 +290,11 @@ class OvsNetClient(NetClientBase):
:param str name: bridge name
:return: nothing
"""
check_cmd([OVS_BIN, "add-br", name])
check_cmd([OVS_BIN, "set", "bridge", name, "stp_enable=false"])
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:stp-max-age=6"])
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:stp-forward-delay=4"])
check_cmd([IP_BIN, "link", "set", name, "up"])
self.run([OVS_BIN, "add-br", name])
self.run([OVS_BIN, "set", "bridge", name, "stp_enable=false"])
self.run([OVS_BIN, "set", "bridge", name, "other_config:stp-max-age=6"])
self.run([OVS_BIN, "set", "bridge", name, "other_config:stp-forward-delay=4"])
self.device_up(name)
def delete_bridge(self, name):
"""
@ -185,8 +303,8 @@ class OvsNetClient(NetClientBase):
:param str name: bridge name
:return: nothing
"""
check_cmd([IP_BIN, "link", "set", name, "down"])
check_cmd([OVS_BIN, "del-br", name])
self.device_down(name)
self.run([OVS_BIN, "del-br", name])
def create_interface(self, bridge_name, interface_name):
"""
@ -196,8 +314,8 @@ class OvsNetClient(NetClientBase):
:param str interface_name: interface name
:return: nothing
"""
check_cmd([OVS_BIN, "add-port", bridge_name, interface_name])
check_cmd([IP_BIN, "link", "set", interface_name, "up"])
self.run([OVS_BIN, "add-port", bridge_name, interface_name])
self.device_up(interface_name)
def delete_interface(self, bridge_name, interface_name):
"""
@ -207,7 +325,7 @@ class OvsNetClient(NetClientBase):
:param str interface_name: interface name
:return: nothing
"""
check_cmd([OVS_BIN, "del-port", bridge_name, interface_name])
self.run([OVS_BIN, "del-port", bridge_name, interface_name])
def existing_bridges(self, _id):
"""
@ -215,7 +333,7 @@ class OvsNetClient(NetClientBase):
:param _id: node id to check bridges for
"""
output = check_cmd([OVS_BIN, "list-br"])
output = self.run([OVS_BIN, "list-br"])
if output:
for line in output.split("\n"):
fields = line.split(".")
@ -230,4 +348,4 @@ class OvsNetClient(NetClientBase):
:param str name: bridge name
:return: nothing
"""
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:mac-aging-time=0"])
self.run([OVS_BIN, "set", "bridge", name, "other_config:mac-aging-time=0"])

View file

@ -629,9 +629,7 @@ class CoreNetwork(CoreNetworkBase):
return
for addr in addrlist:
utils.check_cmd(
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname]
)
self.net_client.create_address(self.brname, str(addr))
class GreTapBridge(CoreNetwork):

View file

@ -100,51 +100,33 @@ class PhysicalNode(CoreNodeBase):
"""
Set hardware address for an interface.
"""
self._netif[ifindex].sethwaddr(addr)
ifname = self.ifname(ifindex)
interface = self._netif[ifindex]
interface.sethwaddr(addr)
if self.up:
self.check_cmd(
[constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)]
)
self.net_client.device_mac(interface.name, str(addr))
def addaddr(self, ifindex, addr):
"""
Add an address to an interface.
"""
interface = self._netif[ifindex]
if self.up:
self.check_cmd(
[
constants.IP_BIN,
"addr",
"add",
str(addr),
"dev",
self.ifname(ifindex),
]
)
self._netif[ifindex].addaddr(addr)
self.net_client.create_address(interface.name, str(addr))
interface.addaddr(addr)
def deladdr(self, ifindex, addr):
"""
Delete an address from an interface.
"""
interface = self._netif[ifindex]
try:
self._netif[ifindex].deladdr(addr)
interface.deladdr(addr)
except ValueError:
logging.exception("trying to delete unknown address: %s", addr)
if self.up:
self.check_cmd(
[
constants.IP_BIN,
"addr",
"del",
str(addr),
"dev",
self.ifname(ifindex),
]
)
self.net_client.delete_address(interface.name, str(addr))
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
"""
@ -159,12 +141,8 @@ class PhysicalNode(CoreNodeBase):
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
if self.up:
self.check_cmd(
[constants.IP_BIN, "link", "set", "dev", netif.localname, "down"]
)
self.check_cmd(
[constants.IP_BIN, "link", "set", netif.localname, "name", netif.name]
)
self.net_client.device_down(netif.localname)
self.net_client.device_name(netif.localname, netif.name)
netif.localname = netif.name
@ -175,9 +153,7 @@ class PhysicalNode(CoreNodeBase):
self.addaddr(ifindex, addr)
if self.up:
self.check_cmd(
[constants.IP_BIN, "link", "set", "dev", netif.localname, "up"]
)
self.net_client.device_up(netif.localname)
def linkconfig(
self,
@ -335,7 +311,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
"""
# interface will also be marked up during net.attach()
self.savestate()
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
self.net_client.device_up(self.localname)
self.up = True
def shutdown(self):
@ -349,18 +325,17 @@ class Rj45Node(CoreNodeBase, CoreInterface):
return
try:
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "down"])
utils.check_cmd([constants.IP_BIN, "addr", "flush", "dev", self.localname])
utils.check_cmd(
[constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"]
)
self.net_client.device_down(self.localname)
self.net_client.device_flush(self.localname)
self.net_client.delete_tc(self.localname)
except CoreCommandError:
logging.exception("error shutting down")
self.up = False
self.restorestate()
# TODO: issue in that both classes inherited from provide the same method with different signatures
# TODO: issue in that both classes inherited from provide the same method with
# different signatures
def attachnet(self, net):
"""
Attach a network.
@ -370,7 +345,8 @@ class Rj45Node(CoreNodeBase, CoreInterface):
"""
CoreInterface.attachnet(self, net)
# TODO: issue in that both classes inherited from provide the same method with different signatures
# TODO: issue in that both classes inherited from provide the same method with
# different signatures
def detachnet(self):
"""
Detach a network.
@ -476,9 +452,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
:raises CoreCommandError: when there is a command exception
"""
if self.up:
utils.check_cmd(
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
)
self.net_client.create_address(self.name, str(addr))
CoreInterface.addaddr(self, addr)
@ -491,9 +465,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
:raises CoreCommandError: when there is a command exception
"""
if self.up:
utils.check_cmd(
[constants.IP_BIN, "addr", "del", str(addr), "dev", self.name]
)
self.net_client.delete_address(self.name, str(addr))
CoreInterface.deladdr(self, addr)
@ -507,8 +479,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
"""
self.old_up = False
self.old_addrs = []
args = [constants.IP_BIN, "addr", "show", "dev", self.localname]
output = utils.check_cmd(args)
output = self.net_client.device_show(self.localname)
for line in output.split("\n"):
items = line.split()
if len(items) < 2:
@ -534,25 +505,14 @@ class Rj45Node(CoreNodeBase, CoreInterface):
"""
for addr in self.old_addrs:
if addr[1] is None:
utils.check_cmd(
[constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname]
)
self.net_client.create_address(self.localname, addr[0])
else:
utils.check_cmd(
[
constants.IP_BIN,
"addr",
"add",
addr[0],
"brd",
addr[1],
"dev",
self.localname,
]
self.net_client.create_address(
self.localname, addr[0], broadcast=addr[1]
)
if self.old_up:
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
self.net_client.device_up(self.localname)
def setposition(self, x=None, y=None, z=None):
"""

View file

@ -4,7 +4,6 @@ Unit tests for testing basic CORE networks.
import os
import stat
import subprocess
import threading
import pytest
@ -109,10 +108,6 @@ class TestCore:
p, stdin, stdout, stderr = client.popen(command)
assert not p.wait()
assert not client.icmd(command)
assert not client.redircmd(
subprocess.PIPE, subprocess.PIPE, subprocess.PIPE, command
)
assert not client.shcmd(command[0])
# check various command using command line
assert not client.cmd(command)
@ -121,15 +116,10 @@ class TestCore:
p, stdin, stdout, stderr = client.popen(command)
assert not p.wait()
assert not client.icmd(command)
assert not client.shcmd(command[0])
# check module methods
assert createclients(session.session_dir)
# check convenience methods for interface information
assert client.getaddr("eth0")
assert client.netifstats()
def test_netif(self, session, ip_prefixes):
"""
Test netif methods.