Merge branch 'develop' into coretk-create-node
This commit is contained in:
commit
130c2a9b8d
42 changed files with 478 additions and 1251 deletions
|
@ -11,6 +11,7 @@ insert_final_newline = true
|
|||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
max_line_length = 88
|
||||
|
||||
[*.am]
|
||||
indent_style = tab
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# CORE [](https://www.codacy.com/app/blakeharnden/core?utm_source=github.com&utm_medium=referral&utm_content=coreemu/core&utm_campaign=Badge_Grade)
|
||||
# CORE
|
||||
|
||||
CORE: Common Open Research Emulator
|
||||
|
||||
|
|
11
configure.ac
11
configure.ac
|
@ -2,7 +2,7 @@
|
|||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
# this defines the CORE version number, must be static for AC_INIT
|
||||
AC_INIT(core, 5.3.1)
|
||||
AC_INIT(core, 5.4.0)
|
||||
|
||||
# autoconf and automake initialization
|
||||
AC_CONFIG_SRCDIR([netns/version.h.in])
|
||||
|
@ -54,6 +54,13 @@ if test "x$enable_python" = "xyes" ; then
|
|||
else
|
||||
want_python=no
|
||||
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],
|
||||
[AS_HELP_STRING([--enable-daemon[=ARG]],
|
||||
[build and install the daemon with Python modules
|
||||
|
@ -110,7 +117,6 @@ if test "x$enable_daemon" = "xyes"; then
|
|||
AC_CHECK_FUNCS([atexit dup2 gettimeofday memset socket strerror uname])
|
||||
|
||||
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])])
|
||||
|
||||
AC_CHECK_PROG(brctl_path, brctl, $as_dir, no, $SEARCHPATH)
|
||||
|
@ -157,6 +163,7 @@ if test "x$enable_daemon" = "xyes"; then
|
|||
CFLAGS=$CFLAGS_save
|
||||
CPPFLAGS=$CPPFLAGS_save
|
||||
fi
|
||||
|
||||
if [ test "x$enable_daemon" = "xyes" || test "x$enable_vnodedonly" = "xyes" ] ; then
|
||||
want_linux_netns=yes
|
||||
PKG_CHECK_MODULES(libev, libev,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<jung.version>2.1.1</jung.version>
|
||||
<jackson.version>2.9.9</jackson.version>
|
||||
<jackson.version>2.10.0.pr2</jackson.version>
|
||||
<grpc.version>1.20.0</grpc.version>
|
||||
<log4j.version>2.9.0</log4j.version>
|
||||
</properties>
|
||||
|
|
|
@ -14,11 +14,7 @@ if WANT_DOCS
|
|||
DOCS = doc
|
||||
endif
|
||||
|
||||
if PYTHON3
|
||||
PYTHONLIBDIR=$(libdir)/python3/dist-packages
|
||||
else
|
||||
PYTHONLIBDIR=$(pythondir)
|
||||
endif
|
||||
PYTHONLIBDIR=$(subst site-packages,dist-packages,$(pythondir))
|
||||
|
||||
SUBDIRS = proto $(DOCS)
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@ url = "https://pypi.org/simple"
|
|||
verify_ssl = true
|
||||
|
||||
[scripts]
|
||||
coredev = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
|
||||
coretest = "python -m pytest -v tests"
|
||||
core = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
|
||||
test = "pytest -v tests"
|
||||
test_emane = "pytest -v tests/emane"
|
||||
|
||||
[dev-packages]
|
||||
grpcio-tools = "*"
|
||||
|
|
|
@ -12,6 +12,7 @@ import grpc
|
|||
|
||||
from core import CoreError
|
||||
from core.api.grpc import core_pb2, core_pb2_grpc
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.emulator.data import (
|
||||
ConfigData,
|
||||
EventData,
|
||||
|
@ -23,7 +24,6 @@ from core.emulator.data import (
|
|||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||
from core.emulator.enumerations import EventTypes, LinkTypes, NodeTypes
|
||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.docker import DockerNode
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
|
@ -444,7 +444,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
if not isinstance(node.id, int):
|
||||
continue
|
||||
|
||||
node_type = nodeutils.get_node_type(node.__class__).value
|
||||
node_type = session.get_node_type(node.__class__)
|
||||
model = getattr(node, "type", None)
|
||||
position = core_pb2.Position(
|
||||
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]
|
||||
|
||||
emane_model = None
|
||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
||||
if isinstance(node, EmaneNode):
|
||||
emane_model = node.model.name
|
||||
|
||||
node_proto = core_pb2.Node(
|
||||
|
@ -464,7 +464,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
name=node.name,
|
||||
emane=emane_model,
|
||||
model=model,
|
||||
type=node_type,
|
||||
type=node_type.value,
|
||||
position=position,
|
||||
services=services,
|
||||
)
|
||||
|
@ -809,18 +809,18 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
interfaces.append(interface_proto)
|
||||
|
||||
emane_model = None
|
||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
||||
if isinstance(node, EmaneNode):
|
||||
emane_model = node.model.name
|
||||
|
||||
services = [x.name for x in getattr(node, "services", [])]
|
||||
position = core_pb2.Position(
|
||||
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(
|
||||
id=node.id,
|
||||
name=node.name,
|
||||
type=node_type,
|
||||
type=node_type.value,
|
||||
emane=emane_model,
|
||||
model=node.type,
|
||||
position=position,
|
||||
|
|
|
@ -12,6 +12,7 @@ import threading
|
|||
|
||||
from core import utils
|
||||
from core.api.tlv import coreapi
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.enumerations import (
|
||||
ConfigDataTypes,
|
||||
ConfigFlags,
|
||||
|
@ -27,11 +28,10 @@ from core.emulator.enumerations import (
|
|||
NodeTypes,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
||||
from core.nodes.interface import GreTap
|
||||
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
|
||||
|
||||
|
||||
|
@ -318,13 +318,13 @@ class CoreBroker(object):
|
|||
# leave this socket connected
|
||||
return
|
||||
|
||||
logging.info(
|
||||
logging.debug(
|
||||
"closing connection with %s @ %s:%s", name, server.host, server.port
|
||||
)
|
||||
server.close()
|
||||
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)
|
||||
if host is not None and port is not None:
|
||||
try:
|
||||
|
@ -492,33 +492,30 @@ class CoreBroker(object):
|
|||
:raises core.CoreError: when node to add net tunnel to does not exist
|
||||
"""
|
||||
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
|
||||
if nodeutils.is_node(net, NodeTypes.EMANE_NET):
|
||||
if isinstance(net, EmaneNet):
|
||||
logging.warning("emane network does not require a tunnel")
|
||||
return None
|
||||
|
||||
server_interface = getattr(net, "serverintf", None)
|
||||
if (
|
||||
nodeutils.is_node(net, NodeTypes.CONTROL_NET)
|
||||
and server_interface is not None
|
||||
):
|
||||
logging.warning(
|
||||
if isinstance(net, CtrlNet) and server_interface is not None:
|
||||
logging.debug(
|
||||
"control networks with server interfaces do not need a tunnel"
|
||||
)
|
||||
return None
|
||||
|
||||
servers = self.getserversbynode(node_id)
|
||||
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
|
||||
|
||||
hosts = []
|
||||
for server in servers:
|
||||
if server.host is None:
|
||||
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)
|
||||
|
||||
if len(hosts) == 0:
|
||||
|
@ -526,7 +523,7 @@ class CoreBroker(object):
|
|||
# get IP address from API message sender (master)
|
||||
if session_client.client_address != "":
|
||||
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)
|
||||
|
||||
r = []
|
||||
|
@ -539,7 +536,7 @@ class CoreBroker(object):
|
|||
myip = host
|
||||
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
|
||||
if key in self.tunnels.keys():
|
||||
logging.info(
|
||||
logging.debug(
|
||||
"tunnel already exists, returning existing tunnel: %s", key
|
||||
)
|
||||
gt = self.tunnels[key]
|
||||
|
@ -658,9 +655,9 @@ class CoreBroker(object):
|
|||
:param int nodenum: node id to add
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("adding net to broker: %s", nodenum)
|
||||
logging.debug("adding net to broker: %s", 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):
|
||||
"""
|
||||
|
@ -824,7 +821,8 @@ class CoreBroker(object):
|
|||
nodetype = message.get_tlv(NodeTlvs.TYPE.value)
|
||||
if nodetype is not None:
|
||||
try:
|
||||
nodecls = nodeutils.get_node_class(NodeTypes(nodetype))
|
||||
nodetype = NodeTypes(nodetype)
|
||||
nodecls = self.session.get_node_class(nodetype)
|
||||
except KeyError:
|
||||
logging.warning("broker invalid node type %s", nodetype)
|
||||
return handle_locally, servers
|
||||
|
|
|
@ -38,7 +38,7 @@ from core.emulator.enumerations import (
|
|||
SessionTlvs,
|
||||
)
|
||||
from core.location.mobility import BasicRangeModel
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.network import WlanNode
|
||||
from core.services.coreservices import ServiceManager, ServiceShim
|
||||
|
||||
|
||||
|
@ -1603,8 +1603,8 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
node = self.session.get_node(node_id)
|
||||
|
||||
# configure mobility models for WLAN added during runtime
|
||||
if event_type == EventTypes.INSTANTIATION_STATE and nodeutils.is_node(
|
||||
node, NodeTypes.WIRELESS_LAN
|
||||
if event_type == EventTypes.INSTANTIATION_STATE and isinstance(
|
||||
node, WlanNode
|
||||
):
|
||||
self.session.start_mobility(node_ids=(node.id,))
|
||||
return ()
|
||||
|
|
|
@ -15,6 +15,7 @@ from core.emane.bypass import EmaneBypassModel
|
|||
from core.emane.commeffect import EmaneCommEffectModel
|
||||
from core.emane.emanemodel import EmaneModel
|
||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.emane.rfpipe import EmaneRfPipeModel
|
||||
from core.emane.tdma import EmaneTdmaModel
|
||||
from core.emulator.enumerations import (
|
||||
|
@ -23,10 +24,8 @@ from core.emulator.enumerations import (
|
|||
ConfigTlvs,
|
||||
MessageFlags,
|
||||
MessageTypes,
|
||||
NodeTypes,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.nodes import nodeutils
|
||||
from core.xml import emanexml
|
||||
|
||||
try:
|
||||
|
@ -266,7 +265,7 @@ class EmaneManager(ModelManager):
|
|||
with self.session._nodes_lock:
|
||||
for node_id in self.session.nodes:
|
||||
node = self.session.nodes[node_id]
|
||||
if nodeutils.is_node(node, NodeTypes.EMANE):
|
||||
if isinstance(node, EmaneNode):
|
||||
logging.debug(
|
||||
"adding emane node: id(%s) name(%s)", node.id, node.name
|
||||
)
|
||||
|
|
|
@ -25,7 +25,6 @@ class EmaneNet(CoreNetworkBase):
|
|||
|
||||
apitype = NodeTypes.EMANE.value
|
||||
linktype = LinkTypes.WIRELESS.value
|
||||
# icon used
|
||||
type = "wlan"
|
||||
|
||||
|
||||
|
@ -36,6 +35,8 @@ class EmaneNode(EmaneNet):
|
|||
Emane controller object that exists in a session.
|
||||
"""
|
||||
|
||||
is_emane = True
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True):
|
||||
super(EmaneNode, self).__init__(session, _id, name, start)
|
||||
self.conf = ""
|
||||
|
|
|
@ -7,7 +7,6 @@ import sys
|
|||
import core.services
|
||||
from core.emulator.emudata import IdGen
|
||||
from core.emulator.session import Session
|
||||
from core.nodes import nodemaps, nodeutils
|
||||
from core.services.coreservices import ServiceManager
|
||||
|
||||
|
||||
|
@ -45,7 +44,7 @@ class CoreEmu(object):
|
|||
os.umask(0)
|
||||
|
||||
# configuration
|
||||
if not config:
|
||||
if config is None:
|
||||
config = {}
|
||||
self.config = config
|
||||
|
||||
|
@ -53,10 +52,6 @@ class CoreEmu(object):
|
|||
self.session_id_gen = IdGen(_id=0)
|
||||
self.sessions = {}
|
||||
|
||||
# set default nodes
|
||||
node_map = nodemaps.NODES
|
||||
nodeutils.set_node_map(node_map)
|
||||
|
||||
# load services
|
||||
self.service_errors = []
|
||||
self.load_services()
|
||||
|
@ -77,15 +72,6 @@ class CoreEmu(object):
|
|||
custom_service_errors = ServiceManager.add_services(service_path)
|
||||
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):
|
||||
"""
|
||||
Shutdown all CORE session.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
|
||||
from core.nodes.physical import PhysicalNode
|
||||
|
||||
|
||||
class IdGen(object):
|
||||
|
@ -13,17 +13,6 @@ class IdGen(object):
|
|||
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):
|
||||
"""
|
||||
Create an interface for a node on a network using provided interface data.
|
||||
|
@ -65,7 +54,7 @@ def link_config(network, interface, link_options, devname=None, interface_two=No
|
|||
}
|
||||
|
||||
# hacky check here, because physical and emane nodes do not conform to the same linkconfig interface
|
||||
if not nodeutils.is_node(network, [NodeTypes.EMANE, NodeTypes.PHYSICAL]):
|
||||
if not isinstance(network, (EmaneNode, PhysicalNode)):
|
||||
config["devname"] = devname
|
||||
|
||||
network.linkconfig(**config)
|
||||
|
|
|
@ -14,18 +14,17 @@ import threading
|
|||
import time
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
import core.nodes.base
|
||||
from core import CoreError, constants, utils
|
||||
from core.api.tlv import coreapi
|
||||
from core.api.tlv.broker import CoreBroker
|
||||
from core.emane.emanemanager import EmaneManager
|
||||
from core.emane.nodes import EmaneNet, EmaneNode
|
||||
from core.emulator.data import EventData, ExceptionData, NodeData
|
||||
from core.emulator.emudata import (
|
||||
IdGen,
|
||||
LinkOptions,
|
||||
NodeOptions,
|
||||
create_interface,
|
||||
is_net_node,
|
||||
link_config,
|
||||
)
|
||||
from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes
|
||||
|
@ -33,14 +32,46 @@ from core.emulator.sessionconfig import SessionConfig, SessionMetaData
|
|||
from core.location.corelocation import CoreLocation
|
||||
from core.location.event import EventLoop
|
||||
from core.location.mobility import MobilityManager
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase
|
||||
from core.nodes.docker import DockerNode
|
||||
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.services.coreservices import CoreServices
|
||||
from core.xml import corexml, corexmldeployment
|
||||
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: EmaneNode,
|
||||
NodeTypes.EMANE_NET: EmaneNet,
|
||||
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):
|
||||
"""
|
||||
|
@ -121,6 +152,33 @@ class Session(object):
|
|||
"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):
|
||||
"""
|
||||
Convenience method for retrieving nodes within link data.
|
||||
|
@ -145,7 +203,7 @@ class Session(object):
|
|||
# both node ids are provided
|
||||
tunnel = self.broker.gettunnel(node_one_id, node_two_id)
|
||||
logging.debug("tunnel between nodes: %s", tunnel)
|
||||
if nodeutils.is_node(tunnel, NodeTypes.TAP_BRIDGE):
|
||||
if isinstance(tunnel, GreTapBridge):
|
||||
net_one = tunnel
|
||||
if tunnel.remotenum == node_one_id:
|
||||
node_one = None
|
||||
|
@ -158,14 +216,14 @@ class Session(object):
|
|||
else:
|
||||
node_two = None
|
||||
|
||||
if is_net_node(node_one):
|
||||
if isinstance(node_one, CoreNetworkBase):
|
||||
if not net_one:
|
||||
net_one = node_one
|
||||
else:
|
||||
net_two = node_one
|
||||
node_one = None
|
||||
|
||||
if is_net_node(node_two):
|
||||
if isinstance(node_two, CoreNetworkBase):
|
||||
if not net_one:
|
||||
net_one = node_two
|
||||
else:
|
||||
|
@ -203,9 +261,7 @@ class Session(object):
|
|||
raise CoreError("no common network found for wireless link/unlink")
|
||||
|
||||
for common_network, interface_one, interface_two in common_networks:
|
||||
if not nodeutils.is_node(
|
||||
common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]
|
||||
):
|
||||
if not isinstance(common_network, (WlanNode, EmaneNode)):
|
||||
logging.info(
|
||||
"skipping common network that is not wireless/emane: %s",
|
||||
common_network,
|
||||
|
@ -268,9 +324,8 @@ class Session(object):
|
|||
node_one.name,
|
||||
node_two.name,
|
||||
)
|
||||
ptp_class = nodeutils.get_node_class(NodeTypes.PEER_TO_PEER)
|
||||
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
|
||||
if node_one and net_one:
|
||||
|
@ -300,7 +355,7 @@ class Session(object):
|
|||
net_one.name,
|
||||
net_two.name,
|
||||
)
|
||||
if nodeutils.is_node(net_two, NodeTypes.RJ45):
|
||||
if isinstance(net_two, Rj45Node):
|
||||
interface = net_two.linknet(net_one)
|
||||
else:
|
||||
interface = net_one.linknet(net_two)
|
||||
|
@ -324,12 +379,12 @@ class Session(object):
|
|||
|
||||
# tunnel node logic
|
||||
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)
|
||||
net_one.setkey(key)
|
||||
if 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)
|
||||
net_two.setkey(key)
|
||||
if addresses:
|
||||
|
@ -337,14 +392,14 @@ class Session(object):
|
|||
|
||||
# physical node connected with tunnel
|
||||
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)
|
||||
addresses = interface_one.get_addresses()
|
||||
node_one.adoptnetif(
|
||||
tunnel, interface_one.id, interface_one.mac, addresses
|
||||
)
|
||||
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)
|
||||
addresses = interface_two.get_addresses()
|
||||
node_two.adoptnetif(
|
||||
|
@ -584,14 +639,11 @@ class Session(object):
|
|||
:param int _id: id for node, defaults to None for generated id
|
||||
:param core.emulator.emudata.NodeOptions node_options: data to create node with
|
||||
:return: created node
|
||||
:raises core.CoreError: when an invalid node type is given
|
||||
"""
|
||||
|
||||
# retrieve node class for given node type
|
||||
try:
|
||||
node_class = nodeutils.get_node_class(_type)
|
||||
except KeyError:
|
||||
logging.error("invalid node type to create: %s", _type)
|
||||
return None
|
||||
# validate node type, get class, or throw error
|
||||
node_class = self.get_node_class(_type)
|
||||
|
||||
# set node start based on current session state, override and check when rj45
|
||||
start = self.state > EventTypes.DEFINITION_STATE.value
|
||||
|
@ -651,10 +703,8 @@ class Session(object):
|
|||
logging.debug("set node type: %s", node.type)
|
||||
self.services.add_services(node, node.type, node_options.services)
|
||||
|
||||
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
|
||||
is_boot_node = isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
||||
node, NodeTypes.RJ45
|
||||
)
|
||||
# boot nodes if created after runtime, CoreNodes, Physical, and RJ45 are all nodes
|
||||
is_boot_node = isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node)
|
||||
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
||||
self.write_nodes()
|
||||
self.add_remove_control_interface(node=node, remove=False)
|
||||
|
@ -1441,12 +1491,10 @@ class Session(object):
|
|||
count = 0
|
||||
for node_id in self.nodes:
|
||||
node = self.nodes[node_id]
|
||||
is_p2p_ctrlnet = nodeutils.is_node(
|
||||
node, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET)
|
||||
is_p2p_ctrlnet = isinstance(node, (PtpNet, CtrlNet))
|
||||
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:
|
||||
continue
|
||||
|
||||
|
@ -1493,7 +1541,7 @@ class Session(object):
|
|||
for node_id in self.nodes:
|
||||
node = self.nodes[node_id]
|
||||
# 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)
|
||||
|
||||
# shutdown emane
|
||||
|
@ -1546,10 +1594,7 @@ class Session(object):
|
|||
start = time.time()
|
||||
for _id in self.nodes:
|
||||
node = self.nodes[_id]
|
||||
# TODO: PyCoreNode is not the type to check
|
||||
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(
|
||||
node, NodeTypes.RJ45
|
||||
):
|
||||
if isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node):
|
||||
# add a control interface if configured
|
||||
logging.info(
|
||||
"booting node(%s): %s",
|
||||
|
@ -1648,8 +1693,7 @@ class Session(object):
|
|||
# no controlnet needed
|
||||
return None
|
||||
else:
|
||||
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
|
||||
prefix_spec = control_net_class.DEFAULT_PREFIX_LIST[net_index]
|
||||
prefix_spec = CtrlNet.DEFAULT_PREFIX_LIST[net_index]
|
||||
logging.debug("prefix spec: %s", prefix_spec)
|
||||
|
||||
server_interface = self.get_control_net_server_interfaces()[net_index]
|
||||
|
@ -1725,9 +1769,8 @@ class Session(object):
|
|||
prefix = prefixes[0]
|
||||
|
||||
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
|
||||
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
|
||||
control_net = self.create_node(
|
||||
cls=control_net_class,
|
||||
cls=CtrlNet,
|
||||
_id=_id,
|
||||
prefix=prefix,
|
||||
assign_address=assign_address,
|
||||
|
|
|
@ -17,8 +17,9 @@ from socket import AF_INET, AF_INET6
|
|||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.data import LinkData, NodeData
|
||||
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.netclient import LinuxNetClient, OvsNetClient
|
||||
|
||||
_DEFAULT_MTU = 1500
|
||||
|
||||
|
@ -886,8 +887,8 @@ class CoreNode(CoreNodeBase):
|
|||
addrlist = []
|
||||
|
||||
with self.lock:
|
||||
# TODO: see if you can move this to emane specific code
|
||||
if nodeutils.is_node(net, NodeTypes.EMANE):
|
||||
# TODO: emane specific code
|
||||
if net.is_emane is True:
|
||||
ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname, net=net)
|
||||
# TUN/TAP is not ready for addressing yet; the device may
|
||||
# take some time to appear, and installing it into a
|
||||
|
@ -1050,6 +1051,7 @@ class CoreNetworkBase(NodeBase):
|
|||
"""
|
||||
|
||||
linktype = LinkTypes.WIRED.value
|
||||
is_emane = False
|
||||
|
||||
def __init__(self, session, _id, name, start=True):
|
||||
"""
|
||||
|
@ -1063,6 +1065,10 @@ class CoreNetworkBase(NodeBase):
|
|||
super(CoreNetworkBase, self).__init__(session, _id, name, start=start)
|
||||
self._linked = {}
|
||||
self._linked_lock = threading.Lock()
|
||||
if session.options.get_config("ovs") == "True":
|
||||
self.net_client = OvsNetClient()
|
||||
else:
|
||||
self.net_client = LinuxNetClient()
|
||||
|
||||
def startup(self):
|
||||
"""
|
||||
|
|
|
@ -7,8 +7,6 @@ import time
|
|||
from builtins import int, range
|
||||
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import nodeutils
|
||||
|
||||
utils.check_executables([constants.IP_BIN])
|
||||
|
||||
|
@ -387,13 +385,12 @@ class TunTap(CoreInterface):
|
|||
if result:
|
||||
break
|
||||
|
||||
# TODO: emane specific code
|
||||
# check if this is an EMANE interface; if so, continue
|
||||
# waiting if EMANE is still running
|
||||
# TODO: remove emane code
|
||||
should_retry = count < 5
|
||||
is_emane_node = nodeutils.is_node(self.net, NodeTypes.EMANE)
|
||||
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
|
||||
else:
|
||||
raise RuntimeError("node device failed to exist")
|
||||
|
|
233
daemon/core/nodes/netclient.py
Normal file
233
daemon/core/nodes/netclient.py
Normal file
|
@ -0,0 +1,233 @@
|
|||
"""
|
||||
Clients for dealing with bridge/interface commands.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import os
|
||||
|
||||
from future.utils import with_metaclass
|
||||
|
||||
from core.constants import BRCTL_BIN, IP_BIN, OVS_BIN
|
||||
from core.utils import check_cmd
|
||||
|
||||
|
||||
class NetClientBase(with_metaclass(abc.ABCMeta)):
|
||||
"""
|
||||
Base client for running command line bridge/interface commands.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_bridge(self, name):
|
||||
"""
|
||||
Create a network bridge to connect interfaces to.
|
||||
|
||||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_bridge(self, name):
|
||||
"""
|
||||
Delete a network bridge.
|
||||
|
||||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_interface(self, bridge_name, interface_name):
|
||||
"""
|
||||
Create an interface associated with a network bridge.
|
||||
|
||||
:param str bridge_name: bridge name
|
||||
:param str interface_name: interface name
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_interface(self, bridge_name, interface_name):
|
||||
"""
|
||||
Delete an interface associated with a network bridge.
|
||||
|
||||
:param str bridge_name: bridge name
|
||||
:param str interface_name: interface name
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def existing_bridges(self, _id):
|
||||
"""
|
||||
Checks if there are any existing bridges for a node.
|
||||
|
||||
:param _id: node id to check bridges for
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def disable_mac_learning(self, name):
|
||||
"""
|
||||
Disable mac learning for a bridge.
|
||||
|
||||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class LinuxNetClient(NetClientBase):
|
||||
"""
|
||||
Client for creating Linux bridges and ip interfaces for nodes.
|
||||
"""
|
||||
|
||||
def create_bridge(self, name):
|
||||
"""
|
||||
Create a Linux bridge and bring it up.
|
||||
|
||||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
check_cmd([BRCTL_BIN, "addbr", name])
|
||||
check_cmd([BRCTL_BIN, "stp", name, "off"])
|
||||
check_cmd([BRCTL_BIN, "setfd", name, "0"])
|
||||
check_cmd([IP_BIN, "link", "set", name, "up"])
|
||||
|
||||
# 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
|
||||
"""
|
||||
check_cmd([IP_BIN, "link", "set", name, "down"])
|
||||
check_cmd([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
|
||||
"""
|
||||
check_cmd([BRCTL_BIN, "addif", bridge_name, interface_name])
|
||||
check_cmd([IP_BIN, "link", "set", interface_name, "up"])
|
||||
|
||||
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
|
||||
"""
|
||||
check_cmd([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 = check_cmd([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(NetClientBase):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
check_cmd([OVS_BIN, "add-br", name])
|
||||
check_cmd([OVS_BIN, "set", "bridge", name, "stp_enable=false"])
|
||||
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:stp-max-age=6"])
|
||||
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:stp-forward-delay=4"])
|
||||
check_cmd([IP_BIN, "link", "set", name, "up"])
|
||||
|
||||
def delete_bridge(self, name):
|
||||
"""
|
||||
Bring down and delete a OVS bridge.
|
||||
|
||||
:param str name: bridge name
|
||||
:return: nothing
|
||||
"""
|
||||
check_cmd([IP_BIN, "link", "set", name, "down"])
|
||||
check_cmd([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
|
||||
"""
|
||||
check_cmd([OVS_BIN, "add-port", bridge_name, interface_name])
|
||||
check_cmd([IP_BIN, "link", "set", interface_name, "up"])
|
||||
|
||||
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
|
||||
"""
|
||||
check_cmd([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 = check_cmd([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
|
||||
"""
|
||||
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:mac-aging-time=0"])
|
|
@ -9,7 +9,7 @@ import threading
|
|||
import time
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core import CoreCommandError, CoreError, constants, utils
|
||||
from core.emulator.data import LinkData
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||
from core.nodes import ipaddress
|
||||
|
@ -314,12 +314,8 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:return: nothing
|
||||
: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
|
||||
ebtablescmds(
|
||||
utils.check_cmd,
|
||||
|
@ -336,11 +332,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
|
||||
|
||||
|
@ -356,8 +347,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
ebq.stopupdateloop(self)
|
||||
|
||||
try:
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "down"])
|
||||
utils.check_cmd([constants.BRCTL_BIN, "delbr", self.brname])
|
||||
self.net_client.delete_bridge(self.brname)
|
||||
ebtablescmds(
|
||||
utils.check_cmd,
|
||||
[
|
||||
|
@ -385,7 +375,8 @@ class CoreNetwork(CoreNetworkBase):
|
|||
del self.session
|
||||
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):
|
||||
"""
|
||||
Attach a network interface.
|
||||
|
@ -394,10 +385,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
utils.check_cmd(
|
||||
[constants.BRCTL_BIN, "addif", self.brname, netif.localname]
|
||||
)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
|
||||
self.net_client.create_interface(self.brname, netif.localname)
|
||||
|
||||
CoreNetworkBase.attach(self, netif)
|
||||
|
||||
|
@ -409,9 +397,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:return: nothing
|
||||
"""
|
||||
if self.up:
|
||||
utils.check_cmd(
|
||||
[constants.BRCTL_BIN, "delif", self.brname, netif.localname]
|
||||
)
|
||||
self.net_client.delete_interface(self.brname, netif.localname)
|
||||
|
||||
CoreNetworkBase.detach(self, netif)
|
||||
|
||||
|
@ -610,10 +596,8 @@ class CoreNetwork(CoreNetworkBase):
|
|||
)
|
||||
self.attach(netif)
|
||||
if net.up:
|
||||
# this is similar to net.attach() but uses netif.name instead
|
||||
# of localname
|
||||
utils.check_cmd([constants.BRCTL_BIN, "addif", net.brname, netif.name])
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", netif.name, "up"])
|
||||
# this is similar to net.attach() but uses netif.name instead of localname
|
||||
self.net_client.create_interface(net.brname, netif.name)
|
||||
i = net.newifindex()
|
||||
net._netif[i] = netif
|
||||
with net._linked_lock:
|
||||
|
@ -822,8 +806,8 @@ class CtrlNet(CoreNetwork):
|
|||
:return: nothing
|
||||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
if self.detectoldbridge():
|
||||
return
|
||||
if self.net_client.existing_bridges(self.id):
|
||||
raise CoreError("old bridges exist for node: %s" % self.id)
|
||||
|
||||
CoreNetwork.startup(self)
|
||||
|
||||
|
@ -848,42 +832,7 @@ class CtrlNet(CoreNetwork):
|
|||
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
||||
|
||||
if self.serverintf:
|
||||
# sets the interface as a port of the bridge
|
||||
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
|
||||
self.net_client.create_interface(self.brname, self.serverintf)
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
|
@ -893,9 +842,7 @@ class CtrlNet(CoreNetwork):
|
|||
"""
|
||||
if self.serverintf is not None:
|
||||
try:
|
||||
utils.check_cmd(
|
||||
[constants.BRCTL_BIN, "delif", self.brname, self.serverintf]
|
||||
)
|
||||
self.net_client.delete_interface(self.brname, self.serverintf)
|
||||
except CoreCommandError:
|
||||
logging.exception(
|
||||
"error deleting server interface %s from bridge %s",
|
||||
|
@ -1100,7 +1047,7 @@ class HubNode(CoreNetwork):
|
|||
|
||||
# TODO: move to startup method
|
||||
if start:
|
||||
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
|
||||
self.net_client.disable_mac_learning(self.brname)
|
||||
|
||||
|
||||
class WlanNode(CoreNetwork):
|
||||
|
@ -1131,7 +1078,7 @@ class WlanNode(CoreNetwork):
|
|||
|
||||
# TODO: move to startup method
|
||||
if start:
|
||||
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
|
||||
self.net_client.disable_mac_learning(self.brname)
|
||||
|
||||
def attach(self, netif):
|
||||
"""
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
"""
|
||||
Provides default node maps that can be used to run core with.
|
||||
"""
|
||||
import core.nodes.base
|
||||
import core.nodes.docker
|
||||
import core.nodes.lxd
|
||||
import core.nodes.network
|
||||
import core.nodes.physical
|
||||
from core.emane.nodes import EmaneNet, EmaneNode
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import physical
|
||||
from core.nodes.network import GreTapBridge
|
||||
|
||||
# legacy core nodes, that leverage linux bridges
|
||||
NODES = {
|
||||
NodeTypes.DEFAULT: core.nodes.base.CoreNode,
|
||||
NodeTypes.PHYSICAL: physical.PhysicalNode,
|
||||
NodeTypes.TBD: None,
|
||||
NodeTypes.SWITCH: core.nodes.network.SwitchNode,
|
||||
NodeTypes.HUB: core.nodes.network.HubNode,
|
||||
NodeTypes.WIRELESS_LAN: core.nodes.network.WlanNode,
|
||||
NodeTypes.RJ45: core.nodes.physical.Rj45Node,
|
||||
NodeTypes.TUNNEL: core.nodes.network.TunnelNode,
|
||||
NodeTypes.KTUNNEL: None,
|
||||
NodeTypes.EMANE: EmaneNode,
|
||||
NodeTypes.EMANE_NET: EmaneNet,
|
||||
NodeTypes.TAP_BRIDGE: GreTapBridge,
|
||||
NodeTypes.PEER_TO_PEER: core.nodes.network.PtpNet,
|
||||
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet,
|
||||
NodeTypes.DOCKER: core.nodes.docker.DockerNode,
|
||||
NodeTypes.LXC: core.nodes.lxd.LxcNode,
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
"""
|
||||
Serves as a global point for storing and retrieving node types needed during simulation.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
_NODE_MAP = None
|
||||
|
||||
|
||||
def _log_map():
|
||||
global _NODE_MAP
|
||||
for key in _NODE_MAP:
|
||||
value = _NODE_MAP[key]
|
||||
name = None
|
||||
if value:
|
||||
name = value.__name__
|
||||
logging.debug("node type (%s) - class (%s)", key.name, name)
|
||||
|
||||
|
||||
def _convert_map(x, y):
|
||||
"""
|
||||
Convenience method to create a human readable version of the node map to log.
|
||||
|
||||
:param dict x: dictionary to reduce node items into
|
||||
:param tuple y: current node item
|
||||
:return: human readable name mapping of the node map
|
||||
"""
|
||||
x[y[0].name] = y[1]
|
||||
return x
|
||||
|
||||
|
||||
def update_node_map(node_map):
|
||||
"""
|
||||
Update the current node map with the provided node map values.
|
||||
|
||||
:param dict node_map: node map to update with
|
||||
"""
|
||||
global _NODE_MAP
|
||||
_NODE_MAP.update(node_map)
|
||||
_log_map()
|
||||
|
||||
|
||||
def set_node_map(node_map):
|
||||
"""
|
||||
Set the global node map that proides a consistent way to retrieve differently configured nodes.
|
||||
|
||||
:param dict node_map: node map to set to
|
||||
:return: nothing
|
||||
"""
|
||||
global _NODE_MAP
|
||||
_NODE_MAP = node_map
|
||||
_log_map()
|
||||
|
||||
|
||||
def get_node_class(node_type):
|
||||
"""
|
||||
Retrieve the node class for a given node type.
|
||||
|
||||
:param int node_type: node type to retrieve class for
|
||||
:return: node class
|
||||
"""
|
||||
global _NODE_MAP
|
||||
return _NODE_MAP[node_type]
|
||||
|
||||
|
||||
def get_node_type(node_class):
|
||||
"""
|
||||
Retrieve the node type given a node class.
|
||||
|
||||
:param class node_class: node class to get type for
|
||||
:return: node type
|
||||
:rtype: core.emulator.enumerations.NodeTypes
|
||||
"""
|
||||
global _NODE_MAP
|
||||
node_type_map = {_NODE_MAP[x]: x for x in _NODE_MAP}
|
||||
return node_type_map.get(node_class)
|
||||
|
||||
|
||||
def is_node(obj, node_types):
|
||||
"""
|
||||
Validates if an object is one of the provided node types.
|
||||
|
||||
:param obj: object to check type for
|
||||
:param int|tuple|list node_types: node type(s) to check against
|
||||
:return: True if the object is one of the node types, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
type_classes = []
|
||||
if isinstance(node_types, (tuple, list)):
|
||||
for node_type in node_types:
|
||||
type_class = get_node_class(node_type)
|
||||
type_classes.append(type_class)
|
||||
else:
|
||||
type_class = get_node_class(node_types)
|
||||
type_classes.append(type_class)
|
||||
|
||||
return isinstance(obj, tuple(type_classes))
|
|
@ -1,825 +0,0 @@
|
|||
"""
|
||||
TODO: probably goes away, or implement the usage of "unshare", or docker formal.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import socket
|
||||
import threading
|
||||
from socket import AF_INET, AF_INET6
|
||||
|
||||
from core import CoreCommandError, constants, utils
|
||||
from core.emulator.data import LinkData
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.interface import GreTap, Veth
|
||||
from core.nodes.network import EbtablesQueue, GreTapBridge
|
||||
|
||||
# a global object because all WLANs share the same queue
|
||||
# cannot have multiple threads invoking the ebtables commnd
|
||||
ebtables_queue = EbtablesQueue()
|
||||
|
||||
ebtables_lock = threading.Lock()
|
||||
|
||||
utils.check_executables([constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN])
|
||||
|
||||
|
||||
def ebtables_commands(call, commands):
|
||||
with ebtables_lock:
|
||||
for command in commands:
|
||||
call(command)
|
||||
|
||||
|
||||
class OvsNet(CoreNetworkBase):
|
||||
"""
|
||||
Used to be LxBrNet.
|
||||
|
||||
Base class for providing Openvswitch functionality to objects that create bridges.
|
||||
"""
|
||||
|
||||
policy = "DROP"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
||||
"""
|
||||
Creates an OvsNet instance.
|
||||
|
||||
:param core.emulator.session.Session session: session this object is a part of
|
||||
:param int _id: object id
|
||||
:param str name: object name
|
||||
:param bool start: start flag
|
||||
:param policy: network policy
|
||||
"""
|
||||
|
||||
CoreNetworkBase.__init__(self, session, _id, name, start)
|
||||
|
||||
if policy:
|
||||
self.policy = policy
|
||||
else:
|
||||
self.policy = self.__class__.policy
|
||||
|
||||
session_id = self.session.short_session_id()
|
||||
self.bridge_name = "b.%s.%s" % (str(self.id), session_id)
|
||||
self.up = False
|
||||
|
||||
if start:
|
||||
self.startup()
|
||||
ebtables_queue.startupdateloop(self)
|
||||
|
||||
def startup(self):
|
||||
"""
|
||||
|
||||
:return:
|
||||
:raises CoreCommandError: when there is a command exception
|
||||
"""
|
||||
utils.check_cmd([constants.OVS_BIN, "add-br", self.bridge_name])
|
||||
|
||||
# turn off spanning tree protocol and forwarding delay
|
||||
# TODO: appears stp and rstp are off by default, make sure this always holds true
|
||||
# TODO: apears ovs only supports rstp forward delay and again it's off by default
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
|
||||
|
||||
# create a new ebtables chain for this bridge
|
||||
ebtables_commands(
|
||||
utils.check_cmd,
|
||||
[
|
||||
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
|
||||
[
|
||||
constants.EBTABLES_BIN,
|
||||
"-A",
|
||||
"FORWARD",
|
||||
"--logical-in",
|
||||
self.bridge_name,
|
||||
"-j",
|
||||
self.bridge_name,
|
||||
],
|
||||
],
|
||||
)
|
||||
|
||||
self.up = True
|
||||
|
||||
def shutdown(self):
|
||||
if not self.up:
|
||||
logging.info("exiting shutdown, object is not up")
|
||||
return
|
||||
|
||||
ebtables_queue.stopupdateloop(self)
|
||||
|
||||
try:
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
|
||||
utils.check_cmd([constants.OVS_BIN, "del-br", self.bridge_name])
|
||||
ebtables_commands(
|
||||
utils.check_cmd,
|
||||
[
|
||||
[
|
||||
constants.EBTABLES_BIN,
|
||||
"-D",
|
||||
"FORWARD",
|
||||
"--logical-in",
|
||||
self.bridge_name,
|
||||
"-j",
|
||||
self.bridge_name,
|
||||
],
|
||||
[constants.EBTABLES_BIN, "-X", self.bridge_name],
|
||||
],
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception("error bringing bridge down and removing it")
|
||||
|
||||
# removes veth pairs used for bridge-to-bridge connections
|
||||
for interface in self.netifs():
|
||||
interface.shutdown()
|
||||
|
||||
self._netif.clear()
|
||||
self._linked.clear()
|
||||
del self.session
|
||||
self.up = False
|
||||
|
||||
def attach(self, interface):
|
||||
if self.up:
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "add-port", self.bridge_name, interface.localname]
|
||||
)
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "link", "set", interface.localname, "up"]
|
||||
)
|
||||
|
||||
CoreNetworkBase.attach(self, interface)
|
||||
|
||||
def detach(self, interface):
|
||||
if self.up:
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "del-port", self.bridge_name, interface.localname]
|
||||
)
|
||||
|
||||
CoreNetworkBase.detach(self, interface)
|
||||
|
||||
def linked(self, interface_one, interface_two):
|
||||
# check if the network interfaces are attached to this network
|
||||
if self._netif[interface_one.netifi] != interface_one:
|
||||
raise ValueError("inconsistency for interface %s" % interface_one.name)
|
||||
|
||||
if self._netif[interface_two.netifi] != interface_two:
|
||||
raise ValueError("inconsistency for interface %s" % interface_two.name)
|
||||
|
||||
try:
|
||||
linked = self._linked[interface_one][interface_two]
|
||||
except KeyError:
|
||||
if self.policy == "ACCEPT":
|
||||
linked = True
|
||||
elif self.policy == "DROP":
|
||||
linked = False
|
||||
else:
|
||||
raise ValueError("unknown policy: %s" % self.policy)
|
||||
|
||||
self._linked[interface_one][interface_two] = linked
|
||||
|
||||
return linked
|
||||
|
||||
def unlink(self, interface_one, interface_two):
|
||||
"""
|
||||
Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
|
||||
filtering rules.
|
||||
"""
|
||||
with self._linked_lock:
|
||||
if not self.linked(interface_one, interface_two):
|
||||
return
|
||||
|
||||
self._linked[interface_one][interface_two] = False
|
||||
|
||||
ebtables_queue.ebchange(self)
|
||||
|
||||
def link(self, interface_one, interface_two):
|
||||
"""
|
||||
Link two interfaces together, resulting in adding or removing
|
||||
ebtables filtering rules.
|
||||
"""
|
||||
with self._linked_lock:
|
||||
if self.linked(interface_one, interface_two):
|
||||
return
|
||||
|
||||
self._linked[interface_one][interface_two] = True
|
||||
|
||||
ebtables_queue.ebchange(self)
|
||||
|
||||
def linkconfig(
|
||||
self,
|
||||
netif,
|
||||
bw=None,
|
||||
delay=None,
|
||||
loss=None,
|
||||
duplicate=None,
|
||||
jitter=None,
|
||||
netif2=None,
|
||||
devname=None,
|
||||
):
|
||||
"""
|
||||
Configure link parameters by applying tc queuing disciplines on the
|
||||
interface.
|
||||
"""
|
||||
if not devname:
|
||||
devname = netif.localname
|
||||
|
||||
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
|
||||
parent = ["root"]
|
||||
|
||||
# attempt to set bandwidth and update as needed if value changed
|
||||
bandwidth_changed = netif.setparam("bw", bw)
|
||||
if bandwidth_changed:
|
||||
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
||||
if bw > 0:
|
||||
if self.up:
|
||||
burst = max(2 * netif.mtu, bw / 1000)
|
||||
limit = 0xFFFF # max IP payload
|
||||
tbf = [
|
||||
"tbf",
|
||||
"rate",
|
||||
str(bw),
|
||||
"burst",
|
||||
str(burst),
|
||||
"limit",
|
||||
str(limit),
|
||||
]
|
||||
logging.info(
|
||||
"linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf]
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||
netif.setparam("has_tbf", True)
|
||||
elif netif.getparam("has_tbf") and bw <= 0:
|
||||
tcd = [] + tc
|
||||
tcd[2] = "delete"
|
||||
|
||||
if self.up:
|
||||
utils.check_cmd(tcd + parent)
|
||||
|
||||
netif.setparam("has_tbf", False)
|
||||
# removing the parent removes the child
|
||||
netif.setparam("has_netem", False)
|
||||
|
||||
if netif.getparam("has_tbf"):
|
||||
parent = ["parent", "1:1"]
|
||||
|
||||
netem = ["netem"]
|
||||
delay_changed = netif.setparam("delay", delay)
|
||||
|
||||
if loss is not None:
|
||||
loss = float(loss)
|
||||
loss_changed = netif.setparam("loss", loss)
|
||||
|
||||
if duplicate is not None:
|
||||
duplicate = int(duplicate)
|
||||
duplicate_changed = netif.setparam("duplicate", duplicate)
|
||||
jitter_changed = netif.setparam("jitter", jitter)
|
||||
|
||||
# if nothing changed return
|
||||
if not any(
|
||||
[
|
||||
bandwidth_changed,
|
||||
delay_changed,
|
||||
loss_changed,
|
||||
duplicate_changed,
|
||||
jitter_changed,
|
||||
]
|
||||
):
|
||||
return
|
||||
|
||||
# jitter and delay use the same delay statement
|
||||
if delay is not None:
|
||||
netem += ["delay", "%sus" % delay]
|
||||
else:
|
||||
netem += ["delay", "0us"]
|
||||
|
||||
if jitter is not None:
|
||||
netem += ["%sus" % jitter, "25%"]
|
||||
|
||||
if loss is not None and loss > 0:
|
||||
netem += ["loss", "%s%%" % min(loss, 100)]
|
||||
|
||||
if duplicate is not None and duplicate > 0:
|
||||
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
|
||||
|
||||
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
|
||||
# possibly remove netem if it exists and parent queue wasn"t removed
|
||||
if not netif.getparam("has_netem"):
|
||||
return
|
||||
|
||||
tc[2] = "delete"
|
||||
|
||||
if self.up:
|
||||
logging.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
||||
utils.check_cmd(tc + parent + ["handle", "10:"])
|
||||
netif.setparam("has_netem", False)
|
||||
elif len(netem) > 1:
|
||||
if self.up:
|
||||
logging.info(
|
||||
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
|
||||
)
|
||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||
netif.setparam("has_netem", True)
|
||||
|
||||
def linknet(self, network):
|
||||
"""
|
||||
Link this bridge with another by creating a veth pair and installing
|
||||
each device into each bridge.
|
||||
"""
|
||||
session_id = self.session.short_session_id()
|
||||
|
||||
try:
|
||||
_id = "%x" % self.id
|
||||
except TypeError:
|
||||
_id = "%s" % self.id
|
||||
|
||||
try:
|
||||
network_id = "%x" % network.id
|
||||
except TypeError:
|
||||
network_id = "%s" % network.id
|
||||
|
||||
localname = "veth%s.%s.%s" % (_id, network_id, session_id)
|
||||
|
||||
if len(localname) >= 16:
|
||||
raise ValueError("interface local name %s too long" % localname)
|
||||
|
||||
name = "veth%s.%s.%s" % (network_id, _id, session_id)
|
||||
if len(name) >= 16:
|
||||
raise ValueError("interface name %s too long" % name)
|
||||
|
||||
interface = Veth(
|
||||
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
|
||||
)
|
||||
self.attach(interface)
|
||||
if network.up:
|
||||
# this is similar to net.attach() but uses netif.name instead of localname
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "add-port", network.bridge_name, interface.name]
|
||||
)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
||||
|
||||
network.attach(interface)
|
||||
interface.net = self
|
||||
interface.othernet = network
|
||||
return interface
|
||||
|
||||
def getlinknetif(self, network):
|
||||
"""
|
||||
Return the interface of that links this net with another net
|
||||
(that were linked using linknet()).
|
||||
"""
|
||||
for interface in self.netifs():
|
||||
if hasattr(interface, "othernet") and interface.othernet == network:
|
||||
return interface
|
||||
|
||||
return None
|
||||
|
||||
def addrconfig(self, addresses):
|
||||
"""
|
||||
Set addresses on the bridge.
|
||||
"""
|
||||
if not self.up:
|
||||
return
|
||||
|
||||
for address in addresses:
|
||||
utils.check_cmd(
|
||||
[constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name]
|
||||
)
|
||||
|
||||
|
||||
class OvsCtrlNet(OvsNet):
|
||||
policy = "ACCEPT"
|
||||
CTRLIF_IDX_BASE = 99 # base control interface index
|
||||
DEFAULT_PREFIX_LIST = [
|
||||
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
|
||||
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
|
||||
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
|
||||
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24",
|
||||
]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
session,
|
||||
_id="ctrlnet",
|
||||
name=None,
|
||||
prefix=None,
|
||||
hostid=None,
|
||||
start=True,
|
||||
assign_address=True,
|
||||
updown_script=None,
|
||||
serverintf=None,
|
||||
):
|
||||
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
||||
self.hostid = hostid
|
||||
self.assign_address = assign_address
|
||||
self.updown_script = updown_script
|
||||
self.serverintf = serverintf
|
||||
OvsNet.__init__(self, session, _id=_id, name=name, start=start)
|
||||
|
||||
def startup(self):
|
||||
if self.detectoldbridge():
|
||||
return
|
||||
|
||||
OvsNet.startup(self)
|
||||
if self.hostid:
|
||||
addr = self.prefix.addr(self.hostid)
|
||||
else:
|
||||
addr = self.prefix.max_addr()
|
||||
|
||||
message = "Added control network bridge: %s %s" % (
|
||||
self.bridge_name,
|
||||
self.prefix,
|
||||
)
|
||||
addresses = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
||||
if self.assign_address:
|
||||
self.addrconfig(addresses=addresses)
|
||||
message += " address %s" % addr
|
||||
logging.info(message)
|
||||
|
||||
if self.updown_script:
|
||||
logging.info(
|
||||
"interface %s updown script %s startup called"
|
||||
% (self.bridge_name, self.updown_script)
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.bridge_name, "startup"])
|
||||
|
||||
if self.serverintf:
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf]
|
||||
)
|
||||
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
|
||||
|
||||
def detectoldbridge(self):
|
||||
"""
|
||||
Occasionally, control net bridges from previously closed sessions are not cleaned up.
|
||||
Check if there are old control net bridges and delete them
|
||||
"""
|
||||
|
||||
output = utils.check_cmd([constants.OVS_BIN, "list-br"])
|
||||
output = output.strip()
|
||||
if output:
|
||||
for line in output.split("\n"):
|
||||
bride_name = line.split(".")
|
||||
if bride_name[0] == "b" and bride_name[1] == self.id:
|
||||
logging.error(
|
||||
"older session may still be running with conflicting id for bridge: %s",
|
||||
line,
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def shutdown(self):
|
||||
if self.serverintf:
|
||||
try:
|
||||
utils.check_cmd(
|
||||
[constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf]
|
||||
)
|
||||
except CoreCommandError:
|
||||
logging.exception(
|
||||
"error deleting server interface %s to controlnet bridge %s",
|
||||
self.serverintf,
|
||||
self.bridge_name,
|
||||
)
|
||||
|
||||
if self.updown_script:
|
||||
try:
|
||||
logging.info(
|
||||
"interface %s updown script (%s shutdown) called",
|
||||
self.bridge_name,
|
||||
self.updown_script,
|
||||
)
|
||||
utils.check_cmd([self.updown_script, self.bridge_name, "shutdown"])
|
||||
except CoreCommandError:
|
||||
logging.exception("error during updown script shutdown")
|
||||
|
||||
OvsNet.shutdown(self)
|
||||
|
||||
def all_link_data(self, flags):
|
||||
"""
|
||||
Do not include CtrlNet in link messages describing this session.
|
||||
"""
|
||||
return []
|
||||
|
||||
|
||||
class OvsPtpNet(OvsNet):
|
||||
policy = "ACCEPT"
|
||||
|
||||
def attach(self, interface):
|
||||
if len(self._netif) >= 2:
|
||||
raise ValueError(
|
||||
"point-to-point links support at most 2 network interfaces"
|
||||
)
|
||||
OvsNet.attach(self, interface)
|
||||
|
||||
def data(self, message_type, lat=None, lon=None, alt=None):
|
||||
"""
|
||||
Do not generate a Node Message for point-to-point links. They are
|
||||
built using a link message instead.
|
||||
"""
|
||||
return None
|
||||
|
||||
def all_link_data(self, flags):
|
||||
"""
|
||||
Build CORE API TLVs for a point-to-point link. One Link message describes this network.
|
||||
"""
|
||||
|
||||
all_links = []
|
||||
|
||||
if len(self._netif) != 2:
|
||||
return all_links
|
||||
|
||||
if1, if2 = self._netif.values()
|
||||
|
||||
unidirectional = 0
|
||||
if if1.getparams() != if2.getparams():
|
||||
unidirectional = 1
|
||||
|
||||
interface1_ip4 = None
|
||||
interface1_ip4_mask = None
|
||||
interface1_ip6 = None
|
||||
interface1_ip6_mask = None
|
||||
for address in if1.addrlist:
|
||||
ip, _sep, mask = address.partition("/")
|
||||
mask = int(mask)
|
||||
if ipaddress.is_ipv4_address(ip):
|
||||
family = AF_INET
|
||||
ipl = socket.inet_pton(family, ip)
|
||||
interface1_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
||||
interface1_ip4_mask = mask
|
||||
else:
|
||||
family = AF_INET6
|
||||
ipl = socket.inet_pton(family, ip)
|
||||
interface1_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
||||
interface1_ip6_mask = mask
|
||||
|
||||
interface2_ip4 = None
|
||||
interface2_ip4_mask = None
|
||||
interface2_ip6 = None
|
||||
interface2_ip6_mask = None
|
||||
for address in if2.addrlist:
|
||||
ip, _sep, mask = address.partition("/")
|
||||
mask = int(mask)
|
||||
if ipaddress.is_ipv4_address(ip):
|
||||
family = AF_INET
|
||||
ipl = socket.inet_pton(family, ip)
|
||||
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
||||
interface2_ip4_mask = mask
|
||||
else:
|
||||
family = AF_INET6
|
||||
ipl = socket.inet_pton(family, ip)
|
||||
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
||||
interface2_ip6_mask = mask
|
||||
|
||||
# TODO: not currently used
|
||||
# loss=netif.getparam("loss")
|
||||
link_data = LinkData(
|
||||
message_type=flags,
|
||||
node1_id=if1.node.id,
|
||||
node2_id=if2.node.id,
|
||||
link_type=self.linktype,
|
||||
unidirectional=unidirectional,
|
||||
delay=if1.getparam("delay"),
|
||||
bandwidth=if1.getparam("bw"),
|
||||
dup=if1.getparam("duplicate"),
|
||||
jitter=if1.getparam("jitter"),
|
||||
interface1_id=if1.node.getifindex(if1),
|
||||
interface1_mac=if1.hwaddr,
|
||||
interface1_ip4=interface1_ip4,
|
||||
interface1_ip4_mask=interface1_ip4_mask,
|
||||
interface1_ip6=interface1_ip6,
|
||||
interface1_ip6_mask=interface1_ip6_mask,
|
||||
interface2_id=if2.node.getifindex(if2),
|
||||
interface2_mac=if2.hwaddr,
|
||||
interface2_ip4=interface2_ip4,
|
||||
interface2_ip4_mask=interface2_ip4_mask,
|
||||
interface2_ip6=interface2_ip6,
|
||||
interface2_ip6_mask=interface2_ip6_mask,
|
||||
)
|
||||
|
||||
all_links.append(link_data)
|
||||
|
||||
# build a 2nd link message for the upstream link parameters
|
||||
# (swap if1 and if2)
|
||||
if unidirectional:
|
||||
link_data = LinkData(
|
||||
message_type=0,
|
||||
node1_id=if2.node.id,
|
||||
node2_id=if1.node.id,
|
||||
delay=if1.getparam("delay"),
|
||||
bandwidth=if1.getparam("bw"),
|
||||
dup=if1.getparam("duplicate"),
|
||||
jitter=if1.getparam("jitter"),
|
||||
unidirectional=1,
|
||||
interface1_id=if2.node.getifindex(if2),
|
||||
interface2_id=if1.node.getifindex(if1),
|
||||
)
|
||||
all_links.append(link_data)
|
||||
|
||||
return all_links
|
||||
|
||||
|
||||
class OvsSwitchNode(OvsNet):
|
||||
apitype = NodeTypes.SWITCH.value
|
||||
policy = "ACCEPT"
|
||||
type = "lanswitch"
|
||||
|
||||
|
||||
class OvsHubNode(OvsNet):
|
||||
apitype = NodeTypes.HUB.value
|
||||
policy = "ACCEPT"
|
||||
type = "hub"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True):
|
||||
"""
|
||||
the Hub node forwards packets to all bridge ports by turning off
|
||||
the MAC address learning
|
||||
"""
|
||||
OvsNet.__init__(self, session, _id, name, start)
|
||||
|
||||
if start:
|
||||
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
|
||||
# TODO: replace "brctl setageing 0"
|
||||
utils.check_cmd(
|
||||
[constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"]
|
||||
)
|
||||
|
||||
|
||||
class OvsWlanNode(OvsNet):
|
||||
apitype = NodeTypes.WIRELESS_LAN.value
|
||||
linktype = LinkTypes.WIRELESS.value
|
||||
policy = "DROP"
|
||||
type = "wlan"
|
||||
|
||||
def __init__(self, session, _id=None, name=None, start=True, policy=None):
|
||||
OvsNet.__init__(self, session, _id, name, start, policy)
|
||||
|
||||
# wireless model such as basic range
|
||||
self.model = None
|
||||
# mobility model such as scripted
|
||||
self.mobility = None
|
||||
|
||||
def attach(self, interface):
|
||||
OvsNet.attach(self, interface)
|
||||
|
||||
if self.model:
|
||||
interface.poshook = self.model.position_callback
|
||||
|
||||
if interface.node is None:
|
||||
return
|
||||
|
||||
x, y, z = interface.node.position.get()
|
||||
# invokes any netif.poshook
|
||||
interface.setposition(x, y, z)
|
||||
# self.model.setlinkparams()
|
||||
|
||||
def setmodel(self, model, config=None):
|
||||
"""
|
||||
Mobility and wireless model.
|
||||
"""
|
||||
logging.info("adding model %s", model.name)
|
||||
|
||||
if model.type == RegisterTlvs.WIRELESS.value:
|
||||
self.model = model(session=self.session, _id=self.id, config=config)
|
||||
if self.model.position_callback:
|
||||
for interface in self.netifs():
|
||||
interface.poshook = self.model.position_callback
|
||||
if interface.node is not None:
|
||||
x, y, z = interface.node.position.get()
|
||||
interface.poshook(interface, x, y, z)
|
||||
self.model.setlinkparams()
|
||||
elif model.type == RegisterTlvs.MOBILITY.value:
|
||||
self.mobility = model(session=self.session, _id=self.id, config=config)
|
||||
|
||||
def updatemodel(self, config):
|
||||
if not self.model:
|
||||
raise ValueError("no model set to update for node(%s)", self.id)
|
||||
logging.info(
|
||||
"node(%s) updating model(%s): %s", self.id, self.model.name, config
|
||||
)
|
||||
self.model.set_configs(config, node_id=self.id)
|
||||
if self.model.position_callback:
|
||||
for netif in self.netifs():
|
||||
netif.poshook = self.model.position_callback
|
||||
if netif.node is not None:
|
||||
x, y, z = netif.node.position.get()
|
||||
netif.poshook(netif, x, y, z)
|
||||
self.model.updateconfig()
|
||||
|
||||
def all_link_data(self, flags):
|
||||
all_links = OvsNet.all_link_data(self, flags)
|
||||
|
||||
if self.model:
|
||||
all_links.extend(self.model.all_link_data(flags))
|
||||
|
||||
return all_links
|
||||
|
||||
|
||||
class OvsTunnelNode(GreTapBridge):
|
||||
apitype = NodeTypes.TUNNEL.value
|
||||
policy = "ACCEPT"
|
||||
type = "tunnel"
|
||||
|
||||
|
||||
class OvsGreTapBridge(OvsNet):
|
||||
"""
|
||||
A network consisting of a bridge with a gretap device for tunneling to
|
||||
another system.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
session,
|
||||
remoteip=None,
|
||||
_id=None,
|
||||
name=None,
|
||||
policy="ACCEPT",
|
||||
localip=None,
|
||||
ttl=255,
|
||||
key=None,
|
||||
start=True,
|
||||
):
|
||||
OvsNet.__init__(
|
||||
self, session=session, _id=_id, name=name, policy=policy, start=False
|
||||
)
|
||||
self.grekey = key
|
||||
if self.grekey is None:
|
||||
self.grekey = self.session.id ^ self.id
|
||||
|
||||
self.localnum = None
|
||||
self.remotenum = None
|
||||
self.remoteip = remoteip
|
||||
self.localip = localip
|
||||
self.ttl = ttl
|
||||
|
||||
if remoteip is None:
|
||||
self.gretap = None
|
||||
else:
|
||||
self.gretap = GreTap(
|
||||
node=self,
|
||||
session=session,
|
||||
remoteip=remoteip,
|
||||
localip=localip,
|
||||
ttl=ttl,
|
||||
key=self.grekey,
|
||||
)
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
def startup(self):
|
||||
"""
|
||||
Creates a bridge and adds the gretap device to it.
|
||||
"""
|
||||
OvsNet.startup(self)
|
||||
|
||||
if self.gretap:
|
||||
self.attach(self.gretap)
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
Detach the gretap device and remove the bridge.
|
||||
"""
|
||||
if self.gretap:
|
||||
self.detach(self.gretap)
|
||||
self.gretap.shutdown()
|
||||
self.gretap = None
|
||||
|
||||
OvsNet.shutdown(self)
|
||||
|
||||
def addrconfig(self, addresses):
|
||||
"""
|
||||
Set the remote tunnel endpoint. This is a one-time method for
|
||||
creating the GreTap device, which requires the remoteip at startup.
|
||||
The 1st address in the provided list is remoteip, 2nd optionally
|
||||
specifies localip.
|
||||
"""
|
||||
if self.gretap:
|
||||
raise ValueError("gretap already exists for %s" % self.name)
|
||||
|
||||
remoteip = addresses[0].split("/")[0]
|
||||
localip = None
|
||||
|
||||
if len(addresses) > 1:
|
||||
localip = addresses[1].split("/")[0]
|
||||
|
||||
self.gretap = GreTap(
|
||||
session=self.session,
|
||||
remoteip=remoteip,
|
||||
localip=localip,
|
||||
ttl=self.ttl,
|
||||
key=self.grekey,
|
||||
)
|
||||
self.attach(self.gretap)
|
||||
|
||||
def setkey(self, key):
|
||||
"""
|
||||
Set the GRE key used for the GreTap device. This needs to be set
|
||||
prior to instantiating the GreTap device (before addrconfig).
|
||||
"""
|
||||
self.grekey = key
|
||||
|
||||
|
||||
OVS_NODES = {
|
||||
NodeTypes.SWITCH: OvsSwitchNode,
|
||||
NodeTypes.HUB: OvsHubNode,
|
||||
NodeTypes.WIRELESS_LAN: OvsWlanNode,
|
||||
NodeTypes.TUNNEL: OvsTunnelNode,
|
||||
NodeTypes.TAP_BRIDGE: OvsGreTapBridge,
|
||||
NodeTypes.PEER_TO_PEER: OvsPtpNet,
|
||||
NodeTypes.CONTROL_NET: OvsCtrlNet,
|
||||
}
|
|
@ -8,6 +8,7 @@ import socket
|
|||
from future.moves.urllib.parse import urlparse
|
||||
|
||||
from core import CoreError, constants
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.emulator.enumerations import (
|
||||
EventTypes,
|
||||
LinkTlvs,
|
||||
|
@ -17,8 +18,8 @@ from core.emulator.enumerations import (
|
|||
NodeTlvs,
|
||||
NodeTypes,
|
||||
)
|
||||
from core.nodes import nodeutils
|
||||
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
|
||||
|
@ -27,14 +28,13 @@ class Bunch(object):
|
|||
Helper class for recording a collection of attributes.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwds):
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Create a Bunch instance.
|
||||
|
||||
:param dict kwds: keyword arguments
|
||||
:return:
|
||||
:param dict kwargs: keyword arguments
|
||||
"""
|
||||
self.__dict__.update(kwds)
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
|
||||
class Sdt(object):
|
||||
|
@ -365,9 +365,7 @@ class Sdt(object):
|
|||
for net in nets:
|
||||
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
||||
for link_data in all_links:
|
||||
is_wireless = nodeutils.is_node(
|
||||
net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
||||
)
|
||||
is_wireless = isinstance(net, (WlanNode, EmaneNode))
|
||||
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
|
||||
if is_wireless and link_data.node1_id == net.id:
|
||||
continue
|
||||
|
@ -401,7 +399,7 @@ class Sdt(object):
|
|||
def handlenodemsg(self, msg):
|
||||
"""
|
||||
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).
|
||||
|
||||
:param msg: node message to handle
|
||||
|
@ -430,7 +428,8 @@ class Sdt(object):
|
|||
model = "router"
|
||||
nodetype = model
|
||||
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
|
||||
else:
|
||||
nodetype = None
|
||||
|
@ -509,6 +508,6 @@ class Sdt(object):
|
|||
n = self.session.get_node(nodenum)
|
||||
except CoreError:
|
||||
return False
|
||||
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
||||
if isinstance(n, (WlanNode, EmaneNode)):
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import nodeutils
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.services.coreservices import CoreService
|
||||
from core.xml import emanexml
|
||||
|
||||
|
@ -22,7 +21,7 @@ class EmaneTransportService(CoreService):
|
|||
transport_commands = []
|
||||
for interface in node.netifs(sort=True):
|
||||
network_node = node.session.get_node(interface.net.id)
|
||||
if nodeutils.is_node(network_node, NodeTypes.EMANE):
|
||||
if isinstance(network_node, EmaneNode):
|
||||
config = node.session.emane.get_configs(
|
||||
network_node.id, network_node.model.name
|
||||
)
|
||||
|
|
|
@ -4,8 +4,10 @@ Assumes installation of FRR via https://deb.frrouting.org/
|
|||
"""
|
||||
|
||||
from core import constants
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.nodes import ipaddress, nodeutils
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.network import PtpNet
|
||||
from core.nodes.physical import Rj45Node
|
||||
from core.services.coreservices import CoreService
|
||||
|
||||
|
||||
|
@ -341,7 +343,7 @@ class FrrService(CoreService):
|
|||
for peerifc in ifc.net.netifs():
|
||||
if peerifc == ifc:
|
||||
continue
|
||||
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
|
||||
if isinstance(peerifc, Rj45Node):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -395,7 +397,7 @@ class FRROspfv2(FrrService):
|
|||
Helper to detect whether interface is connected to a notional
|
||||
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 ""
|
||||
|
||||
|
@ -481,7 +483,7 @@ class FRROspfv3(FrrService):
|
|||
Helper to detect whether interface is connected to a notional
|
||||
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 ""
|
||||
|
||||
|
|
|
@ -3,8 +3,11 @@ quagga.py: defines routing services provided by Quagga.
|
|||
"""
|
||||
|
||||
from core import constants
|
||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||
from core.nodes import ipaddress, nodeutils
|
||||
from core.emane.nodes import EmaneNode
|
||||
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
|
||||
|
||||
|
||||
|
@ -267,7 +270,7 @@ class QuaggaService(CoreService):
|
|||
for peerifc in ifc.net.netifs():
|
||||
if peerifc == ifc:
|
||||
continue
|
||||
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
|
||||
if isinstance(peerifc, Rj45Node):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -321,7 +324,7 @@ class Ospfv2(QuaggaService):
|
|||
Helper to detect whether interface is connected to a notional
|
||||
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 ""
|
||||
|
||||
|
@ -407,7 +410,7 @@ class Ospfv3(QuaggaService):
|
|||
Helper to detect whether interface is connected to a notional
|
||||
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 ""
|
||||
|
||||
|
@ -457,9 +460,7 @@ class Ospfv3mdr(Ospfv3):
|
|||
cfg = cls.mtucheck(ifc)
|
||||
# Uncomment the following line to use Address Family Translation for IPv4
|
||||
cfg += " ipv6 ospf6 instance-id 65\n"
|
||||
if ifc.net is not None and nodeutils.is_node(
|
||||
ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
|
||||
):
|
||||
if ifc.net is not None and isinstance(ifc.net, (WlanNode, EmaneNode)):
|
||||
return (
|
||||
cfg
|
||||
+ """\
|
||||
|
|
|
@ -4,11 +4,12 @@ from lxml import etree
|
|||
|
||||
import core.nodes.base
|
||||
import core.nodes.physical
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import nodeutils
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.ipaddress import MacAddress
|
||||
from core.nodes.network import CtrlNet
|
||||
|
||||
|
||||
def write_xml_file(xml_element, file_path, doctype=None):
|
||||
|
@ -408,7 +409,7 @@ class CoreXmlWriter(object):
|
|||
is_network_or_rj45 = isinstance(
|
||||
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:
|
||||
self.write_network(node)
|
||||
# device node
|
||||
|
@ -457,7 +458,7 @@ class CoreXmlWriter(object):
|
|||
interface_name = node_interface.name
|
||||
|
||||
# check if emane interface
|
||||
if nodeutils.is_node(node_interface.net, NodeTypes.EMANE):
|
||||
if isinstance(node_interface.net, EmaneNode):
|
||||
nem = node_interface.net.getnemid(node_interface)
|
||||
add_attribute(interface, "nem", nem)
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import socket
|
|||
from lxml import etree
|
||||
|
||||
from core import constants, utils
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes import ipaddress, nodeutils
|
||||
from core.emane.nodes import EmaneNode
|
||||
from core.nodes import ipaddress
|
||||
from core.nodes.base import CoreNodeBase
|
||||
|
||||
|
||||
|
@ -144,7 +144,7 @@ class CoreXmlDeployment(object):
|
|||
|
||||
for netif in node.netifs():
|
||||
emane_element = None
|
||||
if nodeutils.is_node(netif.net, NodeTypes.EMANE):
|
||||
if isinstance(netif.net, EmaneNode):
|
||||
emane_element = add_emane_interface(host_element, netif)
|
||||
|
||||
parent_element = host_element
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
configparser==4.0.1
|
||||
configparser==4.0.2
|
||||
future==0.17.1
|
||||
grpcio==1.23.0
|
||||
grpcio-tools==1.21.1
|
||||
|
|
|
@ -44,12 +44,11 @@ def start_udp(mainserver, server_address):
|
|||
mainserver.udpthread.start()
|
||||
|
||||
|
||||
def cored(cfg, use_ovs):
|
||||
def cored(cfg):
|
||||
"""
|
||||
Start the CoreServer object and enter the server loop.
|
||||
|
||||
:param dict cfg: core configuration
|
||||
:param bool use_ovs: flag to determine if ovs nodes should be used
|
||||
:return: nothing
|
||||
"""
|
||||
host = cfg["listenaddr"]
|
||||
|
@ -60,9 +59,6 @@ def cored(cfg, use_ovs):
|
|||
try:
|
||||
address = (host, port)
|
||||
server = CoreServer(address, CoreHandler, cfg)
|
||||
if use_ovs:
|
||||
from core.nodes.openvswitch import OVS_NODES
|
||||
server.coreemu.update_nodes(OVS_NODES)
|
||||
except:
|
||||
logging.exception("error starting main server on: %s:%s", host, port)
|
||||
sys.exit(1)
|
||||
|
@ -156,11 +152,8 @@ def main():
|
|||
cfg = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
|
||||
banner()
|
||||
|
||||
# check if ovs flag was provided
|
||||
use_ovs = len(sys.argv) == 2 and sys.argv[1] == "ovs"
|
||||
|
||||
try:
|
||||
cored(cfg, use_ovs)
|
||||
cored(cfg)
|
||||
except KeyboardInterrupt:
|
||||
logging.info("keyboard interrupt, stopping core daemon")
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ line_length=88
|
|||
|
||||
[flake8]
|
||||
ignore=E501,W503,E203
|
||||
max-line-length=100
|
||||
max-line-length=88
|
||||
max-complexity=26
|
||||
select=B,C,E,F,W,T4
|
||||
exclude=*_pb2*.py,utm.py,doc,build
|
||||
|
|
|
@ -32,6 +32,9 @@ To leverage the dev environment you need python 3.6+.
|
|||
# change to daemon directory
|
||||
cd $REPO/daemon
|
||||
|
||||
# copy setup.py for installation
|
||||
cp setup.py.in setup.py
|
||||
|
||||
# install pipenv
|
||||
pip3 install pipenv
|
||||
|
||||
|
|
|
@ -230,26 +230,26 @@ pip3 install grpcio-tools
|
|||
### Ubuntu 18.04 Requirements
|
||||
|
||||
```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
|
||||
|
||||
```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
|
||||
|
||||
```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
|
||||
|
||||
```shell
|
||||
./bootstrap.sh
|
||||
# use python2 or python3 depending on desired version
|
||||
# $VERSION should be path to python2/3
|
||||
PYTHON=$VERSION ./configure
|
||||
make
|
||||
sudo make install
|
||||
|
@ -268,7 +268,7 @@ sudo apt install python3-sphinx
|
|||
sudo yum install python3-sphinx
|
||||
|
||||
./bootstrap.sh
|
||||
# use python2 or python3 depending on desired version
|
||||
# $VERSION should be path to python2/3
|
||||
PYTHON=$VERSION ./configure
|
||||
make doc
|
||||
```
|
||||
|
@ -282,8 +282,10 @@ Build package commands, DESTDIR is used to make install into and then for packag
|
|||
|
||||
```shell
|
||||
./bootstrap.sh
|
||||
# use python2 or python3 depending on desired version
|
||||
PYTHON=$VERSION ./configure
|
||||
# for python2
|
||||
PYTHON=python2 ./configure
|
||||
# for python3
|
||||
PYTHON=python3 ./configure --enable-python3
|
||||
make
|
||||
mkdir /tmp/core-build
|
||||
make fpm DESTDIR=/tmp/core-build
|
||||
|
|
|
@ -2706,7 +2706,7 @@ proc sendNodeTypeInfo { sock reset } {
|
|||
set typesinuse ""
|
||||
foreach node $node_list {
|
||||
set type [nodeType $node]
|
||||
if { $type != "router" && $type != "OVS" } { continue }
|
||||
if { $type != "router" } { continue }
|
||||
set model [getNodeModel $node]
|
||||
if { [lsearch $typesinuse $model] < 0 } { lappend typesinuse $model }
|
||||
}
|
||||
|
@ -2910,7 +2910,6 @@ proc getNodeTypeAPI { node } {
|
|||
router { return 0x0 }
|
||||
netns { return 0x0 }
|
||||
jail { return 0x0 }
|
||||
OVS { return 0x0 }
|
||||
physical { return 0x1 }
|
||||
tbd { return 0x3 }
|
||||
lanswitch { return 0x4 }
|
||||
|
|
|
@ -333,7 +333,7 @@ proc redrawAll {} {
|
|||
|
||||
proc drawNode { c node } {
|
||||
global showNodeLabels
|
||||
global router pc host lanswitch rj45 hub pseudo OVS
|
||||
global router pc host lanswitch rj45 hub pseudo
|
||||
global curcanvas zoom
|
||||
global wlan
|
||||
if { $c == "" } { set c .c } ;# default canvas
|
||||
|
@ -348,7 +348,7 @@ proc drawNode { c node } {
|
|||
set cimg ""
|
||||
set imgzoom $zoom
|
||||
if { $zoom == 0.75 || $zoom == 1.5 } { set imgzoom 1.0 }
|
||||
if { $type == "router" || $type == "OVS" } {
|
||||
if { $type == "router" } {
|
||||
set model [getNodeModel $node]
|
||||
set cimg [getNodeTypeImage $model normal]
|
||||
}
|
||||
|
@ -1535,7 +1535,7 @@ proc raiseAll {c} {
|
|||
proc button1 { c x y button } {
|
||||
global node_list plot_list curcanvas zoom
|
||||
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 lastX lastY
|
||||
global background selectbox
|
||||
|
@ -1607,10 +1607,7 @@ proc button1 { c x y button } {
|
|||
rectangle text} $activetool] < 0 } {
|
||||
if { $g_view_locked == 1 } { return }
|
||||
if { $activetoolp == "routers" } {
|
||||
if {$activetool != "OVS"} {
|
||||
set node [newNode router]
|
||||
} else {
|
||||
set node [newNode OVS]}
|
||||
setNodeModel $node $activetool
|
||||
} else {
|
||||
set node [newNode $activetool]
|
||||
|
@ -2550,7 +2547,7 @@ proc popupConfigDialog { c } {
|
|||
-side right -padx 4 -pady 4
|
||||
# end Boeing
|
||||
pack $wi.ftop -side top
|
||||
if { $type == "router" || $type == "OVS"} {
|
||||
if { $type == "router" } {
|
||||
|
||||
ttk::frame $wi.model -borderwidth 4
|
||||
ttk::label $wi.model.label -text "Type:"
|
||||
|
|
|
@ -108,7 +108,7 @@ proc autoIPv4addr { node iface } {
|
|||
|
||||
set peer_node [logicalPeerByIfc $node $iface]
|
||||
# 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 ifc [ifcList $l2node] {
|
||||
set peer [logicalPeerByIfc $l2node $ifc]
|
||||
|
|
|
@ -747,15 +747,12 @@ proc newLink { lnode1 lnode2 } {
|
|||
global defLinkColor defLinkWidth
|
||||
global curcanvas
|
||||
global systype
|
||||
if { ([nodeType $lnode1] == "lanswitch" ||[nodeType $lnode1] == "OVS") && \
|
||||
if { [nodeType $lnode1] == "lanswitch" && \
|
||||
[nodeType $lnode2] != "router" && \
|
||||
([nodeType $lnode2] != "lanswitch" || [nodeType $lnode2] != "OVS") } {
|
||||
set regular no }
|
||||
if { ([nodeType $lnode2] == "lanswitch" || [nodeType $lnode2] == "OVS") && \
|
||||
[nodeType $lnode2] != "lanswitch" } { set regular no }
|
||||
if { [nodeType $lnode2] == "lanswitch" && \
|
||||
[nodeType $lnode1] != "router" && \
|
||||
([nodeType $lnode1] != "lanswitch" || [nodeType $lnode1] != "OVS" )} {
|
||||
#Khaled: puts "connecting '$lnode1' (type: '[nodeType $lnode1]') to '$lnode2' (type: '[nodeType $lnode2]') "
|
||||
set regular no }
|
||||
[nodeType $lnode1] != "lanswitch" } { set regular no }
|
||||
if { [nodeType $lnode1] == "hub" && \
|
||||
[nodeType $lnode2] == "hub" } { set regular no }
|
||||
# Boeing: added tunnel, ktunnel types to behave as rj45
|
||||
|
@ -771,7 +768,7 @@ proc newLink { lnode1 lnode2 } {
|
|||
set othernode $lnode1
|
||||
}
|
||||
# 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} {
|
||||
return
|
||||
}
|
||||
|
@ -836,18 +833,13 @@ proc newLink { lnode1 lnode2 } {
|
|||
} elseif {$delay != ""} {
|
||||
lappend $link "delay $delay"
|
||||
}
|
||||
# Exclude OVS from network layer nodes IP address asignments
|
||||
if { ([[typemodel $lnode2].layer] == "NETWORK") && ([nodeType $lnode2] != "OVS") } {
|
||||
|
||||
#Khaled: puts "Assigning '$lnode2' (type: '[nodeType $lnode2]') an automatic IP address"
|
||||
|
||||
if { [[typemodel $lnode2].layer] == "NETWORK" } {
|
||||
if { $ipv4_addr2 == "" } { autoIPv4addr $lnode2 $ifname2 }
|
||||
if { $ipv6_addr2 == "" } { autoIPv6addr $lnode2 $ifname2 }
|
||||
}
|
||||
# tunnels also excluded from link settings
|
||||
# OVS and Lanswitch should go side by side
|
||||
} elseif { (([nodeType $lnode1] == "lanswitch" || [nodeType $lnode1] == "OVS" )|| \
|
||||
([nodeType $lnode2] == "lanswitch"|| [nodeType $lnode2] == "OVS") || \
|
||||
} elseif { ([nodeType $lnode1] == "lanswitch" || \
|
||||
[nodeType $lnode2] == "lanswitch" || \
|
||||
[string first eth "$ifname1 $ifname2"] != -1) && \
|
||||
[nodeType $lnode1] != "rj45" && [nodeType $lnode2] != "rj45" && \
|
||||
[nodeType $lnode1] != "tunnel" && [nodeType $lnode2] != "tunnel" && \
|
||||
|
@ -859,11 +851,9 @@ proc newLink { lnode1 lnode2 } {
|
|||
}
|
||||
|
||||
lappend link_list $link
|
||||
# Exclude OVS from Network layer node configs
|
||||
if { [nodeType $lnode2] != "pseudo" &&
|
||||
[nodeType $lnode1] != "wlan" &&
|
||||
([[typemodel $lnode1].layer] == "NETWORK" && [nodeType $lnode1] != "OVS") } {
|
||||
|
||||
[[typemodel $lnode1].layer] == "NETWORK" } {
|
||||
if { $ipv4_addr1 == "" && $do_auto_addressing } {
|
||||
autoIPv4addr $lnode1 $ifname1
|
||||
}
|
||||
|
@ -874,8 +864,7 @@ proc newLink { lnode1 lnode2 } {
|
|||
# assume wlan is always lnode1
|
||||
if { [nodeType $lnode1] != "pseudo" &&
|
||||
[nodeType $lnode1] != "wlan" &&
|
||||
([[typemodel $lnode2].layer] == "NETWORK" && [nodeType $lnode2] != "OVS") } {
|
||||
|
||||
[[typemodel $lnode2].layer] == "NETWORK" } {
|
||||
if { $ipv4_addr2 == "" && $do_auto_addressing } {
|
||||
autoIPv4addr $lnode2 $ifname2
|
||||
}
|
||||
|
|
|
@ -21,12 +21,10 @@ array set g_node_types_default {
|
|||
5 {prouter router_green.gif router_green.gif \
|
||||
{zebra OSPFv2 OSPFv3 IPForward} \
|
||||
physical {built-in type for physical nodes}}
|
||||
6 {OVS lanswitch.gif lanswitch.gif {DefaultRoute SSH OvsService} OVS {} }
|
||||
|
||||
}
|
||||
|
||||
# possible machine types for nodes
|
||||
set MACHINE_TYPES "netns physical OVS"
|
||||
set MACHINE_TYPES "netns physical"
|
||||
|
||||
# array populated from nodes.conf file
|
||||
array set g_node_types { }
|
||||
|
@ -184,7 +182,7 @@ proc getNodeTypeServices { type } {
|
|||
# node type from the toolbar
|
||||
proc getNodeTypeMachineType { type } {
|
||||
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]
|
||||
if { $i < 0 } { return $default_machine_type }; # failsafe
|
||||
return [lindex $g_node_types($i) 4]
|
||||
|
@ -208,7 +206,7 @@ proc getNodeTypeProfile { type } {
|
|||
# node type from the toolbar
|
||||
proc getNodeTypeMachineType { type } {
|
||||
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]
|
||||
if { $i < 0 } { return $default_machine_type }; # failsafe
|
||||
return [lindex $g_node_types($i) 4]
|
||||
|
@ -714,7 +712,6 @@ proc lanswitch.layer {} { return LINK }
|
|||
proc hub.layer {} { return LINK }
|
||||
proc tunnel.layer {} { return LINK }
|
||||
proc wlan.layer {} { return LINK }
|
||||
proc OVS.layer {} { return NETWORK }
|
||||
proc router.layer {} { return NETWORK }
|
||||
proc router.shellcmd { n } { return "vtysh" }
|
||||
|
||||
|
|
|
@ -9,11 +9,7 @@
|
|||
|
||||
if WANT_PYTHON
|
||||
|
||||
if PYTHON3
|
||||
PYTHONLIBDIR=$(libdir)/python3/dist-packages
|
||||
else
|
||||
PYTHONLIBDIR=$(pythondir)
|
||||
endif
|
||||
PYTHONLIBDIR=$(subst site-packages,dist-packages,$(pythondir))
|
||||
|
||||
SETUPPY = setup.py
|
||||
SETUPPYFLAGS = -v
|
||||
|
|
|
@ -10,17 +10,16 @@ import sys
|
|||
|
||||
import ns.core
|
||||
import ns.mobility
|
||||
|
||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
||||
from corens3.obj import Ns3LteNet
|
||||
from corens3.obj import Ns3Session
|
||||
|
||||
from core.nodes import ipaddress
|
||||
|
||||
|
||||
def ltesession(opt):
|
||||
"""
|
||||
Run a test LTE session.
|
||||
"""
|
||||
nodeutils.set_node_map(nodemaps.NODES)
|
||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||
lte = session.create_node(cls=Ns3LteNet, name="wlan1")
|
||||
lte.setsubchannels(range(25), range(50, 100))
|
||||
|
|
|
@ -27,11 +27,11 @@ import optparse
|
|||
import sys
|
||||
|
||||
import ns.core
|
||||
|
||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
||||
from corens3.obj import Ns3Session
|
||||
from corens3.obj import Ns3WifiNet
|
||||
|
||||
from core.nodes import ipaddress
|
||||
|
||||
|
||||
def add_to_server(session):
|
||||
"""
|
||||
|
@ -50,7 +50,6 @@ def wifisession(opt):
|
|||
"""
|
||||
Run a test wifi session.
|
||||
"""
|
||||
nodeutils.set_node_map(nodemaps.NODES)
|
||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||
session.name = "ns3wifi"
|
||||
session.filename = session.name + ".py"
|
||||
|
|
|
@ -14,13 +14,14 @@ How to run this:
|
|||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
from builtins import range
|
||||
|
||||
import ns.core
|
||||
import ns.network
|
||||
from corens3.obj import Ns3Session
|
||||
from corens3.obj import Ns3WifiNet
|
||||
|
||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
||||
from core.nodes import ipaddress
|
||||
|
||||
|
||||
def add_to_server(session):
|
||||
|
@ -40,7 +41,6 @@ def wifisession(opt):
|
|||
"""
|
||||
Run a random walk wifi session.
|
||||
"""
|
||||
nodeutils.set_node_map(nodemaps.NODES)
|
||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||
session.name = "ns3wifirandomwalk"
|
||||
session.filename = session.name + ".py"
|
||||
|
@ -54,7 +54,7 @@ def wifisession(opt):
|
|||
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
|
||||
services_str = "zebra|OSPFv3MDR|IPForward"
|
||||
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.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||
nodes.append(node)
|
||||
|
|
|
@ -12,17 +12,18 @@ Current issues:
|
|||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
from builtins import range
|
||||
|
||||
from core.nodes import nodeutils, nodemaps, ipaddress
|
||||
from corens3.obj import Ns3Session
|
||||
from corens3.obj import Ns3WimaxNet
|
||||
|
||||
from core.nodes import ipaddress
|
||||
|
||||
|
||||
def wimaxsession(opt):
|
||||
"""
|
||||
Run a test wimax session.
|
||||
"""
|
||||
nodeutils.set_node_map(nodemaps.NODES)
|
||||
session = Ns3Session(1, persistent=True, duration=opt.duration)
|
||||
wimax = session.create_node(cls=Ns3WimaxNet, name="wlan1")
|
||||
# wimax.wimax.EnableLogComponents()
|
||||
|
@ -33,7 +34,7 @@ def wimaxsession(opt):
|
|||
# classifier = (0, 65000, 0, 65000, 1, 1)
|
||||
classifier = (0, 65000, 0, 65000, 17, 1)
|
||||
nodes = []
|
||||
for i in xrange(1, opt.numnodes + 1):
|
||||
for i in range(1, opt.numnodes + 1):
|
||||
node = session.addnode(name="n%d" % i)
|
||||
if i == 1:
|
||||
wimax.setbasestation(node)
|
||||
|
|
Loading…
Add table
Reference in a new issue