Merge pull request #277 from coreemu/core-error

CoreError
This commit is contained in:
bharnden 2019-09-11 14:06:28 -07:00 committed by GitHub
commit 8a39d070b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 212 additions and 217 deletions

View file

@ -5,6 +5,7 @@ verify_ssl = true
[scripts]
coredev = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
coretest = "python -m pytest -v tests"
[dev-packages]
grpcio-tools = "*"

View file

@ -31,3 +31,11 @@ class CoreCommandError(subprocess.CalledProcessError):
def __str__(self):
return "Command(%s), Status(%s):\n%s" % (self.cmd, self.returncode, self.output)
class CoreError(Exception):
"""
Used for errors when dealing with CoreEmu and Sessions.
"""
pass

View file

@ -10,6 +10,7 @@ from queue import Empty, Queue
import grpc
from core import CoreError
from core.api.grpc import core_pb2, core_pb2_grpc
from core.emulator.data import (
ConfigData,
@ -638,7 +639,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
lon = request.position.lon
alt = request.position.alt
node_options.set_location(lat, lon, alt)
result = session.update_node(node_id, node_options)
result = True
try:
session.update_node(node_id, node_options)
except CoreError:
result = False
return core_pb2.EditNodeResponse(result=result)
def DeleteNode(self, request, context):

View file

@ -15,7 +15,7 @@ import time
from multiprocessing.pool import ThreadPool
import core.nodes.base
from core import constants, utils
from core import CoreError, constants, utils
from core.api.tlv import coreapi
from core.api.tlv.broker import CoreBroker
from core.emane.emanemanager import EmaneManager
@ -190,16 +190,17 @@ class Session(object):
:param list objects: possible objects to deal with
:param bool connect: link interfaces if True, unlink otherwise
:return: nothing
:raises core.CoreError: when objects to link is less than 2, or no common networks are found
"""
objects = [x for x in objects if x]
if len(objects) < 2:
raise ValueError("wireless link failure: %s", objects)
raise CoreError("wireless link failure: %s" % objects)
logging.debug(
"handling wireless linking objects(%s) 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")
raise CoreError("no common network found for wireless link/unlink")
for common_network, interface_one, interface_two in common_networks:
if not nodeutils.is_node(
@ -238,7 +239,7 @@ class Session(object):
:param core.emulator.emudata.InterfaceData interface_one: node one interface data, defaults to none
:param core.emulator.emudata.InterfaceData interface_two: node two interface data, defaults to none
:param core.emulator.emudata.LinkOptions link_options: data for creating link, defaults to no options
:return:
:return: nothing
"""
if not link_options:
link_options = LinkOptions()
@ -373,6 +374,7 @@ class Session(object):
:param int interface_two_id: interface id for node two
:param core.emulator.enumerations.LinkTypes link_type: link type to delete
:return: nothing
:raises core.CoreError: when no common network is found for link being deleted
"""
# get node objects identified by link data
node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(
@ -417,7 +419,7 @@ class Session(object):
if interface_one.net != interface_two.net and all(
[interface_one.up, interface_two.up]
):
raise ValueError("no common network found")
raise CoreError("no common network found")
logging.info(
"deleting link node(%s):interface(%s) node(%s):interface(%s)",
@ -478,6 +480,8 @@ class Session(object):
:param int interface_two_id: interface id for node two
:param core.emulator.emudata.LinkOptions link_options: data to update link with
:return: nothing
:raises core.CoreError: when updating a wireless type link, when there is a unknown
link between networks
"""
if not link_options:
link_options = LinkOptions()
@ -495,7 +499,7 @@ class Session(object):
try:
# wireless link
if link_options.type == LinkTypes.WIRELESS.value:
raise ValueError("cannot update wireless link")
raise CoreError("cannot update wireless link")
else:
if not node_one and not node_two:
if net_one and net_two:
@ -508,7 +512,7 @@ class Session(object):
interface = net_two.getlinknetif(net_one)
if not interface:
raise ValueError("modify unknown link between nets")
raise CoreError("modify unknown link between nets")
if upstream:
interface.swapparams("_params_up")
@ -532,7 +536,7 @@ class Session(object):
)
interface.swapparams("_params_up")
else:
raise ValueError("modify link for unknown nodes")
raise CoreError("modify link for unknown nodes")
elif not node_one:
# node1 = layer 2node, node2 = layer3 node
interface = node_two.netif(interface_two_id, net_one)
@ -544,7 +548,7 @@ class Session(object):
else:
common_networks = node_one.commonnets(node_two)
if not common_networks:
raise ValueError("no common network found")
raise CoreError("no common network found")
for net_one, interface_one, interface_two in common_networks:
if (
@ -666,25 +670,17 @@ class Session(object):
:param core.emulator.emudata.NodeOptions node_options: data to update node with
:return: True if node updated, False otherwise
:rtype: bool
:raises core.CoreError: when node to update does not exist
"""
result = False
try:
# get node to update
node = self.get_node(node_id)
# get node to update
node = self.get_node(node_id)
# set node position and broadcast it
self.set_node_position(node, node_options)
# set node position and broadcast it
self.set_node_position(node, node_options)
# update attributes
node.canvas = node_options.canvas
node.icon = node_options.icon
# set node as updated successfully
result = True
except KeyError:
logging.error("failure to update node that does not exist: %s", node_id)
return result
# update attributes
node.canvas = node_options.canvas
node.icon = node_options.icon
def set_node_position(self, node, node_options):
"""
@ -1156,7 +1152,7 @@ class Session(object):
"""
hooks = self._state_hooks.setdefault(state, [])
if hook in hooks:
raise ValueError("attempting to add duplicate state hook")
raise CoreError("attempting to add duplicate state hook")
hooks.append(hook)
if self.state == state:
@ -1283,21 +1279,22 @@ class Session(object):
return node_id
def create_node(self, cls, *clsargs, **clskwds):
def create_node(self, cls, *args, **kwargs):
"""
Create an emulation node.
:param class cls: node class to create
:param list clsargs: list of arguments for the class to create
:param dict clskwds: dictionary of arguments for the class to create
:param list args: list of arguments for the class to create
:param dict kwargs: dictionary of arguments for the class to create
:return: the created node instance
:raises core.CoreError: when id of the node to create already exists
"""
node = cls(self, *clsargs, **clskwds)
node = cls(self, *args, **kwargs)
with self._nodes_lock:
if node.id in self.nodes:
node.shutdown()
raise KeyError("duplicate node id %s for %s" % (node.id, node.name))
raise CoreError("duplicate node id %s for %s" % (node.id, node.name))
self.nodes[node.id] = node
return node
@ -1309,9 +1306,10 @@ class Session(object):
:param int _id: node id to retrieve
:return: node for the given id
:rtype: core.nodes.base.CoreNode
:raises core.CoreError: when node does not exist
"""
if _id not in self.nodes:
raise KeyError("unknown node id %s" % _id)
raise CoreError("unknown node id %s" % _id)
return self.nodes[_id]
def delete_node(self, _id):

View file

@ -234,16 +234,6 @@ class CoreNodeBase(NodeBase):
self.nodedir = None
self.tmpnodedir = False
def addservice(self, service):
"""
Add a services to the service list.
:param core.services.coreservices.CoreService service: service to add
:return: nothing
"""
if service is not None:
self.services.append(service)
def makenodedir(self):
"""
Create the node directory.

View file

@ -415,7 +415,7 @@ class CoreServices(object):
)
continue
logging.info("adding service to node(%s): %s", node.name, service_name)
node.addservice(service)
node.services.append(service)
def all_configs(self):
"""

View file

@ -15,3 +15,6 @@ max-line-length=100
max-complexity=26
select=B,C,E,F,W,T4
exclude=*_pb2*.py,utm.py,doc,build
[tool:pytest]
norecursedirs=distributed emane

View file

@ -188,11 +188,6 @@ def coreserver():
ServiceManager.services.clear()
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

@ -0,0 +1,142 @@
"""
Unit tests for testing CORE EMANE networks.
"""
import os
from xml.etree import ElementTree
import pytest
from core import CoreError
from core.emane.bypass import EmaneBypassModel
from core.emane.commeffect import EmaneCommEffectModel
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.rfpipe import EmaneRfPipeModel
from core.emane.tdma import EmaneTdmaModel
from core.emulator.emudata import NodeOptions
_EMANE_MODELS = [
EmaneIeee80211abgModel,
EmaneRfPipeModel,
EmaneBypassModel,
EmaneCommEffectModel,
EmaneTdmaModel,
]
_DIR = os.path.dirname(os.path.abspath(__file__))
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])
class TestEmane:
@pytest.mark.parametrize("model", _EMANE_MODELS)
def test_models(self, session, model, ip_prefixes):
"""
Test emane models within a basic network.
:param core.emulator.coreemu.EmuSession session: session for test
:param model: emane model to test
:param ip_prefixes: generates ip addresses for nodes
"""
# create emane node for networking the core nodes
emane_network = session.create_emane_network(
model, geo_reference=(47.57917, -122.13232, 2.00000)
)
emane_network.setposition(x=80, y=50)
# configure tdma
if model == EmaneTdmaModel:
session.emane.set_model_config(
emane_network.id,
EmaneTdmaModel.name,
{"schedule": os.path.join(_DIR, "../examples/tdma/schedule.xml")},
)
# create nodes
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)
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.id, emane_network.id, interface_one=interface)
# instantiate session
session.instantiate()
# ping n2 from n1 and assert success
status = ping(node_one, node_two, ip_prefixes, count=5)
assert not status
def test_xml_emane(self, session, tmpdir, ip_prefixes):
"""
Test xml client methods for emane.
:param session: session for test
:param tmpdir: tmpdir to create data in
:param ip_prefixes: generates ip addresses for nodes
"""
# create emane node for networking the core nodes
emane_network = session.create_emane_network(
EmaneIeee80211abgModel,
geo_reference=(47.57917, -122.13232, 2.00000),
config={"test": "1"},
)
emane_network.setposition(x=80, y=50)
# create nodes
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)
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.id, emane_network.id, interface_one=interface)
# instantiate session
session.instantiate()
# get ids for nodes
emane_id = emane_network.id
n1_id = node_one.id
n2_id = node_two.id
# save xml
xml_file = tmpdir.join("session.xml")
file_path = xml_file.strpath
session.save_xml(file_path)
# verify xml file was created and can be parsed
assert xml_file.isfile()
assert ElementTree.parse(file_path)
# stop current session, clearing data
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml
session.open_xml(file_path, start=True)
# retrieve configuration we set originally
value = str(
session.emane.get_config("test", emane_id, EmaneIeee80211abgModel.name)
)
# verify nodes and configuration were restored
assert session.get_node(n1_id)
assert session.get_node(n2_id)
assert session.get_node(emane_id)
assert value == "1"

View file

@ -1,2 +0,0 @@
[pytest]
norecursedirs = distributed

View file

@ -1,68 +0,0 @@
"""
Unit tests for testing CORE EMANE networks.
"""
import os
import pytest
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.rfpipe import EmaneRfPipeModel
from core.emane.tdma import EmaneTdmaModel
from core.emulator.emudata import NodeOptions
_EMANE_MODELS = [
EmaneIeee80211abgModel,
EmaneRfPipeModel,
EmaneBypassModel,
EmaneCommEffectModel,
EmaneTdmaModel,
]
_DIR = os.path.dirname(os.path.abspath(__file__))
class TestEmane:
@pytest.mark.parametrize("model", _EMANE_MODELS)
def test_models(self, session, model, ip_prefixes):
"""
Test emane models within a basic network.
:param core.emulator.coreemu.EmuSession session: session for test
:param model: emane model to test
:param ip_prefixes: generates ip addresses for nodes
"""
# create emane node for networking the core nodes
emane_network = session.create_emane_network(
model, geo_reference=(47.57917, -122.13232, 2.00000)
)
emane_network.setposition(x=80, y=50)
# configure tdma
if model == EmaneTdmaModel:
session.emane.set_model_config(
emane_network.id,
EmaneTdmaModel.name,
{"schedule": os.path.join(_DIR, "../examples/tdma/schedule.xml")},
)
# create nodes
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)
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.id, emane_network.id, interface_one=interface)
# instantiate session
session.instantiate()
# ping n2 from n1 and assert success
status = ping(node_one, node_two, ip_prefixes, count=5)
assert not status

View file

@ -5,6 +5,7 @@ from queue import Queue
import grpc
import pytest
from core import CoreError
from core.api.grpc import core_pb2
from core.api.grpc.client import CoreGrpcClient
from core.config import ConfigShim
@ -239,7 +240,7 @@ class TestGrpc:
# then
assert response.result is expected
if expected is True:
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert session.get_node(node.id)
def test_node_command(self, grpc_server):

View file

@ -7,6 +7,7 @@ import time
import mock
import pytest
from core import CoreError
from core.api.tlv import coreapi
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emulator.enumerations import (
@ -92,7 +93,7 @@ class TestGui:
coreserver.request_handler.handle_message(message)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
coreserver.session.get_node(node_id)
def test_link_add_node_to_net(self, coreserver):

View file

@ -3,7 +3,7 @@ import time
import pytest
from core import utils
from core import CoreError, utils
from core.emulator.emudata import NodeOptions
from core.emulator.enumerations import NodeTypes
@ -53,7 +53,7 @@ class TestNodes:
session.delete_node(node.id)
# then
with pytest.raises(KeyError):
with pytest.raises(CoreError):
session.get_node(node.id)
@pytest.mark.parametrize("net_type", NET_TYPES)

View file

@ -2,7 +2,7 @@ from xml.etree import ElementTree
import pytest
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core import CoreError
from core.emulator.emudata import LinkOptions, NodeOptions
from core.emulator.enumerations import NodeTypes
from core.location.mobility import BasicRangeModel
@ -84,9 +84,9 @@ class TestXml:
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml
@ -145,9 +145,9 @@ class TestXml:
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml
@ -205,9 +205,9 @@ class TestXml:
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml
@ -222,74 +222,6 @@ class TestXml:
assert session.get_node(wlan_id)
assert value == "1"
def test_xml_emane(self, session, tmpdir, ip_prefixes):
"""
Test xml client methods for emane.
:param session: session for test
:param tmpdir: tmpdir to create data in
:param ip_prefixes: generates ip addresses for nodes
"""
# create emane node for networking the core nodes
emane_network = session.create_emane_network(
EmaneIeee80211abgModel,
geo_reference=(47.57917, -122.13232, 2.00000),
config={"test": "1"},
)
emane_network.setposition(x=80, y=50)
# create nodes
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)
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.id, emane_network.id, interface_one=interface)
# instantiate session
session.instantiate()
# get ids for nodes
emane_id = emane_network.id
n1_id = node_one.id
n2_id = node_two.id
# save xml
xml_file = tmpdir.join("session.xml")
file_path = xml_file.strpath
session.save_xml(file_path)
# verify xml file was created and can be parsed
assert xml_file.isfile()
assert ElementTree.parse(file_path)
# stop current session, clearing data
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
assert not session.get_node(n2_id)
# load saved xml
session.open_xml(file_path, start=True)
# retrieve configuration we set originally
value = str(
session.emane.get_config("test", emane_id, EmaneIeee80211abgModel.name)
)
# verify nodes and configuration were restored
assert session.get_node(n1_id)
assert session.get_node(n2_id)
assert session.get_node(emane_id)
assert value == "1"
def test_network_to_network(self, session, tmpdir):
"""
Test xml generation when dealing with network to network nodes.
@ -324,9 +256,9 @@ class TestXml:
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml
@ -383,9 +315,9 @@ class TestXml:
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml
@ -450,9 +382,9 @@ class TestXml:
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml
@ -532,9 +464,9 @@ class TestXml:
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n1_id)
with pytest.raises(KeyError):
with pytest.raises(CoreError):
assert not session.get_node(n2_id)
# load saved xml

View file

@ -66,17 +66,6 @@ sudo apt install python-configparser python-enum34 python-future python-grpcio p
sudo apt install python3-configparser python3-enum34 python3-future python3-grpcio python3-lxml
```
## Other Distros
The newly added gRPC API which depends on python library grpcio is not commonly found within system repos.
To account for this it would be recommended to install the python dependencies using the **requirements.txt** found in
the latest release.
```shell
# will need to pip3 for python3 usage
sudo pip install -r requirements.txt
```
# Pre-Req Installing OSPF MDR
Virtual networks generally require some form of routing in order to work (e.g. to automatically populate routing