commit
839a1b9368
70 changed files with 1009 additions and 3331 deletions
|
@ -11,6 +11,7 @@ insert_final_newline = true
|
||||||
[*.py]
|
[*.py]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
max_line_length = 88
|
||||||
|
|
||||||
[*.am]
|
[*.am]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
|
@ -19,7 +19,7 @@ scripting network emulation.
|
||||||
* Documentation hosted on GitHub
|
* Documentation hosted on GitHub
|
||||||
* <http://coreemu.github.io/core/>
|
* <http://coreemu.github.io/core/>
|
||||||
* Basic Script Examples
|
* Basic Script Examples
|
||||||
* [Examples](daemon/examples/api)
|
* [Examples](daemon/examples/python)
|
||||||
* Custom Service Example
|
* Custom Service Example
|
||||||
* [sample.py](daemon/examples/myservices/sample.py)
|
* [sample.py](daemon/examples/myservices/sample.py)
|
||||||
* Custom Emane Model Example
|
* Custom Emane Model Example
|
||||||
|
|
38
configure.ac
38
configure.ac
|
@ -2,7 +2,7 @@
|
||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
# this defines the CORE version number, must be static for AC_INIT
|
# this defines the CORE version number, must be static for AC_INIT
|
||||||
AC_INIT(core, 5.4.0)
|
AC_INIT(core, 5.5.0)
|
||||||
|
|
||||||
# autoconf and automake initialization
|
# autoconf and automake initialization
|
||||||
AC_CONFIG_SRCDIR([netns/version.h.in])
|
AC_CONFIG_SRCDIR([netns/version.h.in])
|
||||||
|
@ -54,6 +54,13 @@ if test "x$enable_python" = "xyes" ; then
|
||||||
else
|
else
|
||||||
want_python=no
|
want_python=no
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([python3],
|
||||||
|
[AS_HELP_STRING([--enable-python3],
|
||||||
|
[sets python3 flag for building packages])],
|
||||||
|
[enable_python3=yes], [enable_python3=no])
|
||||||
|
AM_CONDITIONAL([PYTHON3], [test "x$enable_python3" == "xyes"])
|
||||||
|
|
||||||
AC_ARG_ENABLE([daemon],
|
AC_ARG_ENABLE([daemon],
|
||||||
[AS_HELP_STRING([--enable-daemon[=ARG]],
|
[AS_HELP_STRING([--enable-daemon[=ARG]],
|
||||||
[build and install the daemon with Python modules
|
[build and install the daemon with Python modules
|
||||||
|
@ -110,39 +117,61 @@ if test "x$enable_daemon" = "xyes"; then
|
||||||
AC_CHECK_FUNCS([atexit dup2 gettimeofday memset socket strerror uname])
|
AC_CHECK_FUNCS([atexit dup2 gettimeofday memset socket strerror uname])
|
||||||
|
|
||||||
AM_PATH_PYTHON(2.7)
|
AM_PATH_PYTHON(2.7)
|
||||||
AM_CONDITIONAL([PYTHON3], [test "x$PYTHON" == "xpython3"])
|
|
||||||
AS_IF([$PYTHON -m grpc_tools.protoc -h &> /dev/null], [], [AC_MSG_ERROR([please install python grpcio-tools])])
|
AS_IF([$PYTHON -m grpc_tools.protoc -h &> /dev/null], [], [AC_MSG_ERROR([please install python grpcio-tools])])
|
||||||
|
|
||||||
AC_CHECK_PROG(brctl_path, brctl, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(brctl_path, brctl, $as_dir, no, $SEARCHPATH)
|
||||||
if test "x$brctl_path" = "xno" ; then
|
if test "x$brctl_path" = "xno" ; then
|
||||||
AC_MSG_ERROR([Could not locate brctl (from bridge-utils package).])
|
AC_MSG_ERROR([Could not locate brctl (from bridge-utils package).])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(sysctl_path, sysctl, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(sysctl_path, sysctl, $as_dir, no, $SEARCHPATH)
|
||||||
|
if test "x$sysctl_path" = "xno" ; then
|
||||||
|
AC_MSG_ERROR([Could not locate sysctl (from procps package).])
|
||||||
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(ebtables_path, ebtables, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(ebtables_path, ebtables, $as_dir, no, $SEARCHPATH)
|
||||||
if test "x$ebtables_path" = "xno" ; then
|
if test "x$ebtables_path" = "xno" ; then
|
||||||
AC_MSG_ERROR([Could not locate ebtables (from ebtables package).])
|
AC_MSG_ERROR([Could not locate ebtables (from ebtables package).])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(ip_path, ip, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(ip_path, ip, $as_dir, no, $SEARCHPATH)
|
||||||
if test "x$ip_path" = "xno" ; then
|
if test "x$ip_path" = "xno" ; then
|
||||||
AC_MSG_ERROR([Could not locate ip (from iproute package).])
|
AC_MSG_ERROR([Could not locate ip (from iproute package).])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(tc_path, tc, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(tc_path, tc, $as_dir, no, $SEARCHPATH)
|
||||||
if test "x$tc_path" = "xno" ; then
|
if test "x$tc_path" = "xno" ; then
|
||||||
AC_MSG_ERROR([Could not locate tc (from iproute package).])
|
AC_MSG_ERROR([Could not locate tc (from iproute package).])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_CHECK_PROG(ethtool_path, ethtool, $as_dir, no, $SEARCHPATH)
|
||||||
|
if test "x$ethtool_path" = "xno" ; then
|
||||||
|
AC_MSG_ERROR([Could not locate ethtool (from package ethtool)])
|
||||||
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(mount_path, mount, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(mount_path, mount, $as_dir, no, $SEARCHPATH)
|
||||||
|
if test "x$mount_path" = "xno" ; then
|
||||||
|
AC_MSG_ERROR([Could not locate mount (from package mount)])
|
||||||
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(umount_path, umount, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(umount_path, umount, $as_dir, no, $SEARCHPATH)
|
||||||
|
if test "x$umount_path" = "xno" ; then
|
||||||
|
AC_MSG_ERROR([Could not locate umount (from package mount)])
|
||||||
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(convert, convert, yes, no, $SEARCHPATH)
|
AC_CHECK_PROG(convert, convert, yes, no, $SEARCHPATH)
|
||||||
if test "x$convert" = "xno" ; then
|
if test "x$convert" = "xno" ; then
|
||||||
AC_MSG_WARN([Could not locate ImageMagick convert.])
|
AC_MSG_WARN([Could not locate ImageMagick convert.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(ovs_vs_path, ovs-vsctl, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(ovs_vs_path, ovs-vsctl, $as_dir, no, $SEARCHPATH)
|
||||||
if test "x$ovs_vs_path" = "xno" ; then
|
if test "x$ovs_vs_path" = "xno" ; then
|
||||||
AC_MSG_WARN([Could not locate ovs-vsctl cannot use OVS nodes])
|
AC_MSG_WARN([Could not locate ovs-vsctl cannot use OVS mode])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CHECK_PROG(ovs_of_path, ovs-ofctl, $as_dir, no, $SEARCHPATH)
|
AC_CHECK_PROG(ovs_of_path, ovs-ofctl, $as_dir, no, $SEARCHPATH)
|
||||||
if test "x$ovs_of_path" = "xno" ; then
|
if test "x$ovs_of_path" = "xno" ; then
|
||||||
AC_MSG_WARN([Could not locate ovs-ofctl cannot use OVS nodes])
|
AC_MSG_WARN([Could not locate ovs-ofctl cannot use OVS mode])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CFLAGS_save=$CFLAGS
|
CFLAGS_save=$CFLAGS
|
||||||
|
@ -157,6 +186,7 @@ if test "x$enable_daemon" = "xyes"; then
|
||||||
CFLAGS=$CFLAGS_save
|
CFLAGS=$CFLAGS_save
|
||||||
CPPFLAGS=$CPPFLAGS_save
|
CPPFLAGS=$CPPFLAGS_save
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ test "x$enable_daemon" = "xyes" || test "x$enable_vnodedonly" = "xyes" ] ; then
|
if [ test "x$enable_daemon" = "xyes" || test "x$enable_vnodedonly" = "xyes" ] ; then
|
||||||
want_linux_netns=yes
|
want_linux_netns=yes
|
||||||
PKG_CHECK_MODULES(libev, libev,
|
PKG_CHECK_MODULES(libev, libev,
|
||||||
|
|
|
@ -14,11 +14,7 @@ if WANT_DOCS
|
||||||
DOCS = doc
|
DOCS = doc
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if PYTHON3
|
PYTHONLIBDIR=$(subst site-packages,dist-packages,$(pythondir))
|
||||||
PYTHONLIBDIR=$(libdir)/python3/dist-packages
|
|
||||||
else
|
|
||||||
PYTHONLIBDIR=$(pythondir)
|
|
||||||
endif
|
|
||||||
|
|
||||||
SUBDIRS = proto $(DOCS)
|
SUBDIRS = proto $(DOCS)
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@ url = "https://pypi.org/simple"
|
||||||
verify_ssl = true
|
verify_ssl = true
|
||||||
|
|
||||||
[scripts]
|
[scripts]
|
||||||
coredev = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
|
core = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
|
||||||
coretest = "python -m pytest -v tests"
|
test = "pytest -v tests"
|
||||||
|
test_emane = "pytest -v tests/emane"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
grpcio-tools = "*"
|
grpcio-tools = "*"
|
||||||
|
|
|
@ -1,41 +1,4 @@
|
||||||
import json
|
|
||||||
import logging.config
|
import logging.config
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from core import constants
|
|
||||||
|
|
||||||
# setup default null handler
|
# setup default null handler
|
||||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||||
|
|
||||||
|
|
||||||
def load_logging_config(config_path=None):
|
|
||||||
"""
|
|
||||||
Load CORE logging configuration file.
|
|
||||||
|
|
||||||
:param str config_path: path to logging config file,
|
|
||||||
when None defaults to /etc/core/logging.conf
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if not config_path:
|
|
||||||
config_path = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
|
|
||||||
with open(config_path, "r") as log_config_file:
|
|
||||||
log_config = json.load(log_config_file)
|
|
||||||
logging.config.dictConfig(log_config)
|
|
||||||
|
|
||||||
|
|
||||||
class CoreCommandError(subprocess.CalledProcessError):
|
|
||||||
"""
|
|
||||||
Used when encountering internal CORE command errors.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output)
|
|
||||||
|
|
||||||
|
|
||||||
class CoreError(Exception):
|
|
||||||
"""
|
|
||||||
Used for errors when dealing with CoreEmu and Sessions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ from queue import Empty, Queue
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core import CoreError
|
|
||||||
from core.api.grpc import core_pb2, core_pb2_grpc
|
from core.api.grpc import core_pb2, core_pb2_grpc
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.data import (
|
from core.emulator.data import (
|
||||||
ConfigData,
|
ConfigData,
|
||||||
EventData,
|
EventData,
|
||||||
|
@ -22,8 +22,8 @@ from core.emulator.data import (
|
||||||
)
|
)
|
||||||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||||
from core.emulator.enumerations import EventTypes, LinkTypes, NodeTypes
|
from core.emulator.enumerations import EventTypes, LinkTypes, NodeTypes
|
||||||
|
from core.errors import CoreError
|
||||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||||
from core.nodes import nodeutils
|
|
||||||
from core.nodes.base import CoreNetworkBase
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.docker import DockerNode
|
from core.nodes.docker import DockerNode
|
||||||
from core.nodes.ipaddress import MacAddress
|
from core.nodes.ipaddress import MacAddress
|
||||||
|
@ -444,7 +444,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
if not isinstance(node.id, int):
|
if not isinstance(node.id, int):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
node_type = nodeutils.get_node_type(node.__class__).value
|
node_type = session.get_node_type(node.__class__)
|
||||||
model = getattr(node, "type", None)
|
model = getattr(node, "type", None)
|
||||||
position = core_pb2.Position(
|
position = core_pb2.Position(
|
||||||
x=node.position.x, y=node.position.y, z=node.position.z
|
x=node.position.x, y=node.position.y, z=node.position.z
|
||||||
|
@ -456,7 +456,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
services = [x.name for x in services]
|
services = [x.name for x in services]
|
||||||
|
|
||||||
emane_model = None
|
emane_model = None
|
||||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
if isinstance(node, EmaneNet):
|
||||||
emane_model = node.model.name
|
emane_model = node.model.name
|
||||||
|
|
||||||
node_proto = core_pb2.Node(
|
node_proto = core_pb2.Node(
|
||||||
|
@ -464,7 +464,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
name=node.name,
|
name=node.name,
|
||||||
emane=emane_model,
|
emane=emane_model,
|
||||||
model=model,
|
model=model,
|
||||||
type=node_type,
|
type=node_type.value,
|
||||||
position=position,
|
position=position,
|
||||||
services=services,
|
services=services,
|
||||||
)
|
)
|
||||||
|
@ -809,18 +809,18 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
interfaces.append(interface_proto)
|
interfaces.append(interface_proto)
|
||||||
|
|
||||||
emane_model = None
|
emane_model = None
|
||||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
if isinstance(node, EmaneNet):
|
||||||
emane_model = node.model.name
|
emane_model = node.model.name
|
||||||
|
|
||||||
services = [x.name for x in getattr(node, "services", [])]
|
services = [x.name for x in getattr(node, "services", [])]
|
||||||
position = core_pb2.Position(
|
position = core_pb2.Position(
|
||||||
x=node.position.x, y=node.position.y, z=node.position.z
|
x=node.position.x, y=node.position.y, z=node.position.z
|
||||||
)
|
)
|
||||||
node_type = nodeutils.get_node_type(node.__class__).value
|
node_type = session.get_node_type(node.__class__)
|
||||||
node_proto = core_pb2.Node(
|
node_proto = core_pb2.Node(
|
||||||
id=node.id,
|
id=node.id,
|
||||||
name=node.name,
|
name=node.name,
|
||||||
type=node_type,
|
type=node_type.value,
|
||||||
emane=emane_model,
|
emane=emane_model,
|
||||||
model=node.type,
|
model=node.type,
|
||||||
position=position,
|
position=position,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import threading
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.api.tlv import coreapi
|
from core.api.tlv import coreapi
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.enumerations import (
|
from core.emulator.enumerations import (
|
||||||
ConfigDataTypes,
|
ConfigDataTypes,
|
||||||
ConfigFlags,
|
ConfigFlags,
|
||||||
|
@ -27,11 +28,10 @@ from core.emulator.enumerations import (
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
RegisterTlvs,
|
RegisterTlvs,
|
||||||
)
|
)
|
||||||
from core.nodes import nodeutils
|
|
||||||
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
||||||
from core.nodes.interface import GreTap
|
from core.nodes.interface import GreTap
|
||||||
from core.nodes.ipaddress import IpAddress
|
from core.nodes.ipaddress import IpAddress
|
||||||
from core.nodes.network import GreTapBridge
|
from core.nodes.network import CtrlNet, GreTapBridge
|
||||||
from core.nodes.physical import PhysicalNode
|
from core.nodes.physical import PhysicalNode
|
||||||
|
|
||||||
|
|
||||||
|
@ -318,13 +318,13 @@ class CoreBroker(object):
|
||||||
# leave this socket connected
|
# leave this socket connected
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info(
|
logging.debug(
|
||||||
"closing connection with %s @ %s:%s", name, server.host, server.port
|
"closing connection with %s @ %s:%s", name, server.host, server.port
|
||||||
)
|
)
|
||||||
server.close()
|
server.close()
|
||||||
del self.servers[name]
|
del self.servers[name]
|
||||||
|
|
||||||
logging.info("adding broker server(%s): %s:%s", name, host, port)
|
logging.debug("adding broker server(%s): %s:%s", name, host, port)
|
||||||
server = CoreDistributedServer(name, host, port)
|
server = CoreDistributedServer(name, host, port)
|
||||||
if host is not None and port is not None:
|
if host is not None and port is not None:
|
||||||
try:
|
try:
|
||||||
|
@ -492,33 +492,30 @@ class CoreBroker(object):
|
||||||
:raises core.CoreError: when node to add net tunnel to does not exist
|
:raises core.CoreError: when node to add net tunnel to does not exist
|
||||||
"""
|
"""
|
||||||
net = self.session.get_node(node_id)
|
net = self.session.get_node(node_id)
|
||||||
logging.info("adding net tunnel for: id(%s) %s", node_id, net)
|
logging.debug("adding net tunnel for: id(%s) %s", node_id, net.name)
|
||||||
|
|
||||||
# add other nets here that do not require tunnels
|
# add other nets here that do not require tunnels
|
||||||
if nodeutils.is_node(net, NodeTypes.EMANE_NET):
|
if isinstance(net, EmaneNet):
|
||||||
logging.warning("emane network does not require a tunnel")
|
logging.debug("emane network does not require a tunnel")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
server_interface = getattr(net, "serverintf", None)
|
server_interface = getattr(net, "serverintf", None)
|
||||||
if (
|
if isinstance(net, CtrlNet) and server_interface is not None:
|
||||||
nodeutils.is_node(net, NodeTypes.CONTROL_NET)
|
logging.debug(
|
||||||
and server_interface is not None
|
|
||||||
):
|
|
||||||
logging.warning(
|
|
||||||
"control networks with server interfaces do not need a tunnel"
|
"control networks with server interfaces do not need a tunnel"
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
servers = self.getserversbynode(node_id)
|
servers = self.getserversbynode(node_id)
|
||||||
if len(servers) < 2:
|
if len(servers) < 2:
|
||||||
logging.warning("not enough servers to create a tunnel: %s", servers)
|
logging.debug("not enough servers to create a tunnel for node: %s", node_id)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
hosts = []
|
hosts = []
|
||||||
for server in servers:
|
for server in servers:
|
||||||
if server.host is None:
|
if server.host is None:
|
||||||
continue
|
continue
|
||||||
logging.info("adding server host for net tunnel: %s", server.host)
|
logging.debug("adding server host for net tunnel: %s", server.host)
|
||||||
hosts.append(server.host)
|
hosts.append(server.host)
|
||||||
|
|
||||||
if len(hosts) == 0:
|
if len(hosts) == 0:
|
||||||
|
@ -526,7 +523,7 @@ class CoreBroker(object):
|
||||||
# get IP address from API message sender (master)
|
# get IP address from API message sender (master)
|
||||||
if session_client.client_address != "":
|
if session_client.client_address != "":
|
||||||
address = session_client.client_address[0]
|
address = session_client.client_address[0]
|
||||||
logging.info("adding session_client host: %s", address)
|
logging.debug("adding session_client host: %s", address)
|
||||||
hosts.append(address)
|
hosts.append(address)
|
||||||
|
|
||||||
r = []
|
r = []
|
||||||
|
@ -539,7 +536,7 @@ class CoreBroker(object):
|
||||||
myip = host
|
myip = host
|
||||||
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
|
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
|
||||||
if key in self.tunnels.keys():
|
if key in self.tunnels.keys():
|
||||||
logging.info(
|
logging.debug(
|
||||||
"tunnel already exists, returning existing tunnel: %s", key
|
"tunnel already exists, returning existing tunnel: %s", key
|
||||||
)
|
)
|
||||||
gt = self.tunnels[key]
|
gt = self.tunnels[key]
|
||||||
|
@ -658,9 +655,9 @@ class CoreBroker(object):
|
||||||
:param int nodenum: node id to add
|
:param int nodenum: node id to add
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.info("adding net to broker: %s", nodenum)
|
logging.debug("adding net to broker: %s", nodenum)
|
||||||
self.network_nodes.add(nodenum)
|
self.network_nodes.add(nodenum)
|
||||||
logging.info("broker network nodes: %s", self.network_nodes)
|
logging.debug("broker network nodes: %s", self.network_nodes)
|
||||||
|
|
||||||
def addphys(self, nodenum):
|
def addphys(self, nodenum):
|
||||||
"""
|
"""
|
||||||
|
@ -824,7 +821,8 @@ class CoreBroker(object):
|
||||||
nodetype = message.get_tlv(NodeTlvs.TYPE.value)
|
nodetype = message.get_tlv(NodeTlvs.TYPE.value)
|
||||||
if nodetype is not None:
|
if nodetype is not None:
|
||||||
try:
|
try:
|
||||||
nodecls = nodeutils.get_node_class(NodeTypes(nodetype))
|
nodetype = NodeTypes(nodetype)
|
||||||
|
nodecls = self.session.get_node_class(nodetype)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logging.warning("broker invalid node type %s", nodetype)
|
logging.warning("broker invalid node type %s", nodetype)
|
||||||
return handle_locally, servers
|
return handle_locally, servers
|
||||||
|
|
|
@ -14,7 +14,7 @@ from builtins import range
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from queue import Empty, Queue
|
from queue import Empty, Queue
|
||||||
|
|
||||||
from core import CoreError, utils
|
from core import utils
|
||||||
from core.api.tlv import coreapi, dataconversion, structutils
|
from core.api.tlv import coreapi, dataconversion, structutils
|
||||||
from core.config import ConfigShim
|
from core.config import ConfigShim
|
||||||
from core.emulator.data import ConfigData, EventData, ExceptionData, FileData
|
from core.emulator.data import ConfigData, EventData, ExceptionData, FileData
|
||||||
|
@ -37,8 +37,9 @@ from core.emulator.enumerations import (
|
||||||
RegisterTlvs,
|
RegisterTlvs,
|
||||||
SessionTlvs,
|
SessionTlvs,
|
||||||
)
|
)
|
||||||
|
from core.errors import CoreError
|
||||||
from core.location.mobility import BasicRangeModel
|
from core.location.mobility import BasicRangeModel
|
||||||
from core.nodes import nodeutils
|
from core.nodes.network import WlanNode
|
||||||
from core.services.coreservices import ServiceManager, ServiceShim
|
from core.services.coreservices import ServiceManager, ServiceShim
|
||||||
|
|
||||||
|
|
||||||
|
@ -1247,10 +1248,10 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
||||||
values = []
|
values = []
|
||||||
group_strings = []
|
group_strings = []
|
||||||
start_index = 1
|
start_index = 1
|
||||||
logging.info("sorted groups: %s", groups)
|
logging.debug("sorted groups: %s", groups)
|
||||||
for group in groups:
|
for group in groups:
|
||||||
services = sorted(group_map[group], key=lambda x: x.name.lower())
|
services = sorted(group_map[group], key=lambda x: x.name.lower())
|
||||||
logging.info("sorted services for group(%s): %s", group, services)
|
logging.debug("sorted services for group(%s): %s", group, services)
|
||||||
end_index = start_index + len(services) - 1
|
end_index = start_index + len(services) - 1
|
||||||
group_strings.append("%s:%s-%s" % (group, start_index, end_index))
|
group_strings.append("%s:%s-%s" % (group, start_index, end_index))
|
||||||
start_index += len(services)
|
start_index += len(services)
|
||||||
|
@ -1603,8 +1604,8 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
||||||
node = self.session.get_node(node_id)
|
node = self.session.get_node(node_id)
|
||||||
|
|
||||||
# configure mobility models for WLAN added during runtime
|
# configure mobility models for WLAN added during runtime
|
||||||
if event_type == EventTypes.INSTANTIATION_STATE and nodeutils.is_node(
|
if event_type == EventTypes.INSTANTIATION_STATE and isinstance(
|
||||||
node, NodeTypes.WIRELESS_LAN
|
node, WlanNode
|
||||||
):
|
):
|
||||||
self.session.start_mobility(node_ids=(node.id,))
|
self.session.start_mobility(node_ids=(node.id,))
|
||||||
return ()
|
return ()
|
||||||
|
|
|
@ -1,29 +1,20 @@
|
||||||
import os
|
from core.utils import which
|
||||||
|
|
||||||
COREDPY_VERSION = "@PACKAGE_VERSION@"
|
COREDPY_VERSION = "@PACKAGE_VERSION@"
|
||||||
CORE_STATE_DIR = "@CORE_STATE_DIR@"
|
|
||||||
CORE_CONF_DIR = "@CORE_CONF_DIR@"
|
CORE_CONF_DIR = "@CORE_CONF_DIR@"
|
||||||
CORE_DATA_DIR = "@CORE_DATA_DIR@"
|
CORE_DATA_DIR = "@CORE_DATA_DIR@"
|
||||||
QUAGGA_STATE_DIR = "@CORE_STATE_DIR@/run/quagga"
|
QUAGGA_STATE_DIR = "@CORE_STATE_DIR@/run/quagga"
|
||||||
FRR_STATE_DIR = "@CORE_STATE_DIR@/run/frr"
|
FRR_STATE_DIR = "@CORE_STATE_DIR@/run/frr"
|
||||||
|
|
||||||
|
VNODED_BIN = which("vnoded", required=True)
|
||||||
def which(command):
|
VCMD_BIN = which("vcmd", required=True)
|
||||||
for path in os.environ["PATH"].split(os.pathsep):
|
BRCTL_BIN = which("brctl", required=True)
|
||||||
command_path = os.path.join(path, command)
|
SYSCTL_BIN = which("sysctl", required=True)
|
||||||
if os.path.isfile(command_path) and os.access(command_path, os.X_OK):
|
IP_BIN = which("ip", required=True)
|
||||||
return command_path
|
ETHTOOL_BIN = which("ethtool", required=True)
|
||||||
|
TC_BIN = which("tc", required=True)
|
||||||
|
EBTABLES_BIN = which("ebtables", required=True)
|
||||||
VNODED_BIN = which("vnoded")
|
MOUNT_BIN = which("mount", required=True)
|
||||||
VCMD_BIN = which("vcmd")
|
UMOUNT_BIN = which("umount", required=True)
|
||||||
BRCTL_BIN = which("brctl")
|
OVS_BIN = which("ovs-vsctl", required=False)
|
||||||
SYSCTL_BIN = which("sysctl")
|
OVS_FLOW_BIN = which("ovs-ofctl", required=False)
|
||||||
IP_BIN = which("ip")
|
|
||||||
ETHTOOL_BIN = which("ethtool")
|
|
||||||
TC_BIN = which("tc")
|
|
||||||
EBTABLES_BIN = which("ebtables")
|
|
||||||
MOUNT_BIN = which("mount")
|
|
||||||
UMOUNT_BIN = which("umount")
|
|
||||||
OVS_BIN = which("ovs-vsctl")
|
|
||||||
OVS_FLOW_BIN = which("ovs-ofctl")
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from core import CoreCommandError, CoreError, constants, utils
|
from core import utils
|
||||||
from core.api.tlv import coreapi, dataconversion
|
from core.api.tlv import coreapi, dataconversion
|
||||||
from core.config import ConfigGroup, ConfigShim, Configuration, ModelManager
|
from core.config import ConfigGroup, ConfigShim, Configuration, ModelManager
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
|
@ -15,6 +15,7 @@ from core.emane.bypass import EmaneBypassModel
|
||||||
from core.emane.commeffect import EmaneCommEffectModel
|
from core.emane.commeffect import EmaneCommEffectModel
|
||||||
from core.emane.emanemodel import EmaneModel
|
from core.emane.emanemodel import EmaneModel
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emane.rfpipe import EmaneRfPipeModel
|
from core.emane.rfpipe import EmaneRfPipeModel
|
||||||
from core.emane.tdma import EmaneTdmaModel
|
from core.emane.tdma import EmaneTdmaModel
|
||||||
from core.emulator.enumerations import (
|
from core.emulator.enumerations import (
|
||||||
|
@ -23,10 +24,9 @@ from core.emulator.enumerations import (
|
||||||
ConfigTlvs,
|
ConfigTlvs,
|
||||||
MessageFlags,
|
MessageFlags,
|
||||||
MessageTypes,
|
MessageTypes,
|
||||||
NodeTypes,
|
|
||||||
RegisterTlvs,
|
RegisterTlvs,
|
||||||
)
|
)
|
||||||
from core.nodes import nodeutils
|
from core.errors import CoreCommandError, CoreError
|
||||||
from core.xml import emanexml
|
from core.xml import emanexml
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -54,8 +54,8 @@ DEFAULT_EMANE_PREFIX = "/usr"
|
||||||
class EmaneManager(ModelManager):
|
class EmaneManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
EMANE controller object. Lives in a Session instance and is used for
|
EMANE controller object. Lives in a Session instance and is used for
|
||||||
building EMANE config files from all of the EmaneNode objects in this
|
building EMANE config files for all EMANE networks in this emulation, and for
|
||||||
emulation, and for controlling the EMANE daemons.
|
controlling the EMANE daemons.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "emane"
|
name = "emane"
|
||||||
|
@ -73,7 +73,7 @@ class EmaneManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
super(EmaneManager, self).__init__()
|
super(EmaneManager, self).__init__()
|
||||||
self.session = session
|
self.session = session
|
||||||
self._emane_nodes = {}
|
self._emane_nets = {}
|
||||||
self._emane_node_lock = threading.Lock()
|
self._emane_node_lock = threading.Lock()
|
||||||
self._ifccounts = {}
|
self._ifccounts = {}
|
||||||
self._ifccountslock = threading.Lock()
|
self._ifccountslock = threading.Lock()
|
||||||
|
@ -227,38 +227,39 @@ class EmaneManager(ModelManager):
|
||||||
emane_model.load(emane_prefix)
|
emane_model.load(emane_prefix)
|
||||||
self.models[emane_model.name] = emane_model
|
self.models[emane_model.name] = emane_model
|
||||||
|
|
||||||
def add_node(self, emane_node):
|
def add_node(self, emane_net):
|
||||||
"""
|
"""
|
||||||
Add a new EmaneNode object to this Emane controller object
|
Add EMANE network object to this manager.
|
||||||
|
|
||||||
:param core.emane.nodes.EmaneNode emane_node: emane node to add
|
:param core.emane.nodes.EmaneNet emane_net: emane node to add
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
if emane_node.id in self._emane_nodes:
|
if emane_net.id in self._emane_nets:
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
"non-unique EMANE object id %s for %s" % (emane_node.id, emane_node)
|
"non-unique EMANE object id %s for %s" % (emane_net.id, emane_net)
|
||||||
)
|
)
|
||||||
self._emane_nodes[emane_node.id] = emane_node
|
self._emane_nets[emane_net.id] = emane_net
|
||||||
|
|
||||||
def getnodes(self):
|
def getnodes(self):
|
||||||
"""
|
"""
|
||||||
Return a set of CoreNodes that are linked to an EmaneNode,
|
Return a set of CoreNodes that are linked to an EMANE network,
|
||||||
e.g. containers having one or more radio interfaces.
|
e.g. containers having one or more radio interfaces.
|
||||||
"""
|
"""
|
||||||
# assumes self._objslock already held
|
# assumes self._objslock already held
|
||||||
nodes = set()
|
nodes = set()
|
||||||
for emane_node in self._emane_nodes.values():
|
for emane_net in self._emane_nets.values():
|
||||||
for netif in emane_node.netifs():
|
for netif in emane_net.netifs():
|
||||||
nodes.add(netif.node)
|
nodes.add(netif.node)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
"""
|
"""
|
||||||
Populate self._objs with EmaneNodes; perform distributed setup;
|
Setup duties for EMANE manager.
|
||||||
associate models with EmaneNodes from self.config. Returns
|
|
||||||
Emane.(SUCCESS, NOT_NEEDED, NOT_READY) in order to delay session
|
:return: SUCCESS, NOT_NEEDED, NOT_READY in order to delay session
|
||||||
instantiation.
|
instantiation
|
||||||
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
logging.debug("emane setup")
|
logging.debug("emane setup")
|
||||||
|
|
||||||
|
@ -266,13 +267,13 @@ class EmaneManager(ModelManager):
|
||||||
with self.session._nodes_lock:
|
with self.session._nodes_lock:
|
||||||
for node_id in self.session.nodes:
|
for node_id in self.session.nodes:
|
||||||
node = self.session.nodes[node_id]
|
node = self.session.nodes[node_id]
|
||||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
if isinstance(node, EmaneNet):
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"adding emane node: id(%s) name(%s)", node.id, node.name
|
"adding emane node: id(%s) name(%s)", node.id, node.name
|
||||||
)
|
)
|
||||||
self.add_node(node)
|
self.add_node(node)
|
||||||
|
|
||||||
if not self._emane_nodes:
|
if not self._emane_nets:
|
||||||
logging.debug("no emane nodes in session")
|
logging.debug("no emane nodes in session")
|
||||||
return EmaneManager.NOT_NEEDED
|
return EmaneManager.NOT_NEEDED
|
||||||
|
|
||||||
|
@ -326,9 +327,12 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
"""
|
"""
|
||||||
After all the EmaneNode objects have been added, build XML files
|
After all the EMANE networks have been added, build XML files
|
||||||
and start the daemons. Returns Emane.(SUCCESS, NOT_NEEDED, or
|
and start the daemons.
|
||||||
NOT_READY) which is used to delay session instantiation.
|
|
||||||
|
:return: SUCCESS, NOT_NEEDED, NOT_READY in order to delay session
|
||||||
|
instantiation
|
||||||
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
self.reset()
|
self.reset()
|
||||||
r = self.setup()
|
r = self.setup()
|
||||||
|
@ -347,8 +351,8 @@ class EmaneManager(ModelManager):
|
||||||
self.startdaemons()
|
self.startdaemons()
|
||||||
self.installnetifs()
|
self.installnetifs()
|
||||||
|
|
||||||
for node_id in self._emane_nodes:
|
for node_id in self._emane_nets:
|
||||||
emane_node = self._emane_nodes[node_id]
|
emane_node = self._emane_nets[node_id]
|
||||||
for netif in emane_node.netifs():
|
for netif in emane_node.netifs():
|
||||||
nems.append(
|
nems.append(
|
||||||
(netif.node.name, netif.name, emane_node.getnemid(netif))
|
(netif.node.name, netif.name, emane_node.getnemid(netif))
|
||||||
|
@ -373,8 +377,8 @@ class EmaneManager(ModelManager):
|
||||||
return
|
return
|
||||||
|
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nets.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nets[key]
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"post startup for emane node: %s - %s",
|
"post startup for emane node: %s - %s",
|
||||||
emane_node.id,
|
emane_node.id,
|
||||||
|
@ -387,11 +391,11 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
remove all EmaneNode objects from the dictionary,
|
Remove all EMANE networks from the dictionary, reset port numbers and
|
||||||
reset port numbers and nem id counters
|
nem id counters
|
||||||
"""
|
"""
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
self._emane_nodes.clear()
|
self._emane_nets.clear()
|
||||||
|
|
||||||
# don't clear self._ifccounts here; NEM counts are needed for buildxml
|
# don't clear self._ifccounts here; NEM counts are needed for buildxml
|
||||||
self.platformport = self.session.options.get_config_int(
|
self.platformport = self.session.options.get_config_int(
|
||||||
|
@ -409,7 +413,7 @@ class EmaneManager(ModelManager):
|
||||||
self._ifccounts.clear()
|
self._ifccounts.clear()
|
||||||
|
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
if not self._emane_nodes:
|
if not self._emane_nets:
|
||||||
return
|
return
|
||||||
logging.info("stopping EMANE daemons.")
|
logging.info("stopping EMANE daemons.")
|
||||||
self.deinstallnetifs()
|
self.deinstallnetifs()
|
||||||
|
@ -449,7 +453,7 @@ class EmaneManager(ModelManager):
|
||||||
master = False
|
master = False
|
||||||
|
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
if self._emane_nodes:
|
if self._emane_nets:
|
||||||
master = self.session.master
|
master = self.session.master
|
||||||
logging.info("emane check distributed as master: %s.", master)
|
logging.info("emane check distributed as master: %s.", master)
|
||||||
|
|
||||||
|
@ -459,8 +463,8 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
nemcount = 0
|
nemcount = 0
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
for key in self._emane_nodes:
|
for key in self._emane_nets:
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nets[key]
|
||||||
nemcount += emane_node.numnetif()
|
nemcount += emane_node.numnetif()
|
||||||
|
|
||||||
nemid = int(self.get_config("nem_id_start"))
|
nemid = int(self.get_config("nem_id_start"))
|
||||||
|
@ -470,7 +474,7 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
# build an ordered list of servers so platform ID is deterministic
|
# build an ordered list of servers so platform ID is deterministic
|
||||||
servers = []
|
servers = []
|
||||||
for key in sorted(self._emane_nodes):
|
for key in sorted(self._emane_nets):
|
||||||
for server in self.session.broker.getserversbynode(key):
|
for server in self.session.broker.getserversbynode(key):
|
||||||
if server not in servers:
|
if server not in servers:
|
||||||
servers.append(server)
|
servers.append(server)
|
||||||
|
@ -566,11 +570,10 @@ class EmaneManager(ModelManager):
|
||||||
|
|
||||||
def check_node_models(self):
|
def check_node_models(self):
|
||||||
"""
|
"""
|
||||||
Associate EmaneModel classes with EmaneNode nodes. The model
|
Associate EMANE model classes with EMANE network nodes.
|
||||||
configurations are stored in self.configs.
|
|
||||||
"""
|
"""
|
||||||
for node_id in self._emane_nodes:
|
for node_id in self._emane_nets:
|
||||||
emane_node = self._emane_nodes[node_id]
|
emane_node = self._emane_nets[node_id]
|
||||||
logging.debug("checking emane model for node: %s", node_id)
|
logging.debug("checking emane model for node: %s", node_id)
|
||||||
|
|
||||||
# skip nodes that already have a model set
|
# skip nodes that already have a model set
|
||||||
|
@ -596,13 +599,13 @@ class EmaneManager(ModelManager):
|
||||||
def nemlookup(self, nemid):
|
def nemlookup(self, nemid):
|
||||||
"""
|
"""
|
||||||
Look for the given numerical NEM ID and return the first matching
|
Look for the given numerical NEM ID and return the first matching
|
||||||
EmaneNode and NEM interface.
|
EMANE network and NEM interface.
|
||||||
"""
|
"""
|
||||||
emane_node = None
|
emane_node = None
|
||||||
netif = None
|
netif = None
|
||||||
|
|
||||||
for node_id in self._emane_nodes:
|
for node_id in self._emane_nets:
|
||||||
emane_node = self._emane_nodes[node_id]
|
emane_node = self._emane_nets[node_id]
|
||||||
netif = emane_node.getnemnetif(nemid)
|
netif = emane_node.getnemnetif(nemid)
|
||||||
if netif is not None:
|
if netif is not None:
|
||||||
break
|
break
|
||||||
|
@ -616,8 +619,8 @@ class EmaneManager(ModelManager):
|
||||||
Return the number of NEMs emulated locally.
|
Return the number of NEMs emulated locally.
|
||||||
"""
|
"""
|
||||||
count = 0
|
count = 0
|
||||||
for node_id in self._emane_nodes:
|
for node_id in self._emane_nets:
|
||||||
emane_node = self._emane_nodes[node_id]
|
emane_node = self._emane_nets[node_id]
|
||||||
count += len(emane_node.netifs())
|
count += len(emane_node.netifs())
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
@ -629,20 +632,19 @@ class EmaneManager(ModelManager):
|
||||||
platform_xmls = {}
|
platform_xmls = {}
|
||||||
|
|
||||||
# assume self._objslock is already held here
|
# assume self._objslock is already held here
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nets.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nets[key]
|
||||||
nemid = emanexml.build_node_platform_xml(
|
nemid = emanexml.build_node_platform_xml(
|
||||||
self, ctrlnet, emane_node, nemid, platform_xmls
|
self, ctrlnet, emane_node, nemid, platform_xmls
|
||||||
)
|
)
|
||||||
|
|
||||||
def buildnemxml(self):
|
def buildnemxml(self):
|
||||||
"""
|
"""
|
||||||
Builds the xxxnem.xml, xxxmac.xml, and xxxphy.xml files which
|
Builds the nem, mac, and phy xml files for each EMANE network.
|
||||||
are defined on a per-EmaneNode basis.
|
|
||||||
"""
|
"""
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nets):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_net = self._emane_nets[key]
|
||||||
emanexml.build_xml_files(self, emane_node)
|
emanexml.build_xml_files(self, emane_net)
|
||||||
|
|
||||||
def buildtransportxml(self):
|
def buildtransportxml(self):
|
||||||
"""
|
"""
|
||||||
|
@ -731,13 +733,11 @@ class EmaneManager(ModelManager):
|
||||||
)
|
)
|
||||||
|
|
||||||
# multicast route is needed for OTA data
|
# multicast route is needed for OTA data
|
||||||
args = [constants.IP_BIN, "route", "add", otagroup, "dev", otadev]
|
node.node_net_client.create_route(otagroup, otadev)
|
||||||
node.network_cmd(args)
|
|
||||||
|
|
||||||
# multicast route is also needed for event data if on control network
|
# multicast route is also needed for event data if on control network
|
||||||
if eventservicenetidx >= 0 and eventgroup != otagroup:
|
if eventservicenetidx >= 0 and eventgroup != otagroup:
|
||||||
args = [constants.IP_BIN, "route", "add", eventgroup, "dev", eventdev]
|
node.node_net_client.create_route(eventgroup, eventdev)
|
||||||
node.network_cmd(args)
|
|
||||||
|
|
||||||
# start emane
|
# start emane
|
||||||
args = emanecmd + [
|
args = emanecmd + [
|
||||||
|
@ -786,8 +786,8 @@ class EmaneManager(ModelManager):
|
||||||
Install TUN/TAP virtual interfaces into their proper namespaces
|
Install TUN/TAP virtual interfaces into their proper namespaces
|
||||||
now that the EMANE daemons are running.
|
now that the EMANE daemons are running.
|
||||||
"""
|
"""
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nets.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nets[key]
|
||||||
logging.info("emane install netifs for node: %d", key)
|
logging.info("emane install netifs for node: %d", key)
|
||||||
emane_node.installnetifs()
|
emane_node.installnetifs()
|
||||||
|
|
||||||
|
@ -795,8 +795,8 @@ class EmaneManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
Uninstall TUN/TAP virtual interfaces.
|
Uninstall TUN/TAP virtual interfaces.
|
||||||
"""
|
"""
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nets.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nets[key]
|
||||||
emane_node.deinstallnetifs()
|
emane_node.deinstallnetifs()
|
||||||
|
|
||||||
def doeventmonitor(self):
|
def doeventmonitor(self):
|
||||||
|
|
|
@ -4,10 +4,10 @@ Defines Emane Models used within CORE.
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from core import CoreError
|
|
||||||
from core.config import ConfigGroup, Configuration
|
from core.config import ConfigGroup, Configuration
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emulator.enumerations import ConfigDataTypes
|
from core.emulator.enumerations import ConfigDataTypes
|
||||||
|
from core.errors import CoreError
|
||||||
from core.location.mobility import WirelessModel
|
from core.location.mobility import WirelessModel
|
||||||
from core.xml import emanexml
|
from core.xml import emanexml
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""
|
"""
|
||||||
nodes.py: definition of an EmaneNode class for implementing configuration
|
Provides an EMANE network node class, which has several attached NEMs that
|
||||||
control of an EMANE emulation. An EmaneNode has several attached NEMs that
|
|
||||||
share the same MAC+PHY model.
|
share the same MAC+PHY model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -19,25 +18,19 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class EmaneNet(CoreNetworkBase):
|
class EmaneNet(CoreNetworkBase):
|
||||||
"""
|
|
||||||
EMANE network base class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
apitype = NodeTypes.EMANE.value
|
|
||||||
linktype = LinkTypes.WIRELESS.value
|
|
||||||
# icon used
|
|
||||||
type = "wlan"
|
|
||||||
|
|
||||||
|
|
||||||
class EmaneNode(EmaneNet):
|
|
||||||
"""
|
"""
|
||||||
EMANE node contains NEM configuration and causes connected nodes
|
EMANE node contains NEM configuration and causes connected nodes
|
||||||
to have TAP interfaces (instead of VEth). These are managed by the
|
to have TAP interfaces (instead of VEth). These are managed by the
|
||||||
Emane controller object that exists in a session.
|
Emane controller object that exists in a session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
apitype = NodeTypes.EMANE.value
|
||||||
|
linktype = LinkTypes.WIRELESS.value
|
||||||
|
type = "wlan"
|
||||||
|
is_emane = True
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, start=True):
|
def __init__(self, session, _id=None, name=None, start=True):
|
||||||
super(EmaneNode, self).__init__(session, _id, name, start)
|
super(EmaneNet, self).__init__(session, _id, name, start)
|
||||||
self.conf = ""
|
self.conf = ""
|
||||||
self.up = False
|
self.up = False
|
||||||
self.nemidmap = {}
|
self.nemidmap = {}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import sys
|
||||||
import core.services
|
import core.services
|
||||||
from core.emulator.emudata import IdGen
|
from core.emulator.emudata import IdGen
|
||||||
from core.emulator.session import Session
|
from core.emulator.session import Session
|
||||||
from core.nodes import nodemaps, nodeutils
|
|
||||||
from core.services.coreservices import ServiceManager
|
from core.services.coreservices import ServiceManager
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@ class CoreEmu(object):
|
||||||
os.umask(0)
|
os.umask(0)
|
||||||
|
|
||||||
# configuration
|
# configuration
|
||||||
if not config:
|
if config is None:
|
||||||
config = {}
|
config = {}
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
|
@ -53,10 +52,6 @@ class CoreEmu(object):
|
||||||
self.session_id_gen = IdGen(_id=0)
|
self.session_id_gen = IdGen(_id=0)
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
|
|
||||||
# set default nodes
|
|
||||||
node_map = nodemaps.NODES
|
|
||||||
nodeutils.set_node_map(node_map)
|
|
||||||
|
|
||||||
# load services
|
# load services
|
||||||
self.service_errors = []
|
self.service_errors = []
|
||||||
self.load_services()
|
self.load_services()
|
||||||
|
@ -77,15 +72,6 @@ class CoreEmu(object):
|
||||||
custom_service_errors = ServiceManager.add_services(service_path)
|
custom_service_errors = ServiceManager.add_services(service_path)
|
||||||
self.service_errors.extend(custom_service_errors)
|
self.service_errors.extend(custom_service_errors)
|
||||||
|
|
||||||
def update_nodes(self, node_map):
|
|
||||||
"""
|
|
||||||
Updates node map used by core.
|
|
||||||
|
|
||||||
:param dict node_map: node map to update existing node map with
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
nodeutils.update_node_map(node_map)
|
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""
|
"""
|
||||||
Shutdown all CORE session.
|
Shutdown all CORE session.
|
||||||
|
@ -99,12 +85,13 @@ class CoreEmu(object):
|
||||||
session = sessions[_id]
|
session = sessions[_id]
|
||||||
session.shutdown()
|
session.shutdown()
|
||||||
|
|
||||||
def create_session(self, _id=None, master=True):
|
def create_session(self, _id=None, master=True, _cls=Session):
|
||||||
"""
|
"""
|
||||||
Create a new CORE session, set to master if running standalone.
|
Create a new CORE session, set to master if running standalone.
|
||||||
|
|
||||||
:param int _id: session id for new session
|
:param int _id: session id for new session
|
||||||
:param bool master: sets session to master
|
:param bool master: sets session to master
|
||||||
|
:param class _cls: Session class to use
|
||||||
:return: created session
|
:return: created session
|
||||||
:rtype: EmuSession
|
:rtype: EmuSession
|
||||||
"""
|
"""
|
||||||
|
@ -114,7 +101,7 @@ class CoreEmu(object):
|
||||||
if _id not in self.sessions:
|
if _id not in self.sessions:
|
||||||
break
|
break
|
||||||
|
|
||||||
session = Session(_id, config=self.config)
|
session = _cls(_id, config=self.config)
|
||||||
logging.info("created session: %s", _id)
|
logging.info("created session: %s", _id)
|
||||||
if master:
|
if master:
|
||||||
session.master = True
|
session.master = True
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes import nodeutils
|
from core.emulator.enumerations import LinkTypes
|
||||||
from core.nodes.base import CoreNetworkBase
|
|
||||||
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
||||||
|
from core.nodes.physical import PhysicalNode
|
||||||
|
|
||||||
|
|
||||||
class IdGen(object):
|
class IdGen(object):
|
||||||
|
@ -13,17 +13,6 @@ class IdGen(object):
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
|
|
||||||
def is_net_node(node):
|
|
||||||
"""
|
|
||||||
Convenience method for testing if a legacy core node is considered a network node.
|
|
||||||
|
|
||||||
:param object node: object to test against
|
|
||||||
:return: True if object is an instance of a network node, False otherwise
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
return isinstance(node, CoreNetworkBase)
|
|
||||||
|
|
||||||
|
|
||||||
def create_interface(node, network, interface_data):
|
def create_interface(node, network, interface_data):
|
||||||
"""
|
"""
|
||||||
Create an interface for a node on a network using provided interface data.
|
Create an interface for a node on a network using provided interface data.
|
||||||
|
@ -64,8 +53,9 @@ def link_config(network, interface, link_options, devname=None, interface_two=No
|
||||||
"netif2": interface_two,
|
"netif2": interface_two,
|
||||||
}
|
}
|
||||||
|
|
||||||
# hacky check here, because physical and emane nodes do not conform to the same linkconfig interface
|
# hacky check here, because physical and emane nodes do not conform to the same
|
||||||
if not nodeutils.is_node(network, [NodeTypes.EMANE, NodeTypes.PHYSICAL]):
|
# linkconfig interface
|
||||||
|
if not isinstance(network, (EmaneNet, PhysicalNode)):
|
||||||
config["devname"] = devname
|
config["devname"] = devname
|
||||||
|
|
||||||
network.linkconfig(**config)
|
network.linkconfig(**config)
|
||||||
|
@ -81,7 +71,8 @@ class NodeOptions(object):
|
||||||
Create a NodeOptions object.
|
Create a NodeOptions object.
|
||||||
|
|
||||||
:param str name: name of node, defaults to node class name postfix with its id
|
:param str name: name of node, defaults to node class name postfix with its id
|
||||||
:param str model: defines services for default and physical nodes, defaults to "router"
|
:param str model: defines services for default and physical nodes, defaults to
|
||||||
|
"router"
|
||||||
:param str image: image to use for docker nodes
|
:param str image: image to use for docker nodes
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -133,7 +124,8 @@ class LinkOptions(object):
|
||||||
"""
|
"""
|
||||||
Create a LinkOptions object.
|
Create a LinkOptions object.
|
||||||
|
|
||||||
:param core.emulator.enumerations.LinkTypes _type: type of link, defaults to wired
|
:param core.emulator.enumerations.LinkTypes _type: type of link, defaults to
|
||||||
|
wired
|
||||||
"""
|
"""
|
||||||
self.type = _type
|
self.type = _type
|
||||||
self.session = None
|
self.session = None
|
||||||
|
@ -202,12 +194,13 @@ class IpPrefixes(object):
|
||||||
|
|
||||||
def create_interface(self, node, name=None, mac=None):
|
def create_interface(self, node, name=None, mac=None):
|
||||||
"""
|
"""
|
||||||
Creates interface data for linking nodes, using the nodes unique id for generation, along with a random
|
Creates interface data for linking nodes, using the nodes unique id for
|
||||||
mac address, unless provided.
|
generation, along with a random mac address, unless provided.
|
||||||
|
|
||||||
:param core.nodes.base.CoreNode node: node to create interface for
|
:param core.nodes.base.CoreNode node: node to create interface for
|
||||||
:param str name: name to set for interface, default is eth{id}
|
:param str name: name to set for interface, default is eth{id}
|
||||||
:param str mac: mac address to use for this interface, default is random generation
|
:param str mac: mac address to use for this interface, default is random
|
||||||
|
generation
|
||||||
:return: new interface data for the provided node
|
:return: new interface data for the provided node
|
||||||
:rtype: InterfaceData
|
:rtype: InterfaceData
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -14,33 +14,65 @@ import threading
|
||||||
import time
|
import time
|
||||||
from multiprocessing.pool import ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
|
|
||||||
import core.nodes.base
|
from core import constants, utils
|
||||||
from core import CoreError, constants, utils
|
|
||||||
from core.api.tlv import coreapi
|
from core.api.tlv import coreapi
|
||||||
from core.api.tlv.broker import CoreBroker
|
from core.api.tlv.broker import CoreBroker
|
||||||
from core.emane.emanemanager import EmaneManager
|
from core.emane.emanemanager import EmaneManager
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.data import EventData, ExceptionData, NodeData
|
from core.emulator.data import EventData, ExceptionData, NodeData
|
||||||
from core.emulator.emudata import (
|
from core.emulator.emudata import (
|
||||||
IdGen,
|
IdGen,
|
||||||
LinkOptions,
|
LinkOptions,
|
||||||
NodeOptions,
|
NodeOptions,
|
||||||
create_interface,
|
create_interface,
|
||||||
is_net_node,
|
|
||||||
link_config,
|
link_config,
|
||||||
)
|
)
|
||||||
from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes
|
from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes
|
||||||
from core.emulator.sessionconfig import SessionConfig, SessionMetaData
|
from core.emulator.sessionconfig import SessionConfig, SessionMetaData
|
||||||
|
from core.errors import CoreError
|
||||||
from core.location.corelocation import CoreLocation
|
from core.location.corelocation import CoreLocation
|
||||||
from core.location.event import EventLoop
|
from core.location.event import EventLoop
|
||||||
from core.location.mobility import MobilityManager
|
from core.location.mobility import MobilityManager
|
||||||
from core.nodes import nodeutils
|
from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.docker import DockerNode
|
||||||
from core.nodes.ipaddress import MacAddress
|
from core.nodes.ipaddress import MacAddress
|
||||||
|
from core.nodes.lxd import LxcNode
|
||||||
|
from core.nodes.network import (
|
||||||
|
CtrlNet,
|
||||||
|
GreTapBridge,
|
||||||
|
HubNode,
|
||||||
|
PtpNet,
|
||||||
|
SwitchNode,
|
||||||
|
TunnelNode,
|
||||||
|
WlanNode,
|
||||||
|
)
|
||||||
|
from core.nodes.physical import PhysicalNode, Rj45Node
|
||||||
from core.plugins.sdt import Sdt
|
from core.plugins.sdt import Sdt
|
||||||
from core.services.coreservices import CoreServices
|
from core.services.coreservices import CoreServices
|
||||||
from core.xml import corexml, corexmldeployment
|
from core.xml import corexml, corexmldeployment
|
||||||
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
|
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
|
||||||
|
|
||||||
|
# maps for converting from API call node type values to classes and vice versa
|
||||||
|
NODES = {
|
||||||
|
NodeTypes.DEFAULT: CoreNode,
|
||||||
|
NodeTypes.PHYSICAL: PhysicalNode,
|
||||||
|
NodeTypes.TBD: None,
|
||||||
|
NodeTypes.SWITCH: SwitchNode,
|
||||||
|
NodeTypes.HUB: HubNode,
|
||||||
|
NodeTypes.WIRELESS_LAN: WlanNode,
|
||||||
|
NodeTypes.RJ45: Rj45Node,
|
||||||
|
NodeTypes.TUNNEL: TunnelNode,
|
||||||
|
NodeTypes.KTUNNEL: None,
|
||||||
|
NodeTypes.EMANE: EmaneNet,
|
||||||
|
NodeTypes.EMANE_NET: None,
|
||||||
|
NodeTypes.TAP_BRIDGE: GreTapBridge,
|
||||||
|
NodeTypes.PEER_TO_PEER: PtpNet,
|
||||||
|
NodeTypes.CONTROL_NET: CtrlNet,
|
||||||
|
NodeTypes.DOCKER: DockerNode,
|
||||||
|
NodeTypes.LXC: LxcNode,
|
||||||
|
}
|
||||||
|
NODES_TYPE = {NODES[x]: x for x in NODES}
|
||||||
|
|
||||||
|
|
||||||
class Session(object):
|
class Session(object):
|
||||||
"""
|
"""
|
||||||
|
@ -121,6 +153,33 @@ class Session(object):
|
||||||
"host": ("DefaultRoute", "SSH"),
|
"host": ("DefaultRoute", "SSH"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_node_class(cls, _type):
|
||||||
|
"""
|
||||||
|
Retrieve the class for a given node type.
|
||||||
|
|
||||||
|
:param core.emulator.enumerations.NodeTypes _type: node type to get class for
|
||||||
|
:return: node class
|
||||||
|
"""
|
||||||
|
node_class = NODES.get(_type)
|
||||||
|
if node_class is None:
|
||||||
|
raise CoreError("invalid node type: %s" % _type)
|
||||||
|
return node_class
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_node_type(cls, _class):
|
||||||
|
"""
|
||||||
|
Retrieve node type for a given node class.
|
||||||
|
|
||||||
|
:param _class: node class to get a node type for
|
||||||
|
:return: node type
|
||||||
|
:rtype: core.emulator.enumerations.NodeTypes
|
||||||
|
"""
|
||||||
|
node_type = NODES_TYPE.get(_class)
|
||||||
|
if node_type is None:
|
||||||
|
raise CoreError("invalid node class: %s" % _class)
|
||||||
|
return node_type
|
||||||
|
|
||||||
def _link_nodes(self, node_one_id, node_two_id):
|
def _link_nodes(self, node_one_id, node_two_id):
|
||||||
"""
|
"""
|
||||||
Convenience method for retrieving nodes within link data.
|
Convenience method for retrieving nodes within link data.
|
||||||
|
@ -145,7 +204,7 @@ class Session(object):
|
||||||
# both node ids are provided
|
# both node ids are provided
|
||||||
tunnel = self.broker.gettunnel(node_one_id, node_two_id)
|
tunnel = self.broker.gettunnel(node_one_id, node_two_id)
|
||||||
logging.debug("tunnel between nodes: %s", tunnel)
|
logging.debug("tunnel between nodes: %s", tunnel)
|
||||||
if nodeutils.is_node(tunnel, NodeTypes.TAP_BRIDGE):
|
if isinstance(tunnel, GreTapBridge):
|
||||||
net_one = tunnel
|
net_one = tunnel
|
||||||
if tunnel.remotenum == node_one_id:
|
if tunnel.remotenum == node_one_id:
|
||||||
node_one = None
|
node_one = None
|
||||||
|
@ -158,14 +217,14 @@ class Session(object):
|
||||||
else:
|
else:
|
||||||
node_two = None
|
node_two = None
|
||||||
|
|
||||||
if is_net_node(node_one):
|
if isinstance(node_one, CoreNetworkBase):
|
||||||
if not net_one:
|
if not net_one:
|
||||||
net_one = node_one
|
net_one = node_one
|
||||||
else:
|
else:
|
||||||
net_two = node_one
|
net_two = node_one
|
||||||
node_one = None
|
node_one = None
|
||||||
|
|
||||||
if is_net_node(node_two):
|
if isinstance(node_two, CoreNetworkBase):
|
||||||
if not net_one:
|
if not net_one:
|
||||||
net_one = node_two
|
net_one = node_two
|
||||||
else:
|
else:
|
||||||
|
@ -203,9 +262,7 @@ class Session(object):
|
||||||
raise CoreError("no common network found for wireless link/unlink")
|
raise CoreError("no common network found for wireless link/unlink")
|
||||||
|
|
||||||
for common_network, interface_one, interface_two in common_networks:
|
for common_network, interface_one, interface_two in common_networks:
|
||||||
if not nodeutils.is_node(
|
if not isinstance(common_network, (WlanNode, EmaneNet)):
|
||||||
common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]
|
|
||||||
):
|
|
||||||
logging.info(
|
logging.info(
|
||||||
"skipping common network that is not wireless/emane: %s",
|
"skipping common network that is not wireless/emane: %s",
|
||||||
common_network,
|
common_network,
|
||||||
|
@ -268,9 +325,8 @@ class Session(object):
|
||||||
node_one.name,
|
node_one.name,
|
||||||
node_two.name,
|
node_two.name,
|
||||||
)
|
)
|
||||||
ptp_class = nodeutils.get_node_class(NodeTypes.PEER_TO_PEER)
|
|
||||||
start = self.state > EventTypes.DEFINITION_STATE.value
|
start = self.state > EventTypes.DEFINITION_STATE.value
|
||||||
net_one = self.create_node(cls=ptp_class, start=start)
|
net_one = self.create_node(cls=PtpNet, start=start)
|
||||||
|
|
||||||
# node to network
|
# node to network
|
||||||
if node_one and net_one:
|
if node_one and net_one:
|
||||||
|
@ -300,7 +356,7 @@ class Session(object):
|
||||||
net_one.name,
|
net_one.name,
|
||||||
net_two.name,
|
net_two.name,
|
||||||
)
|
)
|
||||||
if nodeutils.is_node(net_two, NodeTypes.RJ45):
|
if isinstance(net_two, Rj45Node):
|
||||||
interface = net_two.linknet(net_one)
|
interface = net_two.linknet(net_one)
|
||||||
else:
|
else:
|
||||||
interface = net_one.linknet(net_two)
|
interface = net_one.linknet(net_two)
|
||||||
|
@ -324,12 +380,12 @@ class Session(object):
|
||||||
|
|
||||||
# tunnel node logic
|
# tunnel node logic
|
||||||
key = link_options.key
|
key = link_options.key
|
||||||
if key and nodeutils.is_node(net_one, NodeTypes.TUNNEL):
|
if key and isinstance(net_one, TunnelNode):
|
||||||
logging.info("setting tunnel key for: %s", net_one.name)
|
logging.info("setting tunnel key for: %s", net_one.name)
|
||||||
net_one.setkey(key)
|
net_one.setkey(key)
|
||||||
if addresses:
|
if addresses:
|
||||||
net_one.addrconfig(addresses)
|
net_one.addrconfig(addresses)
|
||||||
if key and nodeutils.is_node(net_two, NodeTypes.TUNNEL):
|
if key and isinstance(net_two, TunnelNode):
|
||||||
logging.info("setting tunnel key for: %s", net_two.name)
|
logging.info("setting tunnel key for: %s", net_two.name)
|
||||||
net_two.setkey(key)
|
net_two.setkey(key)
|
||||||
if addresses:
|
if addresses:
|
||||||
|
@ -337,14 +393,14 @@ class Session(object):
|
||||||
|
|
||||||
# physical node connected with tunnel
|
# physical node connected with tunnel
|
||||||
if not net_one and not net_two and (node_one or node_two):
|
if not net_one and not net_two and (node_one or node_two):
|
||||||
if node_one and nodeutils.is_node(node_one, NodeTypes.PHYSICAL):
|
if node_one and isinstance(node_one, PhysicalNode):
|
||||||
logging.info("adding link for physical node: %s", node_one.name)
|
logging.info("adding link for physical node: %s", node_one.name)
|
||||||
addresses = interface_one.get_addresses()
|
addresses = interface_one.get_addresses()
|
||||||
node_one.adoptnetif(
|
node_one.adoptnetif(
|
||||||
tunnel, interface_one.id, interface_one.mac, addresses
|
tunnel, interface_one.id, interface_one.mac, addresses
|
||||||
)
|
)
|
||||||
link_config(node_one, tunnel, link_options)
|
link_config(node_one, tunnel, link_options)
|
||||||
elif node_two and nodeutils.is_node(node_two, NodeTypes.PHYSICAL):
|
elif node_two and isinstance(node_two, PhysicalNode):
|
||||||
logging.info("adding link for physical node: %s", node_two.name)
|
logging.info("adding link for physical node: %s", node_two.name)
|
||||||
addresses = interface_two.get_addresses()
|
addresses = interface_two.get_addresses()
|
||||||
node_two.adoptnetif(
|
node_two.adoptnetif(
|
||||||
|
@ -584,14 +640,11 @@ class Session(object):
|
||||||
:param int _id: id for node, defaults to None for generated id
|
:param int _id: id for node, defaults to None for generated id
|
||||||
:param core.emulator.emudata.NodeOptions node_options: data to create node with
|
:param core.emulator.emudata.NodeOptions node_options: data to create node with
|
||||||
:return: created node
|
:return: created node
|
||||||
|
:raises core.CoreError: when an invalid node type is given
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# retrieve node class for given node type
|
# validate node type, get class, or throw error
|
||||||
try:
|
node_class = self.get_node_class(_type)
|
||||||
node_class = nodeutils.get_node_class(_type)
|
|
||||||
except KeyError:
|
|
||||||
logging.error("invalid node type to create: %s", _type)
|
|
||||||
return None
|
|
||||||
|
|
||||||
# set node start based on current session state, override and check when rj45
|
# set node start based on current session state, override and check when rj45
|
||||||
start = self.state > EventTypes.DEFINITION_STATE.value
|
start = self.state > EventTypes.DEFINITION_STATE.value
|
||||||
|
@ -651,10 +704,8 @@ class Session(object):
|
||||||
logging.debug("set node type: %s", node.type)
|
logging.debug("set node type: %s", node.type)
|
||||||
self.services.add_services(node, node.type, node_options.services)
|
self.services.add_services(node, node.type, node_options.services)
|
||||||
|
|
||||||
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
|
# boot nodes if created after runtime, CoreNodes, Physical, and RJ45 are all nodes
|
||||||
is_boot_node = isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
is_boot_node = isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node)
|
||||||
node, NodeTypes.RJ45
|
|
||||||
)
|
|
||||||
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
||||||
self.write_nodes()
|
self.write_nodes()
|
||||||
self.add_remove_control_interface(node=node, remove=False)
|
self.add_remove_control_interface(node=node, remove=False)
|
||||||
|
@ -1178,8 +1229,8 @@ class Session(object):
|
||||||
"""
|
"""
|
||||||
if state == EventTypes.RUNTIME_STATE.value:
|
if state == EventTypes.RUNTIME_STATE.value:
|
||||||
self.emane.poststartup()
|
self.emane.poststartup()
|
||||||
xml_file_version = self.options.get_config("xmlfilever")
|
|
||||||
if xml_file_version in ("1.0",):
|
# create session deployed xml
|
||||||
xml_file_name = os.path.join(self.session_dir, "session-deployed.xml")
|
xml_file_name = os.path.join(self.session_dir, "session-deployed.xml")
|
||||||
xml_writer = corexml.CoreXmlWriter(self)
|
xml_writer = corexml.CoreXmlWriter(self)
|
||||||
corexmldeployment.CoreXmlDeployment(self, xml_writer.scenario)
|
corexmldeployment.CoreXmlDeployment(self, xml_writer.scenario)
|
||||||
|
@ -1441,12 +1492,10 @@ class Session(object):
|
||||||
count = 0
|
count = 0
|
||||||
for node_id in self.nodes:
|
for node_id in self.nodes:
|
||||||
node = self.nodes[node_id]
|
node = self.nodes[node_id]
|
||||||
is_p2p_ctrlnet = nodeutils.is_node(
|
is_p2p_ctrlnet = isinstance(node, (PtpNet, CtrlNet))
|
||||||
node, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET)
|
is_tap = isinstance(node, GreTapBridge) and not isinstance(
|
||||||
|
node, TunnelNode
|
||||||
)
|
)
|
||||||
is_tap = nodeutils.is_node(
|
|
||||||
node, NodeTypes.TAP_BRIDGE
|
|
||||||
) and not nodeutils.is_node(node, NodeTypes.TUNNEL)
|
|
||||||
if is_p2p_ctrlnet or is_tap:
|
if is_p2p_ctrlnet or is_tap:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -1493,7 +1542,7 @@ class Session(object):
|
||||||
for node_id in self.nodes:
|
for node_id in self.nodes:
|
||||||
node = self.nodes[node_id]
|
node = self.nodes[node_id]
|
||||||
# TODO: determine if checking for CoreNode alone is ok
|
# TODO: determine if checking for CoreNode alone is ok
|
||||||
if isinstance(node, core.nodes.base.CoreNodeBase):
|
if isinstance(node, CoreNodeBase):
|
||||||
self.services.stop_services(node)
|
self.services.stop_services(node)
|
||||||
|
|
||||||
# shutdown emane
|
# shutdown emane
|
||||||
|
@ -1546,10 +1595,7 @@ class Session(object):
|
||||||
start = time.time()
|
start = time.time()
|
||||||
for _id in self.nodes:
|
for _id in self.nodes:
|
||||||
node = self.nodes[_id]
|
node = self.nodes[_id]
|
||||||
# TODO: PyCoreNode is not the type to check
|
if isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node):
|
||||||
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
|
||||||
node, NodeTypes.RJ45
|
|
||||||
):
|
|
||||||
# add a control interface if configured
|
# add a control interface if configured
|
||||||
logging.info(
|
logging.info(
|
||||||
"booting node(%s): %s",
|
"booting node(%s): %s",
|
||||||
|
@ -1648,8 +1694,7 @@ class Session(object):
|
||||||
# no controlnet needed
|
# no controlnet needed
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
|
prefix_spec = CtrlNet.DEFAULT_PREFIX_LIST[net_index]
|
||||||
prefix_spec = control_net_class.DEFAULT_PREFIX_LIST[net_index]
|
|
||||||
logging.debug("prefix spec: %s", prefix_spec)
|
logging.debug("prefix spec: %s", prefix_spec)
|
||||||
|
|
||||||
server_interface = self.get_control_net_server_interfaces()[net_index]
|
server_interface = self.get_control_net_server_interfaces()[net_index]
|
||||||
|
@ -1676,7 +1721,7 @@ class Session(object):
|
||||||
if net_index == 0:
|
if net_index == 0:
|
||||||
updown_script = self.options.get_config("controlnet_updown_script")
|
updown_script = self.options.get_config("controlnet_updown_script")
|
||||||
if not updown_script:
|
if not updown_script:
|
||||||
logging.warning("controlnet updown script not configured")
|
logging.debug("controlnet updown script not configured")
|
||||||
|
|
||||||
prefixes = prefix_spec.split()
|
prefixes = prefix_spec.split()
|
||||||
if len(prefixes) > 1:
|
if len(prefixes) > 1:
|
||||||
|
@ -1725,9 +1770,8 @@ class Session(object):
|
||||||
prefix = prefixes[0]
|
prefix = prefixes[0]
|
||||||
|
|
||||||
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
|
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
|
||||||
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
|
|
||||||
control_net = self.create_node(
|
control_net = self.create_node(
|
||||||
cls=control_net_class,
|
cls=CtrlNet,
|
||||||
_id=_id,
|
_id=_id,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
assign_address=assign_address,
|
assign_address=assign_address,
|
||||||
|
|
21
daemon/core/errors.py
Normal file
21
daemon/core/errors.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
"""
|
||||||
|
Provides CORE specific errors.
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
class CoreCommandError(subprocess.CalledProcessError):
|
||||||
|
"""
|
||||||
|
Used when encountering internal CORE command errors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output)
|
||||||
|
|
||||||
|
|
||||||
|
class CoreError(Exception):
|
||||||
|
"""
|
||||||
|
Used for errors when dealing with CoreEmu and Sessions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
|
@ -11,7 +11,7 @@ import time
|
||||||
from builtins import int
|
from builtins import int
|
||||||
from functools import total_ordering
|
from functools import total_ordering
|
||||||
|
|
||||||
from core import CoreError, utils
|
from core import utils
|
||||||
from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
|
from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
|
||||||
from core.emulator.data import EventData, LinkData
|
from core.emulator.data import EventData, LinkData
|
||||||
from core.emulator.enumerations import (
|
from core.emulator.enumerations import (
|
||||||
|
@ -23,6 +23,7 @@ from core.emulator.enumerations import (
|
||||||
NodeTlvs,
|
NodeTlvs,
|
||||||
RegisterTlvs,
|
RegisterTlvs,
|
||||||
)
|
)
|
||||||
|
from core.errors import CoreError
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase
|
||||||
from core.nodes.ipaddress import IpAddress
|
from core.nodes.ipaddress import IpAddress
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,15 @@ import threading
|
||||||
from builtins import range
|
from builtins import range
|
||||||
from socket import AF_INET, AF_INET6
|
from socket import AF_INET, AF_INET6
|
||||||
|
|
||||||
from core import CoreCommandError, constants, utils
|
from core import constants, utils
|
||||||
from core.emulator.data import LinkData, NodeData
|
from core.emulator.data import LinkData, NodeData
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||||
from core.nodes import client, ipaddress, nodeutils
|
from core.nodes import client, ipaddress
|
||||||
from core.nodes.interface import CoreInterface, TunTap, Veth
|
from core.nodes.interface import CoreInterface, TunTap, Veth
|
||||||
|
from core.nodes.netclient import LinuxNetClient, OvsNetClient
|
||||||
|
|
||||||
_DEFAULT_MTU = 1500
|
_DEFAULT_MTU = 1500
|
||||||
|
|
||||||
utils.check_executables([constants.IP_BIN])
|
|
||||||
|
|
||||||
|
|
||||||
class NodeBase(object):
|
class NodeBase(object):
|
||||||
"""
|
"""
|
||||||
|
@ -54,7 +53,7 @@ class NodeBase(object):
|
||||||
self.type = None
|
self.type = None
|
||||||
self.server = None
|
self.server = None
|
||||||
self.services = None
|
self.services = None
|
||||||
# ifindex is key, PyCoreNetIf instance is value
|
# ifindex is key, CoreInterface instance is value
|
||||||
self._netif = {}
|
self._netif = {}
|
||||||
self.ifindex = 0
|
self.ifindex = 0
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
|
@ -62,6 +61,11 @@ class NodeBase(object):
|
||||||
self.opaque = None
|
self.opaque = None
|
||||||
self.position = Position()
|
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):
|
def startup(self):
|
||||||
"""
|
"""
|
||||||
Each object implements its own startup method.
|
Each object implements its own startup method.
|
||||||
|
@ -78,6 +82,18 @@ class NodeBase(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
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):
|
def setposition(self, x=None, y=None, z=None):
|
||||||
"""
|
"""
|
||||||
Set the (x,y,z) position of the object.
|
Set the (x,y,z) position of the object.
|
||||||
|
@ -360,6 +376,18 @@ class CoreNodeBase(NodeBase):
|
||||||
|
|
||||||
return common
|
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):
|
def check_cmd(self, args):
|
||||||
"""
|
"""
|
||||||
Runs shell command on node.
|
Runs shell command on node.
|
||||||
|
@ -434,6 +462,12 @@ class CoreNode(CoreNodeBase):
|
||||||
self.lock = threading.RLock()
|
self.lock = threading.RLock()
|
||||||
self._mounts = []
|
self._mounts = []
|
||||||
self.bootsh = bootsh
|
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:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
|
@ -489,11 +523,11 @@ class CoreNode(CoreNodeBase):
|
||||||
|
|
||||||
# bring up the loopback interface
|
# bring up the loopback interface
|
||||||
logging.debug("bringing up 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
|
# set hostname for node
|
||||||
logging.debug("setting hostname: %s", self.name)
|
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
|
# mark node as up
|
||||||
self.up = True
|
self.up = True
|
||||||
|
@ -568,9 +602,10 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
return self.client.cmd_output(args)
|
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
|
:param list[str]|str args: command to run
|
||||||
:return: combined stdout and stderr
|
:return: combined stdout and stderr
|
||||||
|
@ -625,15 +660,8 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
source = os.path.abspath(source)
|
source = os.path.abspath(source)
|
||||||
logging.debug("node(%s) mounting: %s at %s", self.name, source, target)
|
logging.debug("node(%s) mounting: %s at %s", self.name, source, target)
|
||||||
cmd = 'mkdir -p "%s" && %s -n --bind "%s" "%s"' % (
|
self.client.check_cmd(["mkdir", "-p", target])
|
||||||
target,
|
self.client.check_cmd([constants.MOUNT_BIN, "-n", "--bind", source, target])
|
||||||
constants.MOUNT_BIN,
|
|
||||||
source,
|
|
||||||
target,
|
|
||||||
)
|
|
||||||
status, output = self.client.shcmd_result(cmd)
|
|
||||||
if status:
|
|
||||||
raise CoreCommandError(status, cmd, output)
|
|
||||||
self._mounts.append((source, target))
|
self._mounts.append((source, target))
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self):
|
||||||
|
@ -682,22 +710,16 @@ class CoreNode(CoreNodeBase):
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd(
|
self.net_client.device_ns(veth.name, str(self.pid))
|
||||||
[constants.IP_BIN, "link", "set", veth.name, "netns", str(self.pid)]
|
self.node_net_client.device_name(veth.name, ifname)
|
||||||
)
|
self.node_net_client.checksums_off(ifname)
|
||||||
self.network_cmd(
|
|
||||||
[constants.IP_BIN, "link", "set", veth.name, "name", ifname]
|
|
||||||
)
|
|
||||||
self.network_cmd(
|
|
||||||
[constants.ETHTOOL_BIN, "-K", ifname, "rx", "off", "tx", "off"]
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
||||||
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)
|
logging.debug("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
|
||||||
|
@ -707,7 +729,8 @@ class CoreNode(CoreNodeBase):
|
||||||
logging.debug("interface mac: %s - %s", veth.name, veth.hwaddr)
|
logging.debug("interface mac: %s - %s", veth.name, veth.hwaddr)
|
||||||
|
|
||||||
try:
|
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)
|
self.addnetif(veth, ifindex)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
veth.shutdown()
|
veth.shutdown()
|
||||||
|
@ -758,105 +781,47 @@ class CoreNode(CoreNodeBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
:raises CoreCommandError: when a non-zero exit status occurs
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
"""
|
"""
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
interface = self._netif[ifindex]
|
||||||
|
interface.sethwaddr(addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
args = [
|
self.node_net_client.device_mac(interface.name, str(addr))
|
||||||
constants.IP_BIN,
|
|
||||||
"link",
|
|
||||||
"set",
|
|
||||||
"dev",
|
|
||||||
self.ifname(ifindex),
|
|
||||||
"address",
|
|
||||||
str(addr),
|
|
||||||
]
|
|
||||||
self.network_cmd(args)
|
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex, addr):
|
||||||
"""
|
"""
|
||||||
Add interface address.
|
Add interface address.
|
||||||
|
|
||||||
:param int ifindex: index of interface to add address to
|
: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
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
|
interface = self._netif[ifindex]
|
||||||
|
interface.addaddr(addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
# check if addr is ipv6
|
address = str(addr)
|
||||||
if ":" in str(addr):
|
# ipv6 check
|
||||||
args = [
|
broadcast = None
|
||||||
constants.IP_BIN,
|
if ":" not in address:
|
||||||
"addr",
|
broadcast = "+"
|
||||||
"add",
|
self.node_net_client.create_address(interface.name, address, broadcast)
|
||||||
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)
|
|
||||||
|
|
||||||
def deladdr(self, ifindex, addr):
|
def deladdr(self, ifindex, addr):
|
||||||
"""
|
"""
|
||||||
Delete address from an interface.
|
Delete address from an interface.
|
||||||
|
|
||||||
: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 core.nodes.ipaddress.IpAddress addr: address to delete from interface
|
||||||
:return: nothing
|
:return: nothing
|
||||||
:raises CoreCommandError: when a non-zero exit status occurs
|
:raises CoreCommandError: when a non-zero exit status occurs
|
||||||
"""
|
"""
|
||||||
|
interface = self._netif[ifindex]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._netif[ifindex].deladdr(addr)
|
interface.deladdr(addr)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.exception("trying to delete unknown address: %s" % addr)
|
logging.exception("trying to delete unknown address: %s" % addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
self.network_cmd(
|
self.node_net_client.delete_address(interface.name, str(addr))
|
||||||
[
|
|
||||||
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)
|
|
||||||
|
|
||||||
def ifup(self, ifindex):
|
def ifup(self, ifindex):
|
||||||
"""
|
"""
|
||||||
|
@ -866,9 +831,8 @@ class CoreNode(CoreNodeBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
self.network_cmd(
|
interface_name = self.ifname(ifindex)
|
||||||
[constants.IP_BIN, "link", "set", self.ifname(ifindex), "up"]
|
self.node_net_client.device_up(interface_name)
|
||||||
)
|
|
||||||
|
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
|
@ -886,8 +850,8 @@ class CoreNode(CoreNodeBase):
|
||||||
addrlist = []
|
addrlist = []
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
# TODO: see if you can move this to emane specific code
|
# TODO: emane specific code
|
||||||
if nodeutils.is_node(net, NodeTypes.EMANE):
|
if net.is_emane is True:
|
||||||
ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname, net=net)
|
ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname, net=net)
|
||||||
# TUN/TAP is not ready for addressing yet; the device may
|
# TUN/TAP is not ready for addressing yet; the device may
|
||||||
# take some time to appear, and installing it into a
|
# take some time to appear, and installing it into a
|
||||||
|
@ -919,7 +883,7 @@ class CoreNode(CoreNodeBase):
|
||||||
Connect a node.
|
Connect a node.
|
||||||
|
|
||||||
:param str ifname: name of interface to connect
|
: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
|
:param str otherifname: interface name to connect to
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
|
@ -930,32 +894,14 @@ class CoreNode(CoreNodeBase):
|
||||||
tmp2 = "tmp." + "".join(
|
tmp2 = "tmp." + "".join(
|
||||||
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
[random.choice(string.ascii_lowercase) for _ in range(tmplen)]
|
||||||
)
|
)
|
||||||
utils.check_cmd(
|
self.net_client.create_veth(tmp1, tmp2)
|
||||||
[
|
self.net_client.device_ns(tmp1, str(self.pid))
|
||||||
constants.IP_BIN,
|
self.node_net_client.device_name(tmp1, ifname)
|
||||||
"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])
|
|
||||||
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
|
interface = CoreInterface(node=self, name=ifname, mtu=_DEFAULT_MTU)
|
||||||
self.addnetif(interface, self.newifindex())
|
self.addnetif(interface, self.newifindex())
|
||||||
|
|
||||||
utils.check_cmd(
|
self.net_client.device_ns(tmp2, str(othernode.pid))
|
||||||
[constants.IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)]
|
othernode.node_net_client.device_name(tmp2, otherifname)
|
||||||
)
|
|
||||||
othernode.network_cmd(
|
|
||||||
[constants.IP_BIN, "link", "set", tmp2, "name", otherifname]
|
|
||||||
)
|
|
||||||
other_interface = CoreInterface(
|
other_interface = CoreInterface(
|
||||||
node=othernode, name=otherifname, mtu=_DEFAULT_MTU
|
node=othernode, name=otherifname, mtu=_DEFAULT_MTU
|
||||||
)
|
)
|
||||||
|
@ -972,11 +918,9 @@ class CoreNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
logging.info("adding file from %s to %s", srcname, filename)
|
logging.info("adding file from %s to %s", srcname, filename)
|
||||||
directory = os.path.dirname(filename)
|
directory = os.path.dirname(filename)
|
||||||
|
self.client.check_cmd(["mkdir", "-p", directory])
|
||||||
cmd = 'mkdir -p "%s" && mv "%s" "%s" && sync' % (directory, srcname, filename)
|
self.client.check_cmd(["mv", srcname, filename])
|
||||||
status, output = self.client.shcmd_result(cmd)
|
self.client.check_cmd(["sync"])
|
||||||
if status:
|
|
||||||
raise CoreCommandError(status, cmd, output)
|
|
||||||
|
|
||||||
def hostfilename(self, filename):
|
def hostfilename(self, filename):
|
||||||
"""
|
"""
|
||||||
|
@ -1050,6 +994,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
linktype = LinkTypes.WIRED.value
|
linktype = LinkTypes.WIRED.value
|
||||||
|
is_emane = False
|
||||||
|
|
||||||
def __init__(self, session, _id, name, start=True):
|
def __init__(self, session, _id, name, start=True):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,7 +8,8 @@ import logging
|
||||||
import os
|
import os
|
||||||
from subprocess import PIPE, Popen
|
from subprocess import PIPE, Popen
|
||||||
|
|
||||||
from core import CoreCommandError, constants, utils
|
from core import constants, utils
|
||||||
|
from core.errors import CoreCommandError
|
||||||
|
|
||||||
|
|
||||||
class VnodeClient(object):
|
class VnodeClient(object):
|
||||||
|
@ -25,7 +26,6 @@ class VnodeClient(object):
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.ctrlchnlname = ctrlchnlname
|
self.ctrlchnlname = ctrlchnlname
|
||||||
self._addr = {}
|
|
||||||
|
|
||||||
def _verify_connection(self):
|
def _verify_connection(self):
|
||||||
"""
|
"""
|
||||||
|
@ -145,36 +145,6 @@ class VnodeClient(object):
|
||||||
*args
|
*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"):
|
def term(self, sh="/bin/sh"):
|
||||||
"""
|
"""
|
||||||
Open a terminal on a node.
|
Open a terminal on a node.
|
||||||
|
@ -214,111 +184,3 @@ class VnodeClient(object):
|
||||||
:return: str
|
:return: str
|
||||||
"""
|
"""
|
||||||
return "%s -c %s -- %s" % (constants.VCMD_BIN, self.ctrlchnlname, sh)
|
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
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import utils
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
|
from core.errors import CoreCommandError
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +13,6 @@ class DockerClient(object):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.image = image
|
self.image = image
|
||||||
self.pid = None
|
self.pid = None
|
||||||
self._addr = {}
|
|
||||||
|
|
||||||
def create_container(self):
|
def create_container(self):
|
||||||
utils.check_cmd(
|
utils.check_cmd(
|
||||||
|
@ -94,40 +94,6 @@ class DockerClient(object):
|
||||||
if status:
|
if status:
|
||||||
raise CoreCommandError(status, args, output)
|
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):
|
class DockerNode(CoreNode):
|
||||||
apitype = NodeTypes.DOCKER.value
|
apitype = NodeTypes.DOCKER.value
|
||||||
|
@ -224,7 +190,7 @@ class DockerNode(CoreNode):
|
||||||
raise CoreCommandError(status, args, output)
|
raise CoreCommandError(status, args, output)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def network_cmd(self, args):
|
def node_net_cmd(self, args):
|
||||||
if not self.up:
|
if not self.up:
|
||||||
logging.debug("node down, not running network command: %s", args)
|
logging.debug("node down, not running network command: %s", args)
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -6,11 +6,9 @@ import logging
|
||||||
import time
|
import time
|
||||||
from builtins import int, range
|
from builtins import int, range
|
||||||
|
|
||||||
from core import CoreCommandError, constants, utils
|
from core import utils
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.errors import CoreCommandError
|
||||||
from core.nodes import nodeutils
|
from core.nodes.netclient import LinuxNetClient
|
||||||
|
|
||||||
utils.check_executables([constants.IP_BIN])
|
|
||||||
|
|
||||||
|
|
||||||
class CoreInterface(object):
|
class CoreInterface(object):
|
||||||
|
@ -44,6 +42,7 @@ class CoreInterface(object):
|
||||||
self.netindex = None
|
self.netindex = None
|
||||||
# index used to find flow data
|
# index used to find flow data
|
||||||
self.flow_id = None
|
self.flow_id = None
|
||||||
|
self.net_client = LinuxNetClient(utils.check_cmd)
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
"""
|
"""
|
||||||
|
@ -219,21 +218,8 @@ class Veth(CoreInterface):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
utils.check_cmd(
|
self.net_client.create_veth(self.localname, self.name)
|
||||||
[
|
self.net_client.device_up(self.localname)
|
||||||
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.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
@ -247,15 +233,13 @@ class Veth(CoreInterface):
|
||||||
|
|
||||||
if self.node:
|
if self.node:
|
||||||
try:
|
try:
|
||||||
self.node.network_cmd(
|
self.node.node_net_client.device_flush(self.name)
|
||||||
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
|
|
||||||
)
|
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error shutting down interface")
|
logging.exception("error shutting down interface")
|
||||||
|
|
||||||
if self.localname:
|
if self.localname:
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "delete", self.localname])
|
self.net_client.delete_device(self.localname)
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.info("link already removed: %s", self.localname)
|
logging.info("link already removed: %s", self.localname)
|
||||||
|
|
||||||
|
@ -310,9 +294,7 @@ class TunTap(CoreInterface):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.node.network_cmd(
|
self.node.node_net_client.device_flush(self.name)
|
||||||
[constants.IP_BIN, "-6", "addr", "flush", "dev", self.name]
|
|
||||||
)
|
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error shutting down tunnel tap")
|
logging.exception("error shutting down tunnel tap")
|
||||||
|
|
||||||
|
@ -360,8 +342,11 @@ class TunTap(CoreInterface):
|
||||||
logging.debug("waiting for device local: %s", self.localname)
|
logging.debug("waiting for device local: %s", self.localname)
|
||||||
|
|
||||||
def localdevexists():
|
def localdevexists():
|
||||||
args = [constants.IP_BIN, "link", "show", self.localname]
|
try:
|
||||||
return utils.cmd(args)
|
self.net_client.device_show(self.localname)
|
||||||
|
return 0
|
||||||
|
except CoreCommandError:
|
||||||
|
return 1
|
||||||
|
|
||||||
self.waitfor(localdevexists)
|
self.waitfor(localdevexists)
|
||||||
|
|
||||||
|
@ -374,9 +359,8 @@ class TunTap(CoreInterface):
|
||||||
logging.debug("waiting for device node: %s", self.name)
|
logging.debug("waiting for device node: %s", self.name)
|
||||||
|
|
||||||
def nodedevexists():
|
def nodedevexists():
|
||||||
args = [constants.IP_BIN, "link", "show", self.name]
|
|
||||||
try:
|
try:
|
||||||
self.node.network_cmd(args)
|
self.node.node_net_client.device_show(self.name)
|
||||||
return 0
|
return 0
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
return 1
|
return 1
|
||||||
|
@ -387,13 +371,12 @@ class TunTap(CoreInterface):
|
||||||
if result:
|
if result:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# TODO: emane specific code
|
||||||
# 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
|
|
||||||
should_retry = count < 5
|
should_retry = count < 5
|
||||||
is_emane_node = nodeutils.is_node(self.net, NodeTypes.EMANE)
|
|
||||||
is_emane_running = self.node.session.emane.emanerunning(self.node)
|
is_emane_running = self.node.session.emane.emanerunning(self.node)
|
||||||
if all([should_retry, is_emane_node, is_emane_running]):
|
if all([should_retry, self.net.is_emane, is_emane_running]):
|
||||||
count += 1
|
count += 1
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("node device failed to exist")
|
raise RuntimeError("node device failed to exist")
|
||||||
|
@ -410,13 +393,9 @@ class TunTap(CoreInterface):
|
||||||
"""
|
"""
|
||||||
self.waitfordevicelocal()
|
self.waitfordevicelocal()
|
||||||
netns = str(self.node.pid)
|
netns = str(self.node.pid)
|
||||||
utils.check_cmd(
|
self.net_client.device_ns(self.localname, netns)
|
||||||
[constants.IP_BIN, "link", "set", self.localname, "netns", netns]
|
self.node.node_net_client.device_name(self.localname, self.name)
|
||||||
)
|
self.node.node_net_client.device_up(self.name)
|
||||||
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"])
|
|
||||||
|
|
||||||
def setaddrs(self):
|
def setaddrs(self):
|
||||||
"""
|
"""
|
||||||
|
@ -426,9 +405,7 @@ class TunTap(CoreInterface):
|
||||||
"""
|
"""
|
||||||
self.waitfordevicenode()
|
self.waitfordevicenode()
|
||||||
for addr in self.addrlist:
|
for addr in self.addrlist:
|
||||||
self.node.network_cmd(
|
self.node.node_net_client.create_address(self.name, str(addr))
|
||||||
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class GreTap(CoreInterface):
|
class GreTap(CoreInterface):
|
||||||
|
@ -482,25 +459,11 @@ class GreTap(CoreInterface):
|
||||||
|
|
||||||
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 = [
|
|
||||||
constants.IP_BIN,
|
self.net_client.create_gretap(
|
||||||
"link",
|
self.localname, str(remoteip), str(localip), str(ttl), str(key)
|
||||||
"add",
|
)
|
||||||
self.localname,
|
self.net_client.device_up(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.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
@ -511,10 +474,8 @@ class GreTap(CoreInterface):
|
||||||
"""
|
"""
|
||||||
if self.localname:
|
if self.localname:
|
||||||
try:
|
try:
|
||||||
args = [constants.IP_BIN, "link", "set", self.localname, "down"]
|
self.net_client.device_down(self.localname)
|
||||||
utils.check_cmd(args)
|
self.net_client.delete_device(self.localname)
|
||||||
args = [constants.IP_BIN, "link", "del", self.localname]
|
|
||||||
utils.check_cmd(args)
|
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error during shutdown")
|
logging.exception("error during shutdown")
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,9 @@ import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import utils
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
|
from core.errors import CoreCommandError
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +14,6 @@ class LxdClient(object):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.image = image
|
self.image = image
|
||||||
self.pid = None
|
self.pid = None
|
||||||
self._addr = {}
|
|
||||||
|
|
||||||
def create_container(self):
|
def create_container(self):
|
||||||
utils.check_cmd(
|
utils.check_cmd(
|
||||||
|
@ -90,40 +90,6 @@ class LxdClient(object):
|
||||||
if status:
|
if status:
|
||||||
raise CoreCommandError(status, args, output)
|
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):
|
class LxcNode(CoreNode):
|
||||||
apitype = NodeTypes.LXC.value
|
apitype = NodeTypes.LXC.value
|
||||||
|
@ -227,7 +193,7 @@ class LxcNode(CoreNode):
|
||||||
raise CoreCommandError(status, args, output)
|
raise CoreCommandError(status, args, output)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def network_cmd(self, args):
|
def node_net_cmd(self, args):
|
||||||
if not self.up:
|
if not self.up:
|
||||||
logging.debug("node down, not running network command: %s", args)
|
logging.debug("node down, not running network command: %s", args)
|
||||||
return 0
|
return 0
|
||||||
|
|
351
daemon/core/nodes/netclient.py
Normal file
351
daemon/core/nodes/netclient.py
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
"""
|
||||||
|
Clients for dealing with bridge/interface commands.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from core.constants import BRCTL_BIN, ETHTOOL_BIN, IP_BIN, OVS_BIN, TC_BIN
|
||||||
|
from core.utils import check_cmd
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param str name: bridge name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
if os.path.exists(snoop):
|
||||||
|
with open(snoop, "w") as f:
|
||||||
|
f.write("0")
|
||||||
|
|
||||||
|
def delete_bridge(self, name):
|
||||||
|
"""
|
||||||
|
Bring down and delete a Linux bridge.
|
||||||
|
|
||||||
|
:param str name: bridge name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
self.device_down(name)
|
||||||
|
self.run([BRCTL_BIN, "delbr", name])
|
||||||
|
|
||||||
|
def create_interface(self, bridge_name, interface_name):
|
||||||
|
"""
|
||||||
|
Create an interface associated with a Linux bridge.
|
||||||
|
|
||||||
|
:param str bridge_name: bridge name
|
||||||
|
:param str interface_name: interface name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
self.run([BRCTL_BIN, "addif", bridge_name, interface_name])
|
||||||
|
self.device_up(interface_name)
|
||||||
|
|
||||||
|
def delete_interface(self, bridge_name, interface_name):
|
||||||
|
"""
|
||||||
|
Delete an interface associated with a Linux bridge.
|
||||||
|
|
||||||
|
:param str bridge_name: bridge name
|
||||||
|
:param str interface_name: interface name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
self.run([BRCTL_BIN, "delif", bridge_name, interface_name])
|
||||||
|
|
||||||
|
def existing_bridges(self, _id):
|
||||||
|
"""
|
||||||
|
Checks if there are any existing Linux bridges for a node.
|
||||||
|
|
||||||
|
:param _id: node id to check bridges for
|
||||||
|
"""
|
||||||
|
output = self.run([BRCTL_BIN, "show"])
|
||||||
|
lines = output.split("\n")
|
||||||
|
for line in lines[1:]:
|
||||||
|
columns = line.split()
|
||||||
|
name = columns[0]
|
||||||
|
fields = name.split(".")
|
||||||
|
if len(fields) != 3:
|
||||||
|
continue
|
||||||
|
if fields[0] == "b" and fields[1] == _id:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def disable_mac_learning(self, name):
|
||||||
|
"""
|
||||||
|
Disable mac learning for a Linux bridge.
|
||||||
|
|
||||||
|
:param str name: bridge name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
check_cmd([BRCTL_BIN, "setageing", name, "0"])
|
||||||
|
|
||||||
|
|
||||||
|
class OvsNetClient(LinuxNetClient):
|
||||||
|
"""
|
||||||
|
Client for creating OVS bridges and ip interfaces for nodes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_bridge(self, name):
|
||||||
|
"""
|
||||||
|
Create a OVS bridge and bring it up.
|
||||||
|
|
||||||
|
:param str name: bridge name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
Bring down and delete a OVS bridge.
|
||||||
|
|
||||||
|
:param str name: bridge name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
self.device_down(name)
|
||||||
|
self.run([OVS_BIN, "del-br", name])
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
self.run([OVS_BIN, "add-port", bridge_name, interface_name])
|
||||||
|
self.device_up(interface_name)
|
||||||
|
|
||||||
|
def delete_interface(self, bridge_name, interface_name):
|
||||||
|
"""
|
||||||
|
Delete an interface associated with a OVS bridge.
|
||||||
|
|
||||||
|
:param str bridge_name: bridge name
|
||||||
|
:param str interface_name: interface name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
self.run([OVS_BIN, "del-port", bridge_name, interface_name])
|
||||||
|
|
||||||
|
def existing_bridges(self, _id):
|
||||||
|
"""
|
||||||
|
Checks if there are any existing OVS bridges for a node.
|
||||||
|
|
||||||
|
:param _id: node id to check bridges for
|
||||||
|
"""
|
||||||
|
output = self.run([OVS_BIN, "list-br"])
|
||||||
|
if output:
|
||||||
|
for line in output.split("\n"):
|
||||||
|
fields = line.split(".")
|
||||||
|
if fields[0] == "b" and fields[1] == _id:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def disable_mac_learning(self, name):
|
||||||
|
"""
|
||||||
|
Disable mac learning for a OVS bridge.
|
||||||
|
|
||||||
|
:param str name: bridge name
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
self.run([OVS_BIN, "set", "bridge", name, "other_config:mac-aging-time=0"])
|
|
@ -9,17 +9,14 @@ import threading
|
||||||
import time
|
import time
|
||||||
from socket import AF_INET, AF_INET6
|
from socket import AF_INET, AF_INET6
|
||||||
|
|
||||||
from core import CoreCommandError, constants, utils
|
from core import constants, utils
|
||||||
from core.emulator.data import LinkData
|
from core.emulator.data import LinkData
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||||
|
from core.errors import CoreCommandError, CoreError
|
||||||
from core.nodes import ipaddress
|
from core.nodes import ipaddress
|
||||||
from core.nodes.base import CoreNetworkBase
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.interface import GreTap, Veth
|
from core.nodes.interface import GreTap, Veth
|
||||||
|
|
||||||
utils.check_executables(
|
|
||||||
[constants.BRCTL_BIN, constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN]
|
|
||||||
)
|
|
||||||
|
|
||||||
ebtables_lock = threading.Lock()
|
ebtables_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
@ -314,12 +311,8 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "addbr", self.brname])
|
self.net_client.create_bridge(self.brname)
|
||||||
|
|
||||||
# turn off spanning tree protocol and forwarding delay
|
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "stp", self.brname, "off"])
|
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "setfd", self.brname, "0"])
|
|
||||||
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(
|
ebtablescmds(
|
||||||
utils.check_cmd,
|
utils.check_cmd,
|
||||||
|
@ -336,11 +329,6 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
|
|
||||||
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
|
|
||||||
if os.path.exists(snoop):
|
|
||||||
with open(snoop, "w") as snoop_file:
|
|
||||||
snoop_file.write("0")
|
|
||||||
|
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
|
@ -356,8 +344,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
ebq.stopupdateloop(self)
|
ebq.stopupdateloop(self)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "down"])
|
self.net_client.delete_bridge(self.brname)
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "delbr", self.brname])
|
|
||||||
ebtablescmds(
|
ebtablescmds(
|
||||||
utils.check_cmd,
|
utils.check_cmd,
|
||||||
[
|
[
|
||||||
|
@ -385,7 +372,8 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
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
|
# 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.
|
||||||
|
@ -394,10 +382,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd(
|
self.net_client.create_interface(self.brname, netif.localname)
|
||||||
[constants.BRCTL_BIN, "addif", self.brname, netif.localname]
|
|
||||||
)
|
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
|
|
||||||
|
|
||||||
CoreNetworkBase.attach(self, netif)
|
CoreNetworkBase.attach(self, netif)
|
||||||
|
|
||||||
|
@ -409,9 +394,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd(
|
self.net_client.delete_interface(self.brname, netif.localname)
|
||||||
[constants.BRCTL_BIN, "delif", self.brname, netif.localname]
|
|
||||||
)
|
|
||||||
|
|
||||||
CoreNetworkBase.detach(self, netif)
|
CoreNetworkBase.detach(self, netif)
|
||||||
|
|
||||||
|
@ -610,10 +593,8 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
)
|
)
|
||||||
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 of localname
|
||||||
# of localname
|
self.net_client.create_interface(net.brname, netif.name)
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "addif", net.brname, netif.name])
|
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", netif.name, "up"])
|
|
||||||
i = net.newifindex()
|
i = net.newifindex()
|
||||||
net._netif[i] = netif
|
net._netif[i] = netif
|
||||||
with net._linked_lock:
|
with net._linked_lock:
|
||||||
|
@ -648,9 +629,7 @@ class CoreNetwork(CoreNetworkBase):
|
||||||
return
|
return
|
||||||
|
|
||||||
for addr in addrlist:
|
for addr in addrlist:
|
||||||
utils.check_cmd(
|
self.net_client.create_address(self.brname, str(addr))
|
||||||
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.brname]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class GreTapBridge(CoreNetwork):
|
class GreTapBridge(CoreNetwork):
|
||||||
|
@ -822,8 +801,8 @@ class CtrlNet(CoreNetwork):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
if self.detectoldbridge():
|
if self.net_client.existing_bridges(self.id):
|
||||||
return
|
raise CoreError("old bridges exist for node: %s" % self.id)
|
||||||
|
|
||||||
CoreNetwork.startup(self)
|
CoreNetwork.startup(self)
|
||||||
|
|
||||||
|
@ -848,42 +827,7 @@ class CtrlNet(CoreNetwork):
|
||||||
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
||||||
|
|
||||||
if self.serverintf:
|
if self.serverintf:
|
||||||
# sets the interface as a port of the bridge
|
self.net_client.create_interface(self.brname, self.serverintf)
|
||||||
utils.check_cmd(
|
|
||||||
[constants.BRCTL_BIN, "addif", self.brname, self.serverintf]
|
|
||||||
)
|
|
||||||
|
|
||||||
# bring interface up
|
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
|
||||||
|
|
||||||
def detectoldbridge(self):
|
|
||||||
"""
|
|
||||||
Occasionally, control net bridges from previously closed sessions are not cleaned up.
|
|
||||||
Check if there are old control net bridges and delete them
|
|
||||||
|
|
||||||
:return: True if an old bridge was detected, False otherwise
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
status, output = utils.cmd_output([constants.BRCTL_BIN, "show"])
|
|
||||||
if status != 0:
|
|
||||||
logging.error("Unable to retrieve list of installed bridges")
|
|
||||||
else:
|
|
||||||
lines = output.split("\n")
|
|
||||||
for line in lines[1:]:
|
|
||||||
cols = line.split("\t")
|
|
||||||
oldbr = cols[0]
|
|
||||||
flds = cols[0].split(".")
|
|
||||||
if len(flds) == 3:
|
|
||||||
if flds[0] == "b" and flds[1] == self.id:
|
|
||||||
logging.error(
|
|
||||||
"error: An active control net bridge (%s) found. "
|
|
||||||
"An older session might still be running. "
|
|
||||||
"Stop all sessions and, if needed, delete %s to continue.",
|
|
||||||
oldbr,
|
|
||||||
oldbr,
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""
|
"""
|
||||||
|
@ -893,9 +837,7 @@ class CtrlNet(CoreNetwork):
|
||||||
"""
|
"""
|
||||||
if self.serverintf is not None:
|
if self.serverintf is not None:
|
||||||
try:
|
try:
|
||||||
utils.check_cmd(
|
self.net_client.delete_interface(self.brname, self.serverintf)
|
||||||
[constants.BRCTL_BIN, "delif", self.brname, self.serverintf]
|
|
||||||
)
|
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception(
|
logging.exception(
|
||||||
"error deleting server interface %s from bridge %s",
|
"error deleting server interface %s from bridge %s",
|
||||||
|
@ -1100,7 +1042,7 @@ class HubNode(CoreNetwork):
|
||||||
|
|
||||||
# TODO: move to startup method
|
# TODO: move to startup method
|
||||||
if start:
|
if start:
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
|
self.net_client.disable_mac_learning(self.brname)
|
||||||
|
|
||||||
|
|
||||||
class WlanNode(CoreNetwork):
|
class WlanNode(CoreNetwork):
|
||||||
|
@ -1131,7 +1073,7 @@ class WlanNode(CoreNetwork):
|
||||||
|
|
||||||
# TODO: move to startup method
|
# TODO: move to startup method
|
||||||
if start:
|
if start:
|
||||||
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
|
self.net_client.disable_mac_learning(self.brname)
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
"""
|
|
||||||
Provides default node maps that can be used to run core with.
|
|
||||||
"""
|
|
||||||
import core.nodes.base
|
|
||||||
import core.nodes.docker
|
|
||||||
import core.nodes.lxd
|
|
||||||
import core.nodes.network
|
|
||||||
import core.nodes.physical
|
|
||||||
from core.emane.nodes import EmaneNet, EmaneNode
|
|
||||||
from core.emulator.enumerations import NodeTypes
|
|
||||||
from core.nodes import physical
|
|
||||||
from core.nodes.network import GreTapBridge
|
|
||||||
|
|
||||||
# legacy core nodes, that leverage linux bridges
|
|
||||||
NODES = {
|
|
||||||
NodeTypes.DEFAULT: core.nodes.base.CoreNode,
|
|
||||||
NodeTypes.PHYSICAL: physical.PhysicalNode,
|
|
||||||
NodeTypes.TBD: None,
|
|
||||||
NodeTypes.SWITCH: core.nodes.network.SwitchNode,
|
|
||||||
NodeTypes.HUB: core.nodes.network.HubNode,
|
|
||||||
NodeTypes.WIRELESS_LAN: core.nodes.network.WlanNode,
|
|
||||||
NodeTypes.RJ45: core.nodes.physical.Rj45Node,
|
|
||||||
NodeTypes.TUNNEL: core.nodes.network.TunnelNode,
|
|
||||||
NodeTypes.KTUNNEL: None,
|
|
||||||
NodeTypes.EMANE: EmaneNode,
|
|
||||||
NodeTypes.EMANE_NET: EmaneNet,
|
|
||||||
NodeTypes.TAP_BRIDGE: GreTapBridge,
|
|
||||||
NodeTypes.PEER_TO_PEER: core.nodes.network.PtpNet,
|
|
||||||
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet,
|
|
||||||
NodeTypes.DOCKER: core.nodes.docker.DockerNode,
|
|
||||||
NodeTypes.LXC: core.nodes.lxd.LxcNode,
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
"""
|
|
||||||
Serves as a global point for storing and retrieving node types needed during simulation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
_NODE_MAP = None
|
|
||||||
|
|
||||||
|
|
||||||
def _log_map():
|
|
||||||
global _NODE_MAP
|
|
||||||
for key in _NODE_MAP:
|
|
||||||
value = _NODE_MAP[key]
|
|
||||||
name = None
|
|
||||||
if value:
|
|
||||||
name = value.__name__
|
|
||||||
logging.debug("node type (%s) - class (%s)", key.name, name)
|
|
||||||
|
|
||||||
|
|
||||||
def _convert_map(x, y):
|
|
||||||
"""
|
|
||||||
Convenience method to create a human readable version of the node map to log.
|
|
||||||
|
|
||||||
:param dict x: dictionary to reduce node items into
|
|
||||||
:param tuple y: current node item
|
|
||||||
:return: human readable name mapping of the node map
|
|
||||||
"""
|
|
||||||
x[y[0].name] = y[1]
|
|
||||||
return x
|
|
||||||
|
|
||||||
|
|
||||||
def update_node_map(node_map):
|
|
||||||
"""
|
|
||||||
Update the current node map with the provided node map values.
|
|
||||||
|
|
||||||
:param dict node_map: node map to update with
|
|
||||||
"""
|
|
||||||
global _NODE_MAP
|
|
||||||
_NODE_MAP.update(node_map)
|
|
||||||
_log_map()
|
|
||||||
|
|
||||||
|
|
||||||
def set_node_map(node_map):
|
|
||||||
"""
|
|
||||||
Set the global node map that proides a consistent way to retrieve differently configured nodes.
|
|
||||||
|
|
||||||
:param dict node_map: node map to set to
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
global _NODE_MAP
|
|
||||||
_NODE_MAP = node_map
|
|
||||||
_log_map()
|
|
||||||
|
|
||||||
|
|
||||||
def get_node_class(node_type):
|
|
||||||
"""
|
|
||||||
Retrieve the node class for a given node type.
|
|
||||||
|
|
||||||
:param int node_type: node type to retrieve class for
|
|
||||||
:return: node class
|
|
||||||
"""
|
|
||||||
global _NODE_MAP
|
|
||||||
return _NODE_MAP[node_type]
|
|
||||||
|
|
||||||
|
|
||||||
def get_node_type(node_class):
|
|
||||||
"""
|
|
||||||
Retrieve the node type given a node class.
|
|
||||||
|
|
||||||
:param class node_class: node class to get type for
|
|
||||||
:return: node type
|
|
||||||
:rtype: core.emulator.enumerations.NodeTypes
|
|
||||||
"""
|
|
||||||
global _NODE_MAP
|
|
||||||
node_type_map = {_NODE_MAP[x]: x for x in _NODE_MAP}
|
|
||||||
return node_type_map.get(node_class)
|
|
||||||
|
|
||||||
|
|
||||||
def is_node(obj, node_types):
|
|
||||||
"""
|
|
||||||
Validates if an object is one of the provided node types.
|
|
||||||
|
|
||||||
:param obj: object to check type for
|
|
||||||
:param int|tuple|list node_types: node type(s) to check against
|
|
||||||
:return: True if the object is one of the node types, False otherwise
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
type_classes = []
|
|
||||||
if isinstance(node_types, (tuple, list)):
|
|
||||||
for node_type in node_types:
|
|
||||||
type_class = get_node_class(node_type)
|
|
||||||
type_classes.append(type_class)
|
|
||||||
else:
|
|
||||||
type_class = get_node_class(node_types)
|
|
||||||
type_classes.append(type_class)
|
|
||||||
|
|
||||||
return isinstance(obj, tuple(type_classes))
|
|
|
@ -1,825 +0,0 @@
|
||||||
"""
|
|
||||||
TODO: probably goes away, or implement the usage of "unshare", or docker formal.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import socket
|
|
||||||
import threading
|
|
||||||
from socket import AF_INET, AF_INET6
|
|
||||||
|
|
||||||
from core import CoreCommandError, constants, utils
|
|
||||||
from core.emulator.data import LinkData
|
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
|
||||||
from core.nodes import ipaddress
|
|
||||||
from core.nodes.base import CoreNetworkBase
|
|
||||||
from core.nodes.interface import GreTap, Veth
|
|
||||||
from core.nodes.network import EbtablesQueue, GreTapBridge
|
|
||||||
|
|
||||||
# a global object because all WLANs share the same queue
|
|
||||||
# cannot have multiple threads invoking the ebtables commnd
|
|
||||||
ebtables_queue = EbtablesQueue()
|
|
||||||
|
|
||||||
ebtables_lock = threading.Lock()
|
|
||||||
|
|
||||||
utils.check_executables([constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN])
|
|
||||||
|
|
||||||
|
|
||||||
def ebtables_commands(call, commands):
|
|
||||||
with ebtables_lock:
|
|
||||||
for command in commands:
|
|
||||||
call(command)
|
|
||||||
|
|
||||||
|
|
||||||
class OvsNet(CoreNetworkBase):
|
|
||||||
"""
|
|
||||||
Used to be LxBrNet.
|
|
||||||
|
|
||||||
Base class for providing Openvswitch functionality to objects that create bridges.
|
|
||||||
"""
|
|
||||||
|
|
||||||
policy = "DROP"
|
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
|
||||||
"""
|
|
||||||
Creates an OvsNet instance.
|
|
||||||
|
|
||||||
:param core.emulator.session.Session session: session this object is a part of
|
|
||||||
:param int _id: object id
|
|
||||||
:param str name: object name
|
|
||||||
:param bool start: start flag
|
|
||||||
:param policy: network policy
|
|
||||||
"""
|
|
||||||
|
|
||||||
CoreNetworkBase.__init__(self, session, _id, name, start)
|
|
||||||
|
|
||||||
if policy:
|
|
||||||
self.policy = policy
|
|
||||||
else:
|
|
||||||
self.policy = self.__class__.policy
|
|
||||||
|
|
||||||
session_id = self.session.short_session_id()
|
|
||||||
self.bridge_name = "b.%s.%s" % (str(self.id), session_id)
|
|
||||||
self.up = False
|
|
||||||
|
|
||||||
if start:
|
|
||||||
self.startup()
|
|
||||||
ebtables_queue.startupdateloop(self)
|
|
||||||
|
|
||||||
def startup(self):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:return:
|
|
||||||
:raises CoreCommandError: when there is a command exception
|
|
||||||
"""
|
|
||||||
utils.check_cmd([constants.OVS_BIN, "add-br", self.bridge_name])
|
|
||||||
|
|
||||||
# turn off spanning tree protocol and forwarding delay
|
|
||||||
# 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.IP_BIN, "link", "set", self.bridge_name, "up"])
|
|
||||||
|
|
||||||
# create a new ebtables chain for this bridge
|
|
||||||
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
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
if not self.up:
|
|
||||||
logging.info("exiting shutdown, object is not up")
|
|
||||||
return
|
|
||||||
|
|
||||||
ebtables_queue.stopupdateloop(self)
|
|
||||||
|
|
||||||
try:
|
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
|
|
||||||
utils.check_cmd([constants.OVS_BIN, "del-br", self.bridge_name])
|
|
||||||
ebtables_commands(
|
|
||||||
utils.check_cmd,
|
|
||||||
[
|
|
||||||
[
|
|
||||||
constants.EBTABLES_BIN,
|
|
||||||
"-D",
|
|
||||||
"FORWARD",
|
|
||||||
"--logical-in",
|
|
||||||
self.bridge_name,
|
|
||||||
"-j",
|
|
||||||
self.bridge_name,
|
|
||||||
],
|
|
||||||
[constants.EBTABLES_BIN, "-X", self.bridge_name],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
except CoreCommandError:
|
|
||||||
logging.exception("error bringing bridge down and removing it")
|
|
||||||
|
|
||||||
# removes veth pairs used for bridge-to-bridge connections
|
|
||||||
for interface in self.netifs():
|
|
||||||
interface.shutdown()
|
|
||||||
|
|
||||||
self._netif.clear()
|
|
||||||
self._linked.clear()
|
|
||||||
del self.session
|
|
||||||
self.up = False
|
|
||||||
|
|
||||||
def attach(self, interface):
|
|
||||||
if self.up:
|
|
||||||
utils.check_cmd(
|
|
||||||
[constants.OVS_BIN, "add-port", self.bridge_name, interface.localname]
|
|
||||||
)
|
|
||||||
utils.check_cmd(
|
|
||||||
[constants.IP_BIN, "link", "set", interface.localname, "up"]
|
|
||||||
)
|
|
||||||
|
|
||||||
CoreNetworkBase.attach(self, interface)
|
|
||||||
|
|
||||||
def detach(self, interface):
|
|
||||||
if self.up:
|
|
||||||
utils.check_cmd(
|
|
||||||
[constants.OVS_BIN, "del-port", self.bridge_name, interface.localname]
|
|
||||||
)
|
|
||||||
|
|
||||||
CoreNetworkBase.detach(self, interface)
|
|
||||||
|
|
||||||
def linked(self, interface_one, interface_two):
|
|
||||||
# check if the network interfaces are attached to this network
|
|
||||||
if self._netif[interface_one.netifi] != interface_one:
|
|
||||||
raise ValueError("inconsistency for interface %s" % interface_one.name)
|
|
||||||
|
|
||||||
if self._netif[interface_two.netifi] != interface_two:
|
|
||||||
raise ValueError("inconsistency for interface %s" % interface_two.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
linked = self._linked[interface_one][interface_two]
|
|
||||||
except KeyError:
|
|
||||||
if self.policy == "ACCEPT":
|
|
||||||
linked = True
|
|
||||||
elif self.policy == "DROP":
|
|
||||||
linked = False
|
|
||||||
else:
|
|
||||||
raise ValueError("unknown policy: %s" % self.policy)
|
|
||||||
|
|
||||||
self._linked[interface_one][interface_two] = linked
|
|
||||||
|
|
||||||
return linked
|
|
||||||
|
|
||||||
def unlink(self, interface_one, interface_two):
|
|
||||||
"""
|
|
||||||
Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
|
|
||||||
filtering rules.
|
|
||||||
"""
|
|
||||||
with self._linked_lock:
|
|
||||||
if not self.linked(interface_one, interface_two):
|
|
||||||
return
|
|
||||||
|
|
||||||
self._linked[interface_one][interface_two] = False
|
|
||||||
|
|
||||||
ebtables_queue.ebchange(self)
|
|
||||||
|
|
||||||
def link(self, interface_one, interface_two):
|
|
||||||
"""
|
|
||||||
Link two interfaces together, resulting in adding or removing
|
|
||||||
ebtables filtering rules.
|
|
||||||
"""
|
|
||||||
with self._linked_lock:
|
|
||||||
if self.linked(interface_one, interface_two):
|
|
||||||
return
|
|
||||||
|
|
||||||
self._linked[interface_one][interface_two] = True
|
|
||||||
|
|
||||||
ebtables_queue.ebchange(self)
|
|
||||||
|
|
||||||
def linkconfig(
|
|
||||||
self,
|
|
||||||
netif,
|
|
||||||
bw=None,
|
|
||||||
delay=None,
|
|
||||||
loss=None,
|
|
||||||
duplicate=None,
|
|
||||||
jitter=None,
|
|
||||||
netif2=None,
|
|
||||||
devname=None,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Configure link parameters by applying tc queuing disciplines on the
|
|
||||||
interface.
|
|
||||||
"""
|
|
||||||
if not devname:
|
|
||||||
devname = netif.localname
|
|
||||||
|
|
||||||
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
|
|
||||||
parent = ["root"]
|
|
||||||
|
|
||||||
# attempt to set bandwidth and update as needed if value changed
|
|
||||||
bandwidth_changed = netif.setparam("bw", bw)
|
|
||||||
if bandwidth_changed:
|
|
||||||
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
|
||||||
if bw > 0:
|
|
||||||
if self.up:
|
|
||||||
burst = max(2 * netif.mtu, bw / 1000)
|
|
||||||
limit = 0xFFFF # max IP payload
|
|
||||||
tbf = [
|
|
||||||
"tbf",
|
|
||||||
"rate",
|
|
||||||
str(bw),
|
|
||||||
"burst",
|
|
||||||
str(burst),
|
|
||||||
"limit",
|
|
||||||
str(limit),
|
|
||||||
]
|
|
||||||
logging.info(
|
|
||||||
"linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf]
|
|
||||||
)
|
|
||||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
|
||||||
netif.setparam("has_tbf", True)
|
|
||||||
elif netif.getparam("has_tbf") and bw <= 0:
|
|
||||||
tcd = [] + tc
|
|
||||||
tcd[2] = "delete"
|
|
||||||
|
|
||||||
if self.up:
|
|
||||||
utils.check_cmd(tcd + parent)
|
|
||||||
|
|
||||||
netif.setparam("has_tbf", False)
|
|
||||||
# removing the parent removes the child
|
|
||||||
netif.setparam("has_netem", False)
|
|
||||||
|
|
||||||
if netif.getparam("has_tbf"):
|
|
||||||
parent = ["parent", "1:1"]
|
|
||||||
|
|
||||||
netem = ["netem"]
|
|
||||||
delay_changed = netif.setparam("delay", delay)
|
|
||||||
|
|
||||||
if loss is not None:
|
|
||||||
loss = float(loss)
|
|
||||||
loss_changed = netif.setparam("loss", loss)
|
|
||||||
|
|
||||||
if duplicate is not None:
|
|
||||||
duplicate = int(duplicate)
|
|
||||||
duplicate_changed = netif.setparam("duplicate", duplicate)
|
|
||||||
jitter_changed = netif.setparam("jitter", jitter)
|
|
||||||
|
|
||||||
# if nothing changed return
|
|
||||||
if not any(
|
|
||||||
[
|
|
||||||
bandwidth_changed,
|
|
||||||
delay_changed,
|
|
||||||
loss_changed,
|
|
||||||
duplicate_changed,
|
|
||||||
jitter_changed,
|
|
||||||
]
|
|
||||||
):
|
|
||||||
return
|
|
||||||
|
|
||||||
# jitter and delay use the same delay statement
|
|
||||||
if delay is not None:
|
|
||||||
netem += ["delay", "%sus" % delay]
|
|
||||||
else:
|
|
||||||
netem += ["delay", "0us"]
|
|
||||||
|
|
||||||
if jitter is not None:
|
|
||||||
netem += ["%sus" % jitter, "25%"]
|
|
||||||
|
|
||||||
if loss is not None and loss > 0:
|
|
||||||
netem += ["loss", "%s%%" % min(loss, 100)]
|
|
||||||
|
|
||||||
if duplicate is not None and duplicate > 0:
|
|
||||||
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
|
|
||||||
|
|
||||||
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
|
|
||||||
# possibly remove netem if it exists and parent queue wasn"t removed
|
|
||||||
if not netif.getparam("has_netem"):
|
|
||||||
return
|
|
||||||
|
|
||||||
tc[2] = "delete"
|
|
||||||
|
|
||||||
if self.up:
|
|
||||||
logging.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"])
|
|
||||||
netif.setparam("has_netem", False)
|
|
||||||
elif len(netem) > 1:
|
|
||||||
if self.up:
|
|
||||||
logging.info(
|
|
||||||
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
|
|
||||||
)
|
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
|
||||||
netif.setparam("has_netem", True)
|
|
||||||
|
|
||||||
def linknet(self, network):
|
|
||||||
"""
|
|
||||||
Link this bridge with another by creating a veth pair and installing
|
|
||||||
each device into each bridge.
|
|
||||||
"""
|
|
||||||
session_id = self.session.short_session_id()
|
|
||||||
|
|
||||||
try:
|
|
||||||
_id = "%x" % self.id
|
|
||||||
except TypeError:
|
|
||||||
_id = "%s" % self.id
|
|
||||||
|
|
||||||
try:
|
|
||||||
network_id = "%x" % network.id
|
|
||||||
except TypeError:
|
|
||||||
network_id = "%s" % network.id
|
|
||||||
|
|
||||||
localname = "veth%s.%s.%s" % (_id, network_id, session_id)
|
|
||||||
|
|
||||||
if len(localname) >= 16:
|
|
||||||
raise ValueError("interface local name %s too long" % localname)
|
|
||||||
|
|
||||||
name = "veth%s.%s.%s" % (network_id, _id, session_id)
|
|
||||||
if len(name) >= 16:
|
|
||||||
raise ValueError("interface name %s too long" % name)
|
|
||||||
|
|
||||||
interface = Veth(
|
|
||||||
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
|
|
||||||
)
|
|
||||||
self.attach(interface)
|
|
||||||
if network.up:
|
|
||||||
# this is similar to net.attach() but uses netif.name instead of localname
|
|
||||||
utils.check_cmd(
|
|
||||||
[constants.OVS_BIN, "add-port", network.bridge_name, interface.name]
|
|
||||||
)
|
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
|
||||||
|
|
||||||
network.attach(interface)
|
|
||||||
interface.net = self
|
|
||||||
interface.othernet = network
|
|
||||||
return interface
|
|
||||||
|
|
||||||
def getlinknetif(self, network):
|
|
||||||
"""
|
|
||||||
Return the interface of that links this net with another net
|
|
||||||
(that were linked using linknet()).
|
|
||||||
"""
|
|
||||||
for interface in self.netifs():
|
|
||||||
if hasattr(interface, "othernet") and interface.othernet == network:
|
|
||||||
return interface
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def addrconfig(self, addresses):
|
|
||||||
"""
|
|
||||||
Set addresses on the bridge.
|
|
||||||
"""
|
|
||||||
if not self.up:
|
|
||||||
return
|
|
||||||
|
|
||||||
for address in addresses:
|
|
||||||
utils.check_cmd(
|
|
||||||
[constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OvsCtrlNet(OvsNet):
|
|
||||||
policy = "ACCEPT"
|
|
||||||
CTRLIF_IDX_BASE = 99 # base control interface index
|
|
||||||
DEFAULT_PREFIX_LIST = [
|
|
||||||
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
|
||||||
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
|
||||||
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
|
||||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24",
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
session,
|
|
||||||
_id="ctrlnet",
|
|
||||||
name=None,
|
|
||||||
prefix=None,
|
|
||||||
hostid=None,
|
|
||||||
start=True,
|
|
||||||
assign_address=True,
|
|
||||||
updown_script=None,
|
|
||||||
serverintf=None,
|
|
||||||
):
|
|
||||||
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
|
||||||
self.hostid = hostid
|
|
||||||
self.assign_address = assign_address
|
|
||||||
self.updown_script = updown_script
|
|
||||||
self.serverintf = serverintf
|
|
||||||
OvsNet.__init__(self, session, _id=_id, name=name, start=start)
|
|
||||||
|
|
||||||
def startup(self):
|
|
||||||
if self.detectoldbridge():
|
|
||||||
return
|
|
||||||
|
|
||||||
OvsNet.startup(self)
|
|
||||||
if self.hostid:
|
|
||||||
addr = self.prefix.addr(self.hostid)
|
|
||||||
else:
|
|
||||||
addr = self.prefix.max_addr()
|
|
||||||
|
|
||||||
message = "Added control network bridge: %s %s" % (
|
|
||||||
self.bridge_name,
|
|
||||||
self.prefix,
|
|
||||||
)
|
|
||||||
addresses = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
|
||||||
if self.assign_address:
|
|
||||||
self.addrconfig(addresses=addresses)
|
|
||||||
message += " address %s" % addr
|
|
||||||
logging.info(message)
|
|
||||||
|
|
||||||
if self.updown_script:
|
|
||||||
logging.info(
|
|
||||||
"interface %s updown script %s startup called"
|
|
||||||
% (self.bridge_name, self.updown_script)
|
|
||||||
)
|
|
||||||
utils.check_cmd([self.updown_script, self.bridge_name, "startup"])
|
|
||||||
|
|
||||||
if 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"])
|
|
||||||
|
|
||||||
def detectoldbridge(self):
|
|
||||||
"""
|
|
||||||
Occasionally, control net bridges from previously closed sessions are not cleaned up.
|
|
||||||
Check if there are old control net bridges and delete them
|
|
||||||
"""
|
|
||||||
|
|
||||||
output = utils.check_cmd([constants.OVS_BIN, "list-br"])
|
|
||||||
output = output.strip()
|
|
||||||
if output:
|
|
||||||
for line in output.split("\n"):
|
|
||||||
bride_name = line.split(".")
|
|
||||||
if bride_name[0] == "b" and bride_name[1] == self.id:
|
|
||||||
logging.error(
|
|
||||||
"older session may still be running with conflicting id for bridge: %s",
|
|
||||||
line,
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
if self.serverintf:
|
|
||||||
try:
|
|
||||||
utils.check_cmd(
|
|
||||||
[constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf]
|
|
||||||
)
|
|
||||||
except CoreCommandError:
|
|
||||||
logging.exception(
|
|
||||||
"error deleting server interface %s to controlnet bridge %s",
|
|
||||||
self.serverintf,
|
|
||||||
self.bridge_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.updown_script:
|
|
||||||
try:
|
|
||||||
logging.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 CoreCommandError:
|
|
||||||
logging.exception("error during updown script shutdown")
|
|
||||||
|
|
||||||
OvsNet.shutdown(self)
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
|
||||||
"""
|
|
||||||
Do not include CtrlNet in link messages describing this session.
|
|
||||||
"""
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
class OvsPtpNet(OvsNet):
|
|
||||||
policy = "ACCEPT"
|
|
||||||
|
|
||||||
def attach(self, interface):
|
|
||||||
if len(self._netif) >= 2:
|
|
||||||
raise ValueError(
|
|
||||||
"point-to-point links support at most 2 network interfaces"
|
|
||||||
)
|
|
||||||
OvsNet.attach(self, interface)
|
|
||||||
|
|
||||||
def data(self, message_type, lat=None, lon=None, alt=None):
|
|
||||||
"""
|
|
||||||
Do not generate a Node Message for point-to-point links. They are
|
|
||||||
built using a link message instead.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
|
||||||
"""
|
|
||||||
Build CORE API TLVs for a point-to-point link. One Link message describes this network.
|
|
||||||
"""
|
|
||||||
|
|
||||||
all_links = []
|
|
||||||
|
|
||||||
if len(self._netif) != 2:
|
|
||||||
return all_links
|
|
||||||
|
|
||||||
if1, if2 = self._netif.values()
|
|
||||||
|
|
||||||
unidirectional = 0
|
|
||||||
if if1.getparams() != if2.getparams():
|
|
||||||
unidirectional = 1
|
|
||||||
|
|
||||||
interface1_ip4 = None
|
|
||||||
interface1_ip4_mask = None
|
|
||||||
interface1_ip6 = None
|
|
||||||
interface1_ip6_mask = None
|
|
||||||
for address in if1.addrlist:
|
|
||||||
ip, _sep, mask = address.partition("/")
|
|
||||||
mask = int(mask)
|
|
||||||
if ipaddress.is_ipv4_address(ip):
|
|
||||||
family = AF_INET
|
|
||||||
ipl = socket.inet_pton(family, ip)
|
|
||||||
interface1_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
|
||||||
interface1_ip4_mask = mask
|
|
||||||
else:
|
|
||||||
family = AF_INET6
|
|
||||||
ipl = socket.inet_pton(family, ip)
|
|
||||||
interface1_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
|
||||||
interface1_ip6_mask = mask
|
|
||||||
|
|
||||||
interface2_ip4 = None
|
|
||||||
interface2_ip4_mask = None
|
|
||||||
interface2_ip6 = None
|
|
||||||
interface2_ip6_mask = None
|
|
||||||
for address in if2.addrlist:
|
|
||||||
ip, _sep, mask = address.partition("/")
|
|
||||||
mask = int(mask)
|
|
||||||
if ipaddress.is_ipv4_address(ip):
|
|
||||||
family = AF_INET
|
|
||||||
ipl = socket.inet_pton(family, ip)
|
|
||||||
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
|
||||||
interface2_ip4_mask = mask
|
|
||||||
else:
|
|
||||||
family = AF_INET6
|
|
||||||
ipl = socket.inet_pton(family, ip)
|
|
||||||
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
|
||||||
interface2_ip6_mask = mask
|
|
||||||
|
|
||||||
# TODO: not currently used
|
|
||||||
# loss=netif.getparam("loss")
|
|
||||||
link_data = LinkData(
|
|
||||||
message_type=flags,
|
|
||||||
node1_id=if1.node.id,
|
|
||||||
node2_id=if2.node.id,
|
|
||||||
link_type=self.linktype,
|
|
||||||
unidirectional=unidirectional,
|
|
||||||
delay=if1.getparam("delay"),
|
|
||||||
bandwidth=if1.getparam("bw"),
|
|
||||||
dup=if1.getparam("duplicate"),
|
|
||||||
jitter=if1.getparam("jitter"),
|
|
||||||
interface1_id=if1.node.getifindex(if1),
|
|
||||||
interface1_mac=if1.hwaddr,
|
|
||||||
interface1_ip4=interface1_ip4,
|
|
||||||
interface1_ip4_mask=interface1_ip4_mask,
|
|
||||||
interface1_ip6=interface1_ip6,
|
|
||||||
interface1_ip6_mask=interface1_ip6_mask,
|
|
||||||
interface2_id=if2.node.getifindex(if2),
|
|
||||||
interface2_mac=if2.hwaddr,
|
|
||||||
interface2_ip4=interface2_ip4,
|
|
||||||
interface2_ip4_mask=interface2_ip4_mask,
|
|
||||||
interface2_ip6=interface2_ip6,
|
|
||||||
interface2_ip6_mask=interface2_ip6_mask,
|
|
||||||
)
|
|
||||||
|
|
||||||
all_links.append(link_data)
|
|
||||||
|
|
||||||
# build a 2nd link message for the upstream link parameters
|
|
||||||
# (swap if1 and if2)
|
|
||||||
if unidirectional:
|
|
||||||
link_data = LinkData(
|
|
||||||
message_type=0,
|
|
||||||
node1_id=if2.node.id,
|
|
||||||
node2_id=if1.node.id,
|
|
||||||
delay=if1.getparam("delay"),
|
|
||||||
bandwidth=if1.getparam("bw"),
|
|
||||||
dup=if1.getparam("duplicate"),
|
|
||||||
jitter=if1.getparam("jitter"),
|
|
||||||
unidirectional=1,
|
|
||||||
interface1_id=if2.node.getifindex(if2),
|
|
||||||
interface2_id=if1.node.getifindex(if1),
|
|
||||||
)
|
|
||||||
all_links.append(link_data)
|
|
||||||
|
|
||||||
return all_links
|
|
||||||
|
|
||||||
|
|
||||||
class OvsSwitchNode(OvsNet):
|
|
||||||
apitype = NodeTypes.SWITCH.value
|
|
||||||
policy = "ACCEPT"
|
|
||||||
type = "lanswitch"
|
|
||||||
|
|
||||||
|
|
||||||
class OvsHubNode(OvsNet):
|
|
||||||
apitype = NodeTypes.HUB.value
|
|
||||||
policy = "ACCEPT"
|
|
||||||
type = "hub"
|
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, start=True):
|
|
||||||
"""
|
|
||||||
the Hub node forwards packets to all bridge ports by turning off
|
|
||||||
the MAC address learning
|
|
||||||
"""
|
|
||||||
OvsNet.__init__(self, session, _id, name, start)
|
|
||||||
|
|
||||||
if start:
|
|
||||||
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
|
|
||||||
# TODO: replace "brctl setageing 0"
|
|
||||||
utils.check_cmd(
|
|
||||||
[constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OvsWlanNode(OvsNet):
|
|
||||||
apitype = NodeTypes.WIRELESS_LAN.value
|
|
||||||
linktype = LinkTypes.WIRELESS.value
|
|
||||||
policy = "DROP"
|
|
||||||
type = "wlan"
|
|
||||||
|
|
||||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
|
||||||
OvsNet.__init__(self, session, _id, name, start, policy)
|
|
||||||
|
|
||||||
# wireless model such as basic range
|
|
||||||
self.model = None
|
|
||||||
# mobility model such as scripted
|
|
||||||
self.mobility = None
|
|
||||||
|
|
||||||
def attach(self, interface):
|
|
||||||
OvsNet.attach(self, interface)
|
|
||||||
|
|
||||||
if self.model:
|
|
||||||
interface.poshook = self.model.position_callback
|
|
||||||
|
|
||||||
if interface.node is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
x, y, z = interface.node.position.get()
|
|
||||||
# invokes any netif.poshook
|
|
||||||
interface.setposition(x, y, z)
|
|
||||||
# self.model.setlinkparams()
|
|
||||||
|
|
||||||
def setmodel(self, model, config=None):
|
|
||||||
"""
|
|
||||||
Mobility and wireless model.
|
|
||||||
"""
|
|
||||||
logging.info("adding model %s", model.name)
|
|
||||||
|
|
||||||
if model.type == RegisterTlvs.WIRELESS.value:
|
|
||||||
self.model = model(session=self.session, _id=self.id, config=config)
|
|
||||||
if self.model.position_callback:
|
|
||||||
for interface in self.netifs():
|
|
||||||
interface.poshook = self.model.position_callback
|
|
||||||
if interface.node is not None:
|
|
||||||
x, y, z = interface.node.position.get()
|
|
||||||
interface.poshook(interface, x, y, z)
|
|
||||||
self.model.setlinkparams()
|
|
||||||
elif model.type == RegisterTlvs.MOBILITY.value:
|
|
||||||
self.mobility = model(session=self.session, _id=self.id, config=config)
|
|
||||||
|
|
||||||
def updatemodel(self, config):
|
|
||||||
if not self.model:
|
|
||||||
raise ValueError("no model set to update for node(%s)", self.id)
|
|
||||||
logging.info(
|
|
||||||
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
|
||||||
)
|
|
||||||
self.model.set_configs(config, node_id=self.id)
|
|
||||||
if self.model.position_callback:
|
|
||||||
for netif in self.netifs():
|
|
||||||
netif.poshook = self.model.position_callback
|
|
||||||
if netif.node is not None:
|
|
||||||
x, y, z = netif.node.position.get()
|
|
||||||
netif.poshook(netif, x, y, z)
|
|
||||||
self.model.updateconfig()
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
|
||||||
all_links = OvsNet.all_link_data(self, flags)
|
|
||||||
|
|
||||||
if self.model:
|
|
||||||
all_links.extend(self.model.all_link_data(flags))
|
|
||||||
|
|
||||||
return all_links
|
|
||||||
|
|
||||||
|
|
||||||
class OvsTunnelNode(GreTapBridge):
|
|
||||||
apitype = NodeTypes.TUNNEL.value
|
|
||||||
policy = "ACCEPT"
|
|
||||||
type = "tunnel"
|
|
||||||
|
|
||||||
|
|
||||||
class OvsGreTapBridge(OvsNet):
|
|
||||||
"""
|
|
||||||
A network consisting of a bridge with a gretap device for tunneling to
|
|
||||||
another system.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
session,
|
|
||||||
remoteip=None,
|
|
||||||
_id=None,
|
|
||||||
name=None,
|
|
||||||
policy="ACCEPT",
|
|
||||||
localip=None,
|
|
||||||
ttl=255,
|
|
||||||
key=None,
|
|
||||||
start=True,
|
|
||||||
):
|
|
||||||
OvsNet.__init__(
|
|
||||||
self, session=session, _id=_id, name=name, policy=policy, start=False
|
|
||||||
)
|
|
||||||
self.grekey = key
|
|
||||||
if self.grekey is None:
|
|
||||||
self.grekey = self.session.id ^ self.id
|
|
||||||
|
|
||||||
self.localnum = None
|
|
||||||
self.remotenum = None
|
|
||||||
self.remoteip = remoteip
|
|
||||||
self.localip = localip
|
|
||||||
self.ttl = ttl
|
|
||||||
|
|
||||||
if remoteip is None:
|
|
||||||
self.gretap = None
|
|
||||||
else:
|
|
||||||
self.gretap = GreTap(
|
|
||||||
node=self,
|
|
||||||
session=session,
|
|
||||||
remoteip=remoteip,
|
|
||||||
localip=localip,
|
|
||||||
ttl=ttl,
|
|
||||||
key=self.grekey,
|
|
||||||
)
|
|
||||||
if start:
|
|
||||||
self.startup()
|
|
||||||
|
|
||||||
def startup(self):
|
|
||||||
"""
|
|
||||||
Creates a bridge and adds the gretap device to it.
|
|
||||||
"""
|
|
||||||
OvsNet.startup(self)
|
|
||||||
|
|
||||||
if self.gretap:
|
|
||||||
self.attach(self.gretap)
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
"""
|
|
||||||
Detach the gretap device and remove the bridge.
|
|
||||||
"""
|
|
||||||
if self.gretap:
|
|
||||||
self.detach(self.gretap)
|
|
||||||
self.gretap.shutdown()
|
|
||||||
self.gretap = None
|
|
||||||
|
|
||||||
OvsNet.shutdown(self)
|
|
||||||
|
|
||||||
def addrconfig(self, addresses):
|
|
||||||
"""
|
|
||||||
Set the remote tunnel endpoint. This is a one-time method for
|
|
||||||
creating the GreTap device, which requires the remoteip at startup.
|
|
||||||
The 1st address in the provided list is remoteip, 2nd optionally
|
|
||||||
specifies localip.
|
|
||||||
"""
|
|
||||||
if self.gretap:
|
|
||||||
raise ValueError("gretap already exists for %s" % self.name)
|
|
||||||
|
|
||||||
remoteip = addresses[0].split("/")[0]
|
|
||||||
localip = None
|
|
||||||
|
|
||||||
if len(addresses) > 1:
|
|
||||||
localip = addresses[1].split("/")[0]
|
|
||||||
|
|
||||||
self.gretap = GreTap(
|
|
||||||
session=self.session,
|
|
||||||
remoteip=remoteip,
|
|
||||||
localip=localip,
|
|
||||||
ttl=self.ttl,
|
|
||||||
key=self.grekey,
|
|
||||||
)
|
|
||||||
self.attach(self.gretap)
|
|
||||||
|
|
||||||
def setkey(self, key):
|
|
||||||
"""
|
|
||||||
Set the GRE key used for the GreTap device. This needs to be set
|
|
||||||
prior to instantiating the GreTap device (before addrconfig).
|
|
||||||
"""
|
|
||||||
self.grekey = key
|
|
||||||
|
|
||||||
|
|
||||||
OVS_NODES = {
|
|
||||||
NodeTypes.SWITCH: OvsSwitchNode,
|
|
||||||
NodeTypes.HUB: OvsHubNode,
|
|
||||||
NodeTypes.WIRELESS_LAN: OvsWlanNode,
|
|
||||||
NodeTypes.TUNNEL: OvsTunnelNode,
|
|
||||||
NodeTypes.TAP_BRIDGE: OvsGreTapBridge,
|
|
||||||
NodeTypes.PEER_TO_PEER: OvsPtpNet,
|
|
||||||
NodeTypes.CONTROL_NET: OvsCtrlNet,
|
|
||||||
}
|
|
|
@ -7,8 +7,9 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from core import CoreCommandError, constants, utils
|
from core import constants, utils
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
|
from core.errors import CoreCommandError
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import CoreInterface
|
||||||
from core.nodes.network import CoreNetwork, GreTap
|
from core.nodes.network import CoreNetwork, GreTap
|
||||||
|
@ -99,51 +100,33 @@ class PhysicalNode(CoreNodeBase):
|
||||||
"""
|
"""
|
||||||
Set hardware address for an interface.
|
Set hardware address for an interface.
|
||||||
"""
|
"""
|
||||||
self._netif[ifindex].sethwaddr(addr)
|
interface = self._netif[ifindex]
|
||||||
ifname = self.ifname(ifindex)
|
interface.sethwaddr(addr)
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd(
|
self.net_client.device_mac(interface.name, str(addr))
|
||||||
[constants.IP_BIN, "link", "set", "dev", ifname, "address", str(addr)]
|
|
||||||
)
|
|
||||||
|
|
||||||
def addaddr(self, ifindex, addr):
|
def addaddr(self, ifindex, addr):
|
||||||
"""
|
"""
|
||||||
Add an address to an interface.
|
Add an address to an interface.
|
||||||
"""
|
"""
|
||||||
|
interface = self._netif[ifindex]
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd(
|
self.net_client.create_address(interface.name, str(addr))
|
||||||
[
|
interface.addaddr(addr)
|
||||||
constants.IP_BIN,
|
|
||||||
"addr",
|
|
||||||
"add",
|
|
||||||
str(addr),
|
|
||||||
"dev",
|
|
||||||
self.ifname(ifindex),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self._netif[ifindex].addaddr(addr)
|
|
||||||
|
|
||||||
def deladdr(self, ifindex, addr):
|
def deladdr(self, ifindex, addr):
|
||||||
"""
|
"""
|
||||||
Delete an address from an interface.
|
Delete an address from an interface.
|
||||||
"""
|
"""
|
||||||
|
interface = self._netif[ifindex]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._netif[ifindex].deladdr(addr)
|
interface.deladdr(addr)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.exception("trying to delete unknown address: %s", addr)
|
logging.exception("trying to delete unknown address: %s", addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd(
|
self.net_client.delete_address(interface.name, str(addr))
|
||||||
[
|
|
||||||
constants.IP_BIN,
|
|
||||||
"addr",
|
|
||||||
"del",
|
|
||||||
str(addr),
|
|
||||||
"dev",
|
|
||||||
self.ifname(ifindex),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
def adoptnetif(self, netif, ifindex, hwaddr, addrlist):
|
||||||
"""
|
"""
|
||||||
|
@ -158,12 +141,8 @@ class PhysicalNode(CoreNodeBase):
|
||||||
|
|
||||||
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
# use a more reasonable name, e.g. "gt0" instead of "gt.56286.150"
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd(
|
self.net_client.device_down(netif.localname)
|
||||||
[constants.IP_BIN, "link", "set", "dev", netif.localname, "down"]
|
self.net_client.device_name(netif.localname, netif.name)
|
||||||
)
|
|
||||||
self.check_cmd(
|
|
||||||
[constants.IP_BIN, "link", "set", netif.localname, "name", netif.name]
|
|
||||||
)
|
|
||||||
|
|
||||||
netif.localname = netif.name
|
netif.localname = netif.name
|
||||||
|
|
||||||
|
@ -174,9 +153,7 @@ class PhysicalNode(CoreNodeBase):
|
||||||
self.addaddr(ifindex, addr)
|
self.addaddr(ifindex, addr)
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
self.check_cmd(
|
self.net_client.device_up(netif.localname)
|
||||||
[constants.IP_BIN, "link", "set", "dev", netif.localname, "up"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def linkconfig(
|
def linkconfig(
|
||||||
self,
|
self,
|
||||||
|
@ -334,7 +311,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
"""
|
"""
|
||||||
# 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"])
|
self.net_client.device_up(self.localname)
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
@ -348,18 +325,17 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "down"])
|
self.net_client.device_down(self.localname)
|
||||||
utils.check_cmd([constants.IP_BIN, "addr", "flush", "dev", self.localname])
|
self.net_client.device_flush(self.localname)
|
||||||
utils.check_cmd(
|
self.net_client.delete_tc(self.localname)
|
||||||
[constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"]
|
|
||||||
)
|
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logging.exception("error shutting down")
|
logging.exception("error shutting down")
|
||||||
|
|
||||||
self.up = False
|
self.up = False
|
||||||
self.restorestate()
|
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):
|
def attachnet(self, net):
|
||||||
"""
|
"""
|
||||||
Attach a network.
|
Attach a network.
|
||||||
|
@ -369,7 +345,8 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
"""
|
"""
|
||||||
CoreInterface.attachnet(self, net)
|
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):
|
def detachnet(self):
|
||||||
"""
|
"""
|
||||||
Detach a network.
|
Detach a network.
|
||||||
|
@ -475,9 +452,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd(
|
self.net_client.create_address(self.name, str(addr))
|
||||||
[constants.IP_BIN, "addr", "add", str(addr), "dev", self.name]
|
|
||||||
)
|
|
||||||
|
|
||||||
CoreInterface.addaddr(self, addr)
|
CoreInterface.addaddr(self, addr)
|
||||||
|
|
||||||
|
@ -490,9 +465,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
:raises CoreCommandError: when there is a command exception
|
:raises CoreCommandError: when there is a command exception
|
||||||
"""
|
"""
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd(
|
self.net_client.delete_address(self.name, str(addr))
|
||||||
[constants.IP_BIN, "addr", "del", str(addr), "dev", self.name]
|
|
||||||
)
|
|
||||||
|
|
||||||
CoreInterface.deladdr(self, addr)
|
CoreInterface.deladdr(self, addr)
|
||||||
|
|
||||||
|
@ -506,8 +479,7 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
"""
|
"""
|
||||||
self.old_up = False
|
self.old_up = False
|
||||||
self.old_addrs = []
|
self.old_addrs = []
|
||||||
args = [constants.IP_BIN, "addr", "show", "dev", self.localname]
|
output = self.net_client.device_show(self.localname)
|
||||||
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:
|
||||||
|
@ -533,25 +505,14 @@ class Rj45Node(CoreNodeBase, CoreInterface):
|
||||||
"""
|
"""
|
||||||
for addr in self.old_addrs:
|
for addr in self.old_addrs:
|
||||||
if addr[1] is None:
|
if addr[1] is None:
|
||||||
utils.check_cmd(
|
self.net_client.create_address(self.localname, addr[0])
|
||||||
[constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname]
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
utils.check_cmd(
|
self.net_client.create_address(
|
||||||
[
|
self.localname, addr[0], broadcast=addr[1]
|
||||||
constants.IP_BIN,
|
|
||||||
"addr",
|
|
||||||
"add",
|
|
||||||
addr[0],
|
|
||||||
"brd",
|
|
||||||
addr[1],
|
|
||||||
"dev",
|
|
||||||
self.localname,
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.old_up:
|
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):
|
def setposition(self, x=None, y=None, z=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,7 +7,8 @@ import socket
|
||||||
|
|
||||||
from future.moves.urllib.parse import urlparse
|
from future.moves.urllib.parse import urlparse
|
||||||
|
|
||||||
from core import CoreError, constants
|
from core import constants
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.enumerations import (
|
from core.emulator.enumerations import (
|
||||||
EventTypes,
|
EventTypes,
|
||||||
LinkTlvs,
|
LinkTlvs,
|
||||||
|
@ -17,8 +18,9 @@ from core.emulator.enumerations import (
|
||||||
NodeTlvs,
|
NodeTlvs,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
)
|
)
|
||||||
from core.nodes import nodeutils
|
from core.errors import CoreError
|
||||||
from core.nodes.base import CoreNetworkBase, NodeBase
|
from core.nodes.base import CoreNetworkBase, NodeBase
|
||||||
|
from core.nodes.network import WlanNode
|
||||||
|
|
||||||
|
|
||||||
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
|
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
|
||||||
|
@ -27,14 +29,13 @@ class Bunch(object):
|
||||||
Helper class for recording a collection of attributes.
|
Helper class for recording a collection of attributes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, **kwds):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create a Bunch instance.
|
Create a Bunch instance.
|
||||||
|
|
||||||
:param dict kwds: keyword arguments
|
:param dict kwargs: keyword arguments
|
||||||
:return:
|
|
||||||
"""
|
"""
|
||||||
self.__dict__.update(kwds)
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Sdt(object):
|
class Sdt(object):
|
||||||
|
@ -365,9 +366,7 @@ class Sdt(object):
|
||||||
for net in nets:
|
for net in nets:
|
||||||
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
||||||
for link_data in all_links:
|
for link_data in all_links:
|
||||||
is_wireless = nodeutils.is_node(
|
is_wireless = isinstance(net, (WlanNode, EmaneNet))
|
||||||
net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
|
||||||
)
|
|
||||||
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
|
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
|
||||||
if is_wireless and link_data.node1_id == net.id:
|
if is_wireless and link_data.node1_id == net.id:
|
||||||
continue
|
continue
|
||||||
|
@ -401,7 +400,7 @@ class Sdt(object):
|
||||||
def handlenodemsg(self, msg):
|
def handlenodemsg(self, msg):
|
||||||
"""
|
"""
|
||||||
Process a Node Message to add/delete or move a node on
|
Process a Node Message to add/delete or move a node on
|
||||||
the SDT display. Node properties are found in session._objs or
|
the SDT display. Node properties are found in a session or
|
||||||
self.remotes for remote nodes (or those not yet instantiated).
|
self.remotes for remote nodes (or those not yet instantiated).
|
||||||
|
|
||||||
:param msg: node message to handle
|
:param msg: node message to handle
|
||||||
|
@ -430,7 +429,8 @@ class Sdt(object):
|
||||||
model = "router"
|
model = "router"
|
||||||
nodetype = model
|
nodetype = model
|
||||||
elif nodetype is not None:
|
elif nodetype is not None:
|
||||||
nodetype = nodeutils.get_node_class(NodeTypes(nodetype)).type
|
nodetype = NodeTypes(nodetype)
|
||||||
|
nodetype = self.session.get_node_class(nodetype).type
|
||||||
net = True
|
net = True
|
||||||
else:
|
else:
|
||||||
nodetype = None
|
nodetype = None
|
||||||
|
@ -494,7 +494,7 @@ class Sdt(object):
|
||||||
|
|
||||||
def wlancheck(self, nodenum):
|
def wlancheck(self, nodenum):
|
||||||
"""
|
"""
|
||||||
Helper returns True if a node number corresponds to a WlanNode or EmaneNode.
|
Helper returns True if a node number corresponds to a WLAN or EMANE node.
|
||||||
|
|
||||||
:param int nodenum: node id to check
|
:param int nodenum: node id to check
|
||||||
:return: True if node is wlan or emane, False otherwise
|
:return: True if node is wlan or emane, False otherwise
|
||||||
|
@ -509,6 +509,6 @@ class Sdt(object):
|
||||||
n = self.session.get_node(nodenum)
|
n = self.session.get_node(nodenum)
|
||||||
except CoreError:
|
except CoreError:
|
||||||
return False
|
return False
|
||||||
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
if isinstance(n, (WlanNode, EmaneNet)):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -12,10 +12,11 @@ import logging
|
||||||
import time
|
import time
|
||||||
from multiprocessing.pool import ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
|
|
||||||
from core import CoreCommandError, utils
|
from core import utils
|
||||||
from core.constants import which
|
from core.constants import which
|
||||||
from core.emulator.data import FileData
|
from core.emulator.data import FileData
|
||||||
from core.emulator.enumerations import MessageFlags, RegisterTlvs
|
from core.emulator.enumerations import MessageFlags, RegisterTlvs
|
||||||
|
from core.errors import CoreCommandError
|
||||||
|
|
||||||
|
|
||||||
class ServiceBootError(Exception):
|
class ServiceBootError(Exception):
|
||||||
|
@ -248,6 +249,7 @@ class ServiceManager(object):
|
||||||
|
|
||||||
:param CoreService service: service to add
|
:param CoreService service: service to add
|
||||||
:return: nothing
|
:return: nothing
|
||||||
|
:raises ValueError: when service cannot be loaded
|
||||||
"""
|
"""
|
||||||
name = service.name
|
name = service.name
|
||||||
logging.debug("loading service: class(%s) name(%s)", service.__name__, name)
|
logging.debug("loading service: class(%s) name(%s)", service.__name__, name)
|
||||||
|
@ -258,13 +260,14 @@ class ServiceManager(object):
|
||||||
|
|
||||||
# validate dependent executables are present
|
# validate dependent executables are present
|
||||||
for executable in service.executables:
|
for executable in service.executables:
|
||||||
if not which(executable):
|
which(executable, required=True)
|
||||||
logging.debug(
|
|
||||||
"service(%s) missing executable: %s", service.name, executable
|
# validate service on load succeeds
|
||||||
)
|
try:
|
||||||
raise ValueError(
|
service.on_load()
|
||||||
"service(%s) missing executable: %s" % (service.name, executable)
|
except Exception as e:
|
||||||
)
|
logging.exception("error during service(%s) on load", service.name)
|
||||||
|
raise ValueError(e)
|
||||||
|
|
||||||
# make service available
|
# make service available
|
||||||
cls.services[name] = service
|
cls.services[name] = service
|
||||||
|
@ -294,13 +297,12 @@ class ServiceManager(object):
|
||||||
for service in services:
|
for service in services:
|
||||||
if not service.name:
|
if not service.name:
|
||||||
continue
|
continue
|
||||||
service.on_load()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cls.add(service)
|
cls.add(service)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
service_errors.append(service.name)
|
service_errors.append(service.name)
|
||||||
logging.debug("not loading service: %s", e)
|
logging.debug("not loading service(%s): %s", service.name, e)
|
||||||
return service_errors
|
return service_errors
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes import nodeutils
|
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
from core.xml import emanexml
|
from core.xml import emanexml
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ class EmaneTransportService(CoreService):
|
||||||
transport_commands = []
|
transport_commands = []
|
||||||
for interface in node.netifs(sort=True):
|
for interface in node.netifs(sort=True):
|
||||||
network_node = node.session.get_node(interface.net.id)
|
network_node = node.session.get_node(interface.net.id)
|
||||||
if nodeutils.is_node(network_node, NodeTypes.EMANE):
|
if isinstance(network_node, EmaneNet):
|
||||||
config = node.session.emane.get_configs(
|
config = node.session.emane.get_configs(
|
||||||
network_node.id, network_node.model.name
|
network_node.id, network_node.model.name
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,8 +4,10 @@ Assumes installation of FRR via https://deb.frrouting.org/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from core import constants
|
from core import constants
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emulator.enumerations import LinkTypes
|
||||||
from core.nodes import ipaddress, nodeutils
|
from core.nodes import ipaddress
|
||||||
|
from core.nodes.network import PtpNet
|
||||||
|
from core.nodes.physical import Rj45Node
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
|
|
||||||
|
@ -341,7 +343,7 @@ class FrrService(CoreService):
|
||||||
for peerifc in ifc.net.netifs():
|
for peerifc in ifc.net.netifs():
|
||||||
if peerifc == ifc:
|
if peerifc == ifc:
|
||||||
continue
|
continue
|
||||||
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
|
if isinstance(peerifc, Rj45Node):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -395,7 +397,7 @@ class FRROspfv2(FrrService):
|
||||||
Helper to detect whether interface is connected to a notional
|
Helper to detect whether interface is connected to a notional
|
||||||
point-to-point link.
|
point-to-point link.
|
||||||
"""
|
"""
|
||||||
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
if isinstance(ifc.net, PtpNet):
|
||||||
return " ip ospf network point-to-point\n"
|
return " ip ospf network point-to-point\n"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -481,7 +483,7 @@ class FRROspfv3(FrrService):
|
||||||
Helper to detect whether interface is connected to a notional
|
Helper to detect whether interface is connected to a notional
|
||||||
point-to-point link.
|
point-to-point link.
|
||||||
"""
|
"""
|
||||||
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
if isinstance(ifc.net, PtpNet):
|
||||||
return " ipv6 ospf6 network point-to-point\n"
|
return " ipv6 ospf6 network point-to-point\n"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,11 @@ quagga.py: defines routing services provided by Quagga.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from core import constants
|
from core import constants
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes import ipaddress, nodeutils
|
from core.emulator.enumerations import LinkTypes
|
||||||
|
from core.nodes import ipaddress
|
||||||
|
from core.nodes.network import PtpNet, WlanNode
|
||||||
|
from core.nodes.physical import Rj45Node
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
|
|
||||||
|
@ -267,7 +270,7 @@ class QuaggaService(CoreService):
|
||||||
for peerifc in ifc.net.netifs():
|
for peerifc in ifc.net.netifs():
|
||||||
if peerifc == ifc:
|
if peerifc == ifc:
|
||||||
continue
|
continue
|
||||||
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
|
if isinstance(peerifc, Rj45Node):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -321,7 +324,7 @@ class Ospfv2(QuaggaService):
|
||||||
Helper to detect whether interface is connected to a notional
|
Helper to detect whether interface is connected to a notional
|
||||||
point-to-point link.
|
point-to-point link.
|
||||||
"""
|
"""
|
||||||
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
if isinstance(ifc.net, PtpNet):
|
||||||
return " ip ospf network point-to-point\n"
|
return " ip ospf network point-to-point\n"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -407,7 +410,7 @@ class Ospfv3(QuaggaService):
|
||||||
Helper to detect whether interface is connected to a notional
|
Helper to detect whether interface is connected to a notional
|
||||||
point-to-point link.
|
point-to-point link.
|
||||||
"""
|
"""
|
||||||
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
if isinstance(ifc.net, PtpNet):
|
||||||
return " ipv6 ospf6 network point-to-point\n"
|
return " ipv6 ospf6 network point-to-point\n"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -457,9 +460,7 @@ class Ospfv3mdr(Ospfv3):
|
||||||
cfg = cls.mtucheck(ifc)
|
cfg = cls.mtucheck(ifc)
|
||||||
# Uncomment the following line to use Address Family Translation for IPv4
|
# Uncomment the following line to use Address Family Translation for IPv4
|
||||||
cfg += " ipv6 ospf6 instance-id 65\n"
|
cfg += " ipv6 ospf6 instance-id 65\n"
|
||||||
if ifc.net is not None and nodeutils.is_node(
|
if ifc.net is not None and isinstance(ifc.net, (WlanNode, EmaneNet)):
|
||||||
ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
|
||||||
):
|
|
||||||
return (
|
return (
|
||||||
cfg
|
cfg
|
||||||
+ """\
|
+ """\
|
||||||
|
|
|
@ -4,7 +4,8 @@ utility.py: defines miscellaneous utility services.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from core import CoreCommandError, constants, utils
|
from core import constants, utils
|
||||||
|
from core.errors import CoreCommandError
|
||||||
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix
|
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@ import fcntl
|
||||||
import hashlib
|
import hashlib
|
||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import logging.config
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -14,7 +16,8 @@ import sys
|
||||||
|
|
||||||
from past.builtins import basestring
|
from past.builtins import basestring
|
||||||
|
|
||||||
from core import CoreCommandError
|
from core import constants
|
||||||
|
from core.errors import CoreCommandError
|
||||||
|
|
||||||
DEVNULL = open(os.devnull, "wb")
|
DEVNULL = open(os.devnull, "wb")
|
||||||
|
|
||||||
|
@ -109,17 +112,6 @@ def _is_class(module, member, clazz):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _is_exe(file_path):
|
|
||||||
"""
|
|
||||||
Check if a given file path exists and is an executable file.
|
|
||||||
|
|
||||||
:param str file_path: file path to check
|
|
||||||
:return: True if the file is considered and executable file, False otherwise
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
return os.path.isfile(file_path) and os.access(file_path, os.X_OK)
|
|
||||||
|
|
||||||
|
|
||||||
def close_onexec(fd):
|
def close_onexec(fd):
|
||||||
"""
|
"""
|
||||||
Close on execution of a shell process.
|
Close on execution of a shell process.
|
||||||
|
@ -131,17 +123,26 @@ def close_onexec(fd):
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
|
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
|
||||||
|
|
||||||
|
|
||||||
def check_executables(executables):
|
def which(command, required):
|
||||||
"""
|
"""
|
||||||
Check executables, verify they exist and are executable.
|
Find location of desired executable within current PATH.
|
||||||
|
|
||||||
:param list[str] executables: executable to check
|
:param str command: command to find location for
|
||||||
:return: nothing
|
:param bool required: command is required to be found, false otherwise
|
||||||
:raises EnvironmentError: when an executable doesn't exist or is not executable
|
:return: command location or None
|
||||||
|
:raises ValueError: when not found and required
|
||||||
"""
|
"""
|
||||||
for executable in executables:
|
found_path = None
|
||||||
if not _is_exe(executable):
|
for path in os.environ["PATH"].split(os.pathsep):
|
||||||
raise EnvironmentError("executable not found: %s" % executable)
|
command_path = os.path.join(path, command)
|
||||||
|
if os.path.isfile(command_path) and os.access(command_path, os.X_OK):
|
||||||
|
found_path = command_path
|
||||||
|
break
|
||||||
|
|
||||||
|
if found_path is None and required:
|
||||||
|
raise ValueError("failed to find required executable(%s) in path" % command)
|
||||||
|
|
||||||
|
return found_path
|
||||||
|
|
||||||
|
|
||||||
def make_tuple(obj):
|
def make_tuple(obj):
|
||||||
|
@ -167,7 +168,8 @@ def make_tuple_fromstr(s, value_type):
|
||||||
:return: tuple from string
|
:return: tuple from string
|
||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
# remove tuple braces and strip commands and space from all values in the tuple string
|
# remove tuple braces and strip commands and space from all values in the tuple
|
||||||
|
# string
|
||||||
values = []
|
values = []
|
||||||
for x in s.strip("(), ").split(","):
|
for x in s.strip("(), ").split(","):
|
||||||
x = x.strip("' ")
|
x = x.strip("' ")
|
||||||
|
@ -178,7 +180,8 @@ def make_tuple_fromstr(s, value_type):
|
||||||
|
|
||||||
def split_args(args):
|
def split_args(args):
|
||||||
"""
|
"""
|
||||||
Convenience method for splitting potential string commands into a shell-like syntax list.
|
Convenience method for splitting potential string commands into a shell-like
|
||||||
|
syntax list.
|
||||||
|
|
||||||
:param list/str args: command list or string
|
:param list/str args: command list or string
|
||||||
:return: shell-like syntax list
|
:return: shell-like syntax list
|
||||||
|
@ -227,8 +230,8 @@ def cmd(args, wait=True):
|
||||||
|
|
||||||
def cmd_output(args):
|
def cmd_output(args):
|
||||||
"""
|
"""
|
||||||
Execute a command on the host and return a tuple containing the exit status and result string. stderr output
|
Execute a command on the host and return a tuple containing the exit status and
|
||||||
is folded into the stdout result string.
|
result string. stderr output is folded into the stdout result string.
|
||||||
|
|
||||||
:param list[str]|str args: command arguments
|
:param list[str]|str args: command arguments
|
||||||
:return: command status and stdout
|
:return: command status and stdout
|
||||||
|
@ -248,14 +251,15 @@ def cmd_output(args):
|
||||||
|
|
||||||
def check_cmd(args, **kwargs):
|
def check_cmd(args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Execute a command on the host and return a tuple containing the exit status and result string. stderr output
|
Execute a command on the host and return a tuple containing the exit status and
|
||||||
is folded into the stdout result string.
|
result string. stderr output is folded into the stdout result string.
|
||||||
|
|
||||||
:param list[str]|str args: command arguments
|
:param list[str]|str args: command arguments
|
||||||
:param dict kwargs: keyword arguments to pass to subprocess.Popen
|
:param dict kwargs: keyword arguments to pass to subprocess.Popen
|
||||||
:return: combined stdout and stderr
|
:return: combined stdout and stderr
|
||||||
:rtype: str
|
:rtype: str
|
||||||
:raises CoreCommandError: when there is a non-zero exit status or the file to execute is not found
|
:raises CoreCommandError: when there is a non-zero exit status or the file to
|
||||||
|
execute is not found
|
||||||
"""
|
"""
|
||||||
kwargs["stdout"] = subprocess.PIPE
|
kwargs["stdout"] = subprocess.PIPE
|
||||||
kwargs["stderr"] = subprocess.STDOUT
|
kwargs["stderr"] = subprocess.STDOUT
|
||||||
|
@ -350,7 +354,7 @@ def expand_corepath(pathname, session=None, node=None):
|
||||||
Expand a file path given session information.
|
Expand a file path given session information.
|
||||||
|
|
||||||
:param str pathname: file path to expand
|
:param str pathname: file path to expand
|
||||||
:param core.emulator.session.Session session: core session object to expand path with
|
:param core.emulator.session.Session session: core session object to expand path
|
||||||
:param core.nodes.base.CoreNode node: node to expand path with
|
:param core.nodes.base.CoreNode node: node to expand path with
|
||||||
:return: expanded path
|
:return: expanded path
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
@ -383,7 +387,8 @@ def sysctl_devname(devname):
|
||||||
|
|
||||||
def load_config(filename, d):
|
def load_config(filename, d):
|
||||||
"""
|
"""
|
||||||
Read key=value pairs from a file, into a dict. Skip comments; strip newline characters and spacing.
|
Read key=value pairs from a file, into a dict. Skip comments; strip newline
|
||||||
|
characters and spacing.
|
||||||
|
|
||||||
:param str filename: file to read into a dictionary
|
:param str filename: file to read into a dictionary
|
||||||
:param dict d: dictionary to read file into
|
:param dict d: dictionary to read file into
|
||||||
|
@ -444,3 +449,18 @@ def load_classes(path, clazz):
|
||||||
)
|
)
|
||||||
|
|
||||||
return classes
|
return classes
|
||||||
|
|
||||||
|
|
||||||
|
def load_logging_config(config_path=None):
|
||||||
|
"""
|
||||||
|
Load CORE logging configuration file.
|
||||||
|
|
||||||
|
:param str config_path: path to logging config file,
|
||||||
|
when None defaults to /etc/core/logging.conf
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
if not config_path:
|
||||||
|
config_path = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
|
||||||
|
with open(config_path, "r") as log_config_file:
|
||||||
|
log_config = json.load(log_config_file)
|
||||||
|
logging.config.dictConfig(log_config)
|
||||||
|
|
|
@ -4,11 +4,12 @@ from lxml import etree
|
||||||
|
|
||||||
import core.nodes.base
|
import core.nodes.base
|
||||||
import core.nodes.physical
|
import core.nodes.physical
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
from core.nodes import nodeutils
|
|
||||||
from core.nodes.base import CoreNetworkBase
|
from core.nodes.base import CoreNetworkBase
|
||||||
from core.nodes.ipaddress import MacAddress
|
from core.nodes.ipaddress import MacAddress
|
||||||
|
from core.nodes.network import CtrlNet
|
||||||
|
|
||||||
|
|
||||||
def write_xml_file(xml_element, file_path, doctype=None):
|
def write_xml_file(xml_element, file_path, doctype=None):
|
||||||
|
@ -408,7 +409,7 @@ class CoreXmlWriter(object):
|
||||||
is_network_or_rj45 = isinstance(
|
is_network_or_rj45 = isinstance(
|
||||||
node, (core.nodes.base.CoreNetworkBase, core.nodes.physical.Rj45Node)
|
node, (core.nodes.base.CoreNetworkBase, core.nodes.physical.Rj45Node)
|
||||||
)
|
)
|
||||||
is_controlnet = nodeutils.is_node(node, NodeTypes.CONTROL_NET)
|
is_controlnet = isinstance(node, CtrlNet)
|
||||||
if is_network_or_rj45 and not is_controlnet:
|
if is_network_or_rj45 and not is_controlnet:
|
||||||
self.write_network(node)
|
self.write_network(node)
|
||||||
# device node
|
# device node
|
||||||
|
@ -457,7 +458,7 @@ class CoreXmlWriter(object):
|
||||||
interface_name = node_interface.name
|
interface_name = node_interface.name
|
||||||
|
|
||||||
# check if emane interface
|
# check if emane interface
|
||||||
if nodeutils.is_node(node_interface.net, NodeTypes.EMANE):
|
if isinstance(node_interface.net, EmaneNet):
|
||||||
nem = node_interface.net.getnemid(node_interface)
|
nem = node_interface.net.getnemid(node_interface)
|
||||||
add_attribute(interface, "nem", nem)
|
add_attribute(interface, "nem", nem)
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import socket
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from core import constants, utils
|
from core import constants, utils
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes import ipaddress, nodeutils
|
from core.nodes import ipaddress
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ class CoreXmlDeployment(object):
|
||||||
|
|
||||||
for netif in node.netifs():
|
for netif in node.netifs():
|
||||||
emane_element = None
|
emane_element = None
|
||||||
if nodeutils.is_node(netif.net, NodeTypes.EMANE):
|
if isinstance(netif.net, EmaneNet):
|
||||||
emane_element = add_emane_interface(host_element, netif)
|
emane_element = add_emane_interface(host_element, netif)
|
||||||
|
|
||||||
parent_element = host_element
|
parent_element = host_element
|
||||||
|
|
|
@ -103,9 +103,11 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
||||||
"""
|
"""
|
||||||
Create platform xml for a specific node.
|
Create platform xml for a specific node.
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations
|
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane
|
||||||
:param core.nodes.network.CtrlNet control_net: control net node for this emane network
|
configurations
|
||||||
:param core.emane.nodes.EmaneNode node: node to write platform xml for
|
:param core.nodes.network.CtrlNet control_net: control net node for this emane
|
||||||
|
network
|
||||||
|
:param core.emane.nodes.EmaneNet node: node to write platform xml for
|
||||||
:param int nem_id: nem id to use for interfaces for this node
|
:param int nem_id: nem id to use for interfaces for this node
|
||||||
:param dict platform_xmls: stores platform xml elements to append nem entries to
|
:param dict platform_xmls: stores platform xml elements to append nem entries to
|
||||||
:return: the next nem id that can be used for creating platform xml files
|
:return: the next nem id that can be used for creating platform xml files
|
||||||
|
@ -120,7 +122,7 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
||||||
nem_entries = {}
|
nem_entries = {}
|
||||||
|
|
||||||
if node.model is None:
|
if node.model is None:
|
||||||
logging.warning("warning: EmaneNode %s has no associated model", node.name)
|
logging.warning("warning: EMANE network %s has no associated model", node.name)
|
||||||
return nem_entries
|
return nem_entries
|
||||||
|
|
||||||
for netif in node.netifs():
|
for netif in node.netifs():
|
||||||
|
@ -133,7 +135,8 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
|
||||||
"nem", id=str(nem_id), name=netif.localname, definition=nem_definition
|
"nem", id=str(nem_id), name=netif.localname, definition=nem_definition
|
||||||
)
|
)
|
||||||
|
|
||||||
# check if this is an external transport, get default config if an interface specific one does not exist
|
# check if this is an external transport, get default config if an interface
|
||||||
|
# specific one does not exist
|
||||||
config = emane_manager.getifcconfig(node.model.id, netif, node.model.name)
|
config = emane_manager.getifcconfig(node.model.id, netif, node.model.name)
|
||||||
|
|
||||||
if is_external(config):
|
if is_external(config):
|
||||||
|
@ -220,8 +223,9 @@ def build_xml_files(emane_manager, node):
|
||||||
"""
|
"""
|
||||||
Generate emane xml files required for node.
|
Generate emane xml files required for node.
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations
|
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane
|
||||||
:param core.emane.nodes.EmaneNode node: node to write platform xml for
|
configurations
|
||||||
|
:param core.emane.nodes.EmaneNet node: node to write platform xml for
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.debug("building all emane xml for node(%s): %s", node, node.name)
|
logging.debug("building all emane xml for node(%s): %s", node, node.name)
|
||||||
|
@ -233,7 +237,7 @@ def build_xml_files(emane_manager, node):
|
||||||
if not config:
|
if not config:
|
||||||
return
|
return
|
||||||
|
|
||||||
# build XML for overall network (EmaneNode) configs
|
# build XML for overall network EMANE configs
|
||||||
node.model.build_xml_files(config)
|
node.model.build_xml_files(config)
|
||||||
|
|
||||||
# build XML for specific interface (NEM) configs
|
# build XML for specific interface (NEM) configs
|
||||||
|
@ -243,7 +247,7 @@ def build_xml_files(emane_manager, node):
|
||||||
rtype = "raw"
|
rtype = "raw"
|
||||||
|
|
||||||
for netif in node.netifs():
|
for netif in node.netifs():
|
||||||
# check for interface specific emane configuration and write xml files, if needed
|
# check for interface specific emane configuration and write xml files
|
||||||
config = emane_manager.getifcconfig(node.model.id, netif, node.model.name)
|
config = emane_manager.getifcconfig(node.model.id, netif, node.model.name)
|
||||||
if config:
|
if config:
|
||||||
node.model.build_xml_files(config, netif)
|
node.model.build_xml_files(config, netif)
|
||||||
|
@ -267,8 +271,9 @@ def build_transport_xml(emane_manager, node, transport_type):
|
||||||
"""
|
"""
|
||||||
Build transport xml file for node and transport type.
|
Build transport xml file for node and transport type.
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations
|
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane
|
||||||
:param core.emane.nodes.EmaneNode node: node to write platform xml for
|
configurations
|
||||||
|
:param core.emane.nodes.EmaneNet node: node to write platform xml for
|
||||||
:param str transport_type: transport type to build xml for
|
:param str transport_type: transport type to build xml for
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
|
@ -304,7 +309,7 @@ def create_phy_xml(emane_model, config, file_path):
|
||||||
"""
|
"""
|
||||||
Create the phy xml document.
|
Create the phy xml document.
|
||||||
|
|
||||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create xml
|
||||||
:param dict config: all current configuration values
|
:param dict config: all current configuration values
|
||||||
:param str file_path: path to write file to
|
:param str file_path: path to write file to
|
||||||
:return: nothing
|
:return: nothing
|
||||||
|
@ -323,7 +328,7 @@ def create_mac_xml(emane_model, config, file_path):
|
||||||
"""
|
"""
|
||||||
Create the mac xml document.
|
Create the mac xml document.
|
||||||
|
|
||||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create xml
|
||||||
:param dict config: all current configuration values
|
:param dict config: all current configuration values
|
||||||
:param str file_path: path to write file to
|
:param str file_path: path to write file to
|
||||||
:return: nothing
|
:return: nothing
|
||||||
|
@ -346,7 +351,7 @@ def create_nem_xml(
|
||||||
"""
|
"""
|
||||||
Create the nem xml document.
|
Create the nem xml document.
|
||||||
|
|
||||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create xml
|
||||||
:param dict config: all current configuration values
|
:param dict config: all current configuration values
|
||||||
:param str nem_file: nem file path to write
|
:param str nem_file: nem file path to write
|
||||||
:param str transport_definition: transport file definition path
|
:param str transport_definition: transport file definition path
|
||||||
|
@ -422,7 +427,7 @@ def nem_file_name(emane_model, interface=None):
|
||||||
"""
|
"""
|
||||||
Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml"
|
Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml"
|
||||||
|
|
||||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create file
|
||||||
:param interface: interface for this model
|
:param interface: interface for this model
|
||||||
:return: nem xml filename
|
:return: nem xml filename
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
@ -438,7 +443,7 @@ def shim_file_name(emane_model, interface=None):
|
||||||
"""
|
"""
|
||||||
Return the string name for the SHIM XML file, e.g. "commeffectshim.xml"
|
Return the string name for the SHIM XML file, e.g. "commeffectshim.xml"
|
||||||
|
|
||||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create file
|
||||||
:param interface: interface for this model
|
:param interface: interface for this model
|
||||||
:return: shim xml filename
|
:return: shim xml filename
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
@ -450,7 +455,7 @@ def mac_file_name(emane_model, interface=None):
|
||||||
"""
|
"""
|
||||||
Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml"
|
Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml"
|
||||||
|
|
||||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create file
|
||||||
:param interface: interface for this model
|
:param interface: interface for this model
|
||||||
:return: mac xml filename
|
:return: mac xml filename
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
@ -462,7 +467,7 @@ def phy_file_name(emane_model, interface=None):
|
||||||
"""
|
"""
|
||||||
Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml"
|
Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml"
|
||||||
|
|
||||||
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create file
|
||||||
:param interface: interface for this model
|
:param interface: interface for this model
|
||||||
:return: phy xml filename
|
:return: phy xml filename
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
|
@ -1,12 +1,4 @@
|
||||||
# Configuration file for CORE (core-gui, core-daemon)
|
|
||||||
|
|
||||||
### GUI configuration options ###
|
|
||||||
[core-gui]
|
|
||||||
# no options are presently defined; see the ~/.core preferences file
|
|
||||||
|
|
||||||
### core-daemon configuration options ###
|
|
||||||
[core-daemon]
|
[core-daemon]
|
||||||
xmlfilever = 1.0
|
|
||||||
listenaddr = localhost
|
listenaddr = localhost
|
||||||
port = 4038
|
port = 4038
|
||||||
numthreads = 1
|
numthreads = 1
|
||||||
|
@ -19,12 +11,11 @@ frr_sbin_search = "/usr/local/sbin /usr/sbin /usr/lib/frr"
|
||||||
# this may be a comma-separated list, and directory names should be unique
|
# this may be a comma-separated list, and directory names should be unique
|
||||||
# and not named 'services'
|
# and not named 'services'
|
||||||
#custom_services_dir = /home/username/.core/myservices
|
#custom_services_dir = /home/username/.core/myservices
|
||||||
#
|
|
||||||
# uncomment to establish a standalone control backchannel for accessing nodes
|
# uncomment to establish a standalone control backchannel for accessing nodes
|
||||||
# (overriden by the session option of the same name)
|
# (overriden by the session option of the same name)
|
||||||
#controlnet = 172.16.0.0/24
|
#controlnet = 172.16.0.0/24
|
||||||
#
|
|
||||||
#
|
|
||||||
# uncomment and edit to establish a distributed control backchannel
|
# uncomment and edit to establish a distributed control backchannel
|
||||||
#controlnet = core1:172.16.1.0/24 core2:172.16.2.0/24 core3:172.16.3.0/24 core4:172.16.4.0/24 core5:172.16.5.0/24
|
#controlnet = core1:172.16.1.0/24 core2:172.16.2.0/24 core3:172.16.3.0/24 core4:172.16.4.0/24 core5:172.16.5.0/24
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
|
|
||||||
from core.location.event import EventLoop
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
loop = EventLoop()
|
|
||||||
|
|
||||||
def msg(arg):
|
|
||||||
delta = time.time() - loop.start
|
|
||||||
logging.debug("%s arg: %s", delta, arg)
|
|
||||||
|
|
||||||
def repeat(interval, count):
|
|
||||||
count -= 1
|
|
||||||
msg("repeat: interval: %s; remaining: %s" % (interval, count))
|
|
||||||
if count > 0:
|
|
||||||
loop.add_event(interval, repeat, interval, count)
|
|
||||||
|
|
||||||
def sleep(delay):
|
|
||||||
msg("sleep %s" % delay)
|
|
||||||
time.sleep(delay)
|
|
||||||
msg("sleep done")
|
|
||||||
|
|
||||||
def stop(arg):
|
|
||||||
msg(arg)
|
|
||||||
loop.stop()
|
|
||||||
|
|
||||||
loop.add_event(0, msg, "start")
|
|
||||||
loop.add_event(0, msg, "time zero")
|
|
||||||
|
|
||||||
for delay in 5, 4, 10, -1, 0, 9, 3, 7, 3.14:
|
|
||||||
loop.add_event(delay, msg, "time %s" % delay)
|
|
||||||
|
|
||||||
loop.run()
|
|
||||||
|
|
||||||
loop.add_event(0, repeat, 1, 5)
|
|
||||||
loop.add_event(12, sleep, 10)
|
|
||||||
|
|
||||||
loop.add_event(15.75, stop, "stop time: 15.75")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,212 +0,0 @@
|
||||||
#!/usr/bin/python -i
|
|
||||||
|
|
||||||
# Copyright (c)2010-2013 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
|
|
||||||
# A distributed example where CORE API messaging is used to create a session
|
|
||||||
# on a daemon server. The daemon server defaults to 127.0.0.1:4038
|
|
||||||
# to target a remote machine specify "-d <ip address>" parameter, it needs to be
|
|
||||||
# running the daemon with listenaddr=0.0.0.0 in the core.conf file.
|
|
||||||
# This script creates no nodes locally and therefore can be run as an
|
|
||||||
# unprivileged user.
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import optparse
|
|
||||||
import sys
|
|
||||||
from builtins import range
|
|
||||||
|
|
||||||
import core.nodes.base
|
|
||||||
import core.nodes.network
|
|
||||||
from core.api.tlv import coreapi, dataconversion
|
|
||||||
from core.api.tlv.coreapi import CoreExecuteTlv
|
|
||||||
from core.emulator.enumerations import (
|
|
||||||
CORE_API_PORT,
|
|
||||||
EventTlvs,
|
|
||||||
EventTypes,
|
|
||||||
ExecuteTlvs,
|
|
||||||
LinkTlvs,
|
|
||||||
LinkTypes,
|
|
||||||
MessageFlags,
|
|
||||||
MessageTypes,
|
|
||||||
)
|
|
||||||
from core.emulator.session import Session
|
|
||||||
from core.nodes import ipaddress
|
|
||||||
|
|
||||||
# declare classes for use with Broker
|
|
||||||
|
|
||||||
# node list (count from 1)
|
|
||||||
n = [None]
|
|
||||||
exec_num = 1
|
|
||||||
|
|
||||||
|
|
||||||
def cmd(node, exec_cmd):
|
|
||||||
"""
|
|
||||||
:param node: The node the command should be issued too
|
|
||||||
:param exec_cmd: A string with the command to be run
|
|
||||||
:return: Returns the result of the command
|
|
||||||
"""
|
|
||||||
global exec_num
|
|
||||||
|
|
||||||
# Set up the command api message
|
|
||||||
tlvdata = CoreExecuteTlv.pack(ExecuteTlvs.NODE.value, node.id)
|
|
||||||
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.NUMBER.value, exec_num)
|
|
||||||
tlvdata += CoreExecuteTlv.pack(ExecuteTlvs.COMMAND.value, exec_cmd)
|
|
||||||
msg = coreapi.CoreExecMessage.pack(
|
|
||||||
MessageFlags.STRING.value | MessageFlags.TEXT.value, tlvdata
|
|
||||||
)
|
|
||||||
node.session.broker.handlerawmsg(msg)
|
|
||||||
exec_num += 1
|
|
||||||
|
|
||||||
# Now wait for the response
|
|
||||||
server = node.session.broker.servers["localhost"]
|
|
||||||
server.sock.settimeout(50.0)
|
|
||||||
|
|
||||||
# receive messages until we get our execute response
|
|
||||||
result = None
|
|
||||||
while True:
|
|
||||||
msghdr = server.sock.recv(coreapi.CoreMessage.header_len)
|
|
||||||
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(msghdr)
|
|
||||||
msgdata = server.sock.recv(msglen)
|
|
||||||
|
|
||||||
# If we get the right response return the results
|
|
||||||
print("received response message: %s" % MessageTypes(msgtype))
|
|
||||||
if msgtype == MessageTypes.EXECUTE.value:
|
|
||||||
msg = coreapi.CoreExecMessage(msgflags, msghdr, msgdata)
|
|
||||||
result = msg.get_tlv(ExecuteTlvs.RESULT.value)
|
|
||||||
break
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
usagestr = "usage: %prog [-n] number of nodes [-d] daemon address"
|
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
|
||||||
parser.set_defaults(numnodes=5, daemon="127.0.0.1:" + str(CORE_API_PORT))
|
|
||||||
|
|
||||||
parser.add_option(
|
|
||||||
"-n", "--numnodes", dest="numnodes", type=int, help="number of nodes"
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-d",
|
|
||||||
"--daemon-server",
|
|
||||||
dest="daemon",
|
|
||||||
type=str,
|
|
||||||
help="daemon server IP address",
|
|
||||||
)
|
|
||||||
|
|
||||||
def usage(msg=None, err=0):
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
if msg:
|
|
||||||
sys.stdout.write(msg + "\n\n")
|
|
||||||
parser.print_help()
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
# parse command line options
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if options.numnodes < 1:
|
|
||||||
usage("invalid number of nodes: %s" % options.numnodes)
|
|
||||||
if not options.daemon:
|
|
||||||
usage("daemon server IP address (-d) is a required argument")
|
|
||||||
|
|
||||||
for a in args:
|
|
||||||
sys.stderr.write("ignoring command line argument: %s\n" % a)
|
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
|
||||||
|
|
||||||
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
|
||||||
session = Session(1)
|
|
||||||
server = globals().get("server")
|
|
||||||
if server:
|
|
||||||
server.addsession(session)
|
|
||||||
|
|
||||||
# distributed setup - connect to daemon server
|
|
||||||
daemonport = options.daemon.split(":")
|
|
||||||
daemonip = daemonport[0]
|
|
||||||
|
|
||||||
# Localhost is already set in the session but we change it to be the remote daemon
|
|
||||||
# This stops the remote daemon trying to build a tunnel back which would fail
|
|
||||||
daemon = "localhost"
|
|
||||||
if len(daemonport) > 1:
|
|
||||||
port = int(daemonport[1])
|
|
||||||
else:
|
|
||||||
port = CORE_API_PORT
|
|
||||||
print("connecting to daemon at %s:%d" % (daemon, port))
|
|
||||||
session.broker.addserver(daemon, daemonip, port)
|
|
||||||
|
|
||||||
# Set the local session id to match the port.
|
|
||||||
# Not necessary but seems neater.
|
|
||||||
session.broker.setupserver(daemon)
|
|
||||||
|
|
||||||
# We do not want the recvloop running as we will deal ourselves
|
|
||||||
session.broker.dorecvloop = False
|
|
||||||
|
|
||||||
# Change to configuration state on both machines
|
|
||||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(
|
|
||||||
EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value
|
|
||||||
)
|
|
||||||
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
|
||||||
|
|
||||||
flags = MessageFlags.ADD.value
|
|
||||||
switch = core.nodes.network.SwitchNode(session=session, name="switch", start=False)
|
|
||||||
switch.setposition(x=80, y=50)
|
|
||||||
switch.server = daemon
|
|
||||||
switch_data = switch.data(flags)
|
|
||||||
switch_message = dataconversion.convert_node(switch_data)
|
|
||||||
session.broker.handlerawmsg(switch_message)
|
|
||||||
|
|
||||||
number_of_nodes = options.numnodes
|
|
||||||
|
|
||||||
print(
|
|
||||||
"creating %d remote nodes with addresses from %s" % (options.numnodes, prefix)
|
|
||||||
)
|
|
||||||
|
|
||||||
# create remote nodes via API
|
|
||||||
for i in range(1, number_of_nodes + 1):
|
|
||||||
node = core.nodes.base.CoreNode(
|
|
||||||
session=session, _id=i, name="n%d" % i, start=False
|
|
||||||
)
|
|
||||||
node.setposition(x=150 * i, y=150)
|
|
||||||
node.server = daemon
|
|
||||||
node_data = node.data(flags)
|
|
||||||
node_message = dataconversion.convert_node(node_data)
|
|
||||||
session.broker.handlerawmsg(node_message)
|
|
||||||
n.append(node)
|
|
||||||
|
|
||||||
# create remote links via API
|
|
||||||
for i in range(1, number_of_nodes + 1):
|
|
||||||
tlvdata = coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, switch.id)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
|
||||||
LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i)
|
|
||||||
)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
|
||||||
LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen
|
|
||||||
)
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
|
||||||
session.broker.handlerawmsg(msg)
|
|
||||||
|
|
||||||
# We change the daemon to Instantiation state
|
|
||||||
# We do not change the local session as it would try and build a tunnel and fail
|
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(
|
|
||||||
EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value
|
|
||||||
)
|
|
||||||
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
|
||||||
session.broker.handlerawmsg(msg)
|
|
||||||
|
|
||||||
# Get the ip or last node and ping it from the first
|
|
||||||
print("Pinging from the first to the last node")
|
|
||||||
pingip = cmd(n[-1], "ip -4 -o addr show dev eth0").split()[3].split("/")[0]
|
|
||||||
print(cmd(n[1], "ping -c 5 " + pingip))
|
|
||||||
print("elapsed time: %s" % (datetime.datetime.now() - start))
|
|
||||||
print(
|
|
||||||
"To stop this session, use the core-cleanup script on the remote daemon server."
|
|
||||||
)
|
|
||||||
input("press enter to exit")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
|
||||||
main()
|
|
|
@ -1,151 +0,0 @@
|
||||||
#!/usr/bin/python -i
|
|
||||||
|
|
||||||
# Copyright (c)2010-2013 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
|
|
||||||
# A distributed example where CORE API messaging is used to create a session
|
|
||||||
# distributed across the local server and one slave server. The slave server
|
|
||||||
# must be specified using the '-s <ip address>' parameter, and needs to be
|
|
||||||
# running the daemon with listenaddr=0.0.0.0 in the core.conf file.
|
|
||||||
#
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import optparse
|
|
||||||
import sys
|
|
||||||
from builtins import range
|
|
||||||
|
|
||||||
import core.nodes.base
|
|
||||||
import core.nodes.network
|
|
||||||
from core import constants
|
|
||||||
from core.api.tlv import coreapi, dataconversion
|
|
||||||
from core.emulator.enumerations import (
|
|
||||||
CORE_API_PORT,
|
|
||||||
EventTlvs,
|
|
||||||
EventTypes,
|
|
||||||
LinkTlvs,
|
|
||||||
LinkTypes,
|
|
||||||
MessageFlags,
|
|
||||||
)
|
|
||||||
from core.emulator.session import Session
|
|
||||||
from core.nodes import ipaddress
|
|
||||||
|
|
||||||
# node list (count from 1)
|
|
||||||
n = [None]
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
|
||||||
parser.set_defaults(numnodes=5, slave=None)
|
|
||||||
|
|
||||||
parser.add_option(
|
|
||||||
"-n", "--numnodes", dest="numnodes", type=int, help="number of nodes"
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-s", "--slave-server", dest="slave", type=str, help="slave server IP address"
|
|
||||||
)
|
|
||||||
|
|
||||||
def usage(msg=None, err=0):
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
if msg:
|
|
||||||
sys.stdout.write(msg + "\n\n")
|
|
||||||
parser.print_help()
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
# parse command line options
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if options.numnodes < 1:
|
|
||||||
usage("invalid number of nodes: %s" % options.numnodes)
|
|
||||||
if not options.slave:
|
|
||||||
usage("slave server IP address (-s) is a required argument")
|
|
||||||
|
|
||||||
for a in args:
|
|
||||||
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
|
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
|
||||||
|
|
||||||
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
|
||||||
session = Session(1)
|
|
||||||
server = globals().get("server")
|
|
||||||
if server is not None:
|
|
||||||
server.addsession(session)
|
|
||||||
|
|
||||||
# distributed setup - connect to slave server
|
|
||||||
slaveport = options.slave.split(":")
|
|
||||||
slave = slaveport[0]
|
|
||||||
if len(slaveport) > 1:
|
|
||||||
port = int(slaveport[1])
|
|
||||||
else:
|
|
||||||
port = CORE_API_PORT
|
|
||||||
print("connecting to slave at %s:%d" % (slave, port))
|
|
||||||
session.broker.addserver(slave, slave, port)
|
|
||||||
session.broker.setupserver(slave)
|
|
||||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(
|
|
||||||
EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value
|
|
||||||
)
|
|
||||||
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))
|
|
||||||
|
|
||||||
switch = session.create_node(cls=core.nodes.network.SwitchNode, name="switch")
|
|
||||||
switch.setposition(x=80, y=50)
|
|
||||||
num_local = options.numnodes / 2
|
|
||||||
num_remote = options.numnodes / 2 + options.numnodes % 2
|
|
||||||
print(
|
|
||||||
"creating %d (%d local / %d remote) nodes with addresses from %s"
|
|
||||||
% (options.numnodes, num_local, num_remote, prefix)
|
|
||||||
)
|
|
||||||
for i in range(1, num_local + 1):
|
|
||||||
node = session.create_node(cls=core.nodes.base.CoreNode, name="n%d" % i, _id=i)
|
|
||||||
node.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
|
||||||
node.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
|
||||||
node.setposition(x=150 * i, y=150)
|
|
||||||
n.append(node)
|
|
||||||
|
|
||||||
flags = MessageFlags.ADD.value
|
|
||||||
session.broker.handlerawmsg(switch.tonodemsg(flags=flags))
|
|
||||||
|
|
||||||
# create remote nodes via API
|
|
||||||
for i in range(num_local + 1, options.numnodes + 1):
|
|
||||||
node = core.nodes.base.CoreNode(
|
|
||||||
session=session, _id=i, name="n%d" % i, start=False
|
|
||||||
)
|
|
||||||
node.setposition(x=150 * i, y=150)
|
|
||||||
node.server = slave
|
|
||||||
n.append(node)
|
|
||||||
node_data = node.data(flags)
|
|
||||||
node_message = dataconversion.convert_node(node_data)
|
|
||||||
session.broker.handlerawmsg(node_message)
|
|
||||||
|
|
||||||
# create remote links via API
|
|
||||||
for i in range(num_local + 1, options.numnodes + 1):
|
|
||||||
tlvdata = coreapi.CoreLinkTlv.pack(LinkTlvs.N1_NUMBER.value, switch.id)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.N2_NUMBER.value, i)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.TYPE.value, LinkTypes.WIRED.value)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(LinkTlvs.INTERFACE2_NUMBER.value, 0)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
|
||||||
LinkTlvs.INTERFACE2_IP4.value, prefix.addr(i)
|
|
||||||
)
|
|
||||||
tlvdata += coreapi.CoreLinkTlv.pack(
|
|
||||||
LinkTlvs.INTERFACE2_IP4_MASK.value, prefix.prefixlen
|
|
||||||
)
|
|
||||||
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
|
|
||||||
session.broker.handlerawmsg(msg)
|
|
||||||
|
|
||||||
session.instantiate()
|
|
||||||
tlvdata = coreapi.CoreEventTlv.pack(
|
|
||||||
EventTlvs.TYPE.value, EventTypes.INSTANTIATION_STATE.value
|
|
||||||
)
|
|
||||||
msg = coreapi.CoreEventMessage.pack(0, tlvdata)
|
|
||||||
session.broker.handlerawmsg(msg)
|
|
||||||
|
|
||||||
# start a shell on node 1
|
|
||||||
n[1].client.term("bash")
|
|
||||||
|
|
||||||
print("elapsed time: %s" % (datetime.datetime.now() - start))
|
|
||||||
print("To stop this session, use the 'core-cleanup' script on this server")
|
|
||||||
print("and on the remote slave server.")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__" or __name__ == "__builtin__":
|
|
||||||
main()
|
|
|
@ -1,247 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# Copyright (c)2010-2012 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
#
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
|
|
||||||
"""
|
|
||||||
howmanynodes.py - This is a CORE script that creates network namespace nodes
|
|
||||||
having one virtual Ethernet interface connected to a bridge. It continues to
|
|
||||||
add nodes until an exception occurs. The number of nodes per bridge can be
|
|
||||||
specified.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import optparse
|
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
import core.nodes.base
|
|
||||||
import core.nodes.network
|
|
||||||
from core import constants
|
|
||||||
from core.emulator.session import Session
|
|
||||||
from core.nodes import ipaddress
|
|
||||||
|
|
||||||
GBD = 1024.0 * 1024.0
|
|
||||||
|
|
||||||
|
|
||||||
def linuxversion():
|
|
||||||
""" Return a string having the Linux kernel version.
|
|
||||||
"""
|
|
||||||
f = open("/proc/version", "r")
|
|
||||||
v = f.readline().split()
|
|
||||||
version_str = " ".join(v[:3])
|
|
||||||
f.close()
|
|
||||||
return version_str
|
|
||||||
|
|
||||||
|
|
||||||
MEMKEYS = ("total", "free", "buff", "cached", "stotal", "sfree")
|
|
||||||
|
|
||||||
|
|
||||||
def memfree():
|
|
||||||
""" Returns kilobytes memory [total, free, buff, cached, stotal, sfree].
|
|
||||||
useful stats are:
|
|
||||||
free memory = free + buff + cached
|
|
||||||
swap used = stotal - sfree
|
|
||||||
"""
|
|
||||||
f = open("/proc/meminfo", "r")
|
|
||||||
lines = f.readlines()
|
|
||||||
f.close()
|
|
||||||
kbs = {}
|
|
||||||
for k in MEMKEYS:
|
|
||||||
kbs[k] = 0
|
|
||||||
for l in lines:
|
|
||||||
if l[:9] == "MemTotal:":
|
|
||||||
kbs["total"] = int(l.split()[1])
|
|
||||||
elif l[:8] == "MemFree:":
|
|
||||||
kbs["free"] = int(l.split()[1])
|
|
||||||
elif l[:8] == "Buffers:":
|
|
||||||
kbs["buff"] = int(l.split()[1])
|
|
||||||
elif l[:8] == "Cached:":
|
|
||||||
kbs["cache"] = int(l.split()[1])
|
|
||||||
elif l[:10] == "SwapTotal:":
|
|
||||||
kbs["stotal"] = int(l.split()[1])
|
|
||||||
elif l[:9] == "SwapFree:":
|
|
||||||
kbs["sfree"] = int(l.split()[1])
|
|
||||||
break
|
|
||||||
return kbs
|
|
||||||
|
|
||||||
|
|
||||||
# node list (count from 1)
|
|
||||||
nodelist = [None]
|
|
||||||
switchlist = []
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
|
||||||
parser.set_defaults(
|
|
||||||
waittime=0.2, numnodes=0, bridges=0, retries=0, logfile=None, services=None
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_option(
|
|
||||||
"-w",
|
|
||||||
"--waittime",
|
|
||||||
dest="waittime",
|
|
||||||
type=float,
|
|
||||||
help="number of seconds to wait between node creation"
|
|
||||||
" (default = %s)" % parser.defaults["waittime"],
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-n",
|
|
||||||
"--numnodes",
|
|
||||||
dest="numnodes",
|
|
||||||
type=int,
|
|
||||||
help="number of nodes (default = unlimited)",
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-b",
|
|
||||||
"--bridges",
|
|
||||||
dest="bridges",
|
|
||||||
type=int,
|
|
||||||
help="number of nodes per bridge; 0 = one bridge "
|
|
||||||
"(def. = %s)" % parser.defaults["bridges"],
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-r",
|
|
||||||
"--retry",
|
|
||||||
dest="retries",
|
|
||||||
type=int,
|
|
||||||
help="number of retries on error (default = %s)" % parser.defaults["retries"],
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-l",
|
|
||||||
"--log",
|
|
||||||
dest="logfile",
|
|
||||||
type=str,
|
|
||||||
help="log memory usage to this file (default = %s)"
|
|
||||||
% parser.defaults["logfile"],
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-s",
|
|
||||||
"--services",
|
|
||||||
dest="services",
|
|
||||||
type=str,
|
|
||||||
help="pipe-delimited list of services added to each "
|
|
||||||
"node (default = %s)\n(Example: zebra|OSPFv2|OSPFv3|"
|
|
||||||
"IPForward)" % parser.defaults["services"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def usage(msg=None, err=0):
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
if msg:
|
|
||||||
sys.stdout.write(msg + "\n\n")
|
|
||||||
parser.print_help()
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
|
||||||
|
|
||||||
for a in args:
|
|
||||||
sys.stderr.write("ignoring command line argument: %s\n" % a)
|
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
|
||||||
prefix = ipaddress.Ipv4Prefix("10.83.0.0/16")
|
|
||||||
|
|
||||||
print("Testing how many network namespace nodes this machine can create.")
|
|
||||||
print(" - %s" % linuxversion())
|
|
||||||
mem = memfree()
|
|
||||||
print(
|
|
||||||
" - %.02f GB total memory (%.02f GB swap)"
|
|
||||||
% (mem["total"] / GBD, mem["stotal"] / GBD)
|
|
||||||
)
|
|
||||||
print(" - using IPv4 network prefix %s" % prefix)
|
|
||||||
print(" - using wait time of %s" % options.waittime)
|
|
||||||
print(" - using %d nodes per bridge" % options.bridges)
|
|
||||||
print(" - will retry %d times on failure" % options.retries)
|
|
||||||
print(" - adding these services to each node: %s" % options.services)
|
|
||||||
print(" ")
|
|
||||||
|
|
||||||
lfp = None
|
|
||||||
if options.logfile is not None:
|
|
||||||
# initialize a csv log file header
|
|
||||||
lfp = open(options.logfile, "a")
|
|
||||||
lfp.write("# log from howmanynodes.py %s\n" % time.ctime())
|
|
||||||
lfp.write("# options = %s\n#\n" % options)
|
|
||||||
lfp.write("# numnodes,%s\n" % ",".join(MEMKEYS))
|
|
||||||
lfp.flush()
|
|
||||||
|
|
||||||
session = Session(1)
|
|
||||||
switch = session.create_node(cls=core.nodes.network.SwitchNode)
|
|
||||||
switchlist.append(switch)
|
|
||||||
print("Added bridge %s (%d)." % (switch.brname, len(switchlist)))
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
retry_count = options.retries
|
|
||||||
while True:
|
|
||||||
i += 1
|
|
||||||
# optionally add a bridge (options.bridges nodes per bridge)
|
|
||||||
try:
|
|
||||||
if 0 < options.bridges <= switch.numnetif():
|
|
||||||
switch = session.create_node(cls=core.nodes.network.SwitchNode)
|
|
||||||
switchlist.append(switch)
|
|
||||||
print(
|
|
||||||
"\nAdded bridge %s (%d) for node %d."
|
|
||||||
% (switch.brname, len(switchlist), i)
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
print(
|
|
||||||
"At %d bridges (%d nodes) caught exception:\n%s\n"
|
|
||||||
% (len(switchlist), i - 1, e)
|
|
||||||
)
|
|
||||||
break
|
|
||||||
|
|
||||||
# create a node
|
|
||||||
try:
|
|
||||||
n = session.create_node(cls=core.nodes.base.CoreNode, name="n%d" % i)
|
|
||||||
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
|
||||||
n.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
|
||||||
if options.services is not None:
|
|
||||||
session.services.add_services(n, "", options.services.split("|"))
|
|
||||||
session.services.boot_services(n)
|
|
||||||
nodelist.append(n)
|
|
||||||
if i % 25 == 0:
|
|
||||||
print("\n%s nodes created " % i)
|
|
||||||
mem = memfree()
|
|
||||||
free = mem["free"] + mem["buff"] + mem["cached"]
|
|
||||||
swap = mem["stotal"] - mem["sfree"]
|
|
||||||
print("(%.02f/%.02f GB free/swap)" % (free / GBD, swap / GBD))
|
|
||||||
if lfp:
|
|
||||||
lfp.write("%d," % i)
|
|
||||||
lfp.write("%s\n" % ",".join(str(mem[x]) for x in MEMKEYS))
|
|
||||||
lfp.flush()
|
|
||||||
else:
|
|
||||||
sys.stdout.write(".")
|
|
||||||
sys.stdout.flush()
|
|
||||||
time.sleep(options.waittime)
|
|
||||||
except Exception as e:
|
|
||||||
print("At %d nodes caught exception:\n" % i, e)
|
|
||||||
if retry_count > 0:
|
|
||||||
print("\nWill retry creating node %d." % i)
|
|
||||||
shutil.rmtree(n.nodedir, ignore_errors=True)
|
|
||||||
retry_count -= 1
|
|
||||||
i -= 1
|
|
||||||
time.sleep(options.waittime)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
print("Stopping at %d nodes!" % i)
|
|
||||||
break
|
|
||||||
|
|
||||||
if i == options.numnodes:
|
|
||||||
print("Stopping at %d nodes due to numnodes option." % i)
|
|
||||||
break
|
|
||||||
# node creation was successful at this point
|
|
||||||
retry_count = options.retries
|
|
||||||
|
|
||||||
if lfp:
|
|
||||||
lfp.flush()
|
|
||||||
lfp.close()
|
|
||||||
|
|
||||||
print("elapsed time: %s" % (datetime.datetime.now() - start))
|
|
||||||
print("Use the core-cleanup script to remove nodes and bridges.")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,637 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# Copyright (c)2011-2014 the Boeing Company.
|
|
||||||
# See the LICENSE file included in this distribution.
|
|
||||||
|
|
||||||
# create a random topology running OSPFv3 MDR, wait and then check
|
|
||||||
# that all neighbor states are either full or two-way, and check the routes
|
|
||||||
# in zebra vs those installed in the kernel.
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from builtins import range
|
|
||||||
from string import Template
|
|
||||||
|
|
||||||
import core.nodes.base
|
|
||||||
import core.nodes.network
|
|
||||||
from core.constants import QUAGGA_STATE_DIR
|
|
||||||
from core.emulator.session import Session
|
|
||||||
from core.nodes import ipaddress
|
|
||||||
from core.utils import check_cmd
|
|
||||||
|
|
||||||
quagga_sbin_search = ("/usr/local/sbin", "/usr/sbin", "/usr/lib/quagga")
|
|
||||||
quagga_path = "zebra"
|
|
||||||
|
|
||||||
# sanity check that zebra is installed
|
|
||||||
try:
|
|
||||||
for p in quagga_sbin_search:
|
|
||||||
if os.path.exists(os.path.join(p, "zebra")):
|
|
||||||
quagga_path = p
|
|
||||||
break
|
|
||||||
check_cmd([os.path.join(quagga_path, "zebra"), "-u", "root", "-g", "root", "-v"])
|
|
||||||
except OSError:
|
|
||||||
sys.stderr.write("ERROR: running zebra failed\n")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
class ManetNode(core.nodes.base.CoreNode):
|
|
||||||
""" An Lxc namespace node configured for Quagga OSPFv3 MANET MDR
|
|
||||||
"""
|
|
||||||
|
|
||||||
conftemp = Template(
|
|
||||||
"""\
|
|
||||||
interface eth0
|
|
||||||
ip address $ipaddr
|
|
||||||
ipv6 ospf6 instance-id 65
|
|
||||||
ipv6 ospf6 hello-interval 2
|
|
||||||
ipv6 ospf6 dead-interval 6
|
|
||||||
ipv6 ospf6 retransmit-interval 5
|
|
||||||
ipv6 ospf6 network manet-designated-router
|
|
||||||
ipv6 ospf6 diffhellos
|
|
||||||
ipv6 ospf6 adjacencyconnectivity biconnected
|
|
||||||
ipv6 ospf6 lsafullness mincostlsa
|
|
||||||
!
|
|
||||||
router ospf6
|
|
||||||
router-id $routerid
|
|
||||||
interface eth0 area 0.0.0.0
|
|
||||||
!
|
|
||||||
ip forwarding
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
confdir = "/usr/local/etc/quagga"
|
|
||||||
|
|
||||||
def __init__(self, core, ipaddr, routerid=None, _id=None, name=None, nodedir=None):
|
|
||||||
if routerid is None:
|
|
||||||
routerid = ipaddr.split("/")[0]
|
|
||||||
self.ipaddr = ipaddr
|
|
||||||
self.routerid = routerid
|
|
||||||
core.nodes.base.CoreBaseNode.__init__(self, core, _id, name, nodedir)
|
|
||||||
self.privatedir(self.confdir)
|
|
||||||
self.privatedir(QUAGGA_STATE_DIR)
|
|
||||||
|
|
||||||
def qconf(self):
|
|
||||||
return self.conftemp.substitute(ipaddr=self.ipaddr, routerid=self.routerid)
|
|
||||||
|
|
||||||
def config(self):
|
|
||||||
filename = os.path.join(self.confdir, "Quagga.conf")
|
|
||||||
f = self.opennodefile(filename, "w")
|
|
||||||
f.write(self.qconf())
|
|
||||||
f.close()
|
|
||||||
tmp = self.bootscript()
|
|
||||||
if tmp:
|
|
||||||
self.nodefile(self.bootsh, tmp, mode=0o755)
|
|
||||||
|
|
||||||
def boot(self):
|
|
||||||
self.config()
|
|
||||||
self.session.services.boot_services(self)
|
|
||||||
|
|
||||||
def bootscript(self):
|
|
||||||
return """\
|
|
||||||
#!/bin/sh -e
|
|
||||||
|
|
||||||
STATEDIR=%s
|
|
||||||
|
|
||||||
waitfile()
|
|
||||||
{
|
|
||||||
fname=$1
|
|
||||||
|
|
||||||
i=0
|
|
||||||
until [ -e $fname ]; do
|
|
||||||
i=$(($i + 1))
|
|
||||||
if [ $i -eq 10 ]; then
|
|
||||||
echo "file not found: $fname" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
sleep 0.1
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir -p $STATEDIR
|
|
||||||
|
|
||||||
%s/zebra -d -u root -g root
|
|
||||||
waitfile $STATEDIR/zebra.vty
|
|
||||||
|
|
||||||
%s/ospf6d -d -u root -g root
|
|
||||||
waitfile $STATEDIR/ospf6d.vty
|
|
||||||
|
|
||||||
vtysh -b
|
|
||||||
""" % (
|
|
||||||
QUAGGA_STATE_DIR,
|
|
||||||
quagga_path,
|
|
||||||
quagga_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Route(object):
|
|
||||||
""" Helper class for organzing routing table entries. """
|
|
||||||
|
|
||||||
def __init__(self, prefix=None, gw=None, metric=None):
|
|
||||||
try:
|
|
||||||
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
|
||||||
except Exception as e:
|
|
||||||
raise ValueError(
|
|
||||||
"Invalid prefix given to Route object: %s\n%s" % (prefix, e)
|
|
||||||
)
|
|
||||||
self.gw = gw
|
|
||||||
self.metric = metric
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
try:
|
|
||||||
return (
|
|
||||||
self.prefix == other.prefix
|
|
||||||
and self.gw == other.gw
|
|
||||||
and self.metric == other.metric
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "(%s,%s,%s)" % (self.prefix, self.gw, self.metric)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def key(r):
|
|
||||||
if not r.prefix:
|
|
||||||
return 0
|
|
||||||
return r.prefix.prefix
|
|
||||||
|
|
||||||
|
|
||||||
class ManetExperiment(object):
|
|
||||||
""" A class for building an MDR network and checking and logging its state.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, options, start):
|
|
||||||
""" Initialize with options and start time. """
|
|
||||||
self.session = None
|
|
||||||
# node list
|
|
||||||
self.nodes = []
|
|
||||||
# WLAN network
|
|
||||||
self.net = None
|
|
||||||
self.verbose = options.verbose
|
|
||||||
# dict from OptionParser
|
|
||||||
self.options = options
|
|
||||||
self.start = start
|
|
||||||
self.logbegin()
|
|
||||||
|
|
||||||
def info(self, msg):
|
|
||||||
""" Utility method for writing output to stdout. """
|
|
||||||
print(msg)
|
|
||||||
sys.stdout.flush()
|
|
||||||
self.log(msg)
|
|
||||||
|
|
||||||
def warn(self, msg):
|
|
||||||
""" Utility method for writing output to stderr. """
|
|
||||||
sys.stderr.write(msg)
|
|
||||||
sys.stderr.flush()
|
|
||||||
self.log(msg)
|
|
||||||
|
|
||||||
def logbegin(self):
|
|
||||||
""" Start logging. """
|
|
||||||
self.logfp = None
|
|
||||||
if not self.options.logfile:
|
|
||||||
return
|
|
||||||
self.logfp = open(self.options.logfile, "w")
|
|
||||||
self.log("ospfmanetmdrtest begin: %s\n" % self.start.ctime())
|
|
||||||
|
|
||||||
def logend(self):
|
|
||||||
""" End logging. """
|
|
||||||
if not self.logfp:
|
|
||||||
return
|
|
||||||
end = datetime.datetime.now()
|
|
||||||
self.log("ospfmanetmdrtest end: %s (%s)\n" % (end.ctime(), end - self.start))
|
|
||||||
self.logfp.flush()
|
|
||||||
self.logfp.close()
|
|
||||||
self.logfp = None
|
|
||||||
|
|
||||||
def log(self, msg):
|
|
||||||
""" Write to the log file, if any. """
|
|
||||||
if not self.logfp:
|
|
||||||
return
|
|
||||||
self.logfp.write(msg)
|
|
||||||
|
|
||||||
def logdata(self, nbrs, mdrs, lsdbs, krs, zrs):
|
|
||||||
""" Dump experiment parameters and data to the log file. """
|
|
||||||
self.log("ospfmantetmdrtest data:")
|
|
||||||
self.log("----- parameters -----")
|
|
||||||
self.log("%s" % self.options)
|
|
||||||
self.log("----- neighbors -----")
|
|
||||||
for rtrid in sorted(nbrs.keys()):
|
|
||||||
self.log("%s: %s" % (rtrid, nbrs[rtrid]))
|
|
||||||
self.log("----- mdr levels -----")
|
|
||||||
self.log(mdrs)
|
|
||||||
self.log("----- link state databases -----")
|
|
||||||
for rtrid in sorted(lsdbs.keys()):
|
|
||||||
self.log("%s lsdb:" % rtrid)
|
|
||||||
for line in lsdbs[rtrid].split("\n"):
|
|
||||||
self.log(line)
|
|
||||||
self.log("----- kernel routes -----")
|
|
||||||
for rtrid in sorted(krs.keys()):
|
|
||||||
msg = rtrid + ": "
|
|
||||||
for rt in krs[rtrid]:
|
|
||||||
msg += "%s" % rt
|
|
||||||
self.log(msg)
|
|
||||||
self.log("----- zebra routes -----")
|
|
||||||
for rtrid in sorted(zrs.keys()):
|
|
||||||
msg = rtrid + ": "
|
|
||||||
for rt in zrs[rtrid]:
|
|
||||||
msg += "%s" % rt
|
|
||||||
self.log(msg)
|
|
||||||
|
|
||||||
def topology(self, numnodes, linkprob, verbose=False):
|
|
||||||
""" Build a topology consisting of the given number of ManetNodes
|
|
||||||
connected to a WLAN and probabilty of links and set
|
|
||||||
the session, WLAN, and node list objects.
|
|
||||||
"""
|
|
||||||
# IP subnet
|
|
||||||
prefix = ipaddress.Ipv4Prefix("10.14.0.0/16")
|
|
||||||
self.session = Session(1)
|
|
||||||
# emulated network
|
|
||||||
self.net = self.session.create_node(cls=core.nodes.network.WlanNode)
|
|
||||||
for i in range(1, numnodes + 1):
|
|
||||||
addr = "%s/%s" % (prefix.addr(i), 32)
|
|
||||||
tmp = self.session.create_node(
|
|
||||||
cls=ManetNode, ipaddr=addr, _id="%d" % i, name="n%d" % i
|
|
||||||
)
|
|
||||||
tmp.newnetif(self.net, [addr])
|
|
||||||
self.nodes.append(tmp)
|
|
||||||
# connect nodes with probability linkprob
|
|
||||||
for i in range(numnodes):
|
|
||||||
for j in range(i + 1, numnodes):
|
|
||||||
r = random.random()
|
|
||||||
if r < linkprob:
|
|
||||||
if self.verbose:
|
|
||||||
self.info("linking (%d,%d)" % (i, j))
|
|
||||||
self.net.link(self.nodes[i].netif(0), self.nodes[j].netif(0))
|
|
||||||
# force one link to avoid partitions (should check if this is needed)
|
|
||||||
j = i
|
|
||||||
while j == i:
|
|
||||||
j = random.randint(0, numnodes - 1)
|
|
||||||
if self.verbose:
|
|
||||||
self.info("linking (%d,%d)" % (i, j))
|
|
||||||
self.net.link(self.nodes[i].netif(0), self.nodes[j].netif(0))
|
|
||||||
self.nodes[i].boot()
|
|
||||||
# run the boot.sh script on all nodes to start Quagga
|
|
||||||
for i in range(numnodes):
|
|
||||||
self.nodes[i].cmd(["./%s" % self.nodes[i].bootsh])
|
|
||||||
|
|
||||||
def compareroutes(self, node, kr, zr):
|
|
||||||
""" Compare two lists of Route objects.
|
|
||||||
"""
|
|
||||||
kr.sort(key=Route.key)
|
|
||||||
zr.sort(key=Route.key)
|
|
||||||
if kr != zr:
|
|
||||||
self.warn("kernel and zebra routes differ")
|
|
||||||
if self.verbose:
|
|
||||||
msg = "kernel: "
|
|
||||||
for r in kr:
|
|
||||||
msg += "%s " % r
|
|
||||||
msg += "\nzebra: "
|
|
||||||
for r in zr:
|
|
||||||
msg += "%s " % r
|
|
||||||
self.warn(msg)
|
|
||||||
else:
|
|
||||||
self.info(" kernel and zebra routes match")
|
|
||||||
|
|
||||||
def comparemdrlevels(self, nbrs, mdrs):
|
|
||||||
""" Check that all routers form a connected dominating set, i.e. all
|
|
||||||
routers are either MDR, BMDR, or adjacent to one.
|
|
||||||
"""
|
|
||||||
msg = "All routers form a CDS"
|
|
||||||
for n in self.nodes:
|
|
||||||
if mdrs[n.routerid] != "OTHER":
|
|
||||||
continue
|
|
||||||
connected = False
|
|
||||||
for nbr in nbrs[n.routerid]:
|
|
||||||
if mdrs[nbr] == "MDR" or mdrs[nbr] == "BMDR":
|
|
||||||
connected = True
|
|
||||||
break
|
|
||||||
if not connected:
|
|
||||||
msg = "All routers do not form a CDS"
|
|
||||||
self.warn(
|
|
||||||
"XXX %s: not in CDS; neighbors: %s" % (n.routerid, nbrs[n.routerid])
|
|
||||||
)
|
|
||||||
if self.verbose:
|
|
||||||
self.info(msg)
|
|
||||||
|
|
||||||
def comparelsdbs(self, lsdbs):
|
|
||||||
""" Check LSDBs for consistency.
|
|
||||||
"""
|
|
||||||
msg = "LSDBs of all routers are consistent"
|
|
||||||
prev = self.nodes[0]
|
|
||||||
for n in self.nodes:
|
|
||||||
db = lsdbs[n.routerid]
|
|
||||||
if lsdbs[prev.routerid] != db:
|
|
||||||
msg = "LSDBs of all routers are not consistent"
|
|
||||||
self.warn(
|
|
||||||
"XXX LSDBs inconsistent for %s and %s" % (n.routerid, prev.routerid)
|
|
||||||
)
|
|
||||||
i = 0
|
|
||||||
for entry in lsdbs[n.routerid].split("\n"):
|
|
||||||
preventries = lsdbs[prev.routerid].split("\n")
|
|
||||||
try:
|
|
||||||
preventry = preventries[i]
|
|
||||||
except IndexError:
|
|
||||||
preventry = None
|
|
||||||
if entry != preventry:
|
|
||||||
self.warn("%s: %s" % (n.routerid, entry))
|
|
||||||
self.warn("%s: %s" % (prev.routerid, preventry))
|
|
||||||
i += 1
|
|
||||||
prev = n
|
|
||||||
if self.verbose:
|
|
||||||
self.info(msg)
|
|
||||||
|
|
||||||
def checknodes(self):
|
|
||||||
""" Check the neighbor state and routing tables of all nodes. """
|
|
||||||
nbrs = {}
|
|
||||||
mdrs = {}
|
|
||||||
lsdbs = {}
|
|
||||||
krs = {}
|
|
||||||
zrs = {}
|
|
||||||
v = self.verbose
|
|
||||||
for n in self.nodes:
|
|
||||||
self.info("checking %s" % n.name)
|
|
||||||
nbrs[n.routerid] = Ospf6NeighState(n, verbose=v).run()
|
|
||||||
krs[n.routerid] = KernelRoutes(n, verbose=v).run()
|
|
||||||
zrs[n.routerid] = ZebraRoutes(n, verbose=v).run()
|
|
||||||
self.compareroutes(n, krs[n.routerid], zrs[n.routerid])
|
|
||||||
mdrs[n.routerid] = Ospf6MdrLevel(n, verbose=v).run()
|
|
||||||
lsdbs[n.routerid] = Ospf6Database(n, verbose=v).run()
|
|
||||||
self.comparemdrlevels(nbrs, mdrs)
|
|
||||||
self.comparelsdbs(lsdbs)
|
|
||||||
self.logdata(nbrs, mdrs, lsdbs, krs, zrs)
|
|
||||||
|
|
||||||
|
|
||||||
class Cmd:
|
|
||||||
""" Helper class for running a command on a node and parsing the result. """
|
|
||||||
|
|
||||||
args = ""
|
|
||||||
|
|
||||||
def __init__(self, node, verbose=False):
|
|
||||||
""" Initialize with a CoreNode (LxcNode) """
|
|
||||||
self.id = None
|
|
||||||
self.stdin = None
|
|
||||||
self.out = None
|
|
||||||
self.node = node
|
|
||||||
self.verbose = verbose
|
|
||||||
|
|
||||||
def info(self, msg):
|
|
||||||
""" Utility method for writing output to stdout."""
|
|
||||||
print(msg)
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def warn(self, msg):
|
|
||||||
""" Utility method for writing output to stderr. """
|
|
||||||
sys.stderr.write("XXX %s:" % self.node.routerid, msg)
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
""" This is the primary method used for running this command. """
|
|
||||||
self.open()
|
|
||||||
r = self.parse()
|
|
||||||
self.cleanup()
|
|
||||||
return r
|
|
||||||
|
|
||||||
def open(self):
|
|
||||||
""" Exceute call to node.popen(). """
|
|
||||||
self.id, self.stdin, self.out, self.err = self.node.client.popen(self.args)
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
""" This method is overloaded by child classes and should return some
|
|
||||||
result.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def cleanup(self):
|
|
||||||
""" Close the Popen channels."""
|
|
||||||
self.stdin.close()
|
|
||||||
self.out.close()
|
|
||||||
self.err.close()
|
|
||||||
tmp = self.id.wait()
|
|
||||||
if tmp:
|
|
||||||
self.warn("nonzero exit status:", tmp)
|
|
||||||
|
|
||||||
|
|
||||||
class VtyshCmd(Cmd):
|
|
||||||
""" Runs a vtysh command. """
|
|
||||||
|
|
||||||
def open(self):
|
|
||||||
args = ("vtysh", "-c", self.args)
|
|
||||||
self.id, self.stdin, self.out, self.err = self.node.client.popen(args)
|
|
||||||
|
|
||||||
|
|
||||||
class Ospf6NeighState(VtyshCmd):
|
|
||||||
""" Check a node for OSPFv3 neighbors in the full/two-way states. """
|
|
||||||
|
|
||||||
args = "show ipv6 ospf6 neighbor"
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
# skip first line
|
|
||||||
self.out.readline()
|
|
||||||
nbrlist = []
|
|
||||||
for line in self.out:
|
|
||||||
field = line.split()
|
|
||||||
nbr = field[0]
|
|
||||||
state = field[3].split("/")[0]
|
|
||||||
if not state.lower() in ("full", "twoway"):
|
|
||||||
self.warn("neighbor %s state: %s" % (nbr, state))
|
|
||||||
nbrlist.append(nbr)
|
|
||||||
|
|
||||||
if len(nbrlist) == 0:
|
|
||||||
self.warn("no neighbors")
|
|
||||||
if self.verbose:
|
|
||||||
self.info(" %s has %d neighbors" % (self.node.routerid, len(nbrlist)))
|
|
||||||
return nbrlist
|
|
||||||
|
|
||||||
|
|
||||||
class Ospf6MdrLevel(VtyshCmd):
|
|
||||||
""" Retrieve the OSPFv3 MDR level for a node. """
|
|
||||||
|
|
||||||
args = "show ipv6 ospf6 mdrlevel"
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
line = self.out.readline()
|
|
||||||
# TODO: handle multiple interfaces
|
|
||||||
field = line.split()
|
|
||||||
mdrlevel = field[4]
|
|
||||||
if mdrlevel not in ("MDR", "BMDR", "OTHER"):
|
|
||||||
self.warn("mdrlevel: %s" % mdrlevel)
|
|
||||||
if self.verbose:
|
|
||||||
self.info(" %s is %s" % (self.node.routerid, mdrlevel))
|
|
||||||
return mdrlevel
|
|
||||||
|
|
||||||
|
|
||||||
class Ospf6Database(VtyshCmd):
|
|
||||||
""" Retrieve the OSPFv3 LSDB summary for a node. """
|
|
||||||
|
|
||||||
args = "show ipv6 ospf6 database"
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
db = ""
|
|
||||||
for line in self.out:
|
|
||||||
field = line.split()
|
|
||||||
if len(field) < 8:
|
|
||||||
continue
|
|
||||||
# filter out Age and Duration columns
|
|
||||||
filtered = field[:3] + field[4:7]
|
|
||||||
db += " ".join(filtered) + "\n"
|
|
||||||
return db
|
|
||||||
|
|
||||||
|
|
||||||
class ZebraRoutes(VtyshCmd):
|
|
||||||
""" Return a list of Route objects for a node based on its zebra
|
|
||||||
routing table.
|
|
||||||
"""
|
|
||||||
|
|
||||||
args = "show ip route"
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
for i in range(0, 3):
|
|
||||||
# skip first three lines
|
|
||||||
self.out.readline()
|
|
||||||
r = []
|
|
||||||
prefix = None
|
|
||||||
for line in self.out:
|
|
||||||
field = line.split()
|
|
||||||
if len(field) < 1:
|
|
||||||
continue
|
|
||||||
# only use OSPFv3 selected FIB routes
|
|
||||||
elif field[0][:2] == "o>":
|
|
||||||
prefix = field[1]
|
|
||||||
metric = field[2].split("/")[1][:-1]
|
|
||||||
if field[0][2:] != "*":
|
|
||||||
continue
|
|
||||||
if field[3] == "via":
|
|
||||||
gw = field[4][:-1]
|
|
||||||
else:
|
|
||||||
gw = field[6][:-1]
|
|
||||||
r.append(Route(prefix, gw, metric))
|
|
||||||
prefix = None
|
|
||||||
elif prefix and field[0] == "*":
|
|
||||||
# already have prefix and metric from previous line
|
|
||||||
gw = field[2][:-1]
|
|
||||||
r.append(Route(prefix, gw, metric))
|
|
||||||
prefix = None
|
|
||||||
|
|
||||||
if len(r) == 0:
|
|
||||||
self.warn("no zebra routes")
|
|
||||||
if self.verbose:
|
|
||||||
self.info(" %s has %d zebra routes" % (self.node.routerid, len(r)))
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
class KernelRoutes(Cmd):
|
|
||||||
""" Return a list of Route objects for a node based on its kernel
|
|
||||||
routing table.
|
|
||||||
"""
|
|
||||||
|
|
||||||
args = ("/sbin/ip", "route", "show")
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
r = []
|
|
||||||
prefix = None
|
|
||||||
for line in self.out:
|
|
||||||
field = line.split()
|
|
||||||
if field[0] == "nexthop":
|
|
||||||
if not prefix:
|
|
||||||
# this saves only the first nexthop entry if multiple exist
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
prefix = field[0]
|
|
||||||
metric = field[-1]
|
|
||||||
tmp = prefix.split("/")
|
|
||||||
if len(tmp) < 2:
|
|
||||||
prefix += "/32"
|
|
||||||
if field[1] == "proto":
|
|
||||||
# nexthop entry is on the next line
|
|
||||||
continue
|
|
||||||
# nexthop IP or interface
|
|
||||||
gw = field[2]
|
|
||||||
r.append(Route(prefix, gw, metric))
|
|
||||||
prefix = None
|
|
||||||
|
|
||||||
if len(r) == 0:
|
|
||||||
self.warn("no kernel routes")
|
|
||||||
if self.verbose:
|
|
||||||
self.info(" %s has %d kernel routes" % (self.node.routerid, len(r)))
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
usagestr = "usage: %prog [-h] [options] [args]"
|
|
||||||
parser = optparse.OptionParser(usage=usagestr)
|
|
||||||
parser.set_defaults(numnodes=10, linkprob=0.35, delay=20, seed=None)
|
|
||||||
|
|
||||||
parser.add_option(
|
|
||||||
"-n", "--numnodes", dest="numnodes", type=int, help="number of nodes"
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-p", "--linkprob", dest="linkprob", type=float, help="link probabilty"
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-d", "--delay", dest="delay", type=float, help="wait time before checking"
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-s",
|
|
||||||
"--seed",
|
|
||||||
dest="seed",
|
|
||||||
type=int,
|
|
||||||
help="specify integer to use for random seed",
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-v", "--verbose", dest="verbose", action="store_true", help="be more verbose"
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"-l",
|
|
||||||
"--logfile",
|
|
||||||
dest="logfile",
|
|
||||||
type=str,
|
|
||||||
help="log detailed output to the specified file",
|
|
||||||
)
|
|
||||||
|
|
||||||
def usage(msg=None, err=0):
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
if msg:
|
|
||||||
sys.stdout.write(msg + "\n\n")
|
|
||||||
parser.print_help()
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
# parse command line options
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if options.numnodes < 2:
|
|
||||||
usage("invalid numnodes: %s" % options.numnodes)
|
|
||||||
if options.linkprob <= 0.0 or options.linkprob > 1.0:
|
|
||||||
usage("invalid linkprob: %s" % options.linkprob)
|
|
||||||
if options.delay < 0.0:
|
|
||||||
usage("invalid delay: %s" % options.delay)
|
|
||||||
|
|
||||||
for a in args:
|
|
||||||
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
|
|
||||||
|
|
||||||
if options.seed:
|
|
||||||
random.seed(options.seed)
|
|
||||||
|
|
||||||
me = ManetExperiment(options=options, start=datetime.datetime.now())
|
|
||||||
me.info(
|
|
||||||
"creating topology: numnodes = %s; linkprob = %s"
|
|
||||||
% (options.numnodes, options.linkprob)
|
|
||||||
)
|
|
||||||
me.topology(options.numnodes, options.linkprob)
|
|
||||||
|
|
||||||
me.info("waiting %s sec" % options.delay)
|
|
||||||
time.sleep(options.delay)
|
|
||||||
me.info("checking neighbor state and routes")
|
|
||||||
me.checknodes()
|
|
||||||
me.info("done")
|
|
||||||
me.info("elapsed time: %s" % (datetime.datetime.now() - me.start))
|
|
||||||
me.logend()
|
|
||||||
|
|
||||||
return me
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
me = main()
|
|
|
@ -6,11 +6,11 @@ import datetime
|
||||||
import parser
|
import parser
|
||||||
from builtins import range
|
from builtins import range
|
||||||
|
|
||||||
from core import load_logging_config
|
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
from core.emulator.emudata import IpPrefixes
|
from core.emulator.emudata import IpPrefixes
|
||||||
from core.emulator.enumerations import EventTypes
|
from core.emulator.enumerations import EventTypes
|
||||||
|
from core.utils import load_logging_config
|
||||||
|
|
||||||
load_logging_config()
|
load_logging_config()
|
||||||
|
|
|
@ -9,10 +9,10 @@ import datetime
|
||||||
import parser
|
import parser
|
||||||
from builtins import range
|
from builtins import range
|
||||||
|
|
||||||
from core import load_logging_config
|
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
from core.emulator.emudata import IpPrefixes
|
from core.emulator.emudata import IpPrefixes
|
||||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
from core.utils import load_logging_config
|
||||||
|
|
||||||
load_logging_config()
|
load_logging_config()
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
# nodestep
|
# nodestep
|
||||||
from builtins import range
|
from builtins import range
|
||||||
|
|
||||||
from core import load_logging_config
|
|
||||||
from core.emulator.emudata import IpPrefixes
|
from core.emulator.emudata import IpPrefixes
|
||||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
from core.utils import load_logging_config
|
||||||
|
|
||||||
load_logging_config()
|
load_logging_config()
|
||||||
|
|
|
@ -9,11 +9,11 @@ import datetime
|
||||||
import parser
|
import parser
|
||||||
from builtins import range
|
from builtins import range
|
||||||
|
|
||||||
from core import load_logging_config
|
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||||
from core.emulator.enumerations import EventTypes, NodeTypes
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
from core.location.mobility import BasicRangeModel
|
from core.location.mobility import BasicRangeModel
|
||||||
|
from core.utils import load_logging_config
|
||||||
|
|
||||||
load_logging_config()
|
load_logging_config()
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# (c)2010-2012 the Boeing Company
|
|
||||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
||||||
#
|
|
||||||
# List and stop CORE sessions from the command line.
|
|
||||||
#
|
|
||||||
|
|
||||||
import optparse
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from core.api.tlv import coreapi
|
|
||||||
from core.emulator.enumerations import CORE_API_PORT, MessageFlags, SessionTlvs
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = optparse.OptionParser(usage="usage: %prog [-l] <sessionid>")
|
|
||||||
parser.add_option(
|
|
||||||
"-l", "--list", dest="list", action="store_true", help="list running sessions"
|
|
||||||
)
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if options.list is True:
|
|
||||||
num = "0"
|
|
||||||
flags = MessageFlags.STRING.value
|
|
||||||
else:
|
|
||||||
num = args[0]
|
|
||||||
flags = MessageFlags.DELETE.value
|
|
||||||
tlvdata = coreapi.CoreSessionTlv.pack(SessionTlvs.NUMBER.value, num)
|
|
||||||
message = coreapi.CoreSessionMessage.pack(flags, tlvdata)
|
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
sock.connect(("localhost", CORE_API_PORT))
|
|
||||||
sock.send(message)
|
|
||||||
|
|
||||||
# receive and print a session list
|
|
||||||
if options.list is True:
|
|
||||||
hdr = sock.recv(coreapi.CoreMessage.header_len)
|
|
||||||
msgtype, msgflags, msglen = coreapi.CoreMessage.unpack_header(hdr)
|
|
||||||
data = ""
|
|
||||||
if msglen:
|
|
||||||
data = sock.recv(msglen)
|
|
||||||
message = coreapi.CoreMessage(msgflags, hdr, data)
|
|
||||||
sessions = message.get_tlv(coreapi.SessionTlvs.NUMBER.value)
|
|
||||||
print("sessions: {}".format(sessions))
|
|
||||||
|
|
||||||
sock.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,4 +1,4 @@
|
||||||
configparser==4.0.1
|
configparser==4.0.2
|
||||||
future==0.17.1
|
future==0.17.1
|
||||||
grpcio==1.23.0
|
grpcio==1.23.0
|
||||||
grpcio-tools==1.21.1
|
grpcio-tools==1.21.1
|
||||||
|
|
|
@ -12,12 +12,12 @@ import threading
|
||||||
import time
|
import time
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
|
|
||||||
from core import constants, load_logging_config
|
from core import constants
|
||||||
from core.api.grpc.server import CoreGrpcServer
|
from core.api.grpc.server import CoreGrpcServer
|
||||||
from core.api.tlv.corehandlers import CoreHandler, CoreUdpHandler
|
from core.api.tlv.corehandlers import CoreHandler, CoreUdpHandler
|
||||||
from core.api.tlv.coreserver import CoreServer, CoreUdpServer
|
from core.api.tlv.coreserver import CoreServer, CoreUdpServer
|
||||||
from core.emulator import enumerations
|
from core.emulator import enumerations
|
||||||
from core.utils import close_onexec
|
from core.utils import close_onexec, load_logging_config
|
||||||
|
|
||||||
|
|
||||||
def banner():
|
def banner():
|
||||||
|
@ -44,12 +44,11 @@ def start_udp(mainserver, server_address):
|
||||||
mainserver.udpthread.start()
|
mainserver.udpthread.start()
|
||||||
|
|
||||||
|
|
||||||
def cored(cfg, use_ovs):
|
def cored(cfg):
|
||||||
"""
|
"""
|
||||||
Start the CoreServer object and enter the server loop.
|
Start the CoreServer object and enter the server loop.
|
||||||
|
|
||||||
:param dict cfg: core configuration
|
:param dict cfg: core configuration
|
||||||
:param bool use_ovs: flag to determine if ovs nodes should be used
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
host = cfg["listenaddr"]
|
host = cfg["listenaddr"]
|
||||||
|
@ -60,9 +59,6 @@ def cored(cfg, use_ovs):
|
||||||
try:
|
try:
|
||||||
address = (host, port)
|
address = (host, port)
|
||||||
server = CoreServer(address, CoreHandler, cfg)
|
server = CoreServer(address, CoreHandler, cfg)
|
||||||
if use_ovs:
|
|
||||||
from core.nodes.openvswitch import OVS_NODES
|
|
||||||
server.coreemu.update_nodes(OVS_NODES)
|
|
||||||
except:
|
except:
|
||||||
logging.exception("error starting main server on: %s:%s", host, port)
|
logging.exception("error starting main server on: %s:%s", host, port)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -97,7 +93,6 @@ def get_merged_config(filename):
|
||||||
defaults = {
|
defaults = {
|
||||||
"port": "%d" % enumerations.CORE_API_PORT,
|
"port": "%d" % enumerations.CORE_API_PORT,
|
||||||
"listenaddr": "localhost",
|
"listenaddr": "localhost",
|
||||||
"xmlfilever": "1.0",
|
|
||||||
"numthreads": "1",
|
"numthreads": "1",
|
||||||
"grpcport": "50051",
|
"grpcport": "50051",
|
||||||
"grpcaddress": "localhost",
|
"grpcaddress": "localhost",
|
||||||
|
@ -137,9 +132,9 @@ def get_merged_config(filename):
|
||||||
if not cfg.has_section(section):
|
if not cfg.has_section(section):
|
||||||
cfg.add_section(section)
|
cfg.add_section(section)
|
||||||
|
|
||||||
# merge command line with config file
|
# merge argparse with configparser
|
||||||
for opt in args.__dict__:
|
for opt in vars(args):
|
||||||
val = args.__dict__[opt]
|
val = getattr(args, opt)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
cfg.set(section, opt, str(val))
|
cfg.set(section, opt, str(val))
|
||||||
|
|
||||||
|
@ -156,11 +151,8 @@ def main():
|
||||||
cfg = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
|
cfg = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
|
||||||
banner()
|
banner()
|
||||||
|
|
||||||
# check if ovs flag was provided
|
|
||||||
use_ovs = len(sys.argv) == 2 and sys.argv[1] == "ovs"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cored(cfg, use_ovs)
|
cored(cfg)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logging.info("keyboard interrupt, stopping core daemon")
|
logging.info("keyboard interrupt, stopping core daemon")
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@ from xml.etree import ElementTree
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from core import CoreError
|
|
||||||
from core.emane.bypass import EmaneBypassModel
|
from core.emane.bypass import EmaneBypassModel
|
||||||
from core.emane.commeffect import EmaneCommEffectModel
|
from core.emane.commeffect import EmaneCommEffectModel
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
from core.emane.rfpipe import EmaneRfPipeModel
|
from core.emane.rfpipe import EmaneRfPipeModel
|
||||||
from core.emane.tdma import EmaneTdmaModel
|
from core.emane.tdma import EmaneTdmaModel
|
||||||
from core.emulator.emudata import NodeOptions
|
from core.emulator.emudata import NodeOptions
|
||||||
|
from core.errors import CoreError
|
||||||
|
|
||||||
_EMANE_MODELS = [
|
_EMANE_MODELS = [
|
||||||
EmaneIeee80211abgModel,
|
EmaneIeee80211abgModel,
|
||||||
|
|
|
@ -4,7 +4,6 @@ Unit tests for testing basic CORE networks.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -109,10 +108,6 @@ class TestCore:
|
||||||
p, stdin, stdout, stderr = client.popen(command)
|
p, stdin, stdout, stderr = client.popen(command)
|
||||||
assert not p.wait()
|
assert not p.wait()
|
||||||
assert not client.icmd(command)
|
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
|
# check various command using command line
|
||||||
assert not client.cmd(command)
|
assert not client.cmd(command)
|
||||||
|
@ -121,15 +116,10 @@ class TestCore:
|
||||||
p, stdin, stdout, stderr = client.popen(command)
|
p, stdin, stdout, stderr = client.popen(command)
|
||||||
assert not p.wait()
|
assert not p.wait()
|
||||||
assert not client.icmd(command)
|
assert not client.icmd(command)
|
||||||
assert not client.shcmd(command[0])
|
|
||||||
|
|
||||||
# check module methods
|
# check module methods
|
||||||
assert createclients(session.session_dir)
|
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):
|
def test_netif(self, session, ip_prefixes):
|
||||||
"""
|
"""
|
||||||
Test netif methods.
|
Test netif methods.
|
||||||
|
|
|
@ -5,7 +5,6 @@ from queue import Queue
|
||||||
import grpc
|
import grpc
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from core import CoreError
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2
|
||||||
from core.api.grpc.client import CoreGrpcClient
|
from core.api.grpc.client import CoreGrpcClient
|
||||||
from core.config import ConfigShim
|
from core.config import ConfigShim
|
||||||
|
@ -18,6 +17,7 @@ from core.emulator.enumerations import (
|
||||||
ExceptionLevels,
|
ExceptionLevels,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
)
|
)
|
||||||
|
from core.errors import CoreError
|
||||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import time
|
||||||
import mock
|
import mock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from core import CoreError
|
|
||||||
from core.api.tlv import coreapi
|
from core.api.tlv import coreapi
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
from core.emulator.enumerations import (
|
from core.emulator.enumerations import (
|
||||||
|
@ -24,6 +23,7 @@ from core.emulator.enumerations import (
|
||||||
RegisterTlvs,
|
RegisterTlvs,
|
||||||
SessionTlvs,
|
SessionTlvs,
|
||||||
)
|
)
|
||||||
|
from core.errors import CoreError
|
||||||
from core.location.mobility import BasicRangeModel
|
from core.location.mobility import BasicRangeModel
|
||||||
from core.nodes.ipaddress import Ipv4Prefix
|
from core.nodes.ipaddress import Ipv4Prefix
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from core import CoreError, utils
|
from core import utils
|
||||||
from core.emulator.emudata import NodeOptions
|
from core.emulator.emudata import NodeOptions
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
|
from core.errors import CoreError
|
||||||
|
|
||||||
MODELS = ["router", "host", "PC", "mdr"]
|
MODELS = ["router", "host", "PC", "mdr"]
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ from xml.etree import ElementTree
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from core import CoreError
|
|
||||||
from core.emulator.emudata import LinkOptions, NodeOptions
|
from core.emulator.emudata import LinkOptions, NodeOptions
|
||||||
from core.emulator.enumerations import NodeTypes
|
from core.emulator.enumerations import NodeTypes
|
||||||
|
from core.errors import CoreError
|
||||||
from core.location.mobility import BasicRangeModel
|
from core.location.mobility import BasicRangeModel
|
||||||
from core.services.utility import SshService
|
from core.services.utility import SshService
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,21 @@ Install Path | Description
|
||||||
/etc/init.d/core-daemon|SysV startup script for daemon
|
/etc/init.d/core-daemon|SysV startup script for daemon
|
||||||
/etc/systemd/system/core-daemon.service|Systemd startup script for daemon
|
/etc/systemd/system/core-daemon.service|Systemd startup script for daemon
|
||||||
|
|
||||||
|
# Pre-Req Installing Python
|
||||||
|
|
||||||
|
You may already have these installed, and can ignore this step if so, but if
|
||||||
|
needed you can run the following to install python and pip.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# python 2
|
||||||
|
sudo apt install python
|
||||||
|
sudo apt install python-pip
|
||||||
|
|
||||||
|
# python 3
|
||||||
|
sudo apt install python3
|
||||||
|
sudo apt install python3-pip
|
||||||
|
```
|
||||||
|
|
||||||
# Pre-Req Python Requirements
|
# Pre-Req Python Requirements
|
||||||
|
|
||||||
The newly added gRPC API which depends on python library grpcio is not commonly found within system repos.
|
The newly added gRPC API which depends on python library grpcio is not commonly found within system repos.
|
||||||
|
@ -91,7 +106,7 @@ Requires building from source, from the latest nightly snapshot.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# packages needed beyond what's normally required to build core on ubuntu
|
# packages needed beyond what's normally required to build core on ubuntu
|
||||||
sudo apt install libtool libreadline-dev
|
sudo apt install libtool libreadline-dev autoconf
|
||||||
|
|
||||||
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/nightly_snapshots/quagga-svnsnap.tgz
|
wget https://downloads.pf.itd.nrl.navy.mil/ospf-manet/nightly_snapshots/quagga-svnsnap.tgz
|
||||||
tar xzf quagga-svnsnap.tgz
|
tar xzf quagga-svnsnap.tgz
|
||||||
|
@ -230,26 +245,26 @@ pip3 install grpcio-tools
|
||||||
### Ubuntu 18.04 Requirements
|
### Ubuntu 18.04 Requirements
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt install automake pkg-config gcc libev-dev bridge-utils ebtables python-dev python-setuptools tk libtk-img
|
sudo apt install automake pkg-config gcc libev-dev bridge-utils ebtables python-dev python-setuptools tk libtk-img ethtool
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ubuntu 16.04 Requirements
|
### Ubuntu 16.04 Requirements
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt-get install automake bridge-utils ebtables python-dev libev-dev python-setuptools libtk-img
|
sudo apt-get install automake bridge-utils ebtables python-dev libev-dev python-setuptools libtk-img ethtool
|
||||||
```
|
```
|
||||||
|
|
||||||
### CentOS 7 with Gnome Desktop Requirements
|
### CentOS 7 with Gnome Desktop Requirements
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo yum -y install automake gcc python-devel libev-devel tk
|
sudo yum -y install automake gcc python-devel libev-devel tk ethtool
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build and Install
|
## Build and Install
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./bootstrap.sh
|
./bootstrap.sh
|
||||||
# use python2 or python3 depending on desired version
|
# $VERSION should be path to python2/3
|
||||||
PYTHON=$VERSION ./configure
|
PYTHON=$VERSION ./configure
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
|
@ -268,7 +283,7 @@ sudo apt install python3-sphinx
|
||||||
sudo yum install python3-sphinx
|
sudo yum install python3-sphinx
|
||||||
|
|
||||||
./bootstrap.sh
|
./bootstrap.sh
|
||||||
# use python2 or python3 depending on desired version
|
# $VERSION should be path to python2/3
|
||||||
PYTHON=$VERSION ./configure
|
PYTHON=$VERSION ./configure
|
||||||
make doc
|
make doc
|
||||||
```
|
```
|
||||||
|
@ -282,8 +297,10 @@ Build package commands, DESTDIR is used to make install into and then for packag
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./bootstrap.sh
|
./bootstrap.sh
|
||||||
# use python2 or python3 depending on desired version
|
# for python2
|
||||||
PYTHON=$VERSION ./configure
|
PYTHON=python2 ./configure
|
||||||
|
# for python3
|
||||||
|
PYTHON=python3 ./configure --enable-python3
|
||||||
make
|
make
|
||||||
mkdir /tmp/core-build
|
mkdir /tmp/core-build
|
||||||
make fpm DESTDIR=/tmp/core-build
|
make fpm DESTDIR=/tmp/core-build
|
||||||
|
|
|
@ -2706,7 +2706,7 @@ proc sendNodeTypeInfo { sock reset } {
|
||||||
set typesinuse ""
|
set typesinuse ""
|
||||||
foreach node $node_list {
|
foreach node $node_list {
|
||||||
set type [nodeType $node]
|
set type [nodeType $node]
|
||||||
if { $type != "router" && $type != "OVS" } { continue }
|
if { $type != "router" } { continue }
|
||||||
set model [getNodeModel $node]
|
set model [getNodeModel $node]
|
||||||
if { [lsearch $typesinuse $model] < 0 } { lappend typesinuse $model }
|
if { [lsearch $typesinuse $model] < 0 } { lappend typesinuse $model }
|
||||||
}
|
}
|
||||||
|
@ -2910,7 +2910,6 @@ proc getNodeTypeAPI { node } {
|
||||||
router { return 0x0 }
|
router { return 0x0 }
|
||||||
netns { return 0x0 }
|
netns { return 0x0 }
|
||||||
jail { return 0x0 }
|
jail { return 0x0 }
|
||||||
OVS { return 0x0 }
|
|
||||||
physical { return 0x1 }
|
physical { return 0x1 }
|
||||||
tbd { return 0x3 }
|
tbd { return 0x3 }
|
||||||
lanswitch { return 0x4 }
|
lanswitch { return 0x4 }
|
||||||
|
|
|
@ -333,7 +333,7 @@ proc redrawAll {} {
|
||||||
|
|
||||||
proc drawNode { c node } {
|
proc drawNode { c node } {
|
||||||
global showNodeLabels
|
global showNodeLabels
|
||||||
global router pc host lanswitch rj45 hub pseudo OVS
|
global router pc host lanswitch rj45 hub pseudo
|
||||||
global curcanvas zoom
|
global curcanvas zoom
|
||||||
global wlan
|
global wlan
|
||||||
if { $c == "" } { set c .c } ;# default canvas
|
if { $c == "" } { set c .c } ;# default canvas
|
||||||
|
@ -348,7 +348,7 @@ proc drawNode { c node } {
|
||||||
set cimg ""
|
set cimg ""
|
||||||
set imgzoom $zoom
|
set imgzoom $zoom
|
||||||
if { $zoom == 0.75 || $zoom == 1.5 } { set imgzoom 1.0 }
|
if { $zoom == 0.75 || $zoom == 1.5 } { set imgzoom 1.0 }
|
||||||
if { $type == "router" || $type == "OVS" } {
|
if { $type == "router" } {
|
||||||
set model [getNodeModel $node]
|
set model [getNodeModel $node]
|
||||||
set cimg [getNodeTypeImage $model normal]
|
set cimg [getNodeTypeImage $model normal]
|
||||||
}
|
}
|
||||||
|
@ -1535,7 +1535,7 @@ proc raiseAll {c} {
|
||||||
proc button1 { c x y button } {
|
proc button1 { c x y button } {
|
||||||
global node_list plot_list curcanvas zoom
|
global node_list plot_list curcanvas zoom
|
||||||
global activetool activetoolp newlink curobj changed def_router_model
|
global activetool activetoolp newlink curobj changed def_router_model
|
||||||
global router pc host lanswitch rj45 hub OVS
|
global router pc host lanswitch rj45 hub
|
||||||
global oval rectangle text
|
global oval rectangle text
|
||||||
global lastX lastY
|
global lastX lastY
|
||||||
global background selectbox
|
global background selectbox
|
||||||
|
@ -1607,10 +1607,7 @@ proc button1 { c x y button } {
|
||||||
rectangle text} $activetool] < 0 } {
|
rectangle text} $activetool] < 0 } {
|
||||||
if { $g_view_locked == 1 } { return }
|
if { $g_view_locked == 1 } { return }
|
||||||
if { $activetoolp == "routers" } {
|
if { $activetoolp == "routers" } {
|
||||||
if {$activetool != "OVS"} {
|
|
||||||
set node [newNode router]
|
set node [newNode router]
|
||||||
} else {
|
|
||||||
set node [newNode OVS]}
|
|
||||||
setNodeModel $node $activetool
|
setNodeModel $node $activetool
|
||||||
} else {
|
} else {
|
||||||
set node [newNode $activetool]
|
set node [newNode $activetool]
|
||||||
|
@ -2550,7 +2547,7 @@ proc popupConfigDialog { c } {
|
||||||
-side right -padx 4 -pady 4
|
-side right -padx 4 -pady 4
|
||||||
# end Boeing
|
# end Boeing
|
||||||
pack $wi.ftop -side top
|
pack $wi.ftop -side top
|
||||||
if { $type == "router" || $type == "OVS"} {
|
if { $type == "router" } {
|
||||||
|
|
||||||
ttk::frame $wi.model -borderwidth 4
|
ttk::frame $wi.model -borderwidth 4
|
||||||
ttk::label $wi.model.label -text "Type:"
|
ttk::label $wi.model.label -text "Type:"
|
||||||
|
|
|
@ -108,7 +108,7 @@ proc autoIPv4addr { node iface } {
|
||||||
|
|
||||||
set peer_node [logicalPeerByIfc $node $iface]
|
set peer_node [logicalPeerByIfc $node $iface]
|
||||||
# find addresses of NETWORK layer peer nodes
|
# find addresses of NETWORK layer peer nodes
|
||||||
if { [[typemodel $peer_node].layer] == "LINK" || [nodeType $peer_node] == "OVS" } {
|
if { [[typemodel $peer_node].layer] == "LINK" } {
|
||||||
foreach l2node [listLANnodes $peer_node {}] {
|
foreach l2node [listLANnodes $peer_node {}] {
|
||||||
foreach ifc [ifcList $l2node] {
|
foreach ifc [ifcList $l2node] {
|
||||||
set peer [logicalPeerByIfc $l2node $ifc]
|
set peer [logicalPeerByIfc $l2node $ifc]
|
||||||
|
|
|
@ -747,15 +747,12 @@ proc newLink { lnode1 lnode2 } {
|
||||||
global defLinkColor defLinkWidth
|
global defLinkColor defLinkWidth
|
||||||
global curcanvas
|
global curcanvas
|
||||||
global systype
|
global systype
|
||||||
if { ([nodeType $lnode1] == "lanswitch" ||[nodeType $lnode1] == "OVS") && \
|
if { [nodeType $lnode1] == "lanswitch" && \
|
||||||
[nodeType $lnode2] != "router" && \
|
[nodeType $lnode2] != "router" && \
|
||||||
([nodeType $lnode2] != "lanswitch" || [nodeType $lnode2] != "OVS") } {
|
[nodeType $lnode2] != "lanswitch" } { set regular no }
|
||||||
set regular no }
|
if { [nodeType $lnode2] == "lanswitch" && \
|
||||||
if { ([nodeType $lnode2] == "lanswitch" || [nodeType $lnode2] == "OVS") && \
|
|
||||||
[nodeType $lnode1] != "router" && \
|
[nodeType $lnode1] != "router" && \
|
||||||
([nodeType $lnode1] != "lanswitch" || [nodeType $lnode1] != "OVS" )} {
|
[nodeType $lnode1] != "lanswitch" } { set regular no }
|
||||||
#Khaled: puts "connecting '$lnode1' (type: '[nodeType $lnode1]') to '$lnode2' (type: '[nodeType $lnode2]') "
|
|
||||||
set regular no }
|
|
||||||
if { [nodeType $lnode1] == "hub" && \
|
if { [nodeType $lnode1] == "hub" && \
|
||||||
[nodeType $lnode2] == "hub" } { set regular no }
|
[nodeType $lnode2] == "hub" } { set regular no }
|
||||||
# Boeing: added tunnel, ktunnel types to behave as rj45
|
# Boeing: added tunnel, ktunnel types to behave as rj45
|
||||||
|
@ -771,7 +768,7 @@ proc newLink { lnode1 lnode2 } {
|
||||||
set othernode $lnode1
|
set othernode $lnode1
|
||||||
}
|
}
|
||||||
# only allowed to link with certain types
|
# only allowed to link with certain types
|
||||||
if { [lsearch {router lanswitch hub pc host wlan OVS} \
|
if { [lsearch {router lanswitch hub pc host wlan} \
|
||||||
[nodeType $othernode]] < 0} {
|
[nodeType $othernode]] < 0} {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -836,18 +833,13 @@ proc newLink { lnode1 lnode2 } {
|
||||||
} elseif {$delay != ""} {
|
} elseif {$delay != ""} {
|
||||||
lappend $link "delay $delay"
|
lappend $link "delay $delay"
|
||||||
}
|
}
|
||||||
# Exclude OVS from network layer nodes IP address asignments
|
if { [[typemodel $lnode2].layer] == "NETWORK" } {
|
||||||
if { ([[typemodel $lnode2].layer] == "NETWORK") && ([nodeType $lnode2] != "OVS") } {
|
|
||||||
|
|
||||||
#Khaled: puts "Assigning '$lnode2' (type: '[nodeType $lnode2]') an automatic IP address"
|
|
||||||
|
|
||||||
if { $ipv4_addr2 == "" } { autoIPv4addr $lnode2 $ifname2 }
|
if { $ipv4_addr2 == "" } { autoIPv4addr $lnode2 $ifname2 }
|
||||||
if { $ipv6_addr2 == "" } { autoIPv6addr $lnode2 $ifname2 }
|
if { $ipv6_addr2 == "" } { autoIPv6addr $lnode2 $ifname2 }
|
||||||
}
|
}
|
||||||
# tunnels also excluded from link settings
|
# tunnels also excluded from link settings
|
||||||
# OVS and Lanswitch should go side by side
|
} elseif { ([nodeType $lnode1] == "lanswitch" || \
|
||||||
} elseif { (([nodeType $lnode1] == "lanswitch" || [nodeType $lnode1] == "OVS" )|| \
|
[nodeType $lnode2] == "lanswitch" || \
|
||||||
([nodeType $lnode2] == "lanswitch"|| [nodeType $lnode2] == "OVS") || \
|
|
||||||
[string first eth "$ifname1 $ifname2"] != -1) && \
|
[string first eth "$ifname1 $ifname2"] != -1) && \
|
||||||
[nodeType $lnode1] != "rj45" && [nodeType $lnode2] != "rj45" && \
|
[nodeType $lnode1] != "rj45" && [nodeType $lnode2] != "rj45" && \
|
||||||
[nodeType $lnode1] != "tunnel" && [nodeType $lnode2] != "tunnel" && \
|
[nodeType $lnode1] != "tunnel" && [nodeType $lnode2] != "tunnel" && \
|
||||||
|
@ -859,11 +851,9 @@ proc newLink { lnode1 lnode2 } {
|
||||||
}
|
}
|
||||||
|
|
||||||
lappend link_list $link
|
lappend link_list $link
|
||||||
# Exclude OVS from Network layer node configs
|
|
||||||
if { [nodeType $lnode2] != "pseudo" &&
|
if { [nodeType $lnode2] != "pseudo" &&
|
||||||
[nodeType $lnode1] != "wlan" &&
|
[nodeType $lnode1] != "wlan" &&
|
||||||
([[typemodel $lnode1].layer] == "NETWORK" && [nodeType $lnode1] != "OVS") } {
|
[[typemodel $lnode1].layer] == "NETWORK" } {
|
||||||
|
|
||||||
if { $ipv4_addr1 == "" && $do_auto_addressing } {
|
if { $ipv4_addr1 == "" && $do_auto_addressing } {
|
||||||
autoIPv4addr $lnode1 $ifname1
|
autoIPv4addr $lnode1 $ifname1
|
||||||
}
|
}
|
||||||
|
@ -874,8 +864,7 @@ proc newLink { lnode1 lnode2 } {
|
||||||
# assume wlan is always lnode1
|
# assume wlan is always lnode1
|
||||||
if { [nodeType $lnode1] != "pseudo" &&
|
if { [nodeType $lnode1] != "pseudo" &&
|
||||||
[nodeType $lnode1] != "wlan" &&
|
[nodeType $lnode1] != "wlan" &&
|
||||||
([[typemodel $lnode2].layer] == "NETWORK" && [nodeType $lnode2] != "OVS") } {
|
[[typemodel $lnode2].layer] == "NETWORK" } {
|
||||||
|
|
||||||
if { $ipv4_addr2 == "" && $do_auto_addressing } {
|
if { $ipv4_addr2 == "" && $do_auto_addressing } {
|
||||||
autoIPv4addr $lnode2 $ifname2
|
autoIPv4addr $lnode2 $ifname2
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,10 @@ array set g_node_types_default {
|
||||||
5 {prouter router_green.gif router_green.gif \
|
5 {prouter router_green.gif router_green.gif \
|
||||||
{zebra OSPFv2 OSPFv3 IPForward} \
|
{zebra OSPFv2 OSPFv3 IPForward} \
|
||||||
physical {built-in type for physical nodes}}
|
physical {built-in type for physical nodes}}
|
||||||
6 {OVS lanswitch.gif lanswitch.gif {DefaultRoute SSH OvsService} OVS {} }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# possible machine types for nodes
|
# possible machine types for nodes
|
||||||
set MACHINE_TYPES "netns physical OVS"
|
set MACHINE_TYPES "netns physical"
|
||||||
|
|
||||||
# array populated from nodes.conf file
|
# array populated from nodes.conf file
|
||||||
array set g_node_types { }
|
array set g_node_types { }
|
||||||
|
@ -184,7 +182,7 @@ proc getNodeTypeServices { type } {
|
||||||
# node type from the toolbar
|
# node type from the toolbar
|
||||||
proc getNodeTypeMachineType { type } {
|
proc getNodeTypeMachineType { type } {
|
||||||
global MACHINE_TYPES g_node_types
|
global MACHINE_TYPES g_node_types
|
||||||
set default_machine_type [lindex $MACHINE_TYPES 3]
|
set default_machine_type [lindex $MACHINE_TYPES 0]
|
||||||
set i [getNodeTypeIndex $type]
|
set i [getNodeTypeIndex $type]
|
||||||
if { $i < 0 } { return $default_machine_type }; # failsafe
|
if { $i < 0 } { return $default_machine_type }; # failsafe
|
||||||
return [lindex $g_node_types($i) 4]
|
return [lindex $g_node_types($i) 4]
|
||||||
|
@ -208,7 +206,7 @@ proc getNodeTypeProfile { type } {
|
||||||
# node type from the toolbar
|
# node type from the toolbar
|
||||||
proc getNodeTypeMachineType { type } {
|
proc getNodeTypeMachineType { type } {
|
||||||
global MACHINE_TYPES g_node_types
|
global MACHINE_TYPES g_node_types
|
||||||
set default_machine_type [lindex $MACHINE_TYPES 3]
|
set default_machine_type [lindex $MACHINE_TYPES 0]
|
||||||
set i [getNodeTypeIndex $type]
|
set i [getNodeTypeIndex $type]
|
||||||
if { $i < 0 } { return $default_machine_type }; # failsafe
|
if { $i < 0 } { return $default_machine_type }; # failsafe
|
||||||
return [lindex $g_node_types($i) 4]
|
return [lindex $g_node_types($i) 4]
|
||||||
|
@ -714,7 +712,6 @@ proc lanswitch.layer {} { return LINK }
|
||||||
proc hub.layer {} { return LINK }
|
proc hub.layer {} { return LINK }
|
||||||
proc tunnel.layer {} { return LINK }
|
proc tunnel.layer {} { return LINK }
|
||||||
proc wlan.layer {} { return LINK }
|
proc wlan.layer {} { return LINK }
|
||||||
proc OVS.layer {} { return NETWORK }
|
|
||||||
proc router.layer {} { return NETWORK }
|
proc router.layer {} { return NETWORK }
|
||||||
proc router.shellcmd { n } { return "vtysh" }
|
proc router.shellcmd { n } { return "vtysh" }
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,7 @@
|
||||||
|
|
||||||
if WANT_PYTHON
|
if WANT_PYTHON
|
||||||
|
|
||||||
if PYTHON3
|
PYTHONLIBDIR=$(subst site-packages,dist-packages,$(pythondir))
|
||||||
PYTHONLIBDIR=$(libdir)/python3/dist-packages
|
|
||||||
else
|
|
||||||
PYTHONLIBDIR=$(pythondir)
|
|
||||||
endif
|
|
||||||
|
|
||||||
SETUPPY = setup.py
|
SETUPPY = setup.py
|
||||||
SETUPPYFLAGS = -v
|
SETUPPYFLAGS = -v
|
||||||
|
|
|
@ -10,17 +10,16 @@ import sys
|
||||||
|
|
||||||
import ns.core
|
import ns.core
|
||||||
import ns.mobility
|
import ns.mobility
|
||||||
|
|
||||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
|
||||||
from corens3.obj import Ns3LteNet
|
from corens3.obj import Ns3LteNet
|
||||||
from corens3.obj import Ns3Session
|
from corens3.obj import Ns3Session
|
||||||
|
|
||||||
|
from core.nodes import ipaddress
|
||||||
|
|
||||||
|
|
||||||
def ltesession(opt):
|
def ltesession(opt):
|
||||||
"""
|
"""
|
||||||
Run a test LTE session.
|
Run a test LTE session.
|
||||||
"""
|
"""
|
||||||
nodeutils.set_node_map(nodemaps.NODES)
|
|
||||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||||
lte = session.create_node(cls=Ns3LteNet, name="wlan1")
|
lte = session.create_node(cls=Ns3LteNet, name="wlan1")
|
||||||
lte.setsubchannels(range(25), range(50, 100))
|
lte.setsubchannels(range(25), range(50, 100))
|
||||||
|
|
|
@ -27,11 +27,11 @@ import optparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import ns.core
|
import ns.core
|
||||||
|
|
||||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
|
||||||
from corens3.obj import Ns3Session
|
from corens3.obj import Ns3Session
|
||||||
from corens3.obj import Ns3WifiNet
|
from corens3.obj import Ns3WifiNet
|
||||||
|
|
||||||
|
from core.nodes import ipaddress
|
||||||
|
|
||||||
|
|
||||||
def add_to_server(session):
|
def add_to_server(session):
|
||||||
"""
|
"""
|
||||||
|
@ -50,7 +50,6 @@ def wifisession(opt):
|
||||||
"""
|
"""
|
||||||
Run a test wifi session.
|
Run a test wifi session.
|
||||||
"""
|
"""
|
||||||
nodeutils.set_node_map(nodemaps.NODES)
|
|
||||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||||
session.name = "ns3wifi"
|
session.name = "ns3wifi"
|
||||||
session.filename = session.name + ".py"
|
session.filename = session.name + ".py"
|
||||||
|
|
|
@ -14,13 +14,14 @@ How to run this:
|
||||||
import logging
|
import logging
|
||||||
import optparse
|
import optparse
|
||||||
import sys
|
import sys
|
||||||
|
from builtins import range
|
||||||
|
|
||||||
import ns.core
|
import ns.core
|
||||||
import ns.network
|
import ns.network
|
||||||
from corens3.obj import Ns3Session
|
from corens3.obj import Ns3Session
|
||||||
from corens3.obj import Ns3WifiNet
|
from corens3.obj import Ns3WifiNet
|
||||||
|
|
||||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
from core.nodes import ipaddress
|
||||||
|
|
||||||
|
|
||||||
def add_to_server(session):
|
def add_to_server(session):
|
||||||
|
@ -40,7 +41,6 @@ def wifisession(opt):
|
||||||
"""
|
"""
|
||||||
Run a random walk wifi session.
|
Run a random walk wifi session.
|
||||||
"""
|
"""
|
||||||
nodeutils.set_node_map(nodemaps.NODES)
|
|
||||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||||
session.name = "ns3wifirandomwalk"
|
session.name = "ns3wifirandomwalk"
|
||||||
session.filename = session.name + ".py"
|
session.filename = session.name + ".py"
|
||||||
|
@ -54,7 +54,7 @@ def wifisession(opt):
|
||||||
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
|
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
|
||||||
services_str = "zebra|OSPFv3MDR|IPForward"
|
services_str = "zebra|OSPFv3MDR|IPForward"
|
||||||
nodes = []
|
nodes = []
|
||||||
for i in xrange(1, opt.numnodes + 1):
|
for i in range(1, opt.numnodes + 1):
|
||||||
node = session.addnode(name="n%d" % i)
|
node = session.addnode(name="n%d" % i)
|
||||||
node.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
node.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
|
@ -12,17 +12,18 @@ Current issues:
|
||||||
import logging
|
import logging
|
||||||
import optparse
|
import optparse
|
||||||
import sys
|
import sys
|
||||||
|
from builtins import range
|
||||||
|
|
||||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
|
||||||
from corens3.obj import Ns3Session
|
from corens3.obj import Ns3Session
|
||||||
from corens3.obj import Ns3WimaxNet
|
from corens3.obj import Ns3WimaxNet
|
||||||
|
|
||||||
|
from core.nodes import ipaddress
|
||||||
|
|
||||||
|
|
||||||
def wimaxsession(opt):
|
def wimaxsession(opt):
|
||||||
"""
|
"""
|
||||||
Run a test wimax session.
|
Run a test wimax session.
|
||||||
"""
|
"""
|
||||||
nodeutils.set_node_map(nodemaps.NODES)
|
|
||||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||||
wimax = session.create_node(cls=Ns3WimaxNet, name="wlan1")
|
wimax = session.create_node(cls=Ns3WimaxNet, name="wlan1")
|
||||||
# wimax.wimax.EnableLogComponents()
|
# wimax.wimax.EnableLogComponents()
|
||||||
|
@ -33,7 +34,7 @@ def wimaxsession(opt):
|
||||||
# classifier = (0, 65000, 0, 65000, 1, 1)
|
# classifier = (0, 65000, 0, 65000, 1, 1)
|
||||||
classifier = (0, 65000, 0, 65000, 17, 1)
|
classifier = (0, 65000, 0, 65000, 17, 1)
|
||||||
nodes = []
|
nodes = []
|
||||||
for i in xrange(1, opt.numnodes + 1):
|
for i in range(1, opt.numnodes + 1):
|
||||||
node = session.addnode(name="n%d" % i)
|
node = session.addnode(name="n%d" % i)
|
||||||
if i == 1:
|
if i == 1:
|
||||||
wimax.setbasestation(node)
|
wimax.setbasestation(node)
|
||||||
|
|
Loading…
Add table
Reference in a new issue