Merge pull request #299 from coreemu/develop

Merging 5.5.0 for release
This commit is contained in:
bharnden 2019-10-03 13:19:51 -07:00 committed by GitHub
commit 839a1b9368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 1009 additions and 3331 deletions

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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 = "*"

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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 ()

View file

@ -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")

View file

@ -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):

View file

@ -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

View file

@ -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 = {}

View file

@ -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

View file

@ -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
""" """

View file

@ -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
View 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

View file

@ -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

View file

@ -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):
""" """

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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

View 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"])

View file

@ -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):
""" """

View file

@ -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,
}

View file

@ -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))

View file

@ -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,
}

View file

@ -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):
""" """

View file

@ -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

View file

@ -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

View file

@ -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
) )

View file

@ -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 ""

View file

@ -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
+ """\ + """\

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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

View file

@ -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")

View file

@ -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,

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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"]

View file

@ -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

View file

@ -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

View file

@ -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 }

View file

@ -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:"

View file

@ -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]

View file

@ -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
} }

View file

@ -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" }

View file

@ -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

View file

@ -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))

View file

@ -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"

View file

@ -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)

View file

@ -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)