moved future core server and handler code to act as the default core-daemon, updated future examples and tests to leverage new api

This commit is contained in:
Blake J. Harnden 2018-04-25 16:33:58 -07:00
parent f431895357
commit 8644e9d61e
24 changed files with 618 additions and 2728 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,263 +0,0 @@
"""
Defines server classes and request handlers for TCP and UDP.
"""
import SocketServer
import threading
import time
from core import logger
from core.api import coreapi
from core.enumerations import EventTypes
from core.enumerations import SessionTlvs
from core.session import Session
class CoreServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
"""
TCP server class, manages sessions and spawns request handlers for
incoming connections.
"""
daemon_threads = True
allow_reuse_address = True
servers = set()
def __init__(self, server_address, handler_class, config=None):
"""
Server class initialization takes configuration data and calls
the SocketServer constructor
:param tuple[str, int] server_address: server host and port to use
:param class handler_class: request handler
:param dict config: configuration setting
:return:
"""
self.config = config
self.sessions = {}
self.udpserver = None
self.udpthread = None
self._sessions_lock = threading.Lock()
CoreServer.add_server(self)
SocketServer.TCPServer.__init__(self, server_address, handler_class)
@classmethod
def add_server(cls, server):
"""
Add a core server to the known servers set.
:param CoreServer server: server to add
:return: nothing
"""
cls.servers.add(server)
@classmethod
def remove_server(cls, server):
"""
Remove a core server from the known servers set.
:param CoreServer server: server to remove
:return: nothing
"""
if server in cls.servers:
cls.servers.remove(server)
def shutdown(self):
"""
Shutdown the server, all known sessions, and remove server from known servers set.
:return: nothing
"""
# shutdown all known sessions
for session in self.sessions.values():
session.shutdown()
# remove server from server list
CoreServer.remove_server(self)
def add_session(self, session):
"""
Add a session to our dictionary of sessions, ensuring a unique session number.
:param core.session.Session session: session to add
:return: added session
:raise KeyError: when a session with the same id already exists
"""
with self._sessions_lock:
if session.session_id in self.sessions:
raise KeyError("non-unique session id %s for %s" % (session.session_id, session))
self.sessions[session.session_id] = session
return session
def remove_session(self, session):
"""
Remove a session from our dictionary of sessions.
:param core.session.Session session: session to remove
:return: removed session
:rtype: core.session.Session
"""
with self._sessions_lock:
if session.session_id not in self.sessions:
logger.info("session id %s not found (sessions=%s)", session.session_id, self.sessions.keys())
else:
del self.sessions[session.session_id]
return session
def get_session_ids(self):
"""
Return a list of active session numbers.
:return: known session ids
:rtype: list
"""
with self._sessions_lock:
session_ids = self.sessions.keys()
return session_ids
def create_session(self, session_id=None):
"""
Convenience method for creating sessions with the servers config.
:param int session_id: session id for new session
:return: create session
:rtype: core.session.Session
"""
# create random id when necessary, seems to be 1 case wanted, based on legacy code
# creating a value so high, typical client side generation schemes hopefully wont collide
if not session_id:
session_id = next(
session_id for session_id in xrange(60000, 65000)
if session_id not in self.sessions
)
# create and add session to local manager
session = Session(session_id, config=self.config)
self.add_session(session)
# add shutdown handler to remove session from manager
session.shutdown_handlers.append(self.session_shutdown)
return session
def get_session(self, session_id=None):
"""
Create a new session or retrieve an existing one from our
dictionary of sessions. When the session_id=0 and the use_existing
flag is set, return on of the existing sessions.
:param int session_id: session id of session to retrieve, defaults to returning random session
:return: session
:rtype: core.session.Session
"""
with self._sessions_lock:
# return specified session or none
if session_id:
return self.sessions.get(session_id)
# retrieving known session
session = None
# find runtime session with highest node count
for known_session in filter(lambda x: x.state == EventTypes.RUNTIME_STATE.value,
self.sessions.itervalues()):
if not session or known_session.get_node_count() > session.get_node_count():
session = known_session
# return first known session otherwise
if not session:
for known_session in self.sessions.itervalues():
session = known_session
break
return session
def session_shutdown(self, session):
"""
Handler method to be used as a callback when a session has shutdown.
:param core.session.Session session: session shutting down
:return: nothing
"""
self.remove_session(session)
def to_session_message(self, flags=0):
"""
Build CORE API Sessions message based on current session info.
:param int flags: message flags
:return: session message
"""
id_list = []
name_list = []
file_list = []
node_count_list = []
date_list = []
thumb_list = []
num_sessions = 0
with self._sessions_lock:
for session_id in self.sessions:
session = self.sessions[session_id]
# debug: session.dumpsession()
num_sessions += 1
id_list.append(str(session_id))
name = session.name
if not name:
name = ""
name_list.append(name)
file = session.file_name
if not file:
file = ""
file_list.append(file)
node_count_list.append(str(session.get_node_count()))
date_list.append(time.ctime(session._state_time))
thumb = session.thumbnail
if not thumb:
thumb = ""
thumb_list.append(thumb)
session_ids = "|".join(id_list)
names = "|".join(name_list)
files = "|".join(file_list)
node_counts = "|".join(node_count_list)
dates = "|".join(date_list)
thumbs = "|".join(thumb_list)
if num_sessions > 0:
tlv_data = ""
if len(session_ids) > 0:
tlv_data += coreapi.CoreSessionTlv.pack(SessionTlvs.NUMBER.value, session_ids)
if len(names) > 0:
tlv_data += coreapi.CoreSessionTlv.pack(SessionTlvs.NAME.value, names)
if len(files) > 0:
tlv_data += coreapi.CoreSessionTlv.pack(SessionTlvs.FILE.value, files)
if len(node_counts) > 0:
tlv_data += coreapi.CoreSessionTlv.pack(SessionTlvs.NODE_COUNT.value, node_counts)
if len(dates) > 0:
tlv_data += coreapi.CoreSessionTlv.pack(SessionTlvs.DATE.value, dates)
if len(thumbs) > 0:
tlv_data += coreapi.CoreSessionTlv.pack(SessionTlvs.THUMB.value, thumbs)
message = coreapi.CoreSessionMessage.pack(flags, tlv_data)
else:
message = None
return message
def dump_sessions(self):
"""
Log currently known session information.
"""
logger.info("sessions:")
with self._sessions_lock:
for session_id in self.sessions:
logger.info(session_id)

View file

@ -195,6 +195,9 @@ class FutureSession(Session):
raise ValueError("wireless link failure: %s", objects)
logger.info("handling wireless linking objects(%) connect(%s)", objects, connect)
common_networks = objects[0].commonnets(objects[1])
if not common_networks:
raise ValueError("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]):
logger.info("skipping common network that is not wireless/emane: %s", common_network)
@ -205,8 +208,6 @@ class FutureSession(Session):
common_network.link(interface_one, interface_two)
else:
common_network.unlink(interface_one, interface_two)
else:
raise ValueError("no common network found for wireless link/unlink")
def add_link(self, node_one_id, node_two_id, interface_one=None, interface_two=None, link_options=LinkOptions()):
"""
@ -429,15 +430,17 @@ class FutureSession(Session):
link_config(net_one, interface, link_options)
else:
common_networks = node_one.commonnets(node_two)
if not common_networks:
raise ValueError("no common network found")
for net_one, interface_one, interface_two in common_networks:
if interface_one_id and interface_one_id != node_one.getifindex(interface_one):
if interface_one_id is not None and interface_one_id != node_one.getifindex(interface_one):
continue
link_config(net_one, interface_one, link_options, interface_two=interface_two)
if not link_options.unidirectional:
link_config(net_one, interface_two, link_options, interface_two=interface_one)
else:
raise ValueError("no common network found")
finally:
if node_one:
node_one.lock.release()
@ -610,8 +613,8 @@ class FutureSession(Session):
:return: nothing
"""
self.set_state(state=EventTypes.DATACOLLECT_STATE.value, send_event=True)
self.set_state(state=EventTypes.SHUTDOWN_STATE.value, send_event=True)
self.set_state(EventTypes.DATACOLLECT_STATE, send_event=True)
self.set_state(EventTypes.SHUTDOWN_STATE, send_event=True)
super(FutureSession, self).shutdown()
def custom_delete_object(self, object_id):
@ -753,9 +756,9 @@ class FutureSession(Session):
"""
self.mobility.handleevent(event_data)
def create_emane_node(self, _id=None, node_options=NodeOptions()):
def create_wireless_node(self, _id=None, node_options=NodeOptions()):
"""
Create an EMANE node for use within an EMANE network.
Create a wireless node for use within an wireless/EMANE networks.
:param int _id: int for node, defaults to None and will be generated
:param core.future.futuredata.NodeOptions node_options: options for emane node, model will always be "mdr"
@ -796,6 +799,29 @@ class FutureSession(Session):
values = list(emane_model.getdefaultvalues())
self.emane.setconfig(emane_node.objid, emane_model.name, values)
def set_wireless_model(self, node, model):
"""
Convenience method for setting a wireless model.
:param node: node to set wireless model for
:param core.mobility.WirelessModel model: wireless model to set node to
:return: nothing
"""
values = list(model.getdefaultvalues())
node.setmodel(model, values)
def wireless_link_all(self, network, nodes):
"""
Link all nodes to the provided wireless network.
:param network: wireless network to link nodes to
:param nodes: nodes to link to wireless network
:return: nothing
"""
for node in nodes:
for common_network, interface_one, interface_two in node.commonnets(network):
common_network.link(interface_one, interface_two)
class CoreEmu(object):
"""
@ -876,26 +902,3 @@ class CoreEmu(object):
logger.error("session to delete did not exist: %s", _id)
return result
def set_wireless_model(self, node, model):
"""
Convenience method for setting a wireless model.
:param node: node to set wireless model for
:param core.mobility.WirelessModel model: wireless model to set node to
:return: nothing
"""
values = list(model.getdefaultvalues())
node.setmodel(model, values)
def wireless_link_all(self, network, nodes):
"""
Link all nodes to the provided wireless network.
:param network: wireless network to link nodes to
:param nodes: nodes to link to wireless network
:return: nothing
"""
for node in nodes:
for common_network, interface_one, interface_two in node.commonnets(network):
common_network.link(interface_one, interface_two)

View file

View file

@ -13,29 +13,31 @@ import time
from core import logger
from core.api import coreapi
from core.coreserver import CoreServer
from core.data import ConfigData
from core.data import EventData
from core.enumerations import ConfigTlvs, LinkTypes
from core.enumerations import ConfigTlvs
from core.enumerations import EventTlvs
from core.enumerations import EventTypes
from core.enumerations import ExceptionTlvs
from core.enumerations import ExecuteTlvs
from core.enumerations import FileTlvs
from core.enumerations import LinkTlvs
from core.enumerations import LinkTypes
from core.enumerations import MessageFlags
from core.enumerations import MessageTypes
from core.enumerations import NodeTlvs
from core.enumerations import NodeTypes
from core.enumerations import RegisterTlvs
from core.enumerations import SessionTlvs
from core.future.futuredata import NodeOptions, LinkOptions, InterfaceData
from core.future.futuredata import InterfaceData
from core.future.futuredata import LinkOptions
from core.future.futuredata import NodeOptions
from core.misc import nodeutils
from core.misc import structutils
from core.misc import utils
class FutureHandler(SocketServer.BaseRequestHandler):
class CoreHandler(SocketServer.BaseRequestHandler):
"""
The SocketServer class uses the RequestHandler class for servicing requests.
"""
@ -578,7 +580,7 @@ class FutureHandler(SocketServer.BaseRequestHandler):
self.add_session_handlers()
# set initial session state
self.session.set_state(state=EventTypes.DEFINITION_STATE.value)
self.session.set_state(EventTypes.DEFINITION_STATE)
while True:
try:
@ -1032,13 +1034,13 @@ class FutureHandler(SocketServer.BaseRequestHandler):
session=message.get_tlv(EventTlvs.SESSION.value)
)
event_type = event_data.event_type
if event_type is None:
if event_data.event_type is None:
raise NotImplementedError("Event message missing event type")
event_type = EventTypes(event_data.event_type)
node_id = event_data.node
logger.info("EVENT %d: %s at %s", event_type, EventTypes(event_type).name, time.ctime())
if event_type <= EventTypes.SHUTDOWN_STATE.value:
logger.info("EVENT %s at %s", event_type.name, time.ctime())
if event_type.value <= EventTypes.SHUTDOWN_STATE.value:
if node_id is not None:
try:
node = self.session.get_object(node_id)
@ -1046,19 +1048,18 @@ class FutureHandler(SocketServer.BaseRequestHandler):
raise KeyError("Event message for unknown node %d" % node_id)
# configure mobility models for WLAN added during runtime
if event_type == EventTypes.INSTANTIATION_STATE.value and nodeutils.is_node(node,
NodeTypes.WIRELESS_LAN):
if event_type == EventTypes.INSTANTIATION_STATE and nodeutils.is_node(node, NodeTypes.WIRELESS_LAN):
self.session.start_mobility(node_ids=(node.objid,))
return ()
logger.warn("dropping unhandled Event message with node number")
return ()
self.session.set_state(state=event_type)
self.session.set_state(event_type)
if event_type == EventTypes.DEFINITION_STATE.value:
if event_type == EventTypes.DEFINITION_STATE:
# clear all session objects in order to receive new definitions
self.session.clear()
elif event_type == EventTypes.INSTANTIATION_STATE.value:
elif event_type == EventTypes.INSTANTIATION_STATE:
if len(self.handler_threads) > 1:
# TODO: sync handler threads here before continuing
time.sleep(2.0) # XXX
@ -1068,21 +1069,19 @@ class FutureHandler(SocketServer.BaseRequestHandler):
# after booting nodes attempt to send emulation id for nodes waiting on status
for obj in self.session.objects.itervalues():
self.send_node_emulation_id(obj.objid)
elif event_type == EventTypes.RUNTIME_STATE.value:
elif event_type == EventTypes.RUNTIME_STATE:
if self.session.master:
logger.warn("Unexpected event message: RUNTIME state received at session master")
else:
# master event queue is started in session.checkruntime()
self.session.start_events()
elif event_type == EventTypes.DATACOLLECT_STATE.value:
elif event_type == EventTypes.DATACOLLECT_STATE:
self.session.data_collect()
elif event_type == EventTypes.SHUTDOWN_STATE.value:
elif event_type == EventTypes.SHUTDOWN_STATE:
if self.session.master:
logger.warn("Unexpected event message: SHUTDOWN state received at session master")
elif event_type in (EventTypes.START.value, EventTypes.STOP.value,
EventTypes.RESTART.value,
EventTypes.PAUSE.value,
EventTypes.RECONFIGURE.value):
elif event_type in {EventTypes.START, EventTypes.STOP, EventTypes.RESTART, EventTypes.PAUSE,
EventTypes.RECONFIGURE}:
handled = False
name = event_data.name
if name:
@ -1095,17 +1094,16 @@ class FutureHandler(SocketServer.BaseRequestHandler):
self.session.mobility_event(event_data)
handled = True
if not handled:
logger.warn("Unhandled event message: event type %s (%s)",
event_type, coreapi.state_name(event_type))
elif event_type == EventTypes.FILE_OPEN.value:
logger.warn("Unhandled event message: event type %s ", event_type.name)
elif event_type == EventTypes.FILE_OPEN:
filename = event_data.name
self.session.open_xml(filename, start=False)
self.session.send_objects()
return ()
elif event_type == EventTypes.FILE_SAVE.value:
elif event_type == EventTypes.FILE_SAVE:
filename = event_data.name
self.session.save_xml(filename, self.session.config["xmlfilever"])
elif event_type == EventTypes.SCHEDULED.value:
elif event_type == EventTypes.SCHEDULED:
etime = event_data.time
node = event_data.node
name = event_data.name
@ -1118,7 +1116,7 @@ class FutureHandler(SocketServer.BaseRequestHandler):
else:
raise NotImplementedError
else:
logger.warn("Unhandled event message: event type %d", event_type)
logger.warn("Unhandled event message: event type %s", event_type)
return ()

View file

@ -1,5 +1,5 @@
"""
Defines server classes and request handlers for TCP and UDP.
Defines core server for handling TCP connections.
"""
import SocketServer
@ -7,7 +7,7 @@ import SocketServer
from core.future.coreemu import CoreEmu
class FutureServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
class CoreServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
"""
TCP server class, manages sessions and spawns request handlers for
incoming connections.
@ -28,13 +28,3 @@ class FutureServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
self.coreemu = CoreEmu(config)
self.config = config
SocketServer.TCPServer.__init__(self, server_address, handler_class)
def shutdown(self):
"""
Shutdown the server, all known sessions, and remove server from known servers set.
:return: nothing
"""
# shutdown all known sessions
for session in self.coreemu.sessions.itervalues():
session.shutdown()

View file

@ -300,27 +300,28 @@ class Session(object):
"""
Set the session's current state.
:param int state: state to set to
:param core.enumerations.EventTypes state: state to set to
:param send_event: if true, generate core API event messages
:return: nothing
"""
state_name = coreapi.state_name(state)
state_value = state.value
state_name = state.name
if self.state == state:
if self.state == state_value:
logger.info("session is already in state: %s, skipping change", state_name)
return
self.state = state
self.state = state_value
self._state_time = time.time()
logger.info("changing session %s to state %s(%s) at %s",
self.session_id, state, state_name, self._state_time)
self.session_id, state_value, state_name, self._state_time)
self.write_state(state)
self.run_hooks(state)
self.run_state_hooks(state)
self.write_state(state_value)
self.run_hooks(state_value)
self.run_state_hooks(state_value)
if send_event:
event_data = EventData(event_type=state, time="%s" % time.time())
event_data = EventData(event_type=state_value, time="%s" % time.time())
self.broadcast_event(event_data)
def write_state(self, state):
@ -868,7 +869,7 @@ class Session(object):
# start event loop and set to runtime
self.event_loop.run()
self.set_state(EventTypes.RUNTIME_STATE.value, send_event=True)
self.set_state(EventTypes.RUNTIME_STATE, send_event=True)
def data_collect(self):
"""
@ -908,7 +909,7 @@ class Session(object):
shutdown = False
if node_count == 0:
shutdown = True
self.set_state(state=EventTypes.SHUTDOWN_STATE.value)
self.set_state(EventTypes.SHUTDOWN_STATE)
return shutdown

View file

@ -250,7 +250,7 @@ class CoreDocumentParser0(object):
geo.append(a)
location.setrefgeo(geo[0], geo[1], geo[2])
scale = origin.getAttribute("scale100")
if scale is not None:
if scale is not None and scale:
location.refscale = float(scale)
point = xmlutils.get_one_element(origin, "point")
if point is not None and point.firstChild is not None:

View file

@ -20,7 +20,7 @@ def example(options):
session = coreemu.create_session()
# must be in configuration state for nodes to start, when using "node_add" below
session.set_state(EventTypes.CONFIGURATION_STATE.value)
session.set_state(EventTypes.CONFIGURATION_STATE)
# create emane network node
emane_network = session.create_emane_network(
@ -31,7 +31,7 @@ def example(options):
# create nodes
for i in xrange(options.nodes):
node = session.create_emane_node()
node = session.create_wireless_node()
node.setposition(x=150 * (i + 1), y=150)
interface = prefixes.create_interface(node)
session.add_link(node.objid, emane_network.objid, interface_one=interface)

View file

@ -22,7 +22,7 @@ def example(options):
session = coreemu.create_session()
# must be in configuration state for nodes to start, when using "node_add" below
session.set_state(EventTypes.CONFIGURATION_STATE.value)
session.set_state(EventTypes.CONFIGURATION_STATE)
# create switch network node
switch = session.add_node(_type=NodeTypes.SWITCH)

View file

@ -18,7 +18,7 @@ def example(nodes):
session = coreemu.create_session()
# must be in configuration state for nodes to start, when using "node_add" below
session.set_state(EventTypes.CONFIGURATION_STATE.value)
session.set_state(EventTypes.CONFIGURATION_STATE)
# create switch network node
switch = session.add_node(_type=NodeTypes.SWITCH)

View file

@ -23,11 +23,11 @@ def example(options):
session = coreemu.create_session()
# must be in configuration state for nodes to start, when using "node_add" below
session.set_state(EventTypes.CONFIGURATION_STATE.value)
session.set_state(EventTypes.CONFIGURATION_STATE)
# create wlan network node
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
coreemu.set_wireless_model(wlan, BasicRangeModel)
session.set_wireless_model(wlan, BasicRangeModel)
# create nodes
wireless_nodes = []
@ -38,7 +38,7 @@ def example(options):
wireless_nodes.append(node)
# link all created nodes with the wireless network
coreemu.wireless_link_all(wlan, wireless_nodes)
session.wireless_link_all(wlan, wireless_nodes)
# instantiate session
session.instantiate()

View file

@ -132,7 +132,7 @@ def main():
session.broker.dorecvloop = False
# Change to configuration state on both machines
session.set_state(EventTypes.CONFIGURATION_STATE.value)
session.set_state(EventTypes.CONFIGURATION_STATE)
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))

View file

@ -69,7 +69,7 @@ def main():
print "connecting to slave at %s:%d" % (slave, port)
session.broker.addserver(slave, slave, port)
session.broker.setupserver(slave)
session.set_state(EventTypes.CONFIGURATION_STATE.value)
session.set_state(EventTypes.CONFIGURATION_STATE)
tlvdata = coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.CONFIGURATION_STATE.value)
session.broker.handlerawmsg(coreapi.CoreEventMessage.pack(0, tlvdata))

40
daemon/scripts/core-daemon Executable file → Normal file
View file

@ -6,18 +6,16 @@ message handlers are defined and some support for sending messages.
"""
import ConfigParser
import atexit
import optparse
import signal
import sys
import time
from core import constants
from core import corehandlers
from core import coreserver
from core import enumerations
from core import logger
from core import services
from core.legacy.corehandler import CoreHandler
from core.legacy.coreserver import CoreServer
from core.misc import nodeutils
from core.misc.utils import close_onexec
from core.service import ServiceManager
@ -45,7 +43,7 @@ def cored(cfg=None):
host = "localhost"
try:
server = coreserver.CoreServer((host, port), corehandlers.CoreRequestHandler, cfg)
server = CoreServer((host, port), CoreHandler, cfg)
except:
logger.exception("error starting main server on: %s:%s", host, port)
sys.exit(1)
@ -55,38 +53,6 @@ def cored(cfg=None):
server.serve_forever()
# TODO: should sessions and the main core daemon both catch exit to shutdown independently?
def cleanup():
"""
Runs server shutdown and cleanup when catching an exit signal.
:return: nothing
"""
while coreserver.CoreServer.servers:
server = coreserver.CoreServer.servers.pop()
server.shutdown()
def sighandler(signum, stackframe):
"""
Signal handler when different signals are sent.
:param int signum: singal number sent
:param stackframe: stack frame sent
:return: nothing
"""
logger.error("terminated by signal: %s", signum)
sys.exit(signum)
signal.signal(signal.SIGHUP, sighandler)
signal.signal(signal.SIGINT, sighandler)
signal.signal(signal.SIGTERM, sighandler)
signal.signal(signal.SIGUSR1, sighandler)
signal.signal(signal.SIGUSR2, sighandler)
atexit.register(cleanup)
def get_merged_config(filename):
"""
Return a configuration after merging config file and command-line arguments.

View file

@ -1,145 +0,0 @@
#!/usr/bin/env python
"""
core-daemon: the CORE daemon is a server process that receives CORE API
messages and instantiates emulated nodes and networks within the kernel. Various
message handlers are defined and some support for sending messages.
"""
import ConfigParser
import optparse
import sys
import time
from core import constants
from core import enumerations
from core import logger
from core import services
from core.future.futurehandler import FutureHandler
from core.future.futureserver import FutureServer
from core.misc import nodeutils
from core.misc.utils import close_onexec
from core.service import ServiceManager
def banner():
"""
Output the program banner printed to the terminal or log file.
:return: nothing
"""
logger.info("CORE daemon v.%s started %s", constants.COREDPY_VERSION, time.ctime())
def cored(cfg=None):
"""
Start the CoreServer object and enter the server loop.
:param dict cfg: core configuration
:return: nothing
"""
host = cfg["listenaddr"]
port = int(cfg["port"])
if host == "" or host is None:
host = "localhost"
try:
server = FutureServer((host, port), FutureHandler, cfg)
except:
logger.exception("error starting main server on: %s:%s", host, port)
sys.exit(1)
close_onexec(server.fileno())
logger.info("main server started, listening on: %s:%s", host, port)
server.serve_forever()
def get_merged_config(filename):
"""
Return a configuration after merging config file and command-line arguments.
:param str filename: file name to merge configuration settings with
:return: merged configuration
:rtype: dict
"""
# these are the defaults used in the config file
defaults = {
"port": "%d" % enumerations.CORE_API_PORT,
"listenaddr": "localhost",
"xmlfilever": "1.0",
"numthreads": "1",
}
usagestr = "usage: %prog [-h] [options] [args]\n\n" + \
"CORE daemon v.%s instantiates Linux network namespace " \
"nodes." % constants.COREDPY_VERSION
parser = optparse.OptionParser(usage=usagestr)
parser.add_option("-f", "--configfile", dest="configfile", type="string",
help="read config from specified file; default = %s" % filename)
parser.add_option("-p", "--port", dest="port", type=int,
help="port number to listen on; default = %s" % defaults["port"])
parser.add_option("-t", "--numthreads", dest="numthreads", type=int,
help="number of server threads; default = %s" % defaults["numthreads"])
# parse command line options
options, args = parser.parse_args()
# read the config file
if options.configfile is not None:
filename = options.configfile
del options.configfile
cfg = ConfigParser.SafeConfigParser(defaults)
cfg.read(filename)
section = "core-daemon"
if not cfg.has_section(section):
cfg.add_section(section)
# merge command line with config file
for opt in options.__dict__:
val = options.__dict__[opt]
if val is not None:
cfg.set(section, opt, val.__str__())
return dict(cfg.items(section)), args
def main():
"""
Main program startup.
:return: nothing
"""
# get a configuration merged from config file and command-line arguments
cfg, args = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
for a in args:
logger.error("ignoring command line argument: %s", a)
# attempt load custom services
service_paths = cfg.get("custom_services_dir")
logger.debug("custom service paths: %s", service_paths)
if service_paths:
for service_path in service_paths.split(','):
service_path = service_path.strip()
ServiceManager.add_services(service_path)
banner()
try:
cored(cfg)
except KeyboardInterrupt:
logger.info("keyboard interrupt, stopping core daemon")
sys.exit(0)
if __name__ == "__main__":
# configure nodes to use
if len(sys.argv) == 2 and sys.argv[1] == "ovs":
from core.netns.openvswitch import OVS_NODES
nodeutils.update_node_map(OVS_NODES)
# load default services
services.load()
main()

View file

@ -13,8 +13,6 @@ from core.api.coreapi import CoreEventMessage
from core.api.coreapi import CoreExecMessage
from core.api.coreapi import CoreLinkMessage
from core.api.coreapi import CoreNodeMessage
from core.corehandlers import CoreRequestHandler
from core.coreserver import CoreServer
from core.enumerations import CORE_API_PORT
from core.enumerations import ConfigTlvs
from core.enumerations import EventTlvs
@ -25,10 +23,12 @@ from core.enumerations import LinkTypes
from core.enumerations import MessageFlags
from core.enumerations import NodeTlvs
from core.enumerations import NodeTypes
from core.future.coreemu import CoreEmu
from core.future.futuredata import IpPrefixes
from core.legacy.corehandler import CoreHandler
from core.legacy.coreserver import CoreServer
from core.misc import ipaddress
from core.misc.ipaddress import MacAddress
from core.netns import nodes
from core.session import Session
EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward"
@ -133,110 +133,12 @@ def state_message(state):
])
class Core(object):
def __init__(self, session, ip_prefix):
self.session = session
self.ip_prefix = ip_prefix
self.current_ip = 1
self.nodes = {}
self.node_ips = {}
def create_node(self, name, cls=nodes.CoreNode, objid=None, position=None, services=None, model="host"):
node = self.session.add_object(cls=cls, name=name, objid=objid)
node.type = model
if position:
node.setposition(*position)
if services:
self.session.services.addservicestonode(node, model, services)
self.nodes[name] = node
def add_interface(self, network, name):
node_ip = self.ip_prefix.addr(self.current_ip)
self.current_ip += 1
self.node_ips[name] = node_ip
node = self.nodes[name]
interface_id = node.newnetif(network, ["%s/%s" % (node_ip, self.ip_prefix.prefixlen)])
return node.netif(interface_id)
def get_node(self, name):
"""
Retrieve node from current session.
:param str name: name of node to retrieve
:return: core node
:rtype: core.netns.nodes.CoreNode
"""
return self.nodes[name]
def get_ip(self, name):
return self.node_ips[name]
def link(self, network, from_interface, to_interface):
network.link(from_interface, to_interface)
def configure_link(self, network, interface_one, interface_two, values, unidirectional=False):
network.linkconfig(netif=interface_one, netif2=interface_two, **values)
if not unidirectional:
network.linkconfig(netif=interface_two, netif2=interface_one, **values)
def ping(self, from_name, to_name):
from_node = self.nodes[from_name]
to_ip = str(self.get_ip(to_name))
return from_node.cmd(["ping", "-c", "3", to_ip])
def ping_output(self, from_name, to_name):
from_node = self.nodes[from_name]
to_ip = str(self.get_ip(to_name))
output = from_node.check_cmd(["ping", "-i", "0.05", "-c", "3", to_ip])
return output
def iperf(self, from_name, to_name):
from_node = self.nodes[from_name]
to_node = self.nodes[to_name]
to_ip = str(self.get_ip(to_name))
# run iperf server, run client, kill iperf server
vcmd, stdin, stdout, stderr = to_node.client.popen(["iperf", "-s", "-u", "-y", "C"])
from_node.cmd(["iperf", "-u", "-t", "5", "-c", to_ip])
to_node.cmd(["killall", "-9", "iperf"])
return stdout.read().strip()
def assert_nodes(self):
for node in self.nodes.itervalues():
assert os.path.exists(node.nodedir)
def create_link_network(self):
# create switch
ptp_node = self.session.add_object(cls=nodes.PtpNet)
# create nodes
self.create_node("n1")
self.create_node("n2")
# add interfaces
interface_one = self.add_interface(ptp_node, "n1")
interface_two = self.add_interface(ptp_node, "n2")
# instantiate session
self.session.instantiate()
# assert node directories created
self.assert_nodes()
return ptp_node, interface_one, interface_two
def set_emane_model(self, emane_node, emane_model):
# set the emane model
values = emane_model.getdefaultvalues()
self.session.emane.setconfig(emane_node.objid, emane_model.name, values)
class CoreServerTest(object):
def __init__(self):
address = ("localhost", CORE_API_PORT)
self.server = CoreServer(address, CoreRequestHandler, {
def __init__(self, port=CORE_API_PORT):
self.host = "localhost"
self.port = port
address = (self.host, self.port)
self.server = CoreServer(address, CoreHandler, {
"numthreads": 1,
"daemonize": False,
})
@ -246,29 +148,29 @@ class CoreServerTest(object):
self.session = None
self.request_handler = None
def setup(self, distributed_address):
def setup(self, distributed_address, port):
# validate address
assert distributed_address, "distributed server address was not provided"
# create session
self.session = self.server.create_session(1)
self.session = self.server.coreemu.create_session(1)
self.session.master = True
# create request handler
request_mock = MagicMock()
request_mock.fileno = MagicMock(return_value=1)
self.request_handler = CoreRequestHandler(request_mock, "", self.server)
self.request_handler = CoreHandler(request_mock, "", self.server)
self.request_handler.session = self.session
self.request_handler.add_session_handlers()
self.session.broker.session_clients.append(self.request_handler)
# have broker handle a configuration state change
self.session.set_state(state=EventTypes.DEFINITION_STATE.value)
self.session.set_state(EventTypes.DEFINITION_STATE)
message = state_message(EventTypes.CONFIGURATION_STATE)
self.request_handler.handle_message(message)
# add broker server for distributed core
distributed = "%s:%s:%s" % (self.distributed_server, distributed_address, CORE_API_PORT)
distributed = "%s:%s:%s" % (self.distributed_server, distributed_address, port)
message = CoreConfMessage.create(0, [
(ConfigTlvs.OBJECT, "broker"),
(ConfigTlvs.TYPE, 0),
@ -301,38 +203,24 @@ class CoreServerTest(object):
self.server.server_close()
@pytest.fixture()
@pytest.fixture
def session():
# load default services
services.load()
# create and return session
session_fixture = Session(1, persistent=True)
session_fixture.master = True
# use coreemu and create a session
coreemu = CoreEmu()
session_fixture = coreemu.create_session()
session_fixture.set_state(EventTypes.CONFIGURATION_STATE)
assert os.path.exists(session_fixture.session_dir)
# set location
# session_fixture.master = True
session_fixture.location.setrefgeo(47.57917, -122.13232, 2.00000)
session_fixture.location.refscale = 150.0
# return session fixture
# return created session
yield session_fixture
# cleanup
print "shutting down session"
session_fixture.shutdown()
assert not os.path.exists(session_fixture.session_dir)
# shutdown coreemu
coreemu.shutdown()
@pytest.fixture(scope="module")
def ip_prefix():
return ipaddress.Ipv4Prefix("10.83.0.0/16")
@pytest.fixture()
def core(session, ip_prefix):
return Core(session, ip_prefix)
def ip_prefixes():
return IpPrefixes(ip4_prefix="10.83.0.0/16")
@pytest.fixture()
@ -348,6 +236,11 @@ def cored():
server.shutdown()
def ping(from_node, to_node, ip_prefixes, count=3):
address = ip_prefixes.ip4_address(to_node)
return from_node.cmd(["ping", "-c", str(count), address])
def pytest_addoption(parser):
parser.addoption("--distributed", help="distributed server address")

View file

@ -11,22 +11,22 @@ from xml.etree import ElementTree
import pytest
from mock import MagicMock
from conftest import EMANE_SERVICES
from core.data import ConfigData
from core.enumerations import MessageFlags
from core.enumerations import MessageFlags, NodeTypes
from core.future.futuredata import NodeOptions
from core.mobility import BasicRangeModel
from core.netns import nodes
from core.netns import vnodeclient
from core.netns.vnodeclient import VnodeClient
from core.phys.pnodes import PhysicalNode
from core.service import ServiceManager
from core.xml import xmlsession
_PATH = os.path.abspath(os.path.dirname(__file__))
_SERVICES_PATH = os.path.join(_PATH, "myservices")
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
_XML_VERSIONS = ["0.0", "1.0"]
_NODE_CLASSES = [nodes.PtpNet, nodes.HubNode, nodes.SwitchNode]
_WIRED = [
NodeTypes.PEER_TO_PEER,
NodeTypes.HUB,
NodeTypes.SWITCH
]
def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None):
@ -47,8 +47,13 @@ def createclients(sessiondir, clientcls=VnodeClient, cmdchnlfilterfunc=None):
return map(lambda x: clientcls(os.path.basename(x), x), cmdchnls)
def ping(from_node, to_node, ip_prefixes):
address = ip_prefixes.ip4_address(to_node)
return from_node.cmd(["ping", "-c", "3", address])
class TestCore:
def test_import_service(self, core):
def test_import_service(self):
"""
Test importing a custom service.
@ -58,118 +63,115 @@ class TestCore:
assert ServiceManager.get("MyService")
assert ServiceManager.get("MyService2")
@pytest.mark.parametrize("cls", _NODE_CLASSES)
def test_nodes(self, core, cls):
@pytest.mark.parametrize("net_type", _WIRED)
def test_wired_ping(self, session, net_type, ip_prefixes):
"""
Test ptp node network.
:param conftest.Core core: core fixture to test with
:param cls: node classes that work within a simple network
:param session: session for test
:param core.enumerations.NodeTypes net_type: type of net node to create
:param ip_prefixes: generates ip addresses for nodes
"""
# create ptp
network_node = core.session.add_object(cls=cls)
# create net node
net_node = session.add_node(_type=net_type)
# create nodes
core.create_node("n1")
core.create_node("n2")
node_one = session.add_node()
node_two = session.add_node()
# add interfaces
core.add_interface(network_node, "n1")
core.add_interface(network_node, "n2")
# link nodes to net node
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, net_node.objid, interface_one=interface)
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
session.instantiate()
# ping n2 from n1 and assert success
status = core.ping("n1", "n2")
status = ping(node_one, node_two, ip_prefixes)
assert not status
@pytest.mark.parametrize("version", _XML_VERSIONS)
def test_xml(self, core, tmpdir, version):
def test_xml(self, session, tmpdir, version, ip_prefixes):
"""
Test xml client methods.
:param conftest.Core core: core fixture to test with
:param session: session for test
:param tmpdir: tmpdir to create data in
:param str version: xml version to write and parse
:param ip_prefixes: generates ip addresses for nodes
"""
# create ptp
ptp_node = core.session.add_object(cls=nodes.PtpNet)
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
# create nodes
core.create_node("n1")
core.create_node("n2")
node_one = session.add_node()
node_two = session.add_node()
# add interfaces
core.add_interface(ptp_node, "n1")
core.add_interface(ptp_node, "n2")
# link nodes to ptp net
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
session.instantiate()
# get ids for nodes
n1_id = core.get_node("n1").objid
n2_id = core.get_node("n2").objid
n1_id = node_one.objid
n2_id = node_two.objid
# save xml
xml_file = tmpdir.join("session.xml")
file_path = xml_file.strpath
xmlsession.save_session_xml(core.session, file_path, version)
session.save_xml(file_path, version)
# verify xml file was created and can be parsed
assert xml_file.isfile()
assert ElementTree.parse(file_path)
# stop current session, clearing data
core.session.shutdown()
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
assert not core.session.get_object_by_name(n1_id)
assert not session.get_object(n1_id)
with pytest.raises(KeyError):
assert not core.session.get_object(n2_id)
assert not session.get_object(n2_id)
# load saved xml
xmlsession.open_session_xml(core.session, file_path, start=True)
session.open_xml(file_path, start=True)
# verify nodes have been recreated
assert core.session.get_object(n1_id)
assert core.session.get_object(n2_id)
assert session.get_object(n1_id)
assert session.get_object(n2_id)
def test_vnode_client(self, core):
def test_vnode_client(self, session, ip_prefixes):
"""
Test vnode client methods.
:param conftest.Core core: core fixture to test with
:param session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create ptp
ptp_node = core.session.add_object(cls=nodes.PtpNet)
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
# create nodes
core.create_node("n1")
core.create_node("n2")
node_one = session.add_node()
node_two = session.add_node()
# add interfaces
core.add_interface(ptp_node, "n1")
core.add_interface(ptp_node, "n2")
# link nodes to ptp net
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
# get node client for testing
n1 = core.get_node("n1")
client = n1.client
client = node_one.client
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
session.instantiate()
# check we are connected
assert client.connected()
@ -195,183 +197,154 @@ class TestCore:
assert not client.shcmd(command[0])
# check module methods
assert createclients(core.session.session_dir)
assert createclients(session.session_dir)
# check convenience methods for interface information
assert client.getaddr("eth0")
assert client.netifstats()
def test_netif(self, core):
def test_netif(self, session, ip_prefixes):
"""
Test netif methods.
:param conftest.Core core: core fixture to test with
:param session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create ptp
ptp_node = core.session.add_object(cls=nodes.PtpNet)
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
# create nodes
core.create_node("n1")
core.create_node("n2")
node_one = session.add_node()
node_two = session.add_node()
# add interfaces
n1_interface = core.add_interface(ptp_node, "n1")
n2_interface = core.add_interface(ptp_node, "n2")
# get nodes
n1 = core.get_node("n1")
n2 = core.get_node("n2")
# link nodes to ptp net
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
session.instantiate()
# check link data gets generated
assert ptp_node.all_link_data(MessageFlags.ADD.value)
# check common nets exist between linked nodes
assert n1.commonnets(n2)
assert n2.commonnets(n1)
assert node_one.commonnets(node_two)
assert node_two.commonnets(node_one)
# check we can retrieve netif index
assert n1.getifindex(n1_interface) == 0
assert n2.getifindex(n2_interface) == 0
assert node_one.getifindex(0)
assert node_two.getifindex(0)
# check interface parameters
n1_interface.setparam("test", 1)
assert n1_interface.getparam("test") == 1
assert n1_interface.getparams()
interface = node_one.netif(0)
interface.setparam("test", 1)
assert interface.getparam("test") == 1
assert interface.getparams()
# delete netif and test that if no longer exists
n1.delnetif(0)
assert not n1.netif(0)
node_one.delnetif(0)
assert not node_one.netif(0)
def test_physical(self, core):
"""
Test physical node network.
:param conftest.Core core: core fixture to test with
"""
# create switch node
switch_node = core.session.add_object(cls=nodes.SwitchNode)
# create a physical node
core.create_node(cls=PhysicalNode, name="p1")
# mock method that will not work
physical_node = core.get_node("p1")
physical_node.newnetif = MagicMock(return_value=0)
# create regular node
core.create_node("n1")
# add interface
core.add_interface(switch_node, "n1")
core.add_interface(switch_node, "p1")
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
def test_wlan_basic_range_good(self, core):
def test_wlan_good(self, session, ip_prefixes):
"""
Test basic wlan network.
:param conftest.Core core: core fixture to test with
:param core.future.coreemu.FutureSession session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create wlan
wlan_node = core.session.add_object(cls=nodes.WlanNode)
values = BasicRangeModel.getdefaultvalues()
wlan_node.setmodel(BasicRangeModel, values)
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
session.set_wireless_model(wlan_node, BasicRangeModel)
# create nodes
core.create_node("n1", position=(0, 0), services=EMANE_SERVICES, model="mdr")
core.create_node("n2", position=(0, 0), services=EMANE_SERVICES, model="mdr")
node_options = NodeOptions()
node_options.set_position(0, 0)
node_one = session.create_wireless_node(node_options=node_options)
node_two = session.create_wireless_node(node_options=node_options)
# add interfaces
interface_one = core.add_interface(wlan_node, "n1")
interface_two = core.add_interface(wlan_node, "n2")
# link nodes
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, wlan_node.objid, interface_one=interface)
# link nodes in wlan
core.link(wlan_node, interface_one, interface_two)
session.wireless_link_all(wlan_node, [node_one, node_two])
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
session.instantiate()
# ping n2 from n1 and assert success
status = core.ping("n1", "n2")
status = ping(node_one, node_two, ip_prefixes)
assert not status
def test_wlan_basic_range_bad(self, core):
def test_wlan_bad(self, session, ip_prefixes):
"""
Test basic wlan network with leveraging basic range model.
:param conftest.Core core: core fixture to test with
:param core.future.coreemu.FutureSession session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create wlan
wlan_node = core.session.add_object(cls=nodes.WlanNode)
values = BasicRangeModel.getdefaultvalues()
wlan_node.setmodel(BasicRangeModel, values)
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
session.set_wireless_model(wlan_node, BasicRangeModel)
# create nodes
core.create_node("n1", position=(0, 0), services=EMANE_SERVICES, model="mdr")
core.create_node("n2", position=(0, 0), services=EMANE_SERVICES, model="mdr")
node_options = NodeOptions()
node_options.set_position(0, 0)
node_one = session.create_wireless_node(node_options=node_options)
node_two = session.create_wireless_node(node_options=node_options)
# add interfaces
interface_one = core.add_interface(wlan_node, "n1")
interface_two = core.add_interface(wlan_node, "n2")
# link nodes
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, wlan_node.objid, interface_one=interface)
# link nodes in wlan
core.link(wlan_node, interface_one, interface_two)
# move nodes out of range, default range check is 275
core.get_node("n1").setposition(0, 0)
core.get_node("n2").setposition(500, 500)
session.wireless_link_all(wlan_node, [node_one, node_two])
# instantiate session
core.session.instantiate()
session.instantiate()
# assert node directories created
core.assert_nodes()
# move node two out of range, default range check is 275
time.sleep(5)
update_options = NodeOptions()
update_options.set_position(500, 500)
session.update_node(node_two.objid, update_options)
# ping n2 from n1 and assert failure )
time.sleep(3)
status = core.ping("n1", "n2")
# ping n2 from n1 and assert failure
time.sleep(5)
status = ping(node_one, node_two, ip_prefixes)
assert status
def test_mobility(self, core):
def test_mobility(self, session, ip_prefixes):
"""
Test basic wlan network.
:param conftest.Core core: core fixture to test with
:param core.future.coreemu.FutureSession session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create wlan
wlan_node = core.session.add_object(cls=nodes.WlanNode)
values = BasicRangeModel.getdefaultvalues()
wlan_node.setmodel(BasicRangeModel, values)
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
session.set_wireless_model(wlan_node, BasicRangeModel)
# create nodes
core.create_node("n1", objid=1, position=(0, 0), services=EMANE_SERVICES, model="mdr")
core.create_node("n2", objid=2, position=(0, 0), services=EMANE_SERVICES, model="mdr")
node_options = NodeOptions()
node_options.set_position(0, 0)
node_one = session.create_wireless_node(node_options=node_options)
node_two = session.create_wireless_node(node_options=node_options)
# add interfaces
interface_one = core.add_interface(wlan_node, "n1")
interface_two = core.add_interface(wlan_node, "n2")
# link nodes
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, wlan_node.objid, interface_one=interface)
# link nodes in wlan
core.link(wlan_node, interface_one, interface_two)
session.wireless_link_all(wlan_node, [node_one, node_two])
# configure mobility script for session
config = ConfigData(
@ -382,7 +355,7 @@ class TestCore:
data_values="file=%s|refresh_ms=50|loop=1|autostart=0.0|"
"map=|script_start=|script_pause=|script_stop=" % _MOBILITY_FILE
)
core.session.config_object(config)
session.config_object(config)
# add handler for receiving node updates
event = threading.Event()
@ -390,138 +363,10 @@ class TestCore:
def node_update(_):
event.set()
core.session.node_handlers.append(node_update)
session.node_handlers.append(node_update)
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
session.instantiate()
# validate we receive a node message for updating its location
assert event.wait(5)
def test_link_bandwidth(self, core):
"""
Test ptp node network with modifying link bandwidth.
:param conftest.Core core: core fixture to test with
"""
# create link network
ptp_node, interface_one, interface_two = core.create_link_network()
# output csv index
bandwidth_index = 8
# run iperf, validate normal bandwidth
stdout = core.iperf("n1", "n2")
assert stdout
value = int(stdout.split(',')[bandwidth_index])
assert 900000 <= value <= 1100000
# change bandwidth in bits per second
bandwidth = 500000
core.configure_link(ptp_node, interface_one, interface_two, {
"bw": bandwidth
})
# run iperf again
stdout = core.iperf("n1", "n2")
assert stdout
value = int(stdout.split(',')[bandwidth_index])
assert 400000 <= value <= 600000
def test_link_loss(self, core):
"""
Test ptp node network with modifying link packet loss.
:param conftest.Core core: core fixture to test with
"""
# create link network
ptp_node, interface_one, interface_two = core.create_link_network()
# output csv index
loss_index = -2
# run iperf, validate normal bandwidth
stdout = core.iperf("n1", "n2")
assert stdout
value = float(stdout.split(',')[loss_index])
assert 0 <= value <= 0.5
# change bandwidth in bits per second
loss = 50
core.configure_link(ptp_node, interface_one, interface_two, {
"loss": loss
})
# run iperf again
stdout = core.iperf("n1", "n2")
assert stdout
value = float(stdout.split(',')[loss_index])
assert 40 <= value <= 60
def test_link_delay(self, core):
"""
Test ptp node network with modifying link packet delay.
:param conftest.Core core: core fixture to test with
"""
# create link network
ptp_node, interface_one, interface_two = core.create_link_network()
# run ping for delay information
stdout = core.ping_output("n1", "n2")
assert stdout
rtt_line = stdout.split("\n")[-1]
rtt_values = rtt_line.split("=")[1].split("ms")[0].strip()
rtt_avg = float(rtt_values.split("/")[2])
assert 0 <= rtt_avg <= 0.2
# change delay in microseconds
delay = 1000000
core.configure_link(ptp_node, interface_one, interface_two, {
"delay": delay
})
# run ping for delay information again
stdout = core.ping_output("n1", "n2")
assert stdout
rtt_line = stdout.split("\n")[-1]
rtt_values = rtt_line.split("=")[1].split("ms")[0].strip()
rtt_avg = float(rtt_values.split("/")[2])
assert 1800 <= rtt_avg <= 2200
def test_link_jitter(self, core):
"""
Test ptp node network with modifying link packet jitter.
:param conftest.Core core: core fixture to test with
"""
# create link network
ptp_node, interface_one, interface_two = core.create_link_network()
# output csv index
jitter_index = 9
# run iperf
stdout = core.iperf("n1", "n2")
assert stdout
value = float(stdout.split(",")[jitter_index])
assert -0.5 <= value <= 0.05
# change jitter in microseconds
jitter = 1000000
core.configure_link(ptp_node, interface_one, interface_two, {
"jitter": jitter
})
# run iperf again
stdout = core.iperf("n1", "n2")
assert stdout
value = float(stdout.split(",")[jitter_index])
assert 200 <= value <= 500

View file

@ -4,16 +4,13 @@ Unit tests for testing CORE EMANE networks.
import pytest
from conftest import EMANE_SERVICES
from core.data import ConfigData
from conftest import ping
from core.emane.bypass import EmaneBypassModel
from core.emane.commeffect import EmaneCommEffectModel
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.future.futuredata import NodeOptions
_EMANE_MODELS = [
EmaneIeee80211abgModel,
@ -26,36 +23,37 @@ _EMANE_MODELS = [
class TestEmane:
@pytest.mark.parametrize("model", _EMANE_MODELS)
def test_models(self, core, model):
def test_models(self, session, model, ip_prefixes):
"""
Test emane models within a basic network.
:param conftest.Core core: core fixture to test with
:param core.future.coreemu.FutureSession session: session for test
:param model: emane model to test
:param func setup: setup function to configure emane node
:param ip_prefixes: generates ip addresses for nodes
"""
# create emane node for networking the core nodes
emane_node = core.session.add_object(name="emane", cls=EmaneNode)
emane_node.setposition(x=80, y=50)
# set the emane model
core.set_emane_model(emane_node, model)
emane_network = session.create_emane_network(
model,
geo_reference=(47.57917, -122.13232, 2.00000)
)
emane_network.setposition(x=80, y=50)
# create nodes
core.create_node("n1", objid=1, position=(150, 150), services=EMANE_SERVICES, model="mdr")
core.create_node("n2", objid=2, position=(300, 150), services=EMANE_SERVICES, model="mdr")
node_options = NodeOptions()
node_options.set_position(150, 150)
node_one = session.create_wireless_node(node_options=node_options)
node_options.set_position(300, 150)
node_two = session.create_wireless_node(node_options=node_options)
# add interfaces to nodes
core.add_interface(emane_node, "n1")
core.add_interface(emane_node, "n2")
for i, node in enumerate([node_one, node_two]):
node.setposition(x=150 * (i + 1), y=150)
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, emane_network.objid, interface_one=interface)
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
session.instantiate()
# ping n2 from n1 and assert success
status = core.ping("n1", "n2")
status = ping(node_one, node_two, ip_prefixes, count=5)
assert not status

View file

@ -1,201 +0,0 @@
import os
import time
import pytest
from core.enumerations import NodeTypes, EventTypes
from core.future.coreemu import CoreEmu
from core.future.futuredata import IpPrefixes, NodeOptions, LinkOptions
from core.misc import utils
@pytest.fixture
def future_session():
# use coreemu and create a session
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE.value)
# return created session
yield session
# shutdown coreemu
coreemu.shutdown()
IP4_PREFIX = "10.83.0.0/16"
MODELS = [
"router",
"host",
"PC",
"mdr",
]
NET_TYPES = [
NodeTypes.SWITCH,
NodeTypes.HUB,
NodeTypes.WIRELESS_LAN
]
class TestFuture:
@pytest.mark.parametrize("model", MODELS)
def test_node_add(self, future_session, model):
# given
node_options = NodeOptions(model=model)
# when
node = future_session.add_node(node_options=node_options)
# give time for node services to boot
time.sleep(1)
# then
assert node
assert os.path.exists(node.nodedir)
assert node.alive()
assert node.up
assert node.check_cmd(["ip", "addr", "show", "lo"])
node.validate()
def test_node_update(self, future_session):
# given
node = future_session.add_node()
position_value = 100
update_options = NodeOptions()
update_options.set_position(x=position_value, y=position_value)
# when
future_session.update_node(node.objid, update_options)
# then
assert node.position.x == position_value
assert node.position.y == position_value
def test_node_delete(self, future_session):
# given
node = future_session.add_node()
# when
future_session.delete_node(node.objid)
# then
with pytest.raises(KeyError):
future_session.get_object(node.objid)
@pytest.mark.parametrize("net_type", NET_TYPES)
def test_net(self, future_session, net_type):
# given
# when
node = future_session.add_node(_type=net_type)
# then
assert node
assert node.up
assert utils.check_cmd(["brctl", "show", node.brname])
def test_ptp(self, future_session):
# given
prefixes = IpPrefixes(ip4_prefix=IP4_PREFIX)
node_one = future_session.add_node()
node_two = future_session.add_node()
interface_one = prefixes.create_interface(node_one)
inteface_two = prefixes.create_interface(node_two)
# when
future_session.add_link(node_one.objid, node_two.objid, interface_one, inteface_two)
# then
assert node_one.netif(interface_one.id)
assert node_two.netif(inteface_two.id)
def test_node_to_net(self, future_session):
# given
prefixes = IpPrefixes(ip4_prefix=IP4_PREFIX)
node_one = future_session.add_node()
node_two = future_session.add_node(_type=NodeTypes.SWITCH)
interface_one = prefixes.create_interface(node_one)
# when
future_session.add_link(node_one.objid, node_two.objid, interface_one)
# then
assert node_two.all_link_data(0)
assert node_one.netif(interface_one.id)
def test_net_to_node(self, future_session):
# given
prefixes = IpPrefixes(ip4_prefix=IP4_PREFIX)
node_one = future_session.add_node(_type=NodeTypes.SWITCH)
node_two = future_session.add_node()
interface_two = prefixes.create_interface(node_two)
# when
future_session.add_link(node_one.objid, node_two.objid, interface_two=interface_two)
# then
assert node_one.all_link_data(0)
assert node_two.netif(interface_two.id)
def test_net_to_net(self, future_session):
# given
node_one = future_session.add_node(_type=NodeTypes.SWITCH)
node_two = future_session.add_node(_type=NodeTypes.SWITCH)
# when
future_session.add_link(node_one.objid, node_two.objid)
# then
assert node_one.all_link_data(0)
def test_link_update(self, future_session):
# given
prefixes = IpPrefixes(ip4_prefix=IP4_PREFIX)
node_one = future_session.add_node()
node_two = future_session.add_node(_type=NodeTypes.SWITCH)
interface_one = prefixes.create_interface(node_one)
future_session.add_link(node_one.objid, node_two.objid, interface_one)
interface = node_one.netif(interface_one.id)
output = utils.check_cmd(["tc", "qdisc", "show", "dev", interface.localname])
assert "delay" not in output
assert "rate" not in output
assert "loss" not in output
assert "duplicate" not in output
# when
link_options = LinkOptions()
link_options.delay = 50
link_options.bandwidth = 5000000
link_options.per = 25
link_options.dup = 25
future_session.update_link(node_one.objid, node_two.objid,
interface_one_id=interface_one.id, link_options=link_options)
# then
output = utils.check_cmd(["tc", "qdisc", "show", "dev", interface.localname])
assert "delay" in output
assert "rate" in output
assert "loss" in output
assert "duplicate" in output
def test_link_delete(self, future_session):
# given
prefixes = IpPrefixes(ip4_prefix=IP4_PREFIX)
node_one = future_session.add_node()
node_two = future_session.add_node()
interface_one = prefixes.create_interface(node_one)
interface_two = prefixes.create_interface(node_two)
future_session.add_link(node_one.objid, node_two.objid, interface_one, interface_two)
assert node_one.netif(interface_one.id)
assert node_two.netif(interface_two.id)
assert future_session.get_node_count() == 3
# when
future_session.delete_link(node_one.objid, node_two.objid, interface_one.id, interface_two.id)
# then
assert not node_one.netif(interface_one.id)
assert not node_two.netif(interface_two.id)
assert future_session.get_node_count() == 2

View file

@ -128,7 +128,7 @@ class TestGui:
core.session.broker.dorecvloop = False
# have broker handle a configuration state change
core.session.set_state(EventTypes.CONFIGURATION_STATE.value)
core.session.set_state(EventTypes.CONFIGURATION_STATE)
event_message = state_message(EventTypes.CONFIGURATION_STATE)
core.session.broker.handlerawmsg(event_message)

260
daemon/tests/test_links.py Normal file
View file

@ -0,0 +1,260 @@
from core.enumerations import NodeTypes
from core.future.futuredata import LinkOptions
from core.misc import utils
def create_ptp_network(session, ip_prefixes):
# create nodes
node_one = session.add_node()
node_two = session.add_node()
# link nodes to net node
interface_one = ip_prefixes.create_interface(node_one)
interface_two = ip_prefixes.create_interface(node_two)
session.add_link(node_one.objid, node_two.objid, interface_one, interface_two)
# instantiate session
session.instantiate()
return node_one, node_two
def ping_output(from_node, to_node, ip_prefixes):
address = ip_prefixes.ip4_address(to_node)
output = from_node.check_cmd(["ping", "-i", "0.05", "-c", "3", address])
return output
def iperf(from_node, to_node, ip_prefixes):
# run iperf server, run client, kill iperf server
address = ip_prefixes.ip4_address(to_node)
vcmd, stdin, stdout, stderr = to_node.client.popen(["iperf", "-s", "-u", "-y", "C"])
from_node.cmd(["iperf", "-u", "-t", "5", "-c", address])
to_node.cmd(["killall", "-9", "iperf"])
return stdout.read().strip()
class TestLinks:
def test_ptp(self, session, ip_prefixes):
# given
node_one = session.add_node()
node_two = session.add_node()
interface_one = ip_prefixes.create_interface(node_one)
inteface_two = ip_prefixes.create_interface(node_two)
# when
session.add_link(node_one.objid, node_two.objid, interface_one, inteface_two)
# then
assert node_one.netif(interface_one.id)
assert node_two.netif(inteface_two.id)
def test_node_to_net(self, session, ip_prefixes):
# given
node_one = session.add_node()
node_two = session.add_node(_type=NodeTypes.SWITCH)
interface_one = ip_prefixes.create_interface(node_one)
# when
session.add_link(node_one.objid, node_two.objid, interface_one)
# then
assert node_two.all_link_data(0)
assert node_one.netif(interface_one.id)
def test_net_to_node(self, session, ip_prefixes):
# given
node_one = session.add_node(_type=NodeTypes.SWITCH)
node_two = session.add_node()
interface_two = ip_prefixes.create_interface(node_two)
# when
session.add_link(node_one.objid, node_two.objid, interface_two=interface_two)
# then
assert node_one.all_link_data(0)
assert node_two.netif(interface_two.id)
def test_net_to_net(self, session):
# given
node_one = session.add_node(_type=NodeTypes.SWITCH)
node_two = session.add_node(_type=NodeTypes.SWITCH)
# when
session.add_link(node_one.objid, node_two.objid)
# then
assert node_one.all_link_data(0)
def test_link_update(self, session, ip_prefixes):
# given
node_one = session.add_node()
node_two = session.add_node(_type=NodeTypes.SWITCH)
interface_one = ip_prefixes.create_interface(node_one)
session.add_link(node_one.objid, node_two.objid, interface_one)
interface = node_one.netif(interface_one.id)
output = utils.check_cmd(["tc", "qdisc", "show", "dev", interface.localname])
assert "delay" not in output
assert "rate" not in output
assert "loss" not in output
assert "duplicate" not in output
# when
link_options = LinkOptions()
link_options.delay = 50
link_options.bandwidth = 5000000
link_options.per = 25
link_options.dup = 25
session.update_link(node_one.objid, node_two.objid,
interface_one_id=interface_one.id, link_options=link_options)
# then
output = utils.check_cmd(["tc", "qdisc", "show", "dev", interface.localname])
assert "delay" in output
assert "rate" in output
assert "loss" in output
assert "duplicate" in output
def test_link_delete(self, session, ip_prefixes):
# given
node_one = session.add_node()
node_two = session.add_node()
interface_one = ip_prefixes.create_interface(node_one)
interface_two = ip_prefixes.create_interface(node_two)
session.add_link(node_one.objid, node_two.objid, interface_one, interface_two)
assert node_one.netif(interface_one.id)
assert node_two.netif(interface_two.id)
assert session.get_node_count() == 3
# when
session.delete_link(node_one.objid, node_two.objid, interface_one.id, interface_two.id)
# then
assert not node_one.netif(interface_one.id)
assert not node_two.netif(interface_two.id)
assert session.get_node_count() == 2
def test_link_bandwidth(self, session, ip_prefixes):
"""
Test ptp node network with modifying link bandwidth.
:param core.future.coreemu.FutureSession session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create link network
node_one, node_two = create_ptp_network(session, ip_prefixes)
# output csv index
bandwidth_index = 8
# run iperf, validate normal bandwidth
stdout = iperf(node_one, node_two, ip_prefixes)
assert stdout
value = int(stdout.split(',')[bandwidth_index])
assert 900000 <= value <= 1100000
# change bandwidth in bits per second
link_options = LinkOptions()
link_options.bandwidth = 500000
session.update_link(node_one.objid, node_two.objid, link_options=link_options)
# run iperf again
stdout = iperf(node_one, node_two, ip_prefixes)
assert stdout
value = int(stdout.split(',')[bandwidth_index])
assert 400000 <= value <= 600000
def test_link_loss(self, session, ip_prefixes):
"""
Test ptp node network with modifying link packet loss.
:param core.future.coreemu.FutureSession session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create link network
node_one, node_two = create_ptp_network(session, ip_prefixes)
# output csv index
loss_index = -2
# run iperf, validate normal bandwidth
stdout = iperf(node_one, node_two, ip_prefixes)
assert stdout
value = float(stdout.split(',')[loss_index])
assert 0 <= value <= 0.5
# change bandwidth in bits per second
link_options = LinkOptions()
link_options.per = 50
session.update_link(node_one.objid, node_two.objid, link_options=link_options)
# run iperf again
stdout = iperf(node_one, node_two, ip_prefixes)
assert stdout
value = float(stdout.split(',')[loss_index])
assert 40 <= value <= 60
def test_link_delay(self, session, ip_prefixes):
"""
Test ptp node network with modifying link packet delay.
:param core.future.coreemu.FutureSession session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create link network
node_one, node_two = create_ptp_network(session, ip_prefixes)
# run ping for delay information
stdout = ping_output(node_one, node_two, ip_prefixes)
assert stdout
rtt_line = stdout.split("\n")[-1]
rtt_values = rtt_line.split("=")[1].split("ms")[0].strip()
rtt_avg = float(rtt_values.split("/")[2])
assert 0 <= rtt_avg <= 0.2
# change delay in microseconds
link_options = LinkOptions()
link_options.delay = 1000000
session.update_link(node_one.objid, node_two.objid, link_options=link_options)
# run ping for delay information again
stdout = ping_output(node_one, node_two, ip_prefixes)
assert stdout
rtt_line = stdout.split("\n")[-1]
rtt_values = rtt_line.split("=")[1].split("ms")[0].strip()
rtt_avg = float(rtt_values.split("/")[2])
assert 1800 <= rtt_avg <= 2200
def test_link_jitter(self, session, ip_prefixes):
"""
Test ptp node network with modifying link packet jitter.
:param core.future.coreemu.FutureSession session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create link network
node_one, node_two = create_ptp_network(session, ip_prefixes)
# output csv index
jitter_index = 9
# run iperf
stdout = iperf(node_one, node_two, ip_prefixes)
assert stdout
value = float(stdout.split(",")[jitter_index])
assert -0.5 <= value <= 0.05
# change jitter in microseconds
link_options = LinkOptions()
link_options.jitter = 1000000
session.update_link(node_one.objid, node_two.objid, link_options=link_options)
# run iperf again
stdout = iperf(node_one, node_two, ip_prefixes)
assert stdout
value = float(stdout.split(",")[jitter_index])
assert 200 <= value <= 500

View file

@ -0,0 +1,79 @@
import os
import time
import pytest
from core.enumerations import NodeTypes
from core.future.futuredata import NodeOptions
from core.misc import utils
MODELS = [
"router",
"host",
"PC",
"mdr",
]
NET_TYPES = [
NodeTypes.SWITCH,
NodeTypes.HUB,
NodeTypes.WIRELESS_LAN
]
class TestNodes:
@pytest.mark.parametrize("model", MODELS)
def test_node_add(self, session, model):
# given
node_options = NodeOptions(model=model)
# when
node = session.add_node(node_options=node_options)
# give time for node services to boot
time.sleep(1)
# then
assert node
assert os.path.exists(node.nodedir)
assert node.alive()
assert node.up
assert node.check_cmd(["ip", "addr", "show", "lo"])
node.validate()
def test_node_update(self, session):
# given
node = session.add_node()
position_value = 100
update_options = NodeOptions()
update_options.set_position(x=position_value, y=position_value)
# when
session.update_node(node.objid, update_options)
# then
assert node.position.x == position_value
assert node.position.y == position_value
def test_node_delete(self, session):
# given
node = session.add_node()
# when
session.delete_node(node.objid)
# then
with pytest.raises(KeyError):
session.get_object(node.objid)
@pytest.mark.parametrize("net_type", NET_TYPES)
def test_net(self, session, net_type):
# given
# when
node = session.add_node(_type=net_type)
# then
assert node
assert node.up
assert utils.check_cmd(["brctl", "show", node.brname])

View file

@ -386,7 +386,7 @@ class Ns3Session(Session):
ns.core.Simulator.Run()
# self.evq.run() # event queue may have WayPointMobility events
self.set_state(EventTypes.RUNTIME_STATE.value, send_event=True)
self.set_state(EventTypes.RUNTIME_STATE, send_event=True)
t = threading.Thread(target=runthread)
t.daemon = True
t.start()
@ -454,7 +454,7 @@ class Ns3Session(Session):
Start a thread that updates CORE nodes based on their ns-3
positions.
"""
self.set_state(EventTypes.INSTANTIATION_STATE.value)
self.set_state(EventTypes.INSTANTIATION_STATE)
self.mobilitythread = threading.Thread(
target=self.ns3mobilitythread,
args=(refresh_ms,))