diff --git a/daemon/core/api/coreapi.py b/daemon/core/api/coreapi.py index 97013a22..1fd6dab6 100644 --- a/daemon/core/api/coreapi.py +++ b/daemon/core/api/coreapi.py @@ -23,7 +23,7 @@ from core.enumerations import MessageTypes from core.enumerations import NodeTlvs from core.enumerations import RegisterTlvs from core.enumerations import SessionTlvs -from core.misc import log +from core.misc import log, structutils from core.misc.ipaddress import IpAddress from core.misc.ipaddress import MacAddress @@ -692,6 +692,13 @@ class CoreMessage(object): message_type, message_flags, message_len = struct.unpack(cls.header_format, data[:cls.header_len]) return message_type, message_flags, message_len + @classmethod + def create(cls, flags, values): + tlv_data = structutils.pack_values(cls.tlv_class, values) + packed = cls.pack(flags, tlv_data) + header_data = packed[:cls.header_len] + return cls(flags, header_data, tlv_data) + @classmethod def pack(cls, message_flags, tlv_data): """ diff --git a/daemon/core/bsd/vnet.py b/daemon/core/bsd/vnet.py index b2df2ddd..e9d30726 100644 --- a/daemon/core/bsd/vnet.py +++ b/daemon/core/bsd/vnet.py @@ -30,7 +30,7 @@ class NetgraphNet(PyCoreNet): if policy is not None: self.policy = policy self.name = name - self.ngname = "n_%s_%s" % (str(self.objid), self.session.sessionid) + self.ngname = "n_%s_%s" % (str(self.objid), self.session.session_id) self.ngid = None self.verbose = verbose self._netif = {} diff --git a/daemon/core/netns/vnet.py b/daemon/core/netns/vnet.py index e2899c2b..4b3b9179 100644 --- a/daemon/core/netns/vnet.py +++ b/daemon/core/netns/vnet.py @@ -602,7 +602,7 @@ class GreTapBridge(LxBrNet): LxBrNet.__init__(self, session=session, objid=objid, name=name, policy=policy, start=False) self.grekey = key if self.grekey is None: - self.grekey = self.session.sessionid ^ self.objid + self.grekey = self.session.session_id ^ self.objid self.localnum = None self.remotenum = None self.remoteip = remoteip diff --git a/daemon/core/xen/xen.py b/daemon/core/xen/xen.py index 8d7e4ac5..0ba0ace6 100644 --- a/daemon/core/xen/xen.py +++ b/daemon/core/xen/xen.py @@ -152,7 +152,7 @@ class XenNode(PyCoreNode): self.lock = threading.RLock() self._netif = {} # domU name - self.vmname = "c" + str(session.sessionid) + "-" + name + self.vmname = "c" + str(session.session_id) + "-" + name # LVM volume group name self.vgname = self.getconfigitem('vg_name', vgname) # LVM logical volume name diff --git a/daemon/test.py b/daemon/test.py index 8981473a..8a903ab3 100644 --- a/daemon/test.py +++ b/daemon/test.py @@ -1,9 +1,12 @@ +import sys + import pytest +distributed = sys.argv[1] pytest.main([ "-v", - "--cov-report", - "xml", + "--distributed", distributed, + "--cov-report", "xml", "--cov=.", "tests" ]) diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index 528e0099..9ff75a59 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -1,23 +1,136 @@ """ Unit test fixture module. """ + import os + import pytest -from core.corehandlers import CoreRequestHandler +from mock.mock import MagicMock + from core.coreserver import CoreServer -from core.enumerations import CORE_API_PORT -from core.misc import ipaddress from core.misc import nodemaps from core.misc import nodeutils from core.netns import nodes from core.services import quagga from core.services import utility from core.session import Session +from core.api.coreapi import CoreConfMessage +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.enumerations import ConfigTlvs +from core.enumerations import CORE_API_PORT +from core.enumerations import EventTlvs +from core.enumerations import EventTypes +from core.enumerations import ExecuteTlvs +from core.enumerations import LinkTlvs +from core.enumerations import LinkTypes +from core.enumerations import MessageFlags +from core.enumerations import NodeTlvs +from core.enumerations import NodeTypes +from core.misc import ipaddress +from core.misc.ipaddress import MacAddress EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward" +def node_message(objid, name, emulation_server=None, node_type=NodeTypes.DEFAULT, model=None): + """ + Convenience method for creating a node TLV messages. + + :param int objid: node id + :param str name: node name + :param str emulation_server: distributed server name, if desired + :param core.enumerations.NodeTypes node_type: node type + :param str model: model for node + :return: tlv message + :rtype: core.api.coreapi.CoreNodeMessage + """ + values = [ + (NodeTlvs.NUMBER, objid), + (NodeTlvs.TYPE, node_type.value), + (NodeTlvs.NAME, name), + (NodeTlvs.EMULATION_SERVER, emulation_server), + ] + + if model: + values.append((NodeTlvs.MODEL, model)) + + return CoreNodeMessage.create(MessageFlags.ADD.value, values) + + +def link_message(n1, n2, intf_one=None, address_one=None, intf_two=None, address_two=None): + """ + Convenience method for creating link TLV messages. + + :param int n1: node one id + :param int n2: node two id + :param int intf_one: node one interface id + :param core.misc.ipaddress.IpAddress address_one: node one ip4 address + :param int intf_two: node two interface id + :param core.misc.ipaddress.IpAddress address_two: node two ip4 address + :return: tlv mesage + :rtype: core.api.coreapi.CoreLinkMessage + """ + mac_one, mac_two = None, None + if address_one: + mac_one = MacAddress.random() + if address_two: + mac_two = MacAddress.random() + + return CoreLinkMessage.create(MessageFlags.ADD.value, [ + (LinkTlvs.N1_NUMBER, n1), + (LinkTlvs.N2_NUMBER, n2), + (LinkTlvs.DELAY, 0), + (LinkTlvs.BANDWIDTH, 0), + (LinkTlvs.PER, "0"), + (LinkTlvs.DUP, "0"), + (LinkTlvs.JITTER, 0), + (LinkTlvs.TYPE, LinkTypes.WIRED.value), + (LinkTlvs.INTERFACE1_NUMBER, intf_one), + (LinkTlvs.INTERFACE1_IP4, address_one), + (LinkTlvs.INTERFACE1_IP4_MASK, 24), + (LinkTlvs.INTERFACE1_MAC, mac_one), + (LinkTlvs.INTERFACE2_NUMBER, intf_two), + (LinkTlvs.INTERFACE2_IP4, address_two), + (LinkTlvs.INTERFACE2_IP4_MASK, 24), + (LinkTlvs.INTERFACE2_MAC, mac_two), + ]) + + +def command_message(node, command): + """ + Create an execute command TLV message. + + :param node: node to execute command for + :param command: command to execute + :return: tlv message + :rtype: core.api.coreapi.CoreExecMessage + """ + flags = MessageFlags.STRING.value | MessageFlags.TEXT.value + return CoreExecMessage.create(flags, [ + (ExecuteTlvs.NODE, node.objid), + (ExecuteTlvs.NUMBER, 1), + (ExecuteTlvs.COMMAND, command) + ]) + + +def state_message(state): + """ + Create a event TLV message for a new state. + + :param core.enumerations.EventTypes state: state to create message for + :return: tlv message + :rtype: core.api.coreapi.CoreEventMessage + """ + return CoreEventMessage.create(0, [ + (EventTlvs.TYPE, state.value) + ]) + + class Core(object): def __init__(self, session, ip_prefix): self.session = session @@ -123,6 +236,79 @@ class Core(object): self.session.emane.setconfig(emane_node.objid, emane_model.name, values) +class CoreServerTest(object): + def __init__(self): + # setup nodes + node_map = nodemaps.CLASSIC_NODES + nodeutils.set_node_map(node_map) + + # load emane services + quagga.load_services() + utility.load_services() + + address = ("localhost", CORE_API_PORT) + self.server = CoreServer(address, CoreRequestHandler, { + "numthreads": 1, + "daemonize": False, + }) + + self.distributed_server = "core2" + self.prefix = ipaddress.Ipv4Prefix("10.83.0.0/16") + self.session = None + self.request_handler = None + + def setup(self, distributed_address): + # create session + self.session = self.server.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.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) + 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) + message = CoreConfMessage.create(0, [ + (ConfigTlvs.OBJECT, "broker"), + (ConfigTlvs.TYPE, 0), + (ConfigTlvs.DATA_TYPES, (10,)), + (ConfigTlvs.VALUES, distributed) + ]) + self.request_handler.handle_message(message) + + # set session location + message = CoreConfMessage.create(0, [ + (ConfigTlvs.OBJECT, "location"), + (ConfigTlvs.TYPE, 0), + (ConfigTlvs.DATA_TYPES, (9, 9, 9, 9, 9, 9)), + (ConfigTlvs.VALUES, "0|0| 47.5766974863|-122.125920191|0.0|150.0") + ]) + self.request_handler.handle_message(message) + + # set services for host nodes + message = CoreConfMessage.create(0, [ + (ConfigTlvs.SESSION, str(self.session.session_id)), + (ConfigTlvs.OBJECT, "services"), + (ConfigTlvs.TYPE, 0), + (ConfigTlvs.DATA_TYPES, (10, 10, 10)), + (ConfigTlvs.VALUES, "host|DefaultRoute|SSH") + ]) + self.request_handler.handle_message(message) + + def shutdown(self): + self.server.shutdown() + self.server.server_close() + + @pytest.fixture() def session(): # configure default nodes @@ -167,20 +353,20 @@ def core(session, ip_prefix): @pytest.fixture() def cored(): - # configure default nodes - node_map = nodemaps.CLASSIC_NODES - nodeutils.set_node_map(node_map) - - # load emane services - quagga.load_services() - utility.load_services() - - address = ("localhost", CORE_API_PORT) - server = CoreServer(address, CoreRequestHandler, { - "numthreads": 1, - "daemonize": False - }) + # create and return server + server = CoreServerTest() yield server # cleanup server.shutdown() + + +def pytest_addoption(parser): + parser.addoption("--distributed", help="distributed server address") + + +def pytest_generate_tests(metafunc): + distributed_param = "distributed_address" + if distributed_param in metafunc.fixturenames: + distributed_address = metafunc.config.getoption("distributed") + metafunc.parametrize(distributed_param, [distributed_address]) diff --git a/daemon/tests/test_distributed.py b/daemon/tests/test_distributed.py index 830b3c29..dc36d62d 100644 --- a/daemon/tests/test_distributed.py +++ b/daemon/tests/test_distributed.py @@ -2,129 +2,13 @@ Unit tests for testing CORE with distributed networks. """ -from mock.mock import MagicMock +import conftest -from core.api.coreapi import CoreConfMessage -from core.api.coreapi import CoreConfigTlv -from core.api.coreapi import CoreEventMessage -from core.api.coreapi import CoreEventTlv from core.api.coreapi import CoreExecMessage -from core.api.coreapi import CoreExecuteTlv -from core.api.coreapi import CoreLinkMessage -from core.api.coreapi import CoreLinkTlv -from core.api.coreapi import CoreNodeMessage -from core.api.coreapi import CoreNodeTlv -from core.corehandlers import CoreRequestHandler -from core.enumerations import CORE_API_PORT -from core.enumerations import ConfigTlvs -from core.enumerations import EventTlvs from core.enumerations import EventTypes from core.enumerations import ExecuteTlvs -from core.enumerations import LinkTlvs -from core.enumerations import LinkTypes from core.enumerations import MessageFlags -from core.enumerations import NodeTlvs from core.enumerations import NodeTypes -from core.misc import ipaddress -from core.misc import structutils -from core.misc.ipaddress import MacAddress - - -def node_message(objid, name, emulation_server=None, node_type=NodeTypes.DEFAULT): - """ - Convenience method for creating a node TLV messages. - - :param int objid: node id - :param str name: node name - :param str emulation_server: distributed server name, if desired - :param core.enumerations.NodeTypes node_type: node type - :return: tlv message - :rtype: core.api.coreapi.CoreNodeMessage - """ - tlv_data = structutils.pack_values(CoreNodeTlv, [ - (NodeTlvs.NUMBER, objid), - (NodeTlvs.TYPE, node_type.value), - (NodeTlvs.NAME, name), - (NodeTlvs.MODEL, "host"), - (NodeTlvs.EMULATION_SERVER, emulation_server), - ]) - packed = CoreNodeMessage.pack(MessageFlags.ADD.value, tlv_data) - header_data = packed[:CoreNodeMessage.header_len] - return CoreNodeMessage(MessageFlags.ADD.value, header_data, tlv_data) - - -def link_message(n1, n2, intf_one=None, address_one=None, intf_two=None, address_two=None): - """ - Convenience method for creating link TLV messages. - - :param int n1: node one id - :param int n2: node two id - :param int intf_one: node one interface id - :param core.misc.ipaddress.IpAddress address_one: node one ip4 address - :param int intf_two: node two interface id - :param core.misc.ipaddress.IpAddress address_two: node two ip4 address - :return: tlv mesage - :rtype: core.api.coreapi.CoreLinkMessage - """ - mac_one, mac_two = None, None - if address_one: - mac_one = MacAddress.random() - if address_two: - mac_two = MacAddress.random() - - tlv_data = structutils.pack_values(CoreLinkTlv, [ - (LinkTlvs.N1_NUMBER, n1), - (LinkTlvs.N2_NUMBER, n2), - (LinkTlvs.DELAY, 0), - (LinkTlvs.BANDWIDTH, 0), - (LinkTlvs.PER, "0"), - (LinkTlvs.DUP, "0"), - (LinkTlvs.JITTER, 0), - (LinkTlvs.TYPE, LinkTypes.WIRED.value), - (LinkTlvs.INTERFACE1_NUMBER, intf_one), - (LinkTlvs.INTERFACE1_IP4, address_one), - (LinkTlvs.INTERFACE1_IP4_MASK, 24), - (LinkTlvs.INTERFACE1_MAC, mac_one), - (LinkTlvs.INTERFACE2_NUMBER, intf_two), - (LinkTlvs.INTERFACE2_IP4, address_two), - (LinkTlvs.INTERFACE2_IP4_MASK, 24), - (LinkTlvs.INTERFACE2_MAC, mac_two), - ]) - packed = CoreLinkMessage.pack(MessageFlags.ADD.value, tlv_data) - header_data = packed[:CoreLinkMessage.header_len] - return CoreLinkMessage(MessageFlags.ADD.value, header_data, tlv_data) - - -def command_message(node, command): - """ - Create an execute command TLV message. - - :param node: node to execute command for - :param command: command to execute - :return: tlv message - :rtype: core.api.coreapi.CoreExecMessage - """ - tlv_data = CoreExecuteTlv.pack(ExecuteTlvs.NODE.value, node.objid) - tlv_data += CoreExecuteTlv.pack(ExecuteTlvs.NUMBER.value, 1) - tlv_data += CoreExecuteTlv.pack(ExecuteTlvs.COMMAND.value, command) - flags = MessageFlags.STRING.value | MessageFlags.TEXT.value - packed = CoreExecMessage.pack(flags, tlv_data) - header_data = packed[:CoreExecMessage.header_len] - return CoreExecMessage(flags, header_data, tlv_data) - - -def state_message(state): - """ - Create a event TLV message for a new state. - - :param core.enumerations.EventTypes state: state to create message for - :return: tlv message - :rtype: core.api.coreapi.CoreEventMessage - """ - tlv_data = CoreEventTlv.pack(EventTlvs.TYPE.value, state.value) - packed = CoreEventMessage.pack(0, tlv_data) - header_data = packed[:CoreEventMessage.header_len] - return CoreEventMessage(0, header_data, tlv_data) def validate_response(replies, _): @@ -143,105 +27,113 @@ def validate_response(replies, _): class TestDistributed: - def test_distributed(self, cored): + def test_distributed(self, cored, distributed_address): """ Test session broker creation. - :param conftest.Core core: core fixture to test with + :param core.coreserver.CoreServer conftest.Core cored: core daemon server to test with + :param str distributed_address: distributed server to test against """ - # create session - session = cored.create_session(1) - session.master = True + assert distributed_address, "distributed server address was not provided" - # create request handler - request_mock = MagicMock() - request_mock.fileno = MagicMock(return_value=1) - request_handler = CoreRequestHandler(request_mock, "", cored) - request_handler.session = session - request_handler.add_session_handlers() - session.broker.session_clients.append(request_handler) - - # ip prefix for nodes - prefix = ipaddress.Ipv4Prefix("10.83.0.0/16") - - # add and setup server - distributed_server = "core2" - session.broker.addserver(distributed_server, "10.50.184.152", CORE_API_PORT) - session.broker.setupserver(distributed_server) - - # have broker handle a configuration state change - session.set_state(state=EventTypes.DEFINITION_STATE.value) - message = state_message(EventTypes.CONFIGURATION_STATE) - request_handler.handle_message(message) - - # add broker server for distributed core - tlv_data = structutils.pack_values(CoreConfigTlv, [ - # (ConfigTlvs.SESSION, str(session.session_id)), - (ConfigTlvs.OBJECT, "broker"), - (ConfigTlvs.TYPE, 0), - (ConfigTlvs.DATA_TYPES, (10,)), - (ConfigTlvs.VALUES, "core2:10.50.184.152:4038"), - ]) - packed = CoreConfMessage.pack(0, tlv_data) - header = packed[:CoreConfMessage.header_len] - message = CoreConfMessage(0, header, tlv_data) - request_handler.handle_message(message) - - # set session location - tlv_data = structutils.pack_values(CoreConfigTlv, [ - # (ConfigTlvs.SESSION, str(session.session_id)), - (ConfigTlvs.OBJECT, "location"), - (ConfigTlvs.TYPE, 0), - (ConfigTlvs.DATA_TYPES, (9, 9, 9, 9, 9, 9)), - (ConfigTlvs.VALUES, "0|0| 47.5766974863|-122.125920191|0.0|150.0"), - ]) - packed = CoreConfMessage.pack(0, tlv_data) - header = packed[:CoreConfMessage.header_len] - message = CoreConfMessage(0, header, tlv_data) - request_handler.handle_message(message) - - # set services for host nodes - tlv_data = structutils.pack_values(CoreConfigTlv, [ - (ConfigTlvs.SESSION, str(session.session_id)), - (ConfigTlvs.OBJECT, "services"), - (ConfigTlvs.TYPE, 0), - (ConfigTlvs.DATA_TYPES, (10, 10, 10)), - (ConfigTlvs.VALUES, "host|DefaultRoute|SSH"), - ]) - packed = CoreConfMessage.pack(0, tlv_data) - header = packed[:CoreConfMessage.header_len] - message = CoreConfMessage(0, header, tlv_data) - request_handler.handle_message(message) + # initialize server for testing + cored.setup(distributed_address) # create local node - message = node_message(1, "n1") - request_handler.handle_message(message) + message = conftest.node_message(1, "n1", model="host") + cored.request_handler.handle_message(message) - # create distributed node and give to broker - message = node_message(2, "n2", emulation_server=distributed_server) - request_handler.handle_message(message) + # create distributed node and assign to distributed server + message = conftest.node_message( + 2, + "n2", + emulation_server=cored.distributed_server, + model="host" + ) + cored.request_handler.handle_message(message) - # create distributed switch and give to broker - message = node_message(3, "n3", emulation_server=distributed_server, node_type=NodeTypes.SWITCH) - request_handler.handle_message(message) + # create distributed switch and assign to distributed server + message = conftest.node_message( + 3, + "n3", + emulation_server=cored.distributed_server, + node_type=NodeTypes.SWITCH + ) + cored.request_handler.handle_message(message) # link message one - ip4_address = prefix.addr(1) - message = link_message(1, 3, intf_one=0, address_one=ip4_address) - request_handler.handle_message(message) + ip4_address = cored.prefix.addr(1) + message = conftest.link_message(1, 3, intf_one=0, address_one=ip4_address) + cored.request_handler.handle_message(message) # link message two - ip4_address = prefix.addr(2) - message = link_message(3, 2, intf_two=0, address_two=ip4_address) - request_handler.handle_message(message) + ip4_address = cored.prefix.addr(2) + message = conftest.link_message(3, 2, intf_two=0, address_two=ip4_address) + cored.request_handler.handle_message(message) # change session to instantiation state - message = state_message(EventTypes.INSTANTIATION_STATE) - request_handler.handle_message(message) + message = conftest.state_message(EventTypes.INSTANTIATION_STATE) + cored.request_handler.handle_message(message) # test a ping command - node_one = session.get_object(1) - message = command_message(node_one, "ping -c 5 %s" % ip4_address) - request_handler.dispatch_replies = validate_response - request_handler.handle_message(message) + node_one = cored.session.get_object(1) + message = conftest.command_message(node_one, "ping -c 5 %s" % ip4_address) + cored.request_handler.dispatch_replies = validate_response + cored.request_handler.handle_message(message) + + def test_prouter(self, cored, distributed_address): + """ + Test session broker creation. + + :param core.coreserver.CoreServer conftest.Core cored: core daemon server to test with + :param str distributed_address: distributed server to test against + """ + + assert distributed_address, "distributed server address was not provided" + + # initialize server for testing + cored.setup(distributed_address) + + # create local node + message = conftest.node_message(1, "n1", model="host") + cored.request_handler.handle_message(message) + + # create distributed node and assign to distributed server + message = conftest.node_message( + 2, + "n2", + emulation_server=cored.distributed_server, + node_type=NodeTypes.PHYSICAL, + model="prouter" + ) + cored.request_handler.handle_message(message) + + # create distributed switch and assign to distributed server + message = conftest.node_message( + 3, + "n3", + node_type=NodeTypes.SWITCH + ) + cored.request_handler.handle_message(message) + + # link message one + ip4_address = cored.prefix.addr(1) + message = conftest.link_message(1, 3, intf_one=0, address_one=ip4_address) + cored.request_handler.handle_message(message) + + # link message two + ip4_address = cored.prefix.addr(2) + message = conftest.link_message(3, 2, intf_two=0, address_two=ip4_address) + cored.request_handler.handle_message(message) + + # change session to instantiation state + message = conftest.state_message(EventTypes.INSTANTIATION_STATE) + cored.request_handler.handle_message(message) + + # test a ping command + node_one = cored.session.get_object(1) + message = conftest.command_message(node_one, "ping -c 5 %s" % ip4_address) + cored.request_handler.dispatch_replies = validate_response + cored.request_handler.handle_message(message) + cored.request_handler.handle_message(message) diff --git a/daemon/tests/test_gui.py b/daemon/tests/test_gui.py index d64ef5c0..8d99bef4 100644 --- a/daemon/tests/test_gui.py +++ b/daemon/tests/test_gui.py @@ -109,7 +109,7 @@ class TestGui: """ # set core daemon to run in the background - thread = threading.Thread(target=cored.serve_forever) + thread = threading.Thread(target=cored.server.serve_forever) thread.daemon = True thread.start() @@ -168,7 +168,6 @@ class TestGui: core.session.broker.handlerawmsg(event_message) # Get the ip or last node and ping it from the first - print "pinging from the first to the last node" output, status = run_cmd(node_one, "ip -4 -o addr show dev eth0") pingip = output.split()[3].split("/")[0] output, status = run_cmd(node_two, "ping -c 5 " + pingip)