caught up with latest gitlab commits, added a new tests directory leveraging pytest based tests for testing core functionality

This commit is contained in:
Blake J. Harnden 2017-04-27 13:34:23 -07:00
parent 2fc6345138
commit 14cb7dc251
14 changed files with 570 additions and 96 deletions

View file

@ -38,6 +38,7 @@ from core.phys.pnodes import PhysicalNode
logger = log.get_logger(__name__)
# TODO: name conflict with main core server, probably should rename
class CoreServer(object):
def __init__(self, name, host, port):
self.name = name
@ -93,7 +94,6 @@ class CoreBroker(ConfigurableManager):
self.nodemap_lock = threading.Lock()
# reference counts of nodes on servers
self.nodecounts = {}
self.bootcount = 0
# set of node numbers that are link-layer nodes (networks)
self.network_nodes = set()
# set of node numbers that are PhysicalNode nodes
@ -224,11 +224,6 @@ class CoreBroker(ConfigurableManager):
nodenum = msg.get_tlv(NodeTlvs.NUMBER.value)
if nodenum is not None:
count = self.delnodemap(server, nodenum)
# snoop node add response to increment booted node count
# (only CoreNodes send these response messages)
elif msgflags & (MessageFlags.ADD.value | MessageFlags.LOCAL.value):
self.incrbootcount()
self.session.check_runtime()
elif msgtype == MessageTypes.LINK.value:
# this allows green link lines for remote WLANs
msg = coreapi.CoreLinkMessage(msgflags, msghdr, msgdata)
@ -485,19 +480,6 @@ class CoreBroker(ConfigurableManager):
self.nodecounts[server] = count
return count
def incrbootcount(self):
"""
Count a node that has booted.
"""
self.bootcount += 1
return self.bootcount
def getbootcount(self):
"""
Return the number of booted nodes.
"""
return self.bootcount
def getserversbynode(self, nodenum):
"""
Retrieve a set of emulation servers given a node number.
@ -588,13 +570,14 @@ class CoreBroker(ConfigurableManager):
:rtype: bool
"""
servers = set()
handle_locally = False
# Do not forward messages when in definition state
# (for e.g. configuring services)
if self.session.state == EventTypes.DEFINITION_STATE.value:
return False
# Decide whether message should be handled locally or forwarded, or both
if message.message_type == MessageTypes.NODE.value:
servers = self.handlenodemsg(message)
handle_locally, servers = self.handlenodemsg(message)
elif message.message_type == MessageTypes.EVENT.value:
# broadcast events everywhere
servers = self.getservers()
@ -611,7 +594,7 @@ class CoreBroker(ConfigurableManager):
servers = self.getservers()
if message.message_type == MessageTypes.LINK.value:
# prepare a server list from two node numbers in link message
servers, message = self.handlelinkmsg(message)
handle_locally, servers, message = self.handlelinkmsg(message)
elif len(servers) == 0:
# check for servers based on node numbers in all messages but link
nn = message.node_numbers()
@ -626,7 +609,7 @@ class CoreBroker(ConfigurableManager):
handler(message)
# Perform any message forwarding
handle_locally = self.forwardmsg(message, servers)
handle_locally |= self.forwardmsg(message, servers)
return not handle_locally
def setupserver(self, servername):
@ -694,6 +677,7 @@ class CoreBroker(ConfigurableManager):
:return:
"""
servers = set()
handle_locally = False
serverfiletxt = None
# snoop Node Message for emulation server TLV and record mapping
n = message.tlv_data[NodeTlvs.NUMBER.value]
@ -704,25 +688,21 @@ class CoreBroker(ConfigurableManager):
nodecls = nodeutils.get_node_class(NodeTypes(nodetype))
except KeyError:
logger.warn("broker invalid node type %s" % nodetype)
return servers
return handle_locally, servers
if nodecls is None:
logger.warn("broker unimplemented node type %s" % nodetype)
return servers
return handle_locally, servers
if issubclass(nodecls, PyCoreNet) and nodetype != NodeTypes.WIRELESS_LAN.value:
# network node replicated on all servers; could be optimized
# don"t replicate WLANs, because ebtables rules won"t work
servers = self.getservers()
handle_locally = True
self.addnet(n)
for server in servers:
self.addnodemap(server, n)
# do not record server name for networks since network
# nodes are replicated across all server
return servers
if issubclass(nodecls, PyCoreNet) and nodetype == NodeTypes.WIRELESS_LAN.value:
# special case where remote WLANs not in session._objs, and no
# node response message received, so they are counted here
if message.get_tlv(NodeTlvs.EMULATION_SERVER.value) is not None:
self.incrbootcount()
return handle_locally, servers
elif issubclass(nodecls, PyCoreNode):
name = message.get_tlv(NodeTlvs.NAME.value)
if name:
@ -743,7 +723,7 @@ class CoreBroker(ConfigurableManager):
# hook to update coordinates of physical nodes
if n in self.physical_nodes:
self.session.mobility.physnodeupdateposition(message)
return servers
return handle_locally, servers
def handlelinkmsg(self, message):
"""
@ -804,10 +784,11 @@ class CoreBroker(ConfigurableManager):
self.addtunnel(host, nn[0], nn[1], localn)
elif message.flags & MessageFlags.DELETE.value:
self.deltunnel(nn[0], nn[1])
handle_locally = False
else:
servers = servers1.union(servers2)
return servers, message
return handle_locally, servers, message
def addlinkendpoints(self, message, servers1, servers2):
"""
@ -946,7 +927,7 @@ class CoreBroker(ConfigurableManager):
if server is not None:
server.instantiation_complete = True
if self.session.is_connected():
if self.session_handler:
tlvdata = ""
tlvdata += coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_COMPLETE.value)
msg = coreapi.CoreEventMessage.pack(0, tlvdata)

View file

@ -71,6 +71,7 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
}
self.message_queue = Queue.Queue()
self.node_status_request = {}
self._shutdown_lock = threading.Lock()
self.handler_threads = []
num_threads = int(server.config["numthreads"])
@ -602,22 +603,16 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
self.node_status_request[node_id] = True
elif message.flags & MessageFlags.DELETE.value:
# TODO: logic below seems pointless, when the deletion is attempted regardless
node = None
try:
node = self.session.get_object(node_id)
except KeyError:
logger.exception("error retrieving object: %s", node_id)
with self._shutdown_lock:
self.session.delete_object(node_id)
self.session.delete_object(node_id)
if message.flags & MessageFlags.STRING.value:
tlvdata = ""
tlvdata += coreapi.CoreNodeTlv.pack(NodeTlvs.NUMBER.value, node_id)
flags = MessageFlags.DELETE.value | MessageFlags.LOCAL.value
replies.append(coreapi.CoreNodeMessage.pack(flags, tlvdata))
if message.flags & MessageFlags.STRING.value:
tlv_data = ""
tlv_data += coreapi.CoreNodeTlv.pack(NodeTlvs.NUMBER.value, node_id)
flags = MessageFlags.DELETE.value | MessageFlags.LOCAL.value
replies.append(coreapi.CoreNodeMessage.pack(flags, tlv_data))
self.session.check_shutdown()
self.session.check_shutdown()
# Node modify message (no add/del flag)
else:
try:

View file

@ -457,6 +457,7 @@ class EmaneManager(ConfigurableManager):
servers.append(s)
self._objslock.release()
servers.sort(key=lambda x: x.name)
for server in servers:
if server.name == "localhost":
continue
@ -1139,6 +1140,24 @@ class EmaneManager(ConfigurableManager):
return True
def emanerunning(self, node):
"""
Return True if an EMANE process associated with the given node
is running, False otherwise.
"""
status = -1
cmd = ['pkill', '-0', '-x', 'emane']
try:
if self.version < emane.EMANE092:
status = subprocess.call(cmd)
else:
status = node.cmd(cmd, wait=True)
except IOError:
logger.exception("error checking if emane is running")
return status == 0
class EmaneGlobalModel(EmaneModel):
"""

View file

@ -422,33 +422,34 @@ class BasicRangeModel(WirelessModel):
"""
if netif == netif2:
return
try:
(x, y, z) = self._netifs[netif]
(x2, y2, z2) = self._netifs[netif2]
except KeyError:
return
if x2 is None or y2 is None:
return
d = self.calcdistance((x, y, z), (x2, y2, z2))
# ordering is important, to keep the wlan._linked dict organized
a = min(netif, netif2)
b = max(netif, netif2)
try:
self.wlan._linked_lock.acquire()
linked = self.wlan.linked(a, b)
x, y, z = self._netifs[netif]
x2, y2, z2 = self._netifs[netif2]
if x2 is None or y2 is None:
return
d = self.calcdistance((x, y, z), (x2, y2, z2))
# ordering is important, to keep the wlan._linked dict organized
a = min(netif, netif2)
b = max(netif, netif2)
with self.wlan._linked_lock:
linked = self.wlan.linked(a, b)
logger.info("checking if link distance is out of range: %s > %s", d, self.range)
if d > self.range:
if linked:
self.wlan.unlink(a, b)
self.sendlinkmsg(a, b, unlink=True)
else:
if not linked:
self.wlan.link(a, b)
self.sendlinkmsg(a, b)
except KeyError:
return
finally:
self.wlan._linked_lock.release()
if d > self.range:
if linked:
self.wlan.unlink(a, b)
self.sendlinkmsg(a, b, unlink=True)
else:
if not linked:
self.wlan.link(a, b)
self.sendlinkmsg(a, b)
logger.exception("error getting interfaces during calclinkS")
@staticmethod
def calcdistance(p1, p2):

View file

@ -312,7 +312,7 @@ class WlanNode(LxBrNet):
for netif in self.netifs():
netif.poshook = self.model.position_callback
if netif.node is not None:
(x, y, z) = netif.node.position.get()
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.setlinkparams()
elif model.config_type == RegisterTlvs.MOBILITY.value:

View file

@ -8,7 +8,9 @@ import time
from core import constants
from core.coreobj import PyCoreNetIf
from core.enumerations import NodeTypes
from core.misc import log
from core.misc import nodeutils
from core.misc import utils
logger = log.get_logger(__name__)
@ -116,7 +118,20 @@ class TunTap(PyCoreNetIf):
cmd = (constants.IP_BIN, 'link', 'show', self.name)
return self.node.cmd(cmd)
self.waitfor(nodedevexists)
count = 0
while True:
try:
self.waitfor(nodedevexists)
break
except RuntimeError as e:
# check if this is an EMANE interface; if so, continue
# waiting if EMANE is still running
# TODO: remove emane code
if count < 5 and nodeutils.is_node(self.net, NodeTypes.EMANE) and \
self.node.session.emane.emanerunning(self.node):
count += 1
else:
raise e
def install(self):
"""

View file

@ -40,8 +40,7 @@ class Sdt(object):
DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/"
# default altitude (in meters) for flyto view
DEFAULT_ALT = 2500
# TODO: read in user's nodes.conf here; below are default node types
# from the GUI
# TODO: read in user's nodes.conf here; below are default node types from the GUI
DEFAULT_SPRITES = [
('router', 'router.gif'), ('host', 'host.gif'),
('PC', 'pc.gif'), ('mdr', 'mdr.gif'),

View file

@ -636,12 +636,9 @@ class Session(object):
with self._objects_lock:
try:
obj = self.objects.pop(object_id)
obj.shutdown()
except KeyError:
logger.error("failed to remove object, object with id was not found: %s", object_id)
obj = None
if obj:
obj.shutdown()
def delete_objects(self):
"""
@ -840,23 +837,6 @@ class Session(object):
logger.info("valid runtime state found, returning")
return
# session_node_count = self.get_node_count()
#
# node_count = self.get_node_count()
# count booted nodes not emulated on this server
# TODO: let slave server determine RUNTIME and wait for Event Message
# broker.getbootocunt() counts all CoreNodes from status reponse
# messages, plus any remote WLANs; remote EMANE, hub, switch, etc.
# are already counted in self._objs
# node_count += self.broker.getbootcount()
# logger.info("Checking for runtime with %d of %d session nodes",
# node_count, session_node_count)
# if node_count < session_node_count:
# return # do not have information on all nodes yet
# information on all nodes has been received and they have been started enter the runtime state
# check to verify that all nodes and networks are running
if not self.broker.instantiation_complete():
return

View file

@ -60,6 +60,7 @@ def test(options):
# launches terminal for the first node
# n[0].term("bash")
n[0].icmd(["ping", "-c", "5", "127.0.0.1"])
# wait for rate seconds to allow ebtables commands to commit
time.sleep(EbtablesQueue.rate)

View file

@ -4,3 +4,5 @@ grpcio-tools==1.0.0
pycco==0.5.1
sphinx==1.4.8
sphinx_rtd_theme==0.1.9
pytest==3.0.7
pytest-runner==2.11.1

2
daemon/setup.cfg Normal file
View file

@ -0,0 +1,2 @@
[aliases]
test=pytest

View file

@ -27,6 +27,8 @@ setup(name="core-python",
install_requires=[
"enum34"
],
setup_requires=["pytest-runner"],
tests_require=["pytest"],
description="Python components of CORE",
url="http://www.nrl.navy.mil/itd/ncs/products/core",
author="Boeing Research & Technology",

142
daemon/tests/conftest.py Normal file
View file

@ -0,0 +1,142 @@
"""
Unit test fixture module.
"""
import os
import pytest
from core.session import Session
from core.misc import ipaddress
from core.misc import nodemaps
from core.misc import nodeutils
from core.netns import nodes
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):
node = self.session.add_object(cls=nodes.CoreNode, name=name)
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))
vcmd, stdin, stdout, stderr = from_node.popen(["ping", "-i", "0.05", "-c", "3", to_ip])
return stdout.read().strip()
def iping(self, from_name, to_name):
from_node = self.nodes[from_name]
to_ip = str(self.get_ip(to_name))
from_node.icmd(["ping", "-i", "0.01", "-c", "10", to_ip])
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.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
@pytest.fixture()
def session():
# configure default nodes
node_map = nodemaps.CLASSIC_NODES
nodeutils.set_node_map(node_map)
# create and return session
session_fixture = Session(1, persistent=True)
assert os.path.exists(session_fixture.session_dir)
yield session_fixture
# cleanup
print "shutting down session"
session_fixture.shutdown()
assert not os.path.exists(session_fixture.session_dir)
@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)

335
daemon/tests/test_core.py Normal file
View file

@ -0,0 +1,335 @@
"""
Unit tests for testing with a CORE switch.
"""
from core.mobility import BasicRangeModel
from core.netns import nodes
class TestCore:
def test_ptp(self, core):
"""
Test ptp node network.
:param conftest.Core core: core fixture to test with
"""
# create switch
ptp_node = core.session.add_object(cls=nodes.PtpNet)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
core.add_interface(ptp_node, "n1")
core.add_interface(ptp_node, "n2")
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# ping n2 from n1 and assert success
status = core.ping("n1", "n2")
assert not status
def test_hub(self, core):
"""
Test basic hub network.
:param conftest.Core core: core fixture to test with
"""
# create switch
hub_node = core.session.add_object(cls=nodes.HubNode)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
core.add_interface(hub_node, "n1")
core.add_interface(hub_node, "n2")
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# ping n2 from n1 and assert success
status = core.ping("n1", "n2")
assert not status
def test_switch(self, core):
"""
Test basic switch network.
:param conftest.Core core: core fixture to test with
"""
# create switch
switch_node = core.session.add_object(cls=nodes.SwitchNode)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
core.add_interface(switch_node, "n1")
core.add_interface(switch_node, "n2")
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# ping n2 from n1 and assert success
status = core.ping("n1", "n2")
assert not status
def test_wlan_basic_range_good(self, core):
"""
Test basic wlan network.
:param conftest.Core core: core fixture to test with
"""
# create wlan
wlan_node = core.session.add_object(cls=nodes.WlanNode)
values = BasicRangeModel.getdefaultvalues()
wlan_node.setmodel(BasicRangeModel, values)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
interface_one = core.add_interface(wlan_node, "n1")
interface_two = core.add_interface(wlan_node, "n2")
# link nodes in wlan
core.link(wlan_node, interface_one, interface_two)
# mark node position as together
core.get_node("n1").setposition(0, 0)
core.get_node("n2").setposition(0, 0)
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# ping n2 from n1 and assert success
status = core.ping("n1", "n2")
assert not status
def test_wlan_basic_range_bad(self, core):
"""
Test basic wlan network with leveraging basic range model.
:param conftest.Core core: core fixture to test with
"""
# create wlan
wlan_node = core.session.add_object(cls=nodes.WlanNode)
values = BasicRangeModel.getdefaultvalues()
wlan_node.setmodel(BasicRangeModel, values)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
interface_one = core.add_interface(wlan_node, "n1")
interface_two = core.add_interface(wlan_node, "n2")
# 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)
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# ping n2 from n1 and assert failure
import time
time.sleep(1)
status = core.ping("n1", "n2")
assert status
def test_link_bandwidth(self, core):
"""
Test ptp node network with modifying link bandwidth.
:param conftest.Core core: core fixture to test with
"""
# create switch
ptp_node = core.session.add_object(cls=nodes.PtpNet)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
interface_one = core.add_interface(ptp_node, "n1")
interface_two = core.add_interface(ptp_node, "n2")
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# output csv index
bandwidth_index = 8
# run iperf, validate normal bandwidth
stdout = core.iperf("n1", "n2")
assert stdout
value = int(stdout.split(',')[bandwidth_index])
print "bandwidth before: %s" % value
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 switch
ptp_node = core.session.add_object(cls=nodes.PtpNet)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
interface_one = core.add_interface(ptp_node, "n1")
interface_two = core.add_interface(ptp_node, "n2")
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# 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 45 <= value <= 55
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 switch
ptp_node = core.session.add_object(cls=nodes.PtpNet)
# create nodes
core.create_node("n1")
core.create_node("n2")
# add interfaces
interface_one = core.add_interface(ptp_node, "n1")
interface_two = core.add_interface(ptp_node, "n2")
# instantiate session
core.session.instantiate()
# assert node directories created
core.assert_nodes()
# 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.1
# 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