From d9bd0fa434351e1192804c855c856dcacb0ef783 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2019 23:59:50 +0000 Subject: [PATCH 01/20] Bump jackson.version from 2.9.9 to 2.10.0.pr2 in /corefx Bumps `jackson.version` from 2.9.9 to 2.10.0.pr2. Updates `jackson-core` from 2.9.9 to 2.10.0.pr2 - [Release notes](https://github.com/FasterXML/jackson-core/releases) - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.9.9...jackson-core-2.10.0.pr2) Updates `jackson-databind` from 2.9.9 to 2.10.0.pr2 - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) Updates `jackson-annotations` from 2.9.9 to 2.10.0.pr2 - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) Signed-off-by: dependabot[bot] --- corefx/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/corefx/pom.xml b/corefx/pom.xml index af957aa2..ed5454e0 100644 --- a/corefx/pom.xml +++ b/corefx/pom.xml @@ -13,7 +13,7 @@ 1.8 1.8 2.1.1 - 2.9.9 + 2.10.0.pr2 1.20.0 2.9.0 From d5c257fc9bdbdbab67b6ed0e9ae50df0a40704fc Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 23 Sep 2019 22:02:09 -0700 Subject: [PATCH 02/20] fixed flake8 config to match other tooling --- daemon/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/setup.cfg b/daemon/setup.cfg index 576aa9f0..a3084b8b 100644 --- a/daemon/setup.cfg +++ b/daemon/setup.cfg @@ -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 From 212b8ccb0233862e3d345179b62f1dc1183e4b95 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 23 Sep 2019 22:15:20 -0700 Subject: [PATCH 03/20] bumping version for release --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 64f291b1..4f774f6d 100644 --- a/configure.ac +++ b/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]) From 9c4459e7e79de4e9ccd0dd264e2503d744abf19f Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 23 Sep 2019 22:45:27 -0700 Subject: [PATCH 04/20] update to devguide --- docs/devguide.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/devguide.md b/docs/devguide.md index 5bd02095..ca207a82 100644 --- a/docs/devguide.md +++ b/docs/devguide.md @@ -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 From 1c11c6c573eccc666492f25091043ec923a79f09 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 23 Sep 2019 23:40:59 -0700 Subject: [PATCH 05/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0af9390..099467f0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CORE [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d94eb0244ade4510a106b4af76077a92)](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 From b0c30056f9d3a9e3cc0b2b072d664b57ebc946d3 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Wed, 25 Sep 2019 10:46:41 -0700 Subject: [PATCH 06/20] removed ovs node from gui --- gui/api.tcl | 3 +-- gui/editor.tcl | 13 +++++-------- gui/ipv4.tcl | 2 +- gui/linkcfg.tcl | 33 +++++++++++---------------------- gui/nodes.tcl | 9 +++------ 5 files changed, 21 insertions(+), 39 deletions(-) diff --git a/gui/api.tcl b/gui/api.tcl index 310e5ddc..1801f82c 100644 --- a/gui/api.tcl +++ b/gui/api.tcl @@ -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 } diff --git a/gui/editor.tcl b/gui/editor.tcl index fec8a499..f93d85c1 100644 --- a/gui/editor.tcl +++ b/gui/editor.tcl @@ -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]} + set node [newNode router] 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:" diff --git a/gui/ipv4.tcl b/gui/ipv4.tcl index 76901060..145b0988 100644 --- a/gui/ipv4.tcl +++ b/gui/ipv4.tcl @@ -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] diff --git a/gui/linkcfg.tcl b/gui/linkcfg.tcl index 5c773097..9c7172a1 100644 --- a/gui/linkcfg.tcl +++ b/gui/linkcfg.tcl @@ -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,13 +851,11 @@ 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 + autoIPv4addr $lnode1 $ifname1 } if { $ipv6_addr1 == "" && $do_auto_addressing } { autoIPv6addr $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 } diff --git a/gui/nodes.tcl b/gui/nodes.tcl index cabad158..00e52c5d 100644 --- a/gui/nodes.tcl +++ b/gui/nodes.tcl @@ -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" } From b14012e5e96206f14b7079668ebac1ed36bbab06 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 26 Sep 2019 13:00:12 -0700 Subject: [PATCH 07/20] removed node map file and utilities, added formal class methods to session objects and added checks for specific classes where needed --- daemon/Pipfile | 5 +- daemon/core/api/grpc/server.py | 14 +-- daemon/core/api/tlv/broker.py | 14 ++- daemon/core/api/tlv/corehandlers.py | 6 +- daemon/core/emane/emanemanager.py | 5 +- daemon/core/emane/nodes.py | 3 +- daemon/core/emulator/coreemu.py | 14 --- daemon/core/emulator/emudata.py | 7 +- daemon/core/emulator/session.py | 120 ++++++++++++++++++-------- daemon/core/nodes/base.py | 7 +- daemon/core/nodes/interface.py | 7 +- daemon/core/nodes/nodemaps.py | 32 ------- daemon/core/nodes/nodeutils.py | 97 --------------------- daemon/core/plugins/sdt.py | 21 +++-- daemon/core/services/emaneservices.py | 5 +- daemon/core/services/frr.py | 12 +-- daemon/core/services/quagga.py | 17 ++-- daemon/core/xml/corexml.py | 7 +- daemon/core/xml/corexmldeployment.py | 6 +- daemon/scripts/core-daemon | 3 - ns3/examples/ns3lte.py | 5 +- ns3/examples/ns3wifi.py | 5 +- ns3/examples/ns3wifirandomwalk.py | 6 +- ns3/examples/ns3wimax.py | 7 +- 24 files changed, 161 insertions(+), 264 deletions(-) delete mode 100644 daemon/core/nodes/nodemaps.py delete mode 100644 daemon/core/nodes/nodeutils.py diff --git a/daemon/Pipfile b/daemon/Pipfile index e8fd5954..a689f4dc 100644 --- a/daemon/Pipfile +++ b/daemon/Pipfile @@ -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 = "*" diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index 0a15f8f0..ecb391ce 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -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, diff --git a/daemon/core/api/tlv/broker.py b/daemon/core/api/tlv/broker.py index b5fd5d0b..7c0b3992 100644 --- a/daemon/core/api/tlv/broker.py +++ b/daemon/core/api/tlv/broker.py @@ -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 @@ -495,15 +495,12 @@ class CoreBroker(object): logging.info("adding net tunnel for: id(%s) %s", node_id, net) # 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 - ): + if isinstance(net, CtrlNet) and server_interface is not None: logging.warning( "control networks with server interfaces do not need a tunnel" ) @@ -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 diff --git a/daemon/core/api/tlv/corehandlers.py b/daemon/core/api/tlv/corehandlers.py index bcbb0f57..24e29169 100644 --- a/daemon/core/api/tlv/corehandlers.py +++ b/daemon/core/api/tlv/corehandlers.py @@ -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 () diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index be8ca368..c93d28dd 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -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 ) diff --git a/daemon/core/emane/nodes.py b/daemon/core/emane/nodes.py index 7b987098..79b767d1 100644 --- a/daemon/core/emane/nodes.py +++ b/daemon/core/emane/nodes.py @@ -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 = "" diff --git a/daemon/core/emulator/coreemu.py b/daemon/core/emulator/coreemu.py index 9a039627..c0eae5cd 100644 --- a/daemon/core/emulator/coreemu.py +++ b/daemon/core/emulator/coreemu.py @@ -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 @@ -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. diff --git a/daemon/core/emulator/emudata.py b/daemon/core/emulator/emudata.py index 2cbd3007..0f7a6af6 100644 --- a/daemon/core/emulator/emudata.py +++ b/daemon/core/emulator/emudata.py @@ -1,7 +1,8 @@ -from core.emulator.enumerations import LinkTypes, NodeTypes -from core.nodes import nodeutils +from core.emane.nodes import EmaneNode +from core.emulator.enumerations import LinkTypes from core.nodes.base import CoreNetworkBase from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress +from core.nodes.physical import PhysicalNode class IdGen(object): @@ -65,7 +66,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) diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index e7a8e3f5..375e6f24 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -14,11 +14,11 @@ 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, @@ -33,14 +33,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 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 +153,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 +204,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 @@ -203,9 +262,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 +325,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 +356,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 +380,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 +393,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 +640,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 +704,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 +1492,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 +1542,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 +1595,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 +1694,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 +1770,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, diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index 2f3d7b64..6d06dd79 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -17,7 +17,7 @@ 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 _DEFAULT_MTU = 1500 @@ -886,8 +886,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 +1050,7 @@ class CoreNetworkBase(NodeBase): """ linktype = LinkTypes.WIRED.value + is_emane = False def __init__(self, session, _id, name, start=True): """ diff --git a/daemon/core/nodes/interface.py b/daemon/core/nodes/interface.py index 0db3c6c4..8720d69a 100644 --- a/daemon/core/nodes/interface.py +++ b/daemon/core/nodes/interface.py @@ -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") diff --git a/daemon/core/nodes/nodemaps.py b/daemon/core/nodes/nodemaps.py deleted file mode 100644 index d9d0c9ad..00000000 --- a/daemon/core/nodes/nodemaps.py +++ /dev/null @@ -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, -} diff --git a/daemon/core/nodes/nodeutils.py b/daemon/core/nodes/nodeutils.py deleted file mode 100644 index 95c53165..00000000 --- a/daemon/core/nodes/nodeutils.py +++ /dev/null @@ -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)) diff --git a/daemon/core/plugins/sdt.py b/daemon/core/plugins/sdt.py index 3eaba44a..ce60ccac 100644 --- a/daemon/core/plugins/sdt.py +++ b/daemon/core/plugins/sdt.py @@ -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 diff --git a/daemon/core/services/emaneservices.py b/daemon/core/services/emaneservices.py index 19560579..5cb7ec5c 100644 --- a/daemon/core/services/emaneservices.py +++ b/daemon/core/services/emaneservices.py @@ -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 ) diff --git a/daemon/core/services/frr.py b/daemon/core/services/frr.py index 45516a55..b4332009 100644 --- a/daemon/core/services/frr.py +++ b/daemon/core/services/frr.py @@ -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 "" diff --git a/daemon/core/services/quagga.py b/daemon/core/services/quagga.py index 7d8e8d16..0f213b7b 100644 --- a/daemon/core/services/quagga.py +++ b/daemon/core/services/quagga.py @@ -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 + """\ diff --git a/daemon/core/xml/corexml.py b/daemon/core/xml/corexml.py index 31618277..ad4de155 100644 --- a/daemon/core/xml/corexml.py +++ b/daemon/core/xml/corexml.py @@ -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) diff --git a/daemon/core/xml/corexmldeployment.py b/daemon/core/xml/corexmldeployment.py index 8d926a8c..a5c966d5 100644 --- a/daemon/core/xml/corexmldeployment.py +++ b/daemon/core/xml/corexmldeployment.py @@ -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 diff --git a/daemon/scripts/core-daemon b/daemon/scripts/core-daemon index ea49bb7f..58540ef9 100755 --- a/daemon/scripts/core-daemon +++ b/daemon/scripts/core-daemon @@ -60,9 +60,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) diff --git a/ns3/examples/ns3lte.py b/ns3/examples/ns3lte.py index 2cb17407..52355552 100644 --- a/ns3/examples/ns3lte.py +++ b/ns3/examples/ns3lte.py @@ -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)) diff --git a/ns3/examples/ns3wifi.py b/ns3/examples/ns3wifi.py index 747b184b..e06358a4 100644 --- a/ns3/examples/ns3wifi.py +++ b/ns3/examples/ns3wifi.py @@ -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" diff --git a/ns3/examples/ns3wifirandomwalk.py b/ns3/examples/ns3wifirandomwalk.py index a96f37f6..6371a3f1 100644 --- a/ns3/examples/ns3wifirandomwalk.py +++ b/ns3/examples/ns3wifirandomwalk.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) diff --git a/ns3/examples/ns3wimax.py b/ns3/examples/ns3wimax.py index b1d7212c..e38034e5 100644 --- a/ns3/examples/ns3wimax.py +++ b/ns3/examples/ns3wimax.py @@ -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) From bdf288ff1d347f3f32dfcd2f3489ce594f5978a8 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 26 Sep 2019 13:15:46 -0700 Subject: [PATCH 08/20] removed is_net_node utility to leverage isinstance as other places are consistently --- daemon/core/emulator/emudata.py | 12 ------------ daemon/core/emulator/session.py | 7 +++---- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/daemon/core/emulator/emudata.py b/daemon/core/emulator/emudata.py index 0f7a6af6..467fe2e4 100644 --- a/daemon/core/emulator/emudata.py +++ b/daemon/core/emulator/emudata.py @@ -1,6 +1,5 @@ from core.emane.nodes import EmaneNode from core.emulator.enumerations import LinkTypes -from core.nodes.base import CoreNetworkBase from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress from core.nodes.physical import PhysicalNode @@ -14,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. diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index 375e6f24..4ffb2391 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -25,7 +25,6 @@ from core.emulator.emudata import ( LinkOptions, NodeOptions, create_interface, - is_net_node, link_config, ) from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes @@ -33,7 +32,7 @@ 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.base import CoreNode, 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 @@ -217,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: From b449729a319ad7000735819fa6da3af6d8804e1a Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 26 Sep 2019 15:20:32 -0700 Subject: [PATCH 09/20] added net client to consolidate bridge and interface creation --- .editorconfig | 1 + daemon/core/emulator/coreemu.py | 2 +- daemon/core/nodes/base.py | 5 ++ daemon/core/nodes/netclient.py | 74 ++++++++++++++++++++++++++++ daemon/core/nodes/network.py | 87 ++++++++++++--------------------- daemon/scripts/core-daemon | 8 +-- 6 files changed, 113 insertions(+), 64 deletions(-) create mode 100644 daemon/core/nodes/netclient.py diff --git a/.editorconfig b/.editorconfig index d0466b2f..f1b09263 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,7 @@ insert_final_newline = true [*.py] indent_style = space indent_size = 4 +max_line_length = 88 [*.am] indent_style = tab diff --git a/daemon/core/emulator/coreemu.py b/daemon/core/emulator/coreemu.py index c0eae5cd..1c0923de 100644 --- a/daemon/core/emulator/coreemu.py +++ b/daemon/core/emulator/coreemu.py @@ -44,7 +44,7 @@ class CoreEmu(object): os.umask(0) # configuration - if not config: + if config is None: config = {} self.config = config diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index 6d06dd79..556f5232 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -19,6 +19,7 @@ from core.emulator.data import LinkData, NodeData from core.emulator.enumerations import LinkTypes, NodeTypes from core.nodes import client, ipaddress from core.nodes.interface import CoreInterface, TunTap, Veth +from core.nodes.netclient import BrctlClient, OvsClient _DEFAULT_MTU = 1500 @@ -1064,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"): + self.net_client = OvsClient() + else: + self.net_client = BrctlClient() def startup(self): """ diff --git a/daemon/core/nodes/netclient.py b/daemon/core/nodes/netclient.py new file mode 100644 index 00000000..7b298863 --- /dev/null +++ b/daemon/core/nodes/netclient.py @@ -0,0 +1,74 @@ +""" +Clients for dealing with bridge and interface commands. +""" + +import abc +import os + +from core import constants, utils + + +class NetworkClient(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def create_bridge(self, name): + return NotImplemented + + +class BrctlClient(object): + def create_bridge(self, name): + utils.check_cmd([constants.BRCTL_BIN, "addbr", name]) + # disable spanning tree protocol and set forward delay to 0 + utils.check_cmd([constants.BRCTL_BIN, "stp", name, "off"]) + utils.check_cmd([constants.BRCTL_BIN, "setfd", name, "0"]) + utils.check_cmd([constants.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): + utils.check_cmd([constants.IP_BIN, "link", "set", name, "down"]) + utils.check_cmd([constants.BRCTL_BIN, "delbr", name]) + + def create_interface(self, bridge_name, interface_name): + utils.check_cmd([constants.BRCTL_BIN, "addif", bridge_name, interface_name]) + utils.check_cmd([constants.IP_BIN, "link", "set", interface_name, "up"]) + + def delete_interface(self, bridge_name, interface_name): + utils.check_cmd([constants.BRCTL_BIN, "delif", bridge_name, interface_name]) + + def get_bridges(self): + return utils.check_cmd([constants.OVS_BIN, "list-br"]) + + def disable_mac_learning(self, name): + utils.check_cmd([constants.BRCTL_BIN, "setageing", name, "0"]) + + +class OvsClient(object): + def create_bridge(self, name): + # turn off spanning tree protocol and forwarding delay + # TODO: verify stp and rstp are always off by default + # TODO: ovs only supports rstp forward delay and again it's off by default + utils.check_cmd([constants.OVS_BIN, "add-br", name]) + utils.check_cmd([constants.IP_BIN, "link", "set", name, "up"]) + + def delete_bridge(self, name): + utils.check_cmd([constants.IP_BIN, "link", "set", name, "down"]) + utils.check_cmd([constants.OVS_BIN, "del-br", name]) + + def create_interface(self, bridge_name, interface_name): + utils.check_cmd([constants.OVS_BIN, "add-port", bridge_name, interface_name]) + utils.check_cmd([constants.IP_BIN, "link", "set", interface_name, "up"]) + + def delete_interface(self, bridge_name, interface_name): + utils.check_cmd([constants.OVS_BIN, "del-port", bridge_name, interface_name]) + + def get_bridges(self): + utils.check_cmd([constants.BRCTL_BIN, "show"]) + + def disable_mac_learning(self, name): + pass diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index 6ba4ebd4..852f8110 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -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: @@ -848,41 +832,32 @@ 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"]) + self.net_client.create_interface(self.brname, self.serverintf) 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 + 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 + output = self.net_client.get_bridges() + lines = output.split("\n") + for line in lines[1:]: + cols = line.split("\t") + oldbr = cols[0] + flds = cols[0].split(".") + if len(flds) == 3: + if flds[0] == "b" and flds[1] == self.id: + logging.error( + "error: An active control net bridge (%s) found. " + "An older session might still be running. " + "Stop all sessions and, if needed, delete %s to continue.", + oldbr, + oldbr, + ) + return True return False def shutdown(self): @@ -893,9 +868,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 +1073,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 +1104,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): """ diff --git a/daemon/scripts/core-daemon b/daemon/scripts/core-daemon index 58540ef9..13e6e334 100755 --- a/daemon/scripts/core-daemon +++ b/daemon/scripts/core-daemon @@ -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"] @@ -153,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") From 0c002bb4914b21db905d9de717f78fae00e190e0 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 26 Sep 2019 22:04:29 -0700 Subject: [PATCH 10/20] implemented net client for linux and ovs --- daemon/core/nodes/base.py | 8 +- daemon/core/nodes/netclient.py | 136 ++++++++++++++++++++++++++------- daemon/core/nodes/network.py | 32 +------- 3 files changed, 114 insertions(+), 62 deletions(-) diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index 556f5232..4861f93c 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -19,7 +19,7 @@ from core.emulator.data import LinkData, NodeData from core.emulator.enumerations import LinkTypes, NodeTypes from core.nodes import client, ipaddress from core.nodes.interface import CoreInterface, TunTap, Veth -from core.nodes.netclient import BrctlClient, OvsClient +from core.nodes.netclient import LinuxNetClient, OvsNetClient _DEFAULT_MTU = 1500 @@ -1065,10 +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"): - self.net_client = OvsClient() + if session.options.get_config("ovs") == "True": + self.net_client = OvsNetClient() else: - self.net_client = BrctlClient() + self.net_client = LinuxNetClient() def startup(self): """ diff --git a/daemon/core/nodes/netclient.py b/daemon/core/nodes/netclient.py index 7b298863..0ec2c389 100644 --- a/daemon/core/nodes/netclient.py +++ b/daemon/core/nodes/netclient.py @@ -1,28 +1,90 @@ """ -Clients for dealing with bridge and interface commands. +Clients for dealing with bridge/interface commands. """ import abc import os -from core import constants, utils +from future.utils import with_metaclass + +from core.constants import BRCTL_BIN, IP_BIN, OVS_BIN +from core.utils import check_cmd -class NetworkClient(object): - __metaclass__ = abc.ABCMeta +class NetClientBase(with_metaclass(abc.ABCMeta)): + """ + Base client for running command line bridge/interface commands. + """ @abc.abstractmethod def create_bridge(self, name): - return NotImplemented + """ + 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 branch. + + :param str name: branch name + :return: nothing + """ + pass -class BrctlClient(object): +class LinuxNetClient(NetClientBase): def create_bridge(self, name): - utils.check_cmd([constants.BRCTL_BIN, "addbr", name]) + check_cmd([BRCTL_BIN, "addbr", name]) # disable spanning tree protocol and set forward delay to 0 - utils.check_cmd([constants.BRCTL_BIN, "stp", name, "off"]) - utils.check_cmd([constants.BRCTL_BIN, "setfd", name, "0"]) - utils.check_cmd([constants.IP_BIN, "link", "set", name, "up"]) + 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 @@ -31,44 +93,60 @@ class BrctlClient(object): f.write("0") def delete_bridge(self, name): - utils.check_cmd([constants.IP_BIN, "link", "set", name, "down"]) - utils.check_cmd([constants.BRCTL_BIN, "delbr", name]) + check_cmd([IP_BIN, "link", "set", name, "down"]) + check_cmd([BRCTL_BIN, "delbr", name]) def create_interface(self, bridge_name, interface_name): - utils.check_cmd([constants.BRCTL_BIN, "addif", bridge_name, interface_name]) - utils.check_cmd([constants.IP_BIN, "link", "set", interface_name, "up"]) + 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): - utils.check_cmd([constants.BRCTL_BIN, "delif", bridge_name, interface_name]) + check_cmd([BRCTL_BIN, "delif", bridge_name, interface_name]) - def get_bridges(self): - return utils.check_cmd([constants.OVS_BIN, "list-br"]) + def existing_bridges(self, _id): + 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): - utils.check_cmd([constants.BRCTL_BIN, "setageing", name, "0"]) + check_cmd([BRCTL_BIN, "setageing", name, "0"]) -class OvsClient(object): +class OvsNetClient(NetClientBase): def create_bridge(self, name): # turn off spanning tree protocol and forwarding delay # TODO: verify stp and rstp are always off by default # TODO: ovs only supports rstp forward delay and again it's off by default - utils.check_cmd([constants.OVS_BIN, "add-br", name]) - utils.check_cmd([constants.IP_BIN, "link", "set", name, "up"]) + check_cmd([OVS_BIN, "add-br", name]) + check_cmd([IP_BIN, "link", "set", name, "up"]) def delete_bridge(self, name): - utils.check_cmd([constants.IP_BIN, "link", "set", name, "down"]) - utils.check_cmd([constants.OVS_BIN, "del-br", name]) + check_cmd([IP_BIN, "link", "set", name, "down"]) + check_cmd([OVS_BIN, "del-br", name]) def create_interface(self, bridge_name, interface_name): - utils.check_cmd([constants.OVS_BIN, "add-port", bridge_name, interface_name]) - utils.check_cmd([constants.IP_BIN, "link", "set", interface_name, "up"]) + 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): - utils.check_cmd([constants.OVS_BIN, "del-port", bridge_name, interface_name]) + check_cmd([OVS_BIN, "del-port", bridge_name, interface_name]) - def get_bridges(self): - utils.check_cmd([constants.BRCTL_BIN, "show"]) + def existing_bridges(self, _id): + 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): - pass + check_cmd([OVS_BIN, "set", "bridge", name, "other_config:mac-aging-time=0"]) diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index 852f8110..9a81b8e1 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -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 @@ -806,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) @@ -834,32 +834,6 @@ class CtrlNet(CoreNetwork): if self.serverintf: self.net_client.create_interface(self.brname, self.serverintf) - 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 - """ - output = self.net_client.get_bridges() - lines = output.split("\n") - for line in lines[1:]: - cols = line.split("\t") - oldbr = cols[0] - flds = cols[0].split(".") - if len(flds) == 3: - if flds[0] == "b" and flds[1] == self.id: - logging.error( - "error: An active control net bridge (%s) found. " - "An older session might still be running. " - "Stop all sessions and, if needed, delete %s to continue.", - oldbr, - oldbr, - ) - return True - return False - def shutdown(self): """ Control network shutdown. From 1277ae46864cac274fb9b9bc0fed1dd5d1723e61 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 10:48:24 -0700 Subject: [PATCH 11/20] fleshed out documentation for netclient.py and removed openvswitch.py --- daemon/core/nodes/netclient.py | 93 +++- daemon/core/nodes/openvswitch.py | 825 ------------------------------- 2 files changed, 87 insertions(+), 831 deletions(-) delete mode 100644 daemon/core/nodes/openvswitch.py diff --git a/daemon/core/nodes/netclient.py b/daemon/core/nodes/netclient.py index 0ec2c389..292b6c95 100644 --- a/daemon/core/nodes/netclient.py +++ b/daemon/core/nodes/netclient.py @@ -70,18 +70,27 @@ class NetClientBase(with_metaclass(abc.ABCMeta)): @abc.abstractmethod def disable_mac_learning(self, name): """ - Disable mac learning for a branch. + Disable mac learning for a bridge. - :param str name: branch name + :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]) - # disable spanning tree protocol and set forward delay to 0 check_cmd([BRCTL_BIN, "stp", name, "off"]) check_cmd([BRCTL_BIN, "setfd", name, "0"]) check_cmd([IP_BIN, "link", "set", name, "up"]) @@ -93,17 +102,42 @@ class LinuxNetClient(NetClientBase): 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:]: @@ -117,29 +151,70 @@ class LinuxNetClient(NetClientBase): 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): - # turn off spanning tree protocol and forwarding delay - # TODO: verify stp and rstp are always off by default - # TODO: ovs only supports rstp forward delay and again it's off by default + """ + 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"): @@ -149,4 +224,10 @@ class OvsNetClient(NetClientBase): 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"]) diff --git a/daemon/core/nodes/openvswitch.py b/daemon/core/nodes/openvswitch.py deleted file mode 100644 index 6a95687d..00000000 --- a/daemon/core/nodes/openvswitch.py +++ /dev/null @@ -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, -} From a01ea35f7c4f71c295558cf58eef3eb21cfe1adc Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 11:01:19 -0700 Subject: [PATCH 12/20] bumping configparser version in requirements.txt due to previous version removal --- daemon/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/requirements.txt b/daemon/requirements.txt index ec738953..a32f12de 100644 --- a/daemon/requirements.txt +++ b/daemon/requirements.txt @@ -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 From 4b028661aba80c1e5147498ea71d53ce8a3f8a87 Mon Sep 17 00:00:00 2001 From: Jeff Ahrenholz Date: Fri, 27 Sep 2019 11:02:01 -0700 Subject: [PATCH 13/20] fix #290 use pythondir var as base for setting PYTHONLIBDIR --- daemon/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 2f112b85..0182b291 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -15,7 +15,7 @@ DOCS = doc endif if PYTHON3 -PYTHONLIBDIR=$(libdir)/python3/dist-packages +PYTHONLIBDIR=$(shell echo $(pythondir) | sed -e 's/site-packages/dist-packages/') else PYTHONLIBDIR=$(pythondir) endif From cd747515ea7e8e85034ce201ea94823af3f17123 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 12:11:14 -0700 Subject: [PATCH 14/20] added python path change to ns3 and simplified substitution, which is not version dependent --- daemon/Makefile.am | 6 +----- ns3/Makefile.am | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 0182b291..a6503cc0 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -14,11 +14,7 @@ if WANT_DOCS DOCS = doc endif -if PYTHON3 -PYTHONLIBDIR=$(shell echo $(pythondir) | sed -e 's/site-packages/dist-packages/') -else -PYTHONLIBDIR=$(pythondir) -endif +PYTHONLIBDIR=$(subst site-packages,dist-packages,$(pythondir)) SUBDIRS = proto $(DOCS) diff --git a/ns3/Makefile.am b/ns3/Makefile.am index c1858665..115a7008 100644 --- a/ns3/Makefile.am +++ b/ns3/Makefile.am @@ -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 From 03cf401639a96fd27b9fc39583bf6f47bc3a41c2 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 12:41:34 -0700 Subject: [PATCH 15/20] moved AM_CONDITIONAL out of if statement --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4f774f6d..80b76cec 100644 --- a/configure.ac +++ b/configure.ac @@ -110,7 +110,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 +156,8 @@ if test "x$enable_daemon" = "xyes"; then CFLAGS=$CFLAGS_save CPPFLAGS=$CPPFLAGS_save fi +AM_CONDITIONAL([PYTHON3], [test "x$PYTHON" == "xpython3"]) + if [ test "x$enable_daemon" = "xyes" || test "x$enable_vnodedonly" = "xyes" ] ; then want_linux_netns=yes PKG_CHECK_MODULES(libev, libev, From 3cf557024c21e9aa8b9b703c928843ffb3154d8f Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 13:08:59 -0700 Subject: [PATCH 16/20] added more formal configure option for enabling python3, avoids issues with not having a very specific name for PYTHON being set --- configure.ac | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 80b76cec..fdd04f16 100644 --- a/configure.ac +++ b/configure.ac @@ -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], + [enable python3 default is python2])], + [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 @@ -156,7 +163,6 @@ if test "x$enable_daemon" = "xyes"; then CFLAGS=$CFLAGS_save CPPFLAGS=$CPPFLAGS_save fi -AM_CONDITIONAL([PYTHON3], [test "x$PYTHON" == "xpython3"]) if [ test "x$enable_daemon" = "xyes" || test "x$enable_vnodedonly" = "xyes" ] ; then want_linux_netns=yes From f5aa74ed06bc0cb9be761e7a09dd1382c5a8d2f6 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 13:15:20 -0700 Subject: [PATCH 17/20] updated install doc to refer to new configure option for building packages --- docs/install.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/install.md b/docs/install.md index 761c589c..291c62ab 100644 --- a/docs/install.md +++ b/docs/install.md @@ -249,7 +249,7 @@ sudo yum -y install automake gcc python-devel libev-devel tk ```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 From 07c28e8a93dcc308bd8ec4db627006bfce385fe0 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 14:22:50 -0700 Subject: [PATCH 18/20] updated configure.ac python3 argument help line to be more specific --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fdd04f16..07c8150a 100644 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,7 @@ fi AC_ARG_ENABLE([python3], [AS_HELP_STRING([--enable-python3], - [enable python3 default is python2])], + [sets python3 flag for building packages])], [enable_python3=yes], [enable_python3=no]) AM_CONDITIONAL([PYTHON3], [test "x$enable_python3" == "xyes"]) From ce27a43486289ef4e15b0a7c7fe6dd50f575194f Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 15:07:41 -0700 Subject: [PATCH 19/20] updated install instruction to denote installing ethtool --- docs/install.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/install.md b/docs/install.md index 291c62ab..dbe6ab35 100644 --- a/docs/install.md +++ b/docs/install.md @@ -230,19 +230,19 @@ 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 From b961a8a959b42cbe3a907da9e5579bd7532bd0e2 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Sep 2019 15:31:19 -0700 Subject: [PATCH 20/20] updated broker.py logging to not be as noisy by default --- daemon/core/api/tlv/broker.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/daemon/core/api/tlv/broker.py b/daemon/core/api/tlv/broker.py index 7c0b3992..7b3087b4 100644 --- a/daemon/core/api/tlv/broker.py +++ b/daemon/core/api/tlv/broker.py @@ -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,7 +492,7 @@ 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 isinstance(net, EmaneNet): @@ -501,21 +501,21 @@ class CoreBroker(object): server_interface = getattr(net, "serverintf", None) if isinstance(net, CtrlNet) and server_interface is not None: - logging.warning( + 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: @@ -523,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 = [] @@ -536,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] @@ -655,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): """