diff --git a/.github/workflows/daemon-checks.yml b/.github/workflows/daemon-checks.yml index 023f5165..21dd95dc 100644 --- a/.github/workflows/daemon-checks.yml +++ b/.github/workflows/daemon-checks.yml @@ -17,6 +17,8 @@ jobs: pip install pipenv cd daemon cp setup.py.in setup.py + cp core/constants.py.in core/constants.py + sed -i 's/True/False/g' core/constants.py pipenv install --dev - name: isort run: | @@ -30,3 +32,11 @@ jobs: run: | cd daemon pipenv run flake8 + - name: grpc + run: | + cd daemon/proto + pipenv run python -m grpc_tools.protoc -I . --python_out=.. --grpc_python_out=.. core/api/grpc/core.proto + - name: test + run: | + cd daemon + pipenv run test --mock diff --git a/daemon/core/api/grpc/client.py b/daemon/core/api/grpc/client.py index 496c722f..01a7b1f6 100644 --- a/daemon/core/api/grpc/client.py +++ b/daemon/core/api/grpc/client.py @@ -14,7 +14,7 @@ from core.api.grpc import core_pb2, core_pb2_grpc from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress -class InterfaceHelper(object): +class InterfaceHelper: """ Convenience class to help generate IP4 and IP6 addresses for gRPC clients. """ @@ -133,7 +133,7 @@ def start_streamer(stream, handler): thread.start() -class CoreGrpcClient(object): +class CoreGrpcClient: """ Provides convenience methods for interfacing with the CORE grpc server. """ @@ -148,6 +148,57 @@ class CoreGrpcClient(object): self.stub = None self.channel = None + def start_session( + self, + session_id, + nodes, + links, + location=None, + hooks=None, + emane_config=None, + emane_model_configs=None, + wlan_configs=None, + mobility_configs=None, + ): + """ + Start a session. + + :param int session_id: id of session + :param list nodes: list of nodes to create + :param list links: list of links to create + :param core_pb2.SessionLocation location: location to set + :param list[core_pb2.Hook] hooks: session hooks to set + :param dict emane_config: emane configuration to set + :param list emane_model_configs: emane model configurations to set + :param list wlan_configs: wlan configurations to set + :param list mobility_configs: mobility configurations to set + :return: start session response + :rtype: core_pb2.StartSessionResponse + """ + request = core_pb2.StartSessionRequest( + session_id=session_id, + nodes=nodes, + links=links, + location=location, + hooks=hooks, + emane_config=emane_config, + emane_model_configs=emane_model_configs, + wlan_configs=wlan_configs, + mobility_configs=mobility_configs, + ) + return self.stub.StartSession(request) + + def stop_session(self, session_id): + """ + Stop a running session. + + :param int session_id: id of session + :return: stop session response + :rtype: core_pb2.StopSessionResponse + """ + request = core_pb2.StopSessionRequest(session_id=session_id) + return self.stub.StopSession(request) + def create_session(self, session_id=None): """ Create a session. @@ -204,18 +255,6 @@ class CoreGrpcClient(object): request = core_pb2.GetSessionOptionsRequest(session_id=session_id) return self.stub.GetSessionOptions(request) - def get_session_options_group(self, session_id): - """ - Retrieve session options in a group list. - - :param int session_id: id of session - :return: response with a list of configuration groups - :rtype: core_pb2.GetSessionOptionsGroupResponse - :raises grpc.RpcError: when session doesn't exist - """ - request = core_pb2.GetSessionOptionsGroupRequest(session_id=session_id) - return self.stub.GetSessionOptionsGroup(request) - def set_session_options(self, session_id, config): """ Set options for a session. @@ -231,6 +270,33 @@ class CoreGrpcClient(object): ) return self.stub.SetSessionOptions(request) + def get_session_metadata(self, session_id): + """ + Retrieve session metadata as a dict with id mapping. + + :param int session_id: id of session + :return: response with metadata dict + :rtype: core_pb2.GetSessionMetadataResponse + :raises grpc.RpcError: when session doesn't exist + """ + request = core_pb2.GetSessionMetadataRequest(session_id=session_id) + return self.stub.GetSessionMetadata(request) + + def set_session_metadata(self, session_id, config): + """ + Set metadata for a session. + + :param int session_id: id of session + :param dict[str, str] config: configuration values to set + :return: response with result of success or failure + :rtype: core_pb2.SetSessionMetadataResponse + :raises grpc.RpcError: when session doesn't exist + """ + request = core_pb2.SetSessionMetadataRequest( + session_id=session_id, config=config + ) + return self.stub.SetSessionMetadata(request) + def get_session_location(self, session_id): """ Get session location. @@ -269,9 +335,11 @@ class CoreGrpcClient(object): :rtype: core_pb2.SetSessionLocationResponse :raises grpc.RpcError: when session doesn't exist """ - position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) + location = core_pb2.SessionLocation( + x=x, y=y, z=z, lat=lat, lon=lon, alt=alt, scale=scale + ) request = core_pb2.SetSessionLocationRequest( - session_id=session_id, position=position, scale=scale + session_id=session_id, location=location ) return self.stub.SetSessionLocation(request) @@ -587,8 +655,9 @@ class CoreGrpcClient(object): :rtype: core_pb2.SetMobilityConfigResponse :raises grpc.RpcError: when session or node doesn't exist """ + mobility_config = core_pb2.MobilityConfig(node_id=node_id, config=config) request = core_pb2.SetMobilityConfigRequest( - session_id=session_id, node_id=node_id, config=config + session_id=session_id, mobility_config=mobility_config ) return self.stub.SetMobilityConfig(request) @@ -772,8 +841,9 @@ class CoreGrpcClient(object): :rtype: core_pb2.SetWlanConfigResponse :raises grpc.RpcError: when session doesn't exist """ + wlan_config = core_pb2.WlanConfig(node_id=node_id, config=config) request = core_pb2.SetWlanConfigRequest( - session_id=session_id, node_id=node_id, config=config + session_id=session_id, wlan_config=wlan_config ) return self.stub.SetWlanConfig(request) @@ -846,12 +916,11 @@ class CoreGrpcClient(object): :rtype: core_pb2.SetEmaneModelConfigResponse :raises grpc.RpcError: when session doesn't exist """ + model_config = core_pb2.EmaneModelConfig( + node_id=node_id, model=model, config=config, interface_id=interface_id + ) request = core_pb2.SetEmaneModelConfigRequest( - session_id=session_id, - node_id=node_id, - model=model, - config=config, - interface_id=interface_id, + session_id=session_id, emane_model_config=model_config ) return self.stub.SetEmaneModelConfig(request) diff --git a/daemon/core/api/grpc/grpcutils.py b/daemon/core/api/grpc/grpcutils.py new file mode 100644 index 00000000..166807d0 --- /dev/null +++ b/daemon/core/api/grpc/grpcutils.py @@ -0,0 +1,321 @@ +import logging +import time + +from core import utils +from core.api.grpc import core_pb2 +from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions +from core.emulator.enumerations import LinkTypes, NodeTypes +from core.nodes.base import CoreNetworkBase +from core.nodes.ipaddress import MacAddress + +WORKERS = 10 + + +def add_node_data(node_proto): + """ + Convert node protobuf message to data for creating a node. + + :param core_pb2.Node node_proto: node proto message + :return: node type, id, and options + :rtype: tuple + """ + _id = node_proto.id + _type = node_proto.type + if _type is None: + _type = NodeTypes.DEFAULT.value + _type = NodeTypes(_type) + + options = NodeOptions(name=node_proto.name, model=node_proto.model) + options.icon = node_proto.icon + options.opaque = node_proto.opaque + options.image = node_proto.image + options.services = node_proto.services + if node_proto.server: + options.server = node_proto.server + + position = node_proto.position + options.set_position(position.x, position.y) + options.set_location(position.lat, position.lon, position.alt) + return _type, _id, options + + +def link_interface(interface_proto): + """ + Create interface data from interface proto. + + :param core_pb2.Interface interface_proto: interface proto + :return: interface data + :rtype: InterfaceData + """ + interface = None + if interface_proto: + name = interface_proto.name + if name == "": + name = None + mac = interface_proto.mac + if mac == "": + mac = None + else: + mac = MacAddress.from_string(mac) + interface = InterfaceData( + _id=interface_proto.id, + name=name, + mac=mac, + ip4=interface_proto.ip4, + ip4_mask=interface_proto.ip4mask, + ip6=interface_proto.ip6, + ip6_mask=interface_proto.ip6mask, + ) + return interface + + +def add_link_data(link_proto): + """ + Convert link proto to link interfaces and options data. + + :param core_pb2.Link link_proto: link proto + :return: link interfaces and options + :rtype: tuple + """ + interface_one = link_interface(link_proto.interface_one) + interface_two = link_interface(link_proto.interface_two) + + link_type = None + link_type_value = link_proto.type + if link_type_value is not None: + link_type = LinkTypes(link_type_value) + + options = LinkOptions(_type=link_type) + options_data = link_proto.options + if options_data: + options.delay = options_data.delay + options.bandwidth = options_data.bandwidth + options.per = options_data.per + options.dup = options_data.dup + options.jitter = options_data.jitter + options.mer = options_data.mer + options.burst = options_data.burst + options.mburst = options_data.mburst + options.unidirectional = options_data.unidirectional + options.key = options_data.key + options.opaque = options_data.opaque + + return interface_one, interface_two, options + + +def create_nodes(session, node_protos): + """ + Create nodes using a thread pool and wait for completion. + + :param core.emulator.session.Session session: session to create nodes in + :param list[core_pb2.Node] node_protos: node proto messages + :return: results and exceptions for created nodes + :rtype: tuple + """ + funcs = [] + for node_proto in node_protos: + _type, _id, options = add_node_data(node_proto) + args = (_type, _id, options) + funcs.append((session.add_node, args, {})) + start = time.monotonic() + results, exceptions = utils.threadpool(funcs) + total = time.monotonic() - start + logging.debug("grpc created nodes time: %s", total) + return results, exceptions + + +def create_links(session, link_protos): + """ + Create nodes using a thread pool and wait for completion. + + :param core.emulator.session.Session session: session to create nodes in + :param list[core_pb2.Link] link_protos: link proto messages + :return: results and exceptions for created links + :rtype: tuple + """ + funcs = [] + for link_proto in link_protos: + node_one_id = link_proto.node_one_id + node_two_id = link_proto.node_two_id + interface_one, interface_two, options = add_link_data(link_proto) + args = (node_one_id, node_two_id, interface_one, interface_two, options) + funcs.append((session.add_link, args, {})) + start = time.monotonic() + results, exceptions = utils.threadpool(funcs) + total = time.monotonic() - start + logging.debug("grpc created links time: %s", total) + return results, exceptions + + +def convert_value(value): + """ + Convert value into string. + + :param value: value + :return: string conversion of the value + :rtype: str + """ + if value is not None: + value = str(value) + return value + + +def get_config_options(config, configurable_options): + """ + Retrieve configuration options in a form that is used by the grpc server. + + :param dict config: configuration + :param core.config.ConfigurableOptions configurable_options: configurable options + :return: mapping of configuration ids to configuration options + :rtype: dict[str,core.api.grpc.core_pb2.ConfigOption] + """ + results = {} + for configuration in configurable_options.configurations(): + value = config[configuration.id] + config_option = core_pb2.ConfigOption( + label=configuration.label, + name=configuration.id, + value=value, + type=configuration.type.value, + select=configuration.options, + ) + results[configuration.id] = config_option + for config_group in configurable_options.config_groups(): + start = config_group.start - 1 + stop = config_group.stop + options = list(results.values())[start:stop] + for option in options: + option.group = config_group.name + return results + + +def get_links(session, node): + """ + Retrieve a list of links for grpc to use + + :param core.emulator.Session session: node's section + :param core.nodes.base.CoreNode node: node to get links from + :return: [core.api.grpc.core_pb2.Link] + """ + links = [] + for link_data in node.all_link_data(0): + link = convert_link(session, link_data) + links.append(link) + return links + + +def get_emane_model_id(node_id, interface_id): + """ + Get EMANE model id + + :param int node_id: node id + :param int interface_id: interface id + :return: EMANE model id + :rtype: int + """ + if interface_id >= 0: + return node_id * 1000 + interface_id + else: + return node_id + + +def convert_link(session, link_data): + """ + Convert link_data into core protobuf Link + + :param core.emulator.session.Session session: + :param core.emulator.data.LinkData link_data: + :return: core protobuf Link + :rtype: core.api.grpc.core_pb2.Link + """ + interface_one = None + if link_data.interface1_id is not None: + node = session.get_node(link_data.node1_id) + interface_name = None + if not isinstance(node, CoreNetworkBase): + interface = node.netif(link_data.interface1_id) + interface_name = interface.name + interface_one = core_pb2.Interface( + id=link_data.interface1_id, + name=interface_name, + mac=convert_value(link_data.interface1_mac), + ip4=convert_value(link_data.interface1_ip4), + ip4mask=link_data.interface1_ip4_mask, + ip6=convert_value(link_data.interface1_ip6), + ip6mask=link_data.interface1_ip6_mask, + ) + + interface_two = None + if link_data.interface2_id is not None: + node = session.get_node(link_data.node2_id) + interface_name = None + if not isinstance(node, CoreNetworkBase): + interface = node.netif(link_data.interface2_id) + interface_name = interface.name + interface_two = core_pb2.Interface( + id=link_data.interface2_id, + name=interface_name, + mac=convert_value(link_data.interface2_mac), + ip4=convert_value(link_data.interface2_ip4), + ip4mask=link_data.interface2_ip4_mask, + ip6=convert_value(link_data.interface2_ip6), + ip6mask=link_data.interface2_ip6_mask, + ) + + options = core_pb2.LinkOptions( + opaque=link_data.opaque, + jitter=link_data.jitter, + key=link_data.key, + mburst=link_data.mburst, + mer=link_data.mer, + per=link_data.per, + bandwidth=link_data.bandwidth, + burst=link_data.burst, + delay=link_data.delay, + dup=link_data.dup, + unidirectional=link_data.unidirectional, + ) + + return core_pb2.Link( + type=link_data.link_type, + node_one_id=link_data.node1_id, + node_two_id=link_data.node2_id, + interface_one=interface_one, + interface_two=interface_two, + options=options, + ) + + +def get_net_stats(): + """ + Retrieve status about the current interfaces in the system + + :return: send and receive status of the interfaces in the system + :rtype: dict + """ + with open("/proc/net/dev", "r") as f: + data = f.readlines()[2:] + + stats = {} + for line in data: + line = line.strip() + if not line: + continue + line = line.split() + line[0] = line[0].strip(":") + stats[line[0]] = {"rx": float(line[1]), "tx": float(line[9])} + + return stats + + +def session_location(session, location): + """ + Set session location based on location proto. + + :param core.emulator.session.Session session: session for location + :param core_pb2.SessionLocation location: location to set + :return: nothing + """ + session.location.refxyz = (location.x, location.y, location.z) + session.location.setrefgeo(location.lat, location.lon, location.alt) + session.location.refscale = location.scale diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index de73dca1..e1e1f24d 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -9,7 +9,14 @@ from queue import Empty, Queue import grpc -from core.api.grpc import core_pb2, core_pb2_grpc +from core.api.grpc import core_pb2, core_pb2_grpc, grpcutils +from core.api.grpc.grpcutils import ( + convert_value, + get_config_options, + get_emane_model_id, + get_links, + get_net_stats, +) from core.emane.nodes import EmaneNet from core.emulator.data import ( ConfigData, @@ -19,13 +26,11 @@ from core.emulator.data import ( LinkData, NodeData, ) -from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions -from core.emulator.enumerations import EventTypes, LinkTypes, MessageFlags, NodeTypes +from core.emulator.emudata import LinkOptions, NodeOptions +from core.emulator.enumerations import EventTypes, LinkTypes, MessageFlags from core.errors import CoreCommandError, CoreError from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility -from core.nodes.base import CoreNetworkBase from core.nodes.docker import DockerNode -from core.nodes.ipaddress import MacAddress from core.nodes.lxd import LxcNode from core.services.coreservices import ServiceManager @@ -33,167 +38,6 @@ _ONE_DAY_IN_SECONDS = 60 * 60 * 24 _INTERFACE_REGEX = re.compile(r"\d+") -def convert_value(value): - """ - Convert value into string. - - :param value: value - :return: string conversion of the value - :rtype: str - """ - if value is not None: - value = str(value) - return value - - -def get_config_options(config, configurable_options): - """ - Retrieve configuration options in a form that is used by the grpc server. - - :param dict config: configuration - :param core.config.ConfigurableOptions configurable_options: configurable options - :return: mapping of configuration ids to configuration options - :rtype: dict[str,core.api.grpc.core_pb2.ConfigOption] - """ - results = {} - for configuration in configurable_options.configurations(): - value = config[configuration.id] - config_option = core_pb2.ConfigOption( - label=configuration.label, - name=configuration.id, - value=value, - type=configuration.type.value, - select=configuration.options, - ) - results[configuration.id] = config_option - for config_group in configurable_options.config_groups(): - start = config_group.start - 1 - stop = config_group.stop - options = list(results.values())[start:stop] - for option in options: - option.group = config_group.name - return results - - -def get_links(session, node): - """ - Retrieve a list of links for grpc to use - - :param core.emulator.Session session: node's section - :param core.nodes.base.CoreNode node: node to get links from - :return: [core.api.grpc.core_pb2.Link] - """ - links = [] - for link_data in node.all_link_data(0): - link = convert_link(session, link_data) - links.append(link) - return links - - -def get_emane_model_id(node_id, interface_id): - """ - Get EMANE model id - - :param int node_id: node id - :param int interface_id: interface id - :return: EMANE model id - :rtype: int - """ - if interface_id >= 0: - return node_id * 1000 + interface_id - else: - return node_id - - -def convert_link(session, link_data): - """ - Convert link_data into core protobuf Link - - :param core.emulator.session.Session session: - :param core.emulator.data.LinkData link_data: - :return: core protobuf Link - :rtype: core.api.grpc.core_pb2.Link - """ - interface_one = None - if link_data.interface1_id is not None: - node = session.get_node(link_data.node1_id) - interface_name = None - if not isinstance(node, CoreNetworkBase): - interface = node.netif(link_data.interface1_id) - interface_name = interface.name - interface_one = core_pb2.Interface( - id=link_data.interface1_id, - name=interface_name, - mac=convert_value(link_data.interface1_mac), - ip4=convert_value(link_data.interface1_ip4), - ip4mask=link_data.interface1_ip4_mask, - ip6=convert_value(link_data.interface1_ip6), - ip6mask=link_data.interface1_ip6_mask, - ) - - interface_two = None - if link_data.interface2_id is not None: - node = session.get_node(link_data.node2_id) - interface_name = None - if not isinstance(node, CoreNetworkBase): - interface = node.netif(link_data.interface2_id) - interface_name = interface.name - interface_two = core_pb2.Interface( - id=link_data.interface2_id, - name=interface_name, - mac=convert_value(link_data.interface2_mac), - ip4=convert_value(link_data.interface2_ip4), - ip4mask=link_data.interface2_ip4_mask, - ip6=convert_value(link_data.interface2_ip6), - ip6mask=link_data.interface2_ip6_mask, - ) - - options = core_pb2.LinkOptions( - opaque=link_data.opaque, - jitter=link_data.jitter, - key=link_data.key, - mburst=link_data.mburst, - mer=link_data.mer, - per=link_data.per, - bandwidth=link_data.bandwidth, - burst=link_data.burst, - delay=link_data.delay, - dup=link_data.dup, - unidirectional=link_data.unidirectional, - ) - - return core_pb2.Link( - type=link_data.link_type, - node_one_id=link_data.node1_id, - node_two_id=link_data.node2_id, - interface_one=interface_one, - interface_two=interface_two, - options=options, - ) - - -def get_net_stats(): - """ - Retrieve status about the current interfaces in the system - - :return: send and receive status of the interfaces in the system - :rtype: dict - """ - with open("/proc/net/dev", "r") as f: - data = f.readlines()[2:] - - stats = {} - for line in data: - line = line.strip() - if not line: - continue - line = line.split() - line[0] = line[0].strip(":") - stats[line[0]] = {"rx": float(line[1]), "tx": float(line[9])} - - return stats - - class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): """ Create CoreGrpcServer instance @@ -202,7 +46,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): """ def __init__(self, coreemu): - super(CoreGrpcServer, self).__init__() + super().__init__() self.coreemu = coreemu self.running = True self.server = None @@ -237,7 +81,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): :param int session_id: session id :param grpc.ServicerContext context: - :return: session object that satisfies. If session not found then raise an exception. + :return: session object that satisfies, if session not found then raise an + exception :rtype: core.emulator.session.Session """ session = self.coreemu.sessions.get(session_id) @@ -260,6 +105,80 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): except CoreError: context.abort(grpc.StatusCode.NOT_FOUND, f"node {node_id} not found") + def StartSession(self, request, context): + """ + Start a session. + + :param core.api.grpc.core_pb2.StartSessionRequest request: start session request + :param context: grcp context + :return: start session response + :rtype: core.api.grpc.core_pb2.StartSessionResponse + """ + logging.debug("start session: %s", request) + session = self.get_session(request.session_id, context) + + # clear previous state and setup for creation + session.clear() + session.set_state(EventTypes.CONFIGURATION_STATE) + if not os.path.exists(session.session_dir): + os.mkdir(session.session_dir) + + # location + if request.HasField("location"): + grpcutils.session_location(session, request.location) + + # add all hooks + for hook in request.hooks: + session.add_hook(hook.state, hook.file, None, hook.data) + + # create nodes + grpcutils.create_nodes(session, request.nodes) + + # emane configs + config = session.emane.get_configs() + config.update(request.emane_config) + for config in request.emane_model_configs: + _id = get_emane_model_id(config.node_id, config.interface_id) + session.emane.set_model_config(_id, config.model, config.config) + + # wlan configs + for config in request.wlan_configs: + session.mobility.set_model_config( + config.node_id, BasicRangeModel.name, config.config + ) + + # mobility configs + for config in request.mobility_configs: + session.mobility.set_model_config( + config.node_id, Ns2ScriptedMobility.name, config.config + ) + + # create links + grpcutils.create_links(session, request.links) + + # set to instantiation and start + session.set_state(EventTypes.INSTANTIATION_STATE) + session.instantiate() + + return core_pb2.StartSessionResponse(result=True) + + def StopSession(self, request, context): + """ + Stop a running session. + + :param core.api.grpc.core_pb2.StopSessionRequest request: stop session request + :param context: grcp context + :return: stop session response + :rtype: core.api.grpc.core_pb2.StopSessionResponse + """ + logging.debug("stop session: %s", request) + session = self.get_session(request.session_id, context) + session.data_collect() + session.set_state(EventTypes.DATACOLLECT_STATE) + session.clear() + session.set_state(EventTypes.SHUTDOWN_STATE) + return core_pb2.StopSessionResponse(result=True) + def CreateSession(self, request, context): """ Create a session @@ -326,10 +245,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): session = self.get_session(request.session_id, context) x, y, z = session.location.refxyz lat, lon, alt = session.location.refgeo - position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) - return core_pb2.GetSessionLocationResponse( - position=position, scale=session.location.refscale + scale = session.location.refscale + location = core_pb2.SessionLocation( + x=x, y=y, z=z, lat=lat, lon=lon, alt=alt, scale=scale ) + return core_pb2.GetSessionLocationResponse(location=location) def SetSessionLocation(self, request, context): """ @@ -342,15 +262,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): """ logging.debug("set session location: %s", request) session = self.get_session(request.session_id, context) - session.location.refxyz = ( - request.position.x, - request.position.y, - request.position.z, - ) - session.location.setrefgeo( - request.position.lat, request.position.lon, request.position.alt - ) - session.location.refscale = request.scale + grpcutils.session_location(session, request.location) return core_pb2.SetSessionLocationResponse(result=True) def SetSessionState(self, request, context): @@ -419,6 +331,34 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): config.update(request.config) return core_pb2.SetSessionOptionsResponse(result=True) + def GetSessionMetadata(self, request, context): + """ + Retrieve session metadata. + + :param core.api.grpc.core_pb2.GetSessionMetadata request: get session metadata + request + :param grpc.ServicerContext context: context object + :return: get session metadata response + :rtype: core.api.grpc.core_pb2.GetSessionMetadata + """ + logging.debug("get session metadata: %s", request) + session = self.get_session(request.session_id, context) + return core_pb2.GetSessionMetadataResponse(config=session.metadata) + + def SetSessionMetadata(self, request, context): + """ + Update a session's metadata. + + :param core.api.grpc.core_pb2.SetSessionMetadata request: set metadata request + :param grpc.ServicerContext context: context object + :return: set metadata response + :rtype: core.api.grpc.core_pb2.SetSessionMetadataResponse + """ + logging.debug("set session metadata: %s", request) + session = self.get_session(request.session_id, context) + session.metadata = dict(request.config) + return core_pb2.SetSessionMetadataResponse(result=True) + def GetSession(self, request, context): """ Retrieve requested session @@ -761,32 +701,12 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): """ logging.debug("add node: %s", request) session = self.get_session(request.session_id, context) - - node_proto = request.node - node_id = node_proto.id - node_type = node_proto.type - if node_type is None: - node_type = NodeTypes.DEFAULT.value - node_type = NodeTypes(node_type) - - node_options = NodeOptions(name=node_proto.name, model=node_proto.model) - node_options.icon = node_proto.icon - node_options.opaque = node_proto.opaque - node_options.image = node_proto.image - node_options.services = node_proto.services - if node_proto.server: - node_options.emulation_server = node_proto.server - - position = node_proto.position - node_options.set_position(position.x, position.y) - node_options.set_location(position.lat, position.lon, position.alt) - node = session.add_node(_type=node_type, _id=node_id, node_options=node_options) - + _type, _id, options = grpcutils.add_node_data(request.node) + node = session.add_node(_type=_type, _id=_id, options=options) # configure emane if provided - emane_model = node_proto.emane + emane_model = request.node.emane if emane_model: - session.emane.set_model_config(node_id, emane_model) - + session.emane.set_model_config(id, emane_model) return core_pb2.AddNodeResponse(node_id=node.id) def GetNode(self, request, context): @@ -856,18 +776,18 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): logging.debug("edit node: %s", request) session = self.get_session(request.session_id, context) node = self.get_node(session, request.node_id, context) - node_options = NodeOptions() - node_options.icon = request.icon + options = NodeOptions() + options.icon = request.icon x = request.position.x y = request.position.y - node_options.set_position(x, y) + options.set_position(x, y) lat = request.position.lat lon = request.position.lon alt = request.position.alt - node_options.set_location(lat, lon, alt) + options.set_location(lat, lon, alt) result = True try: - session.update_node(node.id, node_options) + session.edit_node(node.id, options) node_data = node.data(0) session.broadcast_node(node_data) except CoreError: @@ -944,82 +864,16 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): :rtype: core.api.grpc.AddLinkResponse """ logging.debug("add link: %s", request) + # validate session and nodes session = self.get_session(request.session_id, context) - - # validate node exist self.get_node(session, request.link.node_one_id, context) self.get_node(session, request.link.node_two_id, context) + node_one_id = request.link.node_one_id node_two_id = request.link.node_two_id - - interface_one = None - interface_one_data = request.link.interface_one - if interface_one_data: - name = interface_one_data.name - if name == "": - name = None - mac = interface_one_data.mac - if mac == "": - mac = None - else: - mac = MacAddress.from_string(mac) - interface_one = InterfaceData( - _id=interface_one_data.id, - name=name, - mac=mac, - ip4=interface_one_data.ip4, - ip4_mask=interface_one_data.ip4mask, - ip6=interface_one_data.ip6, - ip6_mask=interface_one_data.ip6mask, - ) - - interface_two = None - interface_two_data = request.link.interface_two - if interface_two_data: - name = interface_two_data.name - if name == "": - name = None - mac = interface_two_data.mac - if mac == "": - mac = None - else: - mac = MacAddress.from_string(mac) - interface_two = InterfaceData( - _id=interface_two_data.id, - name=name, - mac=mac, - ip4=interface_two_data.ip4, - ip4_mask=interface_two_data.ip4mask, - ip6=interface_two_data.ip6, - ip6_mask=interface_two_data.ip6mask, - ) - - link_type = None - link_type_value = request.link.type - if link_type_value is not None: - link_type = LinkTypes(link_type_value) - - options_data = request.link.options - link_options = LinkOptions(_type=link_type) - if options_data: - link_options.delay = options_data.delay - link_options.bandwidth = options_data.bandwidth - link_options.per = options_data.per - link_options.dup = options_data.dup - link_options.jitter = options_data.jitter - link_options.mer = options_data.mer - link_options.burst = options_data.burst - link_options.mburst = options_data.mburst - link_options.unidirectional = options_data.unidirectional - link_options.key = options_data.key - link_options.opaque = options_data.opaque - + interface_one, interface_two, options = grpcutils.add_link_data(request.link) session.add_link( - node_one_id, - node_two_id, - interface_one, - interface_two, - link_options=link_options, + node_one_id, node_two_id, interface_one, interface_two, link_options=options ) return core_pb2.AddLinkResponse(result=True) @@ -1166,8 +1020,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): """ logging.debug("set mobility config: %s", request) session = self.get_session(request.session_id, context) + mobility_config = request.mobility_config session.mobility.set_model_config( - request.node_id, Ns2ScriptedMobility.name, request.config + mobility_config.node_id, Ns2ScriptedMobility.name, mobility_config.config ) return core_pb2.SetMobilityConfigResponse(result=True) @@ -1408,12 +1263,13 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): """ logging.debug("set wlan config: %s", request) session = self.get_session(request.session_id, context) + wlan_config = request.wlan_config session.mobility.set_model_config( - request.node_id, BasicRangeModel.name, request.config + wlan_config.node_id, BasicRangeModel.name, wlan_config.config ) if session.state == EventTypes.RUNTIME_STATE.value: - node = self.get_node(session, request.node_id, context) - node.updatemodel(request.config) + node = self.get_node(session, wlan_config.node_id, context) + node.updatemodel(wlan_config.config) return core_pb2.SetWlanConfigResponse(result=True) def GetEmaneConfig(self, request, context): @@ -1494,8 +1350,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): """ logging.debug("set emane model config: %s", request) session = self.get_session(request.session_id, context) - _id = get_emane_model_id(request.node_id, request.interface_id) - session.emane.set_model_config(_id, request.model, request.config) + model_config = request.emane_model_config + _id = get_emane_model_id(model_config.node_id, model_config.interface_id) + session.emane.set_model_config(_id, model_config.model, model_config.config) return core_pb2.SetEmaneModelConfigResponse(result=True) def GetEmaneModelConfigs(self, request, context): diff --git a/daemon/core/api/tlv/coreapi.py b/daemon/core/api/tlv/coreapi.py index 0fd16bf5..5cb013d8 100644 --- a/daemon/core/api/tlv/coreapi.py +++ b/daemon/core/api/tlv/coreapi.py @@ -27,7 +27,7 @@ from core.emulator.enumerations import ( from core.nodes.ipaddress import IpAddress, MacAddress -class CoreTlvData(object): +class CoreTlvData: """ Helper base class used for packing and unpacking values using struct. """ @@ -94,12 +94,12 @@ class CoreTlvDataObj(CoreTlvData): """ Convenience method for packing custom object data. - :param obj: custom object to pack + :param value: custom object to pack :return: length of data and the packed data itself :rtype: tuple """ value = cls.get_value(value) - return super(CoreTlvDataObj, cls).pack(value) + return super().pack(value) @classmethod def unpack(cls, data): @@ -109,7 +109,7 @@ class CoreTlvDataObj(CoreTlvData): :param data: data to unpack custom object from :return: unpacked custom object """ - data = super(CoreTlvDataObj, cls).unpack(data) + data = super().unpack(data) return cls.new_obj(data) @staticmethod @@ -348,7 +348,7 @@ class CoreTlvDataMacAddr(CoreTlvDataObj): return MacAddress(address=value[2:]) -class CoreTlv(object): +class CoreTlv: """ Base class for representing CORE TLVs. """ @@ -670,7 +670,7 @@ class CoreExceptionTlv(CoreTlv): } -class CoreMessage(object): +class CoreMessage: """ Base class for representing CORE messages. """ diff --git a/daemon/core/api/tlv/corehandlers.py b/daemon/core/api/tlv/corehandlers.py index 1e15377a..321306a2 100644 --- a/daemon/core/api/tlv/corehandlers.py +++ b/daemon/core/api/tlv/corehandlers.py @@ -83,13 +83,9 @@ class CoreHandler(socketserver.BaseRequestHandler): self.handler_threads.append(thread) thread.start() - self.master = False self.session = None self.session_clients = {} - - # core emulator self.coreemu = server.coreemu - utils.close_onexec(request.fileno()) socketserver.BaseRequestHandler.__init__(self, request, client_address, server) @@ -127,7 +123,7 @@ class CoreHandler(socketserver.BaseRequestHandler): for thread in self.handler_threads: logging.info("waiting for thread: %s", thread.getName()) thread.join(timeout) - if thread.isAlive(): + if thread.is_alive(): logging.warning( "joining %s failed: still alive after %s sec", thread.getName(), @@ -434,9 +430,7 @@ class CoreHandler(socketserver.BaseRequestHandler): tlv_data += coreapi.CoreRegisterTlv.pack( self.session.options.config_type, self.session.options.name ) - tlv_data += coreapi.CoreRegisterTlv.pack( - self.session.metadata.config_type, self.session.metadata.name - ) + tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.UTILITY.value, "metadata") return coreapi.CoreRegMessage.pack(MessageFlags.ADD.value, tlv_data) @@ -591,12 +585,8 @@ class CoreHandler(socketserver.BaseRequestHandler): port = self.request.getpeername()[1] # TODO: add shutdown handler for session - self.session = self.coreemu.create_session(port, master=False) + self.session = self.coreemu.create_session(port) logging.debug("created new session for client: %s", self.session.id) - - if self.master: - logging.debug("session set to master") - self.session.master = True clients = self.session_clients.setdefault(self.session.id, []) clients.append(self) @@ -698,12 +688,12 @@ class CoreHandler(socketserver.BaseRequestHandler): node_id = message.get_tlv(NodeTlvs.NUMBER.value) - node_options = NodeOptions( + options = NodeOptions( name=message.get_tlv(NodeTlvs.NAME.value), model=message.get_tlv(NodeTlvs.MODEL.value), ) - node_options.set_position( + options.set_position( x=message.get_tlv(NodeTlvs.X_POSITION.value), y=message.get_tlv(NodeTlvs.Y_POSITION.value), ) @@ -717,19 +707,19 @@ class CoreHandler(socketserver.BaseRequestHandler): alt = message.get_tlv(NodeTlvs.ALTITUDE.value) if alt is not None: alt = float(alt) - node_options.set_location(lat=lat, lon=lon, alt=alt) + options.set_location(lat=lat, lon=lon, alt=alt) - node_options.icon = message.get_tlv(NodeTlvs.ICON.value) - node_options.canvas = message.get_tlv(NodeTlvs.CANVAS.value) - node_options.opaque = message.get_tlv(NodeTlvs.OPAQUE.value) - node_options.emulation_server = message.get_tlv(NodeTlvs.EMULATION_SERVER.value) + options.icon = message.get_tlv(NodeTlvs.ICON.value) + options.canvas = message.get_tlv(NodeTlvs.CANVAS.value) + options.opaque = message.get_tlv(NodeTlvs.OPAQUE.value) + options.server = message.get_tlv(NodeTlvs.EMULATION_SERVER.value) services = message.get_tlv(NodeTlvs.SERVICES.value) if services: - node_options.services = services.split("|") + options.services = services.split("|") if message.flags & MessageFlags.ADD.value: - node = self.session.add_node(node_type, node_id, node_options) + node = self.session.add_node(node_type, node_id, options) if node: if message.flags & MessageFlags.STRING.value: self.node_status_request[node.id] = True @@ -748,7 +738,7 @@ class CoreHandler(socketserver.BaseRequestHandler): replies.append(coreapi.CoreNodeMessage.pack(flags, tlvdata)) # node update else: - self.session.update_node(node_id, node_options) + self.session.edit_node(node_id, options) return replies @@ -942,7 +932,7 @@ class CoreHandler(socketserver.BaseRequestHandler): file_name = sys.argv[0] if os.path.splitext(file_name)[1].lower() == ".xml": - session = self.coreemu.create_session(master=False) + session = self.coreemu.create_session() try: session.open_xml(file_name) except Exception: @@ -1012,17 +1002,6 @@ class CoreHandler(socketserver.BaseRequestHandler): logging.debug("ignoring Register message") else: # register capabilities with the GUI - self.master = True - - # find the session containing this client and set the session to master - for _id in self.coreemu.sessions: - clients = self.session_clients.get(_id, []) - if self in clients: - session = self.coreemu.sessions[_id] - logging.debug("setting session to master: %s", session.id) - session.master = True - break - replies.append(self.register()) replies.append(self.session_message()) @@ -1065,7 +1044,7 @@ class CoreHandler(socketserver.BaseRequestHandler): replies = self.handle_config_session(message_type, config_data) elif config_data.object == self.session.location.name: self.handle_config_location(message_type, config_data) - elif config_data.object == self.session.metadata.name: + elif config_data.object == "metadata": replies = self.handle_config_metadata(message_type, config_data) elif config_data.object == "broker": self.handle_config_broker(message_type, config_data) @@ -1092,10 +1071,14 @@ class CoreHandler(socketserver.BaseRequestHandler): if message_type == ConfigFlags.RESET: node_id = config_data.node - self.session.location.reset() - self.session.services.reset() - self.session.mobility.config_reset(node_id) - self.session.emane.config_reset(node_id) + if node_id is not None: + self.session.mobility.config_reset(node_id) + self.session.emane.config_reset(node_id) + else: + self.session.location.reset() + self.session.services.reset() + self.session.mobility.config_reset() + self.session.emane.config_reset() else: raise Exception(f"cant handle config all: {message_type}") @@ -1147,7 +1130,7 @@ class CoreHandler(socketserver.BaseRequestHandler): replies = [] if message_type == ConfigFlags.REQUEST: node_id = config_data.node - metadata_configs = self.session.metadata.get_configs() + metadata_configs = self.session.metadata if metadata_configs is None: metadata_configs = {} data_values = "|".join( @@ -1157,7 +1140,7 @@ class CoreHandler(socketserver.BaseRequestHandler): config_response = ConfigData( message_type=0, node=node_id, - object=self.session.metadata.name, + object="metadata", type=ConfigFlags.NONE.value, data_types=data_types, data_values=data_values, @@ -1167,7 +1150,7 @@ class CoreHandler(socketserver.BaseRequestHandler): values = ConfigShim.str_to_dict(config_data.data_values) for key in values: value = values[key] - self.session.metadata.set_config(key, value) + self.session.metadata[key] = value return replies def handle_config_broker(self, message_type, config_data): @@ -1437,11 +1420,6 @@ class CoreHandler(socketserver.BaseRequestHandler): config = ConfigShim.str_to_dict(values_str) self.session.emane.set_configs(config) - # extra logic to start slave Emane object after nemid has been configured from the master - if message_type == ConfigFlags.UPDATE and self.session.master is False: - # instantiation was previously delayed by setup returning Emane.NOT_READY - self.session.instantiate() - return replies def handle_config_emane_models(self, message_type, config_data): @@ -1609,20 +1587,11 @@ class CoreHandler(socketserver.BaseRequestHandler): for _id in self.session.nodes: self.send_node_emulation_id(_id) elif event_type == EventTypes.RUNTIME_STATE: - if self.session.master: - logging.warning( - "Unexpected event message: RUNTIME state received at session master" - ) - else: - # master event queue is started in session.checkruntime() - self.session.start_events() + logging.warning("Unexpected event message: RUNTIME state received") elif event_type == EventTypes.DATACOLLECT_STATE: self.session.data_collect() elif event_type == EventTypes.SHUTDOWN_STATE: - if self.session.master: - logging.warning( - "Unexpected event message: SHUTDOWN state received at session master" - ) + logging.warning("Unexpected event message: SHUTDOWN state received") elif event_type in { EventTypes.START, EventTypes.STOP, @@ -1820,9 +1789,7 @@ class CoreHandler(socketserver.BaseRequestHandler): # set session to join self.session = session - # add client to session broker and set master if needed - if self.master: - self.session.master = True + # add client to session broker clients = self.session_clients.setdefault(self.session.id, []) clients.append(self) @@ -1982,18 +1949,17 @@ class CoreHandler(socketserver.BaseRequestHandler): self.session.broadcast_config(config_data) # send session metadata - metadata_configs = self.session.metadata.get_configs() + metadata_configs = self.session.metadata if metadata_configs: data_values = "|".join( [f"{x}={metadata_configs[x]}" for x in metadata_configs] ) data_types = tuple( - ConfigDataTypes.STRING.value - for _ in self.session.metadata.get_configs() + ConfigDataTypes.STRING.value for _ in self.session.metadata ) config_data = ConfigData( message_type=0, - object=self.session.metadata.name, + object="metadata", type=ConfigFlags.NONE.value, data_types=data_types, data_values=data_values, @@ -2018,7 +1984,6 @@ class CoreUdpHandler(CoreHandler): MessageTypes.EVENT.value: self.handle_event_message, MessageTypes.SESSION.value: self.handle_session_message, } - self.master = False self.session = None self.coreemu = server.mainserver.coreemu socketserver.BaseRequestHandler.__init__(self, request, client_address, server) diff --git a/daemon/core/api/tlv/dataconversion.py b/daemon/core/api/tlv/dataconversion.py index a8525a05..8e1c270d 100644 --- a/daemon/core/api/tlv/dataconversion.py +++ b/daemon/core/api/tlv/dataconversion.py @@ -24,7 +24,7 @@ def convert_node(node_data): (NodeTlvs.IP6_ADDRESS, node_data.ip6_address), (NodeTlvs.MODEL, node_data.model), (NodeTlvs.EMULATION_ID, node_data.emulation_id), - (NodeTlvs.EMULATION_SERVER, node_data.emulation_server), + (NodeTlvs.EMULATION_SERVER, node_data.server), (NodeTlvs.SESSION, node_data.session), (NodeTlvs.X_POSITION, node_data.x_position), (NodeTlvs.Y_POSITION, node_data.y_position), diff --git a/daemon/core/config.py b/daemon/core/config.py index e55d5f17..e8e73300 100644 --- a/daemon/core/config.py +++ b/daemon/core/config.py @@ -8,7 +8,7 @@ from collections import OrderedDict from core.emulator.data import ConfigData -class ConfigShim(object): +class ConfigShim: """ Provides helper methods for converting newer configuration values into TLV compatible formats. """ @@ -102,7 +102,7 @@ class ConfigShim(object): ) -class Configuration(object): +class Configuration: """ Represents a configuration options. """ @@ -131,7 +131,7 @@ class Configuration(object): return f"{self.__class__.__name__}(id={self.id}, type={self.type}, default={self.default}, options={self.options})" -class ConfigurableManager(object): +class ConfigurableManager: """ Provides convenience methods for storing and retrieving configuration options for nodes. """ @@ -240,7 +240,7 @@ class ConfigurableManager(object): return self.node_configurations.get(node_id) -class ConfigGroup(object): +class ConfigGroup: """ Defines configuration group tabs used for display by ConfigurationOptions. """ @@ -258,7 +258,7 @@ class ConfigGroup(object): self.stop = stop -class ConfigurableOptions(object): +class ConfigurableOptions: """ Provides a base for defining configuration options within CORE. """ @@ -309,7 +309,7 @@ class ModelManager(ConfigurableManager): """ Creates a ModelManager object. """ - super(ModelManager, self).__init__() + super().__init__() self.models = {} self.node_models = {} diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index b40ed118..dc3e2acf 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -62,7 +62,7 @@ class EmaneManager(ModelManager): :param core.session.Session session: session this manager is tied to :return: nothing """ - super(EmaneManager, self).__init__() + super().__init__() self.session = session self._emane_nets = {} self._emane_node_lock = threading.Lock() @@ -128,7 +128,7 @@ class EmaneManager(ModelManager): return config def config_reset(self, node_id=None): - super(EmaneManager, self).config_reset(node_id) + super().config_reset(node_id) self.set_configs(self.emane_config.default_values()) def emane_check(self): @@ -269,37 +269,34 @@ class EmaneManager(ModelManager): # control network bridge required for EMANE 0.9.2 # - needs to exist when eventservice binds to it (initeventservice) - if self.session.master: - otadev = self.get_config("otamanagerdevice") - netidx = self.session.get_control_net_index(otadev) - logging.debug( - "emane ota manager device: index(%s) otadev(%s)", netidx, otadev + otadev = self.get_config("otamanagerdevice") + netidx = self.session.get_control_net_index(otadev) + logging.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev) + if netidx < 0: + logging.error( + "EMANE cannot start, check core config. invalid OTA device provided: %s", + otadev, ) + return EmaneManager.NOT_READY + + self.session.add_remove_control_net( + net_index=netidx, remove=False, conf_required=False + ) + eventdev = self.get_config("eventservicedevice") + logging.debug("emane event service device: eventdev(%s)", eventdev) + if eventdev != otadev: + netidx = self.session.get_control_net_index(eventdev) + logging.debug("emane event service device index: %s", netidx) if netidx < 0: logging.error( - "EMANE cannot start, check core config. invalid OTA device provided: %s", - otadev, + "EMANE cannot start, check core config. invalid event service device: %s", + eventdev, ) return EmaneManager.NOT_READY self.session.add_remove_control_net( net_index=netidx, remove=False, conf_required=False ) - eventdev = self.get_config("eventservicedevice") - logging.debug("emane event service device: eventdev(%s)", eventdev) - if eventdev != otadev: - netidx = self.session.get_control_net_index(eventdev) - logging.debug("emane event service device index: %s", netidx) - if netidx < 0: - logging.error( - "EMANE cannot start, check core config. invalid event service device: %s", - eventdev, - ) - return EmaneManager.NOT_READY - - self.session.add_remove_control_net( - net_index=netidx, remove=False, conf_required=False - ) self.check_node_models() return EmaneManager.SUCCESS @@ -376,7 +373,6 @@ class EmaneManager(ModelManager): with self._emane_node_lock: self._emane_nets.clear() - # don't clear self._ifccounts here; NEM counts are needed for buildxml self.platformport = self.session.options.get_config_int( "emane_platform_port", 8100 ) @@ -585,7 +581,7 @@ class EmaneManager(ModelManager): args = f"{emanecmd} -f {log_file} {platform_xml}" output = node.cmd(args) logging.info("node(%s) emane daemon running: %s", node.name, args) - logging.info("node(%s) emane daemon output: %s", node.name, output) + logging.debug("node(%s) emane daemon output: %s", node.name, output) if not run_emane_on_host: return @@ -871,7 +867,7 @@ class EmaneGlobalModel(EmaneModel): ] def __init__(self, session, _id=None): - super(EmaneGlobalModel, self).__init__(session, _id) + super().__init__(session, _id) def build_xml_files(self, config, interface=None): raise NotImplementedError diff --git a/daemon/core/emane/ieee80211abg.py b/daemon/core/emane/ieee80211abg.py index d4df4282..e7a4d0d7 100644 --- a/daemon/core/emane/ieee80211abg.py +++ b/daemon/core/emane/ieee80211abg.py @@ -19,4 +19,4 @@ class EmaneIeee80211abgModel(emanemodel.EmaneModel): cls.mac_defaults["pcrcurveuri"] = os.path.join( emane_prefix, "share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml" ) - super(EmaneIeee80211abgModel, cls).load(emane_prefix) + super().load(emane_prefix) diff --git a/daemon/core/emane/nodes.py b/daemon/core/emane/nodes.py index e0ceee2f..c7817b41 100644 --- a/daemon/core/emane/nodes.py +++ b/daemon/core/emane/nodes.py @@ -30,7 +30,7 @@ class EmaneNet(CoreNetworkBase): is_emane = True def __init__(self, session, _id=None, name=None, start=True, server=None): - super(EmaneNet, self).__init__(session, _id, name, start, server) + super().__init__(session, _id, name, start, server) self.conf = "" self.up = False self.nemidmap = {} diff --git a/daemon/core/emane/rfpipe.py b/daemon/core/emane/rfpipe.py index 312a5bb2..51820b7d 100644 --- a/daemon/core/emane/rfpipe.py +++ b/daemon/core/emane/rfpipe.py @@ -19,4 +19,4 @@ class EmaneRfPipeModel(emanemodel.EmaneModel): cls.mac_defaults["pcrcurveuri"] = os.path.join( emane_prefix, "share/emane/xml/models/mac/rfpipe/rfpipepcr.xml" ) - super(EmaneRfPipeModel, cls).load(emane_prefix) + super().load(emane_prefix) diff --git a/daemon/core/emane/tdma.py b/daemon/core/emane/tdma.py index afad9d10..59ed9e04 100644 --- a/daemon/core/emane/tdma.py +++ b/daemon/core/emane/tdma.py @@ -32,7 +32,7 @@ class EmaneTdmaModel(emanemodel.EmaneModel): emane_prefix, "share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml", ) - super(EmaneTdmaModel, cls).load(emane_prefix) + super().load(emane_prefix) cls.mac_config.insert( 0, Configuration( diff --git a/daemon/core/emulator/coreemu.py b/daemon/core/emulator/coreemu.py index 9c8b35ee..cdba4e44 100644 --- a/daemon/core/emulator/coreemu.py +++ b/daemon/core/emulator/coreemu.py @@ -29,7 +29,7 @@ signal.signal(signal.SIGUSR1, signal_handler) signal.signal(signal.SIGUSR2, signal_handler) -class CoreEmu(object): +class CoreEmu: """ Provides logic for creating and configuring CORE sessions and the nodes within them. """ @@ -49,7 +49,7 @@ class CoreEmu(object): self.config = config # session management - self.session_id_gen = IdGen(_id=0) + self.session_id_gen = IdGen() self.sessions = {} # load services @@ -79,18 +79,18 @@ class CoreEmu(object): :return: nothing """ logging.info("shutting down all sessions") + self.session_id_gen.id = 0 sessions = self.sessions.copy() self.sessions.clear() for _id in sessions: session = sessions[_id] session.shutdown() - def create_session(self, _id=None, master=True, _cls=Session): + def create_session(self, _id=None, _cls=Session): """ - Create a new CORE session, set to master if running standalone. + Create a new CORE session. :param int _id: session id for new session - :param bool master: sets session to master :param class _cls: Session class to use :return: created session :rtype: EmuSession @@ -103,9 +103,6 @@ class CoreEmu(object): session = _cls(_id, config=self.config) logging.info("created session: %s", _id) - if master: - session.master = True - self.sessions[_id] = session return session diff --git a/daemon/core/emulator/data.py b/daemon/core/emulator/data.py index d5cb3f57..ba0dd457 100644 --- a/daemon/core/emulator/data.py +++ b/daemon/core/emulator/data.py @@ -64,7 +64,7 @@ NodeData = collections.namedtuple( "ip6_address", "model", "emulation_id", - "emulation_server", + "server", "session", "x_position", "y_position", diff --git a/daemon/core/emulator/distributed.py b/daemon/core/emulator/distributed.py index 5abb5bef..c2e90d6b 100644 --- a/daemon/core/emulator/distributed.py +++ b/daemon/core/emulator/distributed.py @@ -21,7 +21,7 @@ LOCK = threading.Lock() CMD_HIDE = True -class DistributedServer(object): +class DistributedServer: """ Provides distributed server interactions. """ @@ -101,7 +101,7 @@ class DistributedServer(object): os.unlink(temp.name) -class DistributedController(object): +class DistributedController: """ Provides logic for dealing with remote tunnels and distributed servers. """ diff --git a/daemon/core/emulator/emudata.py b/daemon/core/emulator/emudata.py index 5a38c69c..5e59eaae 100644 --- a/daemon/core/emulator/emudata.py +++ b/daemon/core/emulator/emudata.py @@ -4,7 +4,7 @@ from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress from core.nodes.physical import PhysicalNode -class IdGen(object): +class IdGen: def __init__(self, _id=0): self.id = _id @@ -29,7 +29,7 @@ def create_interface(node, network, interface_data): ifindex=interface_data.id, ifname=interface_data.name, ) - return node.netif(interface_data.id, network) + return node.netif(interface_data.id) def link_config(network, interface, link_options, devname=None, interface_two=None): @@ -61,7 +61,7 @@ def link_config(network, interface, link_options, devname=None, interface_two=No network.linkconfig(**config) -class NodeOptions(object): +class NodeOptions: """ Options for creating and updating nodes within core. """ @@ -87,7 +87,7 @@ class NodeOptions(object): self.lon = None self.alt = None self.emulation_id = None - self.emulation_server = None + self.server = None self.image = image def set_position(self, x, y): @@ -115,7 +115,7 @@ class NodeOptions(object): self.alt = alt -class LinkOptions(object): +class LinkOptions: """ Options for creating and updating links within core. """ @@ -145,7 +145,7 @@ class LinkOptions(object): self.opaque = None -class IpPrefixes(object): +class IpPrefixes: """ Convenience class to help generate IP4 and IP6 addresses for nodes within CORE. """ @@ -236,7 +236,7 @@ class IpPrefixes(object): ) -class InterfaceData(object): +class InterfaceData: """ Convenience class for storing interface data. """ diff --git a/daemon/core/emulator/enumerations.py b/daemon/core/emulator/enumerations.py index 9f5abece..f426774e 100644 --- a/daemon/core/emulator/enumerations.py +++ b/daemon/core/emulator/enumerations.py @@ -73,18 +73,15 @@ class NodeTypes(Enum): DEFAULT = 0 PHYSICAL = 1 - TBD = 3 SWITCH = 4 HUB = 5 WIRELESS_LAN = 6 RJ45 = 7 TUNNEL = 8 - KTUNNEL = 9 EMANE = 10 TAP_BRIDGE = 11 PEER_TO_PEER = 12 CONTROL_NET = 13 - EMANE_NET = 14 DOCKER = 15 LXC = 16 diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index 725accf8..cba99e8e 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -12,7 +12,6 @@ import subprocess import tempfile import threading import time -from multiprocessing.pool import ThreadPool from core import constants, utils from core.emane.emanemanager import EmaneManager @@ -27,7 +26,7 @@ from core.emulator.emudata import ( link_config, ) from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes -from core.emulator.sessionconfig import SessionConfig, SessionMetaData +from core.emulator.sessionconfig import SessionConfig from core.errors import CoreError from core.location.corelocation import CoreLocation from core.location.event import EventLoop @@ -55,15 +54,12 @@ from core.xml.corexml import CoreXmlReader, CoreXmlWriter NODES = { NodeTypes.DEFAULT: CoreNode, NodeTypes.PHYSICAL: PhysicalNode, - NodeTypes.TBD: None, NodeTypes.SWITCH: SwitchNode, NodeTypes.HUB: HubNode, NodeTypes.WIRELESS_LAN: WlanNode, NodeTypes.RJ45: Rj45Node, NodeTypes.TUNNEL: TunnelNode, - NodeTypes.KTUNNEL: None, NodeTypes.EMANE: EmaneNet, - NodeTypes.EMANE_NET: None, NodeTypes.TAP_BRIDGE: GreTapBridge, NodeTypes.PEER_TO_PEER: PtpNet, NodeTypes.CONTROL_NET: CtrlNet, @@ -74,7 +70,7 @@ NODES_TYPE = {NODES[x]: x for x in NODES} CTRL_NET_ID = 9001 -class Session(object): +class Session: """ CORE session manager. """ @@ -88,7 +84,6 @@ class Session(object): :param bool mkdir: flag to determine if a directory should be made """ self.id = _id - self.master = False # define and create session directory when desired self.session_dir = os.path.join(tempfile.gettempdir(), f"pycore.{self.id}") @@ -134,7 +129,7 @@ class Session(object): for key in config: value = config[key] self.options.set_config(key, value) - self.metadata = SessionMetaData() + self.metadata = {} # distributed support and logic self.distributed = DistributedController(self) @@ -243,7 +238,6 @@ class Session(object): ) return node_one, node_two, net_one, net_two, tunnel - # TODO: this doesn't appear to ever be used, EMANE or basic wireless range def _link_wireless(self, objects, connect): """ Objects to deal with when connecting/disconnecting wireless links. @@ -358,11 +352,7 @@ class Session(object): net_one.name, net_two.name, ) - if isinstance(net_two, Rj45Node): - interface = net_two.linknet(net_one) - else: - interface = net_one.linknet(net_two) - + interface = net_one.linknet(net_two) link_config(net_one, interface, link_options) if not link_options.unidirectional: @@ -597,11 +587,11 @@ class Session(object): 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) + interface = node_two.netif(interface_two_id) link_config(net_one, interface, link_options) elif not node_two: # node2 = layer 2node, node1 = layer3 node - interface = node_one.netif(interface_one_id, net_one) + interface = node_one.netif(interface_one_id) link_config(net_one, interface, link_options) else: common_networks = node_one.commonnets(node_two) @@ -634,13 +624,13 @@ class Session(object): if node_two: node_two.lock.release() - def add_node(self, _type=NodeTypes.DEFAULT, _id=None, node_options=None, _cls=None): + def add_node(self, _type=NodeTypes.DEFAULT, _id=None, options=None, _cls=None): """ Add a node to the session, based on the provided node data. :param core.emulator.enumerations.NodeTypes _type: type of node to create :param int _id: id for node, defaults to None for generated id - :param core.emulator.emudata.NodeOptions node_options: data to create node with + :param core.emulator.emudata.NodeOptions options: data to create node with :param class _cls: optional custom class to use for a created node :return: created node :raises core.CoreError: when an invalid node type is given @@ -666,18 +656,16 @@ class Session(object): break # generate name if not provided - if not node_options: - node_options = NodeOptions() - name = node_options.name + if not options: + options = NodeOptions() + name = options.name if not name: name = f"{node_class.__name__}{_id}" # verify distributed server - server = self.distributed.servers.get(node_options.emulation_server) - if node_options.emulation_server is not None and server is None: - raise CoreError( - f"invalid distributed server: {node_options.emulation_server}" - ) + server = self.distributed.servers.get(options.server) + if options.server is not None and server is None: + raise CoreError(f"invalid distributed server: {options.server}") # create node logging.info( @@ -693,7 +681,7 @@ class Session(object): _id=_id, name=name, start=start, - image=node_options.image, + image=options.image, server=server, ) else: @@ -702,20 +690,20 @@ class Session(object): ) # set node attributes - node.icon = node_options.icon - node.canvas = node_options.canvas - node.opaque = node_options.opaque + node.icon = options.icon + node.canvas = options.canvas + node.opaque = options.opaque # set node position and broadcast it - self.set_node_position(node, node_options) + self.set_node_position(node, options) # add services to needed nodes if isinstance(node, (CoreNode, PhysicalNode, DockerNode, LxcNode)): - node.type = node_options.model + node.type = options.model logging.debug("set node type: %s", node.type) - self.services.add_services(node, node.type, node_options.services) + self.services.add_services(node, node.type, options.services) - # boot nodes if created after runtime, CoreNodes, Physical, and RJ45 are all nodes + # boot nodes after runtime, CoreNodes, Physical, and RJ45 are all nodes is_boot_node = isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node) if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node: self.write_nodes() @@ -724,12 +712,12 @@ class Session(object): return node - def update_node(self, node_id, node_options): + def edit_node(self, node_id, options): """ - Update node information. + Edit node information. :param int node_id: id of node to update - :param core.emulator.emudata.NodeOptions node_options: data to update node with + :param core.emulator.emudata.NodeOptions 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 @@ -738,26 +726,26 @@ class Session(object): node = self.get_node(node_id) # set node position and broadcast it - self.set_node_position(node, node_options) + self.set_node_position(node, options) # update attributes - node.canvas = node_options.canvas - node.icon = node_options.icon + node.canvas = options.canvas + node.icon = options.icon - def set_node_position(self, node, node_options): + def set_node_position(self, node, options): """ Set position for a node, use lat/lon/alt if needed. :param node: node to set position for - :param core.emulator.emudata.NodeOptions node_options: data for node + :param core.emulator.emudata.NodeOptions options: data for node :return: nothing """ # extract location values - x = node_options.x - y = node_options.y - lat = node_options.lat - lon = node_options.lon - alt = node_options.alt + x = options.x + y = options.y + lat = options.lat + lon = options.lon + alt = options.alt # check if we need to generate position from lat/lon/alt has_empty_position = all(i is None for i in [x, y]) @@ -886,10 +874,15 @@ class Session(object): :return: nothing """ + self.emane.shutdown() self.delete_nodes() self.distributed.shutdown() self.del_hooks() self.emane.reset() + self.emane.config_reset() + self.location.reset() + self.services.reset() + self.mobility.config_reset() def start_events(self): """ @@ -908,49 +901,18 @@ class Session(object): """ self.mobility.handleevent(event_data) - def create_wireless_node(self, _id=None, node_options=None): + def set_location(self, lat, lon, alt, scale): """ - Create a wireless node for use within an wireless/EMANE networks. + Set session geospatial location. - :param int _id: int for node, defaults to None and will be generated - :param core.emulator.emudata.NodeOptions node_options: options for emane node, model will always be "mdr" - :return: new emane node - :rtype: core.nodes.network.WlanNode + :param float lat: latitude + :param float lon: longitude + :param float alt: altitude + :param float scale: reference scale + :return: nothing """ - if not node_options: - node_options = NodeOptions() - node_options.model = "mdr" - return self.add_node( - _type=NodeTypes.DEFAULT, _id=_id, node_options=node_options - ) - - def create_emane_network( - self, - model, - geo_reference, - geo_scale=None, - node_options=NodeOptions(), - config=None, - ): - """ - Convenience method for creating an emane network. - - :param model: emane model to use for emane network - :param geo_reference: geo reference point to use for emane node locations - :param geo_scale: geo scale to use for emane node locations, defaults to 1.0 - :param core.emulator.emudata.NodeOptions node_options: options for emane node being created - :param dict config: emane model configuration - :return: create emane network - """ - # required to be set for emane to function properly - self.location.setrefgeo(*geo_reference) - if geo_scale: - self.location.refscale = geo_scale - - # create and return network - emane_network = self.add_node(_type=NodeTypes.EMANE, node_options=node_options) - self.emane.set_model(emane_network, model, config) - return emane_network + self.location.setrefgeo(lat, lon, alt) + self.location.refscale = scale def shutdown(self): """ @@ -960,13 +922,11 @@ class Session(object): self.set_state(EventTypes.DATACOLLECT_STATE, send_event=True) self.set_state(EventTypes.SHUTDOWN_STATE, send_event=True) - # shutdown/cleanup feature helpers - self.emane.shutdown() - self.sdt.shutdown() + # clear out current core session + self.clear() - # remove and shutdown all nodes and tunnels - self.delete_nodes() - self.distributed.shutdown() + # shutdown sdt + self.sdt.shutdown() # remove this sessions working directory preserve = self.options.get_config("preservedir") == "1" @@ -1389,26 +1349,28 @@ class Session(object): """ # delete node and check for session shutdown if a node was removed logging.info("deleting node(%s)", _id) - result = False + node = None with self._nodes_lock: if _id in self.nodes: node = self.nodes.pop(_id) - node.shutdown() - result = True - if result: + if node: + node.shutdown() self.check_shutdown() - return result + return node is not None def delete_nodes(self): """ Clear the nodes dictionary, and call shutdown for each node. """ with self._nodes_lock: + funcs = [] while self.nodes: _, node = self.nodes.popitem() - node.shutdown() + funcs.append((node.shutdown, [], {})) + utils.threadpool(funcs) + self.node_id_gen.id = 0 def write_nodes(self): """ @@ -1547,11 +1509,13 @@ class Session(object): # stop node services with self._nodes_lock: + funcs = [] for node_id in self.nodes: node = self.nodes[node_id] - # TODO: determine if checking for CoreNode alone is ok if isinstance(node, CoreNodeBase): - self.services.stop_services(node) + args = (node,) + funcs.append((self.services.stop_services, args, {})) + utils.threadpool(funcs) # shutdown emane self.emane.shutdown() @@ -1559,7 +1523,8 @@ class Session(object): # update control interface hosts self.update_control_interface_hosts(remove=True) - # remove all four possible control networks. Does nothing if ctrlnet is not installed. + # remove all four possible control networks. Does nothing if ctrlnet is not + # installed. self.add_remove_control_interface(node=None, net_index=0, remove=True) self.add_remove_control_interface(node=None, net_index=1, remove=True) self.add_remove_control_interface(node=None, net_index=2, remove=True) @@ -1590,6 +1555,18 @@ class Session(object): ssid = (self.id >> 8) ^ (self.id & ((1 << 8) - 1)) return f"{ssid:x}" + def boot_node(self, node): + """ + Boot node by adding a control interface when necessary and starting + node services. + + :param core.nodes.base.CoreNodeBase node: node to boot + :return: nothing + """ + logging.info("booting node(%s): %s", node.name, [x.name for x in node.services]) + self.add_remove_control_interface(node=node, remove=False) + self.services.boot_services(node) + def boot_nodes(self): """ Invoke the boot() procedure for all nodes and send back node @@ -1597,29 +1574,18 @@ class Session(object): request flag. """ with self._nodes_lock: - pool = ThreadPool() - results = [] - - start = time.time() + funcs = [] + start = time.monotonic() for _id in self.nodes: node = self.nodes[_id] if isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node): - # add a control interface if configured - logging.info( - "booting node(%s): %s", - node.name, - [x.name for x in node.services], - ) - self.add_remove_control_interface(node=node, remove=False) - result = pool.apply_async(self.services.boot_services, (node,)) - results.append(result) - - pool.close() - pool.join() - for result in results: - result.get() - logging.debug("boot run time: %s", time.time() - start) - + args = (node,) + funcs.append((self.boot_node, args, {})) + results, exceptions = utils.threadpool(funcs) + total = time.monotonic() - start + logging.debug("boot run time: %s", total) + if exceptions: + raise CoreError(exceptions) self.update_control_interface_hosts() def get_control_net_prefixes(self): @@ -1732,28 +1698,19 @@ class Session(object): prefixes = prefix_spec.split() if len(prefixes) > 1: # a list of per-host prefixes is provided - assign_address = True - if self.master: - try: - # split first (master) entry into server and prefix - prefix = prefixes[0].split(":", 1)[1] - except IndexError: - # no server name. possibly only one server - prefix = prefixes[0] - - # len(prefixes) == 1 + try: + # split first (master) entry into server and prefix + prefix = prefixes[0].split(":", 1)[1] + except IndexError: + # no server name. possibly only one server + prefix = prefixes[0] else: - # TODO: can we get the server name from the servers.conf or from the node - # assignments?o - # with one prefix, only master gets a ctrlnet address - assign_address = self.master prefix = prefixes[0] logging.info( - "controlnet(%s) prefix(%s) assign(%s) updown(%s) serverintf(%s)", + "controlnet(%s) prefix(%s) updown(%s) serverintf(%s)", _id, prefix, - assign_address, updown_script, server_interface, ) @@ -1761,7 +1718,7 @@ class Session(object): cls=CtrlNet, _id=_id, prefix=prefix, - assign_address=assign_address, + assign_address=True, updown_script=updown_script, serverintf=server_interface, ) @@ -1778,7 +1735,7 @@ class Session(object): If conf_reqd is False, the control network may be built even when the user has not configured one (e.g. for EMANE.) - :param core.nodes.base.CoreNode node: node to add or remove control interface + :param core.nodes.base.CoreNodeBase node: node to add or remove control interface :param int net_index: network index :param bool remove: flag to check if it should be removed :param bool conf_required: flag to check if conf is required diff --git a/daemon/core/emulator/sessionconfig.py b/daemon/core/emulator/sessionconfig.py index dc5a5133..eb38474b 100644 --- a/daemon/core/emulator/sessionconfig.py +++ b/daemon/core/emulator/sessionconfig.py @@ -61,7 +61,7 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions): config_type = RegisterTlvs.UTILITY.value def __init__(self): - super(SessionConfig, self).__init__() + super().__init__() self.set_configs(self.default_values()) def get_config( @@ -71,9 +71,7 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions): config_type=ConfigurableManager._default_type, default=None, ): - value = super(SessionConfig, self).get_config( - _id, node_id, config_type, default - ) + value = super().get_config(_id, node_id, config_type, default) if value == "": value = default return value @@ -89,14 +87,3 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions): if value is not None: value = int(value) return value - - -class SessionMetaData(ConfigurableManager): - """ - Metadata is simply stored in a configs[] dict. Key=value pairs are - passed in from configure messages destined to the "metadata" object. - The data is not otherwise interpreted or processed. - """ - - name = "metadata" - config_type = RegisterTlvs.UTILITY.value diff --git a/daemon/core/location/corelocation.py b/daemon/core/location/corelocation.py index 5f9e11e3..aeab8896 100644 --- a/daemon/core/location/corelocation.py +++ b/daemon/core/location/corelocation.py @@ -11,7 +11,7 @@ from core.emulator.enumerations import RegisterTlvs from core.location import utm -class CoreLocation(object): +class CoreLocation: """ Member of session class for handling global location data. This keeps track of a latitude/longitude/altitude reference point and scale in diff --git a/daemon/core/location/event.py b/daemon/core/location/event.py index 1872ac18..11e535d3 100644 --- a/daemon/core/location/event.py +++ b/daemon/core/location/event.py @@ -23,7 +23,7 @@ class Timer(threading.Thread): :param args: function arguments :param kwargs: function keyword arguments """ - super(Timer, self).__init__() + super().__init__() self.interval = interval self.function = function @@ -70,7 +70,7 @@ class Timer(threading.Thread): @total_ordering -class Event(object): +class Event: """ Provides event objects that can be used within the EventLoop class. """ @@ -118,7 +118,7 @@ class Event(object): self.canceled = True -class EventLoop(object): +class EventLoop: """ Provides an event loop for running events. """ diff --git a/daemon/core/location/mobility.py b/daemon/core/location/mobility.py index 92bd1b4b..085962ff 100644 --- a/daemon/core/location/mobility.py +++ b/daemon/core/location/mobility.py @@ -38,7 +38,7 @@ class MobilityManager(ModelManager): :param core.emulator.session.Session session: session this manager is tied to """ - super(MobilityManager, self).__init__() + super().__init__() self.session = session self.models[BasicRangeModel.name] = BasicRangeModel self.models[Ns2ScriptedMobility.name] = Ns2ScriptedMobility @@ -302,7 +302,7 @@ class BasicRangeModel(WirelessModel): :param core.session.Session session: related core session :param int _id: object id """ - super(BasicRangeModel, self).__init__(session=session, _id=_id) + super().__init__(session, _id) self.session = session self.wlan = session.get_node(_id) self._netifs = {} @@ -535,7 +535,7 @@ class BasicRangeModel(WirelessModel): @total_ordering -class WayPoint(object): +class WayPoint: """ Maintains information regarding waypoints. """ @@ -587,8 +587,7 @@ class WayPointMobility(WirelessModel): :param int _id: object id :return: """ - super(WayPointMobility, self).__init__(session=session, _id=_id) - + super().__init__(session=session, _id=_id) self.state = self.STATE_STOPPED self.queue = [] self.queue_copy = [] @@ -945,7 +944,7 @@ class Ns2ScriptedMobility(WayPointMobility): :param core.emulator.session.Session session: CORE session instance :param int _id: object id """ - super(Ns2ScriptedMobility, self).__init__(session=session, _id=_id) + super().__init__(session, _id) self._netifs = {} self._netifslock = threading.Lock() @@ -1137,7 +1136,7 @@ class Ns2ScriptedMobility(WayPointMobility): """ logging.info("starting script") laststate = self.state - super(Ns2ScriptedMobility, self).start() + super().start() if laststate == self.STATE_PAUSED: self.statescript("unpause") @@ -1147,7 +1146,7 @@ class Ns2ScriptedMobility(WayPointMobility): :return: nothing """ - super(Ns2ScriptedMobility, self).run() + super().run() self.statescript("run") def pause(self): @@ -1156,7 +1155,7 @@ class Ns2ScriptedMobility(WayPointMobility): :return: nothing """ - super(Ns2ScriptedMobility, self).pause() + super().pause() self.statescript("pause") def stop(self, move_initial=True): @@ -1166,7 +1165,7 @@ class Ns2ScriptedMobility(WayPointMobility): :param bool move_initial: flag to check if we should move node to initial position :return: nothing """ - super(Ns2ScriptedMobility, self).stop(move_initial=move_initial) + super().stop(move_initial=move_initial) self.statescript("stop") def statescript(self, typestr): diff --git a/daemon/core/nodes/base.py b/daemon/core/nodes/base.py index 8b35410d..72fc0fe1 100644 --- a/daemon/core/nodes/base.py +++ b/daemon/core/nodes/base.py @@ -21,7 +21,7 @@ from core.nodes.netclient import get_net_client _DEFAULT_MTU = 1500 -class NodeBase(object): +class NodeBase: """ Base class for CORE nodes (nodes and networks) """ @@ -139,7 +139,7 @@ class NodeBase(object): if sort: return [self._netif[x] for x in sorted(self._netif)] else: - return self._netif.values() + return list(self._netif.values()) def numnetif(self): """ @@ -158,11 +158,9 @@ class NodeBase(object): :return: interface index if found, -1 otherwise :rtype: int """ - for ifindex in self._netif: if self._netif[ifindex] is netif: return ifindex - return -1 def newifindex(self): @@ -194,9 +192,9 @@ class NodeBase(object): x, y, _ = self.getposition() model = self.type - emulation_server = None + server = None if self.server is not None: - emulation_server = self.server.name + server = self.server.name services = self.services if services is not None: @@ -217,7 +215,7 @@ class NodeBase(object): longitude=lon, altitude=alt, model=model, - emulation_server=emulation_server, + server=server, services=services, ) @@ -252,7 +250,7 @@ class CoreNodeBase(NodeBase): :param core.emulator.distributed.DistributedServer server: remote server node will run on, default is None for localhost """ - super(CoreNodeBase, self).__init__(session, _id, name, start, server) + super().__init__(session, _id, name, start, server) self.services = [] self.nodedir = None self.tmpnodedir = False @@ -294,7 +292,6 @@ class CoreNodeBase(NodeBase): if ifindex in self._netif: raise ValueError(f"ifindex {ifindex} already exists") self._netif[ifindex] = netif - # TODO: this should have probably been set ahead, seems bad to me, check for failure and fix netif.netindex = ifindex def delnetif(self, ifindex): @@ -310,13 +307,11 @@ class CoreNodeBase(NodeBase): netif.shutdown() del netif - # TODO: net parameter is not used, remove - def netif(self, ifindex, net=None): + def netif(self, ifindex): """ Retrieve network interface. :param int ifindex: index of interface to retrieve - :param core.nodes.interface.CoreInterface net: network node :return: network interface, or None if not found :rtype: core.nodes.interface.CoreInterface """ @@ -357,7 +352,7 @@ class CoreNodeBase(NodeBase): :param z: z position :return: nothing """ - changed = super(CoreNodeBase, self).setposition(x, y, z) + changed = super().setposition(x, y, z) if changed: for netif in self.netifs(sort=True): netif.setposition(x, y, z) @@ -435,7 +430,7 @@ class CoreNode(CoreNodeBase): :param core.emulator.distributed.DistributedServer server: remote server node will run on, default is None for localhost """ - super(CoreNode, self).__init__(session, _id, name, start, server) + super().__init__(session, _id, name, start, server) self.nodedir = nodedir self.ctrlchnlname = os.path.abspath( os.path.join(self.session.session_dir, self.name) @@ -632,15 +627,14 @@ class CoreNode(CoreNodeBase): :rtype: int """ with self.lock: - return super(CoreNode, self).newifindex() + return super().newifindex() - def newveth(self, ifindex=None, ifname=None, net=None): + def newveth(self, ifindex=None, ifname=None): """ Create a new interface. :param int ifindex: index for the new interface :param str ifname: name for the new interface - :param core.nodes.base.CoreNetworkBase net: network to associate interface with :return: nothing """ with self.lock: @@ -692,13 +686,12 @@ class CoreNode(CoreNodeBase): return ifindex - def newtuntap(self, ifindex=None, ifname=None, net=None): + def newtuntap(self, ifindex=None, ifname=None): """ Create a new tunnel tap. :param int ifindex: interface index :param str ifname: interface name - :param net: network to associate with :return: interface index :rtype: int """ @@ -803,7 +796,7 @@ class CoreNode(CoreNodeBase): with self.lock: # TODO: emane specific code if net.is_emane is True: - ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname, net=net) + ifindex = self.newtuntap(ifindex, ifname) # TUN/TAP is not ready for addressing yet; the device may # take some time to appear, and installing it into a # namespace after it has been bound removes addressing; @@ -815,7 +808,7 @@ class CoreNode(CoreNodeBase): netif.addaddr(address) return ifindex else: - ifindex = self.newveth(ifindex=ifindex, ifname=ifname, net=net) + ifindex = self.newveth(ifindex, ifname) if net is not None: self.attachnet(ifindex, net) @@ -930,7 +923,7 @@ class CoreNetworkBase(NodeBase): :param core.emulator.distributed.DistributedServer server: remote server node will run on, default is None for localhost """ - super(CoreNetworkBase, self).__init__(session, _id, name, start, server) + super().__init__(session, _id, name, start, server) self._linked = {} self._linked_lock = threading.Lock() @@ -1072,7 +1065,7 @@ class CoreNetworkBase(NodeBase): return all_links -class Position(object): +class Position: """ Helper class for Cartesian coordinate position """ diff --git a/daemon/core/nodes/client.py b/daemon/core/nodes/client.py index 3596bfa7..1e949c04 100644 --- a/daemon/core/nodes/client.py +++ b/daemon/core/nodes/client.py @@ -8,7 +8,7 @@ from core import utils from core.constants import VCMD_BIN -class VnodeClient(object): +class VnodeClient: """ Provides client functionality for interacting with a virtual node. """ diff --git a/daemon/core/nodes/docker.py b/daemon/core/nodes/docker.py index a70cfb39..20d6ec20 100644 --- a/daemon/core/nodes/docker.py +++ b/daemon/core/nodes/docker.py @@ -10,7 +10,7 @@ from core.nodes.base import CoreNode from core.nodes.netclient import get_net_client -class DockerClient(object): +class DockerClient: def __init__(self, name, image, run): self.name = name self.image = image @@ -96,9 +96,7 @@ class DockerNode(CoreNode): if image is None: image = "ubuntu" self.image = image - super(DockerNode, self).__init__( - session, _id, name, nodedir, bootsh, start, server - ) + super().__init__(session, _id, name, nodedir, bootsh, start, server) def create_node_net_client(self, use_ovs): """ diff --git a/daemon/core/nodes/interface.py b/daemon/core/nodes/interface.py index a32103b8..84e8f399 100644 --- a/daemon/core/nodes/interface.py +++ b/daemon/core/nodes/interface.py @@ -10,7 +10,7 @@ from core.errors import CoreCommandError from core.nodes.netclient import get_net_client -class CoreInterface(object): +class CoreInterface: """ Base class for network interfaces. """ @@ -40,8 +40,10 @@ class CoreInterface(object): self.poshook = lambda a, b, c, d: None # used with EMANE self.transport_type = None - # interface index on the network + # node interface index self.netindex = None + # net interface index + self.netifi = None # index used to find flow data self.flow_id = None self.server = server @@ -230,7 +232,7 @@ class Veth(CoreInterface): :raises CoreCommandError: when there is a command exception """ # note that net arg is ignored - CoreInterface.__init__(self, session, node, name, mtu, server) + super().__init__(session, node, name, mtu, server) self.localname = localname self.up = False if start: @@ -291,7 +293,7 @@ class TunTap(CoreInterface): will run on, default is None for localhost :param bool start: start flag """ - CoreInterface.__init__(self, session, node, name, mtu, server) + super().__init__(session, node, name, mtu, server) self.localname = localname self.up = False self.transport_type = "virtual" @@ -474,7 +476,7 @@ class GreTap(CoreInterface): will run on, default is None for localhost :raises CoreCommandError: when there is a command exception """ - CoreInterface.__init__(self, session, node, name, mtu, server) + super().__init__(session, node, name, mtu, server) if _id is None: # from PyCoreObj _id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF diff --git a/daemon/core/nodes/ipaddress.py b/daemon/core/nodes/ipaddress.py index df2309ab..d77b3a94 100644 --- a/daemon/core/nodes/ipaddress.py +++ b/daemon/core/nodes/ipaddress.py @@ -9,7 +9,7 @@ import struct from socket import AF_INET, AF_INET6 -class MacAddress(object): +class MacAddress: """ Provides mac address utilities for use within core. """ @@ -77,7 +77,7 @@ class MacAddress(object): return cls(tmpbytes[2:]) -class IpAddress(object): +class IpAddress: """ Provides ip utilities and functionality for use within core. """ @@ -202,7 +202,7 @@ class IpAddress(object): return struct.unpack("!I", value)[0] -class IpPrefix(object): +class IpPrefix: """ Provides ip address generation and prefix utilities. """ @@ -401,7 +401,7 @@ class Ipv4Prefix(IpPrefix): :param str prefixstr: ip prefix """ - IpPrefix.__init__(self, AF_INET, prefixstr) + super().__init__(AF_INET, prefixstr) class Ipv6Prefix(IpPrefix): @@ -415,7 +415,7 @@ class Ipv6Prefix(IpPrefix): :param str prefixstr: ip prefix """ - IpPrefix.__init__(self, AF_INET6, prefixstr) + super().__init__(AF_INET6, prefixstr) def is_ip_address(af, addrstr): diff --git a/daemon/core/nodes/lxd.py b/daemon/core/nodes/lxd.py index 9d5dedc4..fc2ee5cc 100644 --- a/daemon/core/nodes/lxd.py +++ b/daemon/core/nodes/lxd.py @@ -10,7 +10,7 @@ from core.errors import CoreCommandError from core.nodes.base import CoreNode -class LxdClient(object): +class LxdClient: def __init__(self, name, image, run): self.name = name self.image = image @@ -89,9 +89,7 @@ class LxcNode(CoreNode): if image is None: image = "ubuntu" self.image = image - super(LxcNode, self).__init__( - session, _id, name, nodedir, bootsh, start, server - ) + super().__init__(session, _id, name, nodedir, bootsh, start, server) def alive(self): """ @@ -217,6 +215,6 @@ class LxcNode(CoreNode): self.cmd(f"chmod {mode:o} {filename}") def addnetif(self, netif, ifindex): - super(LxcNode, self).addnetif(netif, ifindex) + super().addnetif(netif, ifindex) # adding small delay to allow time for adding addresses to work correctly time.sleep(0.5) diff --git a/daemon/core/nodes/netclient.py b/daemon/core/nodes/netclient.py index 8bd58be7..b201493f 100644 --- a/daemon/core/nodes/netclient.py +++ b/daemon/core/nodes/netclient.py @@ -19,7 +19,7 @@ def get_net_client(use_ovs, run): return LinuxNetClient(run) -class LinuxNetClient(object): +class LinuxNetClient: """ Client for creating Linux bridges and ip interfaces for nodes. """ diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index e57a0a95..80a730e2 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -21,7 +21,7 @@ from core.nodes.netclient import get_net_client ebtables_lock = threading.Lock() -class EbtablesQueue(object): +class EbtablesQueue: """ Helper class for queuing up ebtables commands into rate-limited atomic commits. This improves performance and reliability when there are @@ -257,7 +257,7 @@ class CoreNetwork(CoreNetworkBase): will run on, default is None for localhost :param policy: network policy """ - CoreNetworkBase.__init__(self, session, _id, name, start, server) + super().__init__(session, _id, name, start, server) if name is None: name = str(self.id) if policy is not None: @@ -337,8 +337,6 @@ class CoreNetwork(CoreNetworkBase): del self.session self.up = False - # TODO: this depends on a subtype with localname defined, seems like the - # wrong place for this to live def attach(self, netif): """ Attach a network interface. @@ -348,8 +346,7 @@ class CoreNetwork(CoreNetworkBase): """ if self.up: netif.net_client.create_interface(self.brname, netif.localname) - - CoreNetworkBase.attach(self, netif) + super().attach(netif) def detach(self, netif): """ @@ -360,8 +357,7 @@ class CoreNetwork(CoreNetworkBase): """ if self.up: netif.net_client.delete_interface(self.brname, netif.localname) - - CoreNetworkBase.detach(self, netif) + super().detach(netif) def linked(self, netif1, netif2): """ @@ -654,7 +650,7 @@ class GreTapBridge(CoreNetwork): :return: nothing """ - CoreNetwork.startup(self) + super().startup() if self.gretap: self.attach(self.gretap) @@ -668,7 +664,7 @@ class GreTapBridge(CoreNetwork): self.detach(self.gretap) self.gretap.shutdown() self.gretap = None - CoreNetwork.shutdown(self) + super().shutdown() def addrconfig(self, addrlist): """ @@ -755,7 +751,7 @@ class CtrlNet(CoreNetwork): self.assign_address = assign_address self.updown_script = updown_script self.serverintf = serverintf - CoreNetwork.__init__(self, session, _id, name, start, server) + super().__init__(session, _id, name, start, server) def add_addresses(self, address): """ @@ -786,8 +782,7 @@ class CtrlNet(CoreNetwork): if self.net_client.existing_bridges(self.id): raise CoreError(f"old bridges exist for node: {self.id}") - CoreNetwork.startup(self) - + super().startup() logging.info("added control network bridge: %s %s", self.brname, self.prefix) if self.hostid and self.assign_address: @@ -835,7 +830,7 @@ class CtrlNet(CoreNetwork): except CoreCommandError: logging.exception("error issuing shutdown script shutdown") - CoreNetwork.shutdown(self) + super().shutdown() def all_link_data(self, flags): """ @@ -866,8 +861,7 @@ class PtpNet(CoreNetwork): raise ValueError( "Point-to-point links support at most 2 network interfaces" ) - - CoreNetwork.attach(self, netif) + super().attach(netif) def data(self, message_type, lat=None, lon=None, alt=None): """ @@ -1007,23 +1001,14 @@ class HubNode(CoreNetwork): policy = "ACCEPT" type = "hub" - def __init__(self, session, _id=None, name=None, start=True, server=None): + def startup(self): """ - Creates a HubNode instance. + Startup for a hub node, that disables mac learning after normal startup. - :param core.session.Session session: core session instance - :param int _id: node id - :param str name: node namee - :param bool start: start flag - :param core.emulator.distributed.DistributedServer server: remote server node - will run on, default is None for localhost - :raises CoreCommandError: when there is a command exception + :return: nothing """ - CoreNetwork.__init__(self, session, _id, name, start, server) - - # TODO: move to startup method - if start: - self.net_client.disable_mac_learning(self.brname) + super().startup() + self.net_client.disable_mac_learning(self.brname) class WlanNode(CoreNetwork): @@ -1050,24 +1035,28 @@ class WlanNode(CoreNetwork): will run on, default is None for localhost :param policy: wlan policy """ - CoreNetwork.__init__(self, session, _id, name, start, server, policy) - # wireless model such as basic range + super().__init__(session, _id, name, start, server, policy) + # wireless and mobility models (BasicRangeModel, Ns2WaypointMobility) self.model = None - # mobility model such as scripted self.mobility = None - # TODO: move to startup method - if start: - self.net_client.disable_mac_learning(self.brname) + def startup(self): + """ + Startup for a wlan node, that disables mac learning after normal startup. + + :return: nothing + """ + super().startup() + self.net_client.disable_mac_learning(self.brname) def attach(self, netif): """ Attach a network interface. - :param core.nodes.interface.CoreInterface netif: network interface + :param core.nodes.interface.Veth netif: network interface :return: nothing """ - CoreNetwork.attach(self, netif) + super().attach(netif) if self.model: netif.poshook = self.model.position_callback if netif.node is None: @@ -1099,12 +1088,12 @@ class WlanNode(CoreNetwork): def update_mobility(self, config): if not self.mobility: - raise ValueError("no mobility set to update for node(%s)", self.id) + raise ValueError(f"no mobility set to update for node({self.id})") self.mobility.update_config(config) def updatemodel(self, config): if not self.model: - raise ValueError("no model set to update for node(%s)", self.id) + raise ValueError(f"no model set to update for node({self.id})") logging.debug( "node(%s) updating model(%s): %s", self.id, self.model.name, config ) @@ -1122,11 +1111,9 @@ class WlanNode(CoreNetwork): :return: list of link data :rtype: list[core.emulator.data.LinkData] """ - all_links = CoreNetwork.all_link_data(self, flags) - + all_links = super().all_link_data(flags) if self.model: all_links.extend(self.model.all_link_data(flags)) - return all_links diff --git a/daemon/core/nodes/physical.py b/daemon/core/nodes/physical.py index c1d6328b..4ed38470 100644 --- a/daemon/core/nodes/physical.py +++ b/daemon/core/nodes/physical.py @@ -19,7 +19,7 @@ class PhysicalNode(CoreNodeBase): def __init__( self, session, _id=None, name=None, nodedir=None, start=True, server=None ): - CoreNodeBase.__init__(self, session, _id, name, start, server) + super().__init__(session, _id, name, start, server) if not self.server: raise CoreError("physical nodes must be assigned to a remote server") self.nodedir = nodedir diff --git a/daemon/core/plugins/sdt.py b/daemon/core/plugins/sdt.py index 280a2bc2..410bba9d 100644 --- a/daemon/core/plugins/sdt.py +++ b/daemon/core/plugins/sdt.py @@ -24,7 +24,7 @@ from core.nodes.network import WlanNode # TODO: A named tuple may be more appropriate, than abusing a class dict like this -class Bunch(object): +class Bunch: """ Helper class for recording a collection of attributes. """ @@ -38,7 +38,7 @@ class Bunch(object): self.__dict__.update(kwargs) -class Sdt(object): +class Sdt: """ Helper class for exporting session objects to NRL"s SDT3D. The connect() method initializes the display, and can be invoked diff --git a/daemon/core/services/coreservices.py b/daemon/core/services/coreservices.py index 20553eb1..80168425 100644 --- a/daemon/core/services/coreservices.py +++ b/daemon/core/services/coreservices.py @@ -10,7 +10,6 @@ services. import enum import logging import time -from multiprocessing.pool import ThreadPool from core import utils from core.constants import which @@ -29,7 +28,7 @@ class ServiceMode(enum.Enum): TIMER = 2 -class ServiceDependencies(object): +class ServiceDependencies: """ Can generate boot paths for services, based on their dependencies. Will validate that all services will be booted and that all dependencies exist within the services provided. @@ -127,7 +126,7 @@ class ServiceDependencies(object): return self.path -class ServiceShim(object): +class ServiceShim: keys = [ "dirs", "files", @@ -235,7 +234,7 @@ class ServiceShim(object): return servicesstring[1].split(",") -class ServiceManager(object): +class ServiceManager: """ Manages services available for CORE nodes to use. """ @@ -306,7 +305,7 @@ class ServiceManager(object): return service_errors -class CoreServices(object): +class CoreServices: """ Class for interacting with a list of available startup services for nodes. Mostly used to convert a CoreService into a Config API @@ -462,18 +461,14 @@ class CoreServices(object): :param core.netns.vnode.LxcNode node: node to start services on :return: nothing """ - pool = ThreadPool() - results = [] - + funcs = [] boot_paths = ServiceDependencies(node.services).boot_paths() for boot_path in boot_paths: - result = pool.apply_async(self._start_boot_paths, (node, boot_path)) - results.append(result) - - pool.close() - pool.join() - for result in results: - result.get() + args = (node, boot_path) + funcs.append((self._start_boot_paths, args, {})) + result, exceptions = utils.threadpool(funcs) + if exceptions: + raise ServiceBootError(exceptions) def _start_boot_paths(self, node, boot_path): """ @@ -791,7 +786,7 @@ class CoreServices(object): node.nodefile(file_name, cfg) -class CoreService(object): +class CoreService: """ Parent class used for defining services. """ diff --git a/daemon/core/utils.py b/daemon/core/utils.py index 407258be..413df156 100644 --- a/daemon/core/utils.py +++ b/daemon/core/utils.py @@ -2,6 +2,7 @@ Miscellaneous utility functions, wrappers around some subprocess procedures. """ +import concurrent.futures import fcntl import hashlib import importlib @@ -223,34 +224,6 @@ def cmd(args, env=None, cwd=None, wait=True, shell=False): raise CoreCommandError(-1, args) -def hex_dump(s, bytes_per_word=2, words_per_line=8): - """ - Hex dump of a string. - - :param str s: string to hex dump - :param bytes_per_word: number of bytes per word - :param words_per_line: number of words per line - :return: hex dump of string - """ - dump = "" - count = 0 - total_bytes = bytes_per_word * words_per_line - - while s: - line = s[:total_bytes] - s = s[total_bytes:] - tmp = map( - lambda x: (f"{bytes_per_word:02x}" * bytes_per_word) % x, - zip(*[iter(map(ord, line))] * bytes_per_word), - ) - if len(line) % 2: - tmp.append(f"{ord(line[-1]):x}") - tmp = " ".join(tmp) - dump += f"0x{count:08x}: {tmp}\n" - count += len(line) - return dump[:-1] - - def file_munge(pathname, header, text): """ Insert text at the end of a file, surrounded by header comments. @@ -409,3 +382,29 @@ def load_logging_config(config_path): with open(config_path, "r") as log_config_file: log_config = json.load(log_config_file) logging.config.dictConfig(log_config) + + +def threadpool(funcs, workers=10): + """ + Run provided functions, arguments, and keywords within a threadpool + collecting results and exceptions. + + :param iter funcs: iterable that provides a func, args, kwargs + :param int workers: number of workers for the threadpool + :return: results and exceptions from running functions with args and kwargs + :rtype: tuple + """ + with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor: + futures = [] + for func, args, kwargs in funcs: + future = executor.submit(func, *args, **kwargs) + futures.append(future) + results = [] + exceptions = [] + for future in concurrent.futures.as_completed(futures): + try: + result = future.result() + results.append(result) + except Exception as e: + exceptions.append(e) + return results, exceptions diff --git a/daemon/core/xml/corexml.py b/daemon/core/xml/corexml.py index 220175bc..dcca6b80 100644 --- a/daemon/core/xml/corexml.py +++ b/daemon/core/xml/corexml.py @@ -104,7 +104,7 @@ def add_configuration(parent, name, value): add_attribute(config_element, "value", value) -class NodeElement(object): +class NodeElement: def __init__(self, session, node, element_name): self.session = session self.node = node @@ -131,7 +131,7 @@ class NodeElement(object): add_attribute(position, "alt", alt) -class ServiceElement(object): +class ServiceElement: def __init__(self, service): self.service = service self.element = etree.Element("service") @@ -197,7 +197,7 @@ class ServiceElement(object): class DeviceElement(NodeElement): def __init__(self, session, node): - super(DeviceElement, self).__init__(session, node, "device") + super().__init__(session, node, "device") add_attribute(self.element, "type", node.type) self.add_services() @@ -212,7 +212,7 @@ class DeviceElement(NodeElement): class NetworkElement(NodeElement): def __init__(self, session, node): - super(NetworkElement, self).__init__(session, node, "network") + super().__init__(session, node, "network") model = getattr(self.node, "model", None) if model: add_attribute(self.element, "model", model.name) @@ -232,7 +232,7 @@ class NetworkElement(NodeElement): add_attribute(self.element, "type", node_type) -class CoreXmlWriter(object): +class CoreXmlWriter: def __init__(self, session): self.session = session self.scenario = etree.Element("scenario") @@ -313,13 +313,13 @@ class CoreXmlWriter(object): def write_session_metadata(self): # metadata metadata_elements = etree.Element("session_metadata") - config = self.session.metadata.get_configs() + config = self.session.metadata if not config: return - for _id in config: - value = config[_id] - add_configuration(metadata_elements, _id, value) + for key in config: + value = config[key] + add_configuration(metadata_elements, key, value) if metadata_elements.getchildren(): self.scenario.append(metadata_elements) @@ -527,7 +527,7 @@ class CoreXmlWriter(object): return link_element -class CoreXmlReader(object): +class CoreXmlReader: def __init__(self, session): self.session = session self.scenario = None @@ -574,7 +574,7 @@ class CoreXmlReader(object): value = data.get("value") configs[name] = value logging.info("reading session metadata: %s", configs) - self.session.metadata.set_configs(configs) + self.session.metadata = configs def read_session_options(self): session_options = self.scenario.find("session_options") @@ -737,53 +737,51 @@ class CoreXmlReader(object): node_id = get_int(device_element, "id") name = device_element.get("name") model = device_element.get("type") - node_options = NodeOptions(name, model) + options = NodeOptions(name, model) service_elements = device_element.find("services") if service_elements is not None: - node_options.services = [ - x.get("name") for x in service_elements.iterchildren() - ] + options.services = [x.get("name") for x in service_elements.iterchildren()] position_element = device_element.find("position") if position_element is not None: x = get_int(position_element, "x") y = get_int(position_element, "y") if all([x, y]): - node_options.set_position(x, y) + options.set_position(x, y) lat = get_float(position_element, "lat") lon = get_float(position_element, "lon") alt = get_float(position_element, "alt") if all([lat, lon, alt]): - node_options.set_location(lat, lon, alt) + options.set_location(lat, lon, alt) logging.info("reading node id(%s) model(%s) name(%s)", node_id, model, name) - self.session.add_node(_id=node_id, node_options=node_options) + self.session.add_node(_id=node_id, options=options) def read_network(self, network_element): node_id = get_int(network_element, "id") name = network_element.get("name") node_type = NodeTypes[network_element.get("type")] - node_options = NodeOptions(name) + options = NodeOptions(name) position_element = network_element.find("position") if position_element is not None: x = get_int(position_element, "x") y = get_int(position_element, "y") if all([x, y]): - node_options.set_position(x, y) + options.set_position(x, y) lat = get_float(position_element, "lat") lon = get_float(position_element, "lon") alt = get_float(position_element, "alt") if all([lat, lon, alt]): - node_options.set_location(lat, lon, alt) + options.set_location(lat, lon, alt) logging.info( "reading node id(%s) node_type(%s) name(%s)", node_id, node_type, name ) - self.session.add_node(_type=node_type, _id=node_id, node_options=node_options) + self.session.add_node(_type=node_type, _id=node_id, options=options) def read_links(self): link_elements = self.scenario.find("links") diff --git a/daemon/core/xml/corexmldeployment.py b/daemon/core/xml/corexmldeployment.py index 83bf3333..ec6759e8 100644 --- a/daemon/core/xml/corexmldeployment.py +++ b/daemon/core/xml/corexmldeployment.py @@ -84,7 +84,7 @@ def get_ipv4_addresses(hostname): raise NotImplementedError -class CoreXmlDeployment(object): +class CoreXmlDeployment: def __init__(self, session, scenario): self.session = session self.scenario = scenario diff --git a/daemon/examples/docker/docker2core.py b/daemon/examples/docker/docker2core.py index 1359f2d0..86cf3dfe 100644 --- a/daemon/examples/docker/docker2core.py +++ b/daemon/examples/docker/docker2core.py @@ -15,7 +15,7 @@ if __name__ == "__main__": options = NodeOptions(model=None, image="ubuntu") # create node one - node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options) + node_one = session.add_node(_type=NodeTypes.DOCKER, options=options) interface_one = prefixes.create_interface(node_one) # create node two diff --git a/daemon/examples/docker/docker2docker.py b/daemon/examples/docker/docker2docker.py index 7f3a3fbb..261a8f67 100644 --- a/daemon/examples/docker/docker2docker.py +++ b/daemon/examples/docker/docker2docker.py @@ -17,11 +17,11 @@ if __name__ == "__main__": options = NodeOptions(model=None, image="ubuntu") # create node one - node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options) + node_one = session.add_node(_type=NodeTypes.DOCKER, options=options) interface_one = prefixes.create_interface(node_one) # create node two - node_two = session.add_node(_type=NodeTypes.DOCKER, node_options=options) + node_two = session.add_node(_type=NodeTypes.DOCKER, options=options) interface_two = prefixes.create_interface(node_two) # add link diff --git a/daemon/examples/docker/switch.py b/daemon/examples/docker/switch.py index 154878bc..f66863e5 100644 --- a/daemon/examples/docker/switch.py +++ b/daemon/examples/docker/switch.py @@ -19,11 +19,11 @@ if __name__ == "__main__": switch = session.add_node(_type=NodeTypes.SWITCH) # node one - node_one = session.add_node(_type=NodeTypes.DOCKER, node_options=options) + node_one = session.add_node(_type=NodeTypes.DOCKER, options=options) interface_one = prefixes.create_interface(node_one) # node two - node_two = session.add_node(_type=NodeTypes.DOCKER, node_options=options) + node_two = session.add_node(_type=NodeTypes.DOCKER, options=options) interface_two = prefixes.create_interface(node_two) # node three diff --git a/daemon/examples/grpc/large.py b/daemon/examples/grpc/large.py new file mode 100644 index 00000000..ef1e6cc4 --- /dev/null +++ b/daemon/examples/grpc/large.py @@ -0,0 +1,54 @@ +import logging + +from core.api.grpc import client, core_pb2 + + +def log_event(event): + logging.info("event: %s", event) + + +def main(): + core = client.CoreGrpcClient() + + with core.context_connect(): + # create session + response = core.create_session() + session_id = response.session_id + logging.info("created session: %s", response) + + # create nodes for session + nodes = [] + position = core_pb2.Position(x=50, y=100) + switch = core_pb2.Node(id=1, type=core_pb2.NodeType.SWITCH, position=position) + nodes.append(switch) + for i in range(2, 50): + position = core_pb2.Position(x=50 + 50 * i, y=50) + node = core_pb2.Node(id=i, position=position, model="PC") + nodes.append(node) + + # create links + interface_helper = client.InterfaceHelper(ip4_prefix="10.83.0.0/16") + links = [] + for node in nodes: + interface_one = interface_helper.create_interface(node.id, 0) + link = core_pb2.Link( + type=core_pb2.LinkType.WIRED, + node_one_id=node.id, + node_two_id=switch.id, + interface_one=interface_one, + ) + links.append(link) + + # start session + response = core.start_session(session_id, nodes, links) + logging.info("started session: %s", response) + + input("press enter to shutdown session") + + response = core.stop_session(session_id) + logging.info("stop sessionL %s", response) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + main() diff --git a/daemon/examples/lxd/lxd2core.py b/daemon/examples/lxd/lxd2core.py index e4304145..06b2b6ba 100644 --- a/daemon/examples/lxd/lxd2core.py +++ b/daemon/examples/lxd/lxd2core.py @@ -15,7 +15,7 @@ if __name__ == "__main__": options = NodeOptions(image="ubuntu") # create node one - node_one = session.add_node(_type=NodeTypes.LXC, node_options=options) + node_one = session.add_node(_type=NodeTypes.LXC, options=options) interface_one = prefixes.create_interface(node_one) # create node two diff --git a/daemon/examples/lxd/lxd2lxd.py b/daemon/examples/lxd/lxd2lxd.py index 4f27de95..2449a223 100644 --- a/daemon/examples/lxd/lxd2lxd.py +++ b/daemon/examples/lxd/lxd2lxd.py @@ -17,11 +17,11 @@ if __name__ == "__main__": options = NodeOptions(image="ubuntu:18.04") # create node one - node_one = session.add_node(_type=NodeTypes.LXC, node_options=options) + node_one = session.add_node(_type=NodeTypes.LXC, options=options) interface_one = prefixes.create_interface(node_one) # create node two - node_two = session.add_node(_type=NodeTypes.LXC, node_options=options) + node_two = session.add_node(_type=NodeTypes.LXC, options=options) interface_two = prefixes.create_interface(node_two) # add link diff --git a/daemon/examples/lxd/switch.py b/daemon/examples/lxd/switch.py index 6056326f..7deaae5f 100644 --- a/daemon/examples/lxd/switch.py +++ b/daemon/examples/lxd/switch.py @@ -19,11 +19,11 @@ if __name__ == "__main__": switch = session.add_node(_type=NodeTypes.SWITCH) # node one - node_one = session.add_node(_type=NodeTypes.LXC, node_options=options) + node_one = session.add_node(_type=NodeTypes.LXC, options=options) interface_one = prefixes.create_interface(node_one) # node two - node_two = session.add_node(_type=NodeTypes.LXC, node_options=options) + node_two = session.add_node(_type=NodeTypes.LXC, options=options) interface_two = prefixes.create_interface(node_two) # node three diff --git a/daemon/examples/python/distributed_emane.py b/daemon/examples/python/distributed_emane.py index 74c7c93b..6b61e505 100644 --- a/daemon/examples/python/distributed_emane.py +++ b/daemon/examples/python/distributed_emane.py @@ -31,11 +31,11 @@ def main(args): # create local node, switch, and remote nodes options = NodeOptions(model="mdr") options.set_position(0, 0) - node_one = session.add_node(node_options=options) + node_one = session.add_node(options=options) emane_net = session.add_node(_type=NodeTypes.EMANE) session.emane.set_model(emane_net, EmaneIeee80211abgModel) - options.emulation_server = server_name - node_two = session.add_node(node_options=options) + options.server = server_name + node_two = session.add_node(options=options) # create node interfaces and link interface_one = prefixes.create_interface(node_one) diff --git a/daemon/examples/python/distributed_lxd.py b/daemon/examples/python/distributed_lxd.py index 80366a14..73d24b5a 100644 --- a/daemon/examples/python/distributed_lxd.py +++ b/daemon/examples/python/distributed_lxd.py @@ -23,9 +23,9 @@ def main(args): # create local node, switch, and remote nodes options = NodeOptions(image="ubuntu:18.04") - node_one = session.add_node(_type=NodeTypes.LXC, node_options=options) - options.emulation_server = server_name - node_two = session.add_node(_type=NodeTypes.LXC, node_options=options) + node_one = session.add_node(_type=NodeTypes.LXC, options=options) + options.server = server_name + node_two = session.add_node(_type=NodeTypes.LXC, options=options) # create node interfaces and link interface_one = prefixes.create_interface(node_one) diff --git a/daemon/examples/python/distributed_ptp.py b/daemon/examples/python/distributed_ptp.py index 887fdae4..0138dab3 100644 --- a/daemon/examples/python/distributed_ptp.py +++ b/daemon/examples/python/distributed_ptp.py @@ -23,9 +23,9 @@ def main(args): # create local node, switch, and remote nodes options = NodeOptions() - node_one = session.add_node(node_options=options) - options.emulation_server = server_name - node_two = session.add_node(node_options=options) + node_one = session.add_node(options=options) + options.server = server_name + node_two = session.add_node(options=options) # create node interfaces and link interface_one = prefixes.create_interface(node_one) diff --git a/daemon/examples/python/distributed_switch.py b/daemon/examples/python/distributed_switch.py index e87cd2c9..f659abd2 100644 --- a/daemon/examples/python/distributed_switch.py +++ b/daemon/examples/python/distributed_switch.py @@ -27,8 +27,8 @@ def main(args): node_one = session.add_node() switch = session.add_node(_type=NodeTypes.SWITCH) options = NodeOptions() - options.emulation_server = server_name - node_two = session.add_node(node_options=options) + options.server = server_name + node_two = session.add_node(options=options) # create node interfaces and link interface_one = prefixes.create_interface(node_one) diff --git a/daemon/examples/python/emane80211.py b/daemon/examples/python/emane80211.py index 75098398..3a10321b 100644 --- a/daemon/examples/python/emane80211.py +++ b/daemon/examples/python/emane80211.py @@ -4,11 +4,11 @@ import parser from core.emane.ieee80211abg import EmaneIeee80211abgModel from core.emulator.coreemu import CoreEmu -from core.emulator.emudata import IpPrefixes -from core.emulator.enumerations import EventTypes +from core.emulator.emudata import IpPrefixes, NodeOptions +from core.emulator.enumerations import EventTypes, NodeTypes -def example(options): +def example(args): # ip generator for example prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") @@ -20,14 +20,16 @@ def example(options): session.set_state(EventTypes.CONFIGURATION_STATE) # create emane network node - emane_network = session.create_emane_network( - model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000) - ) - emane_network.setposition(x=80, y=50) + session.set_location(47.57917, -122.13232, 2.00000, 1.0) + options = NodeOptions() + options.set_position(80, 50) + emane_network = session.add_node(_type=NodeTypes.EMANE, options=options) + session.emane.set_model(emane_network, EmaneIeee80211abgModel) # create nodes - for i in range(options.nodes): - node = session.create_wireless_node() + options = NodeOptions(model="mdr") + for i in range(args.nodes): + node = session.add_node(options=options) node.setposition(x=150 * (i + 1), y=150) interface = prefixes.create_interface(node) session.add_link(node.id, emane_network.id, interface_one=interface) @@ -42,12 +44,12 @@ def example(options): def main(): logging.basicConfig(level=logging.INFO) - options = parser.parse_options("emane80211") + args = parser.parse("emane80211") start = datetime.datetime.now() logging.info( - "running emane 80211 example: nodes(%s) time(%s)", options.nodes, options.time + "running emane 80211 example: nodes(%s) time(%s)", args.nodes, args.time ) - example(options) + example(args) logging.info("elapsed time: %s", datetime.datetime.now() - start) diff --git a/daemon/examples/python/parser.py b/daemon/examples/python/parser.py index d9efdab6..28c81343 100644 --- a/daemon/examples/python/parser.py +++ b/daemon/examples/python/parser.py @@ -5,7 +5,7 @@ DEFAULT_TIME = 10 DEFAULT_STEP = 1 -def parse_options(name): +def parse(name): parser = argparse.ArgumentParser(description=f"Run {name} example") parser.add_argument( "-n", @@ -22,11 +22,11 @@ def parse_options(name): help="example iperf run time in seconds", ) - options = parser.parse_args() + args = parser.parse_args() - if options.nodes < 2: - parser.error(f"invalid min number of nodes: {options.nodes}") - if options.time < 1: - parser.error(f"invalid test time: {options.time}") + if args.nodes < 2: + parser.error(f"invalid min number of nodes: {args.nodes}") + if args.time < 1: + parser.error(f"invalid test time: {args.time}") - return options + return args diff --git a/daemon/examples/python/switch.py b/daemon/examples/python/switch.py index 0d952fda..6277149c 100644 --- a/daemon/examples/python/switch.py +++ b/daemon/examples/python/switch.py @@ -7,7 +7,7 @@ from core.emulator.emudata import IpPrefixes from core.emulator.enumerations import EventTypes, NodeTypes -def example(options): +def example(args): # ip generator for example prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") @@ -22,7 +22,7 @@ def example(options): switch = session.add_node(_type=NodeTypes.SWITCH) # create nodes - for _ in range(options.nodes): + for _ in range(args.nodes): node = session.add_node() interface = prefixes.create_interface(node) session.add_link(node.id, switch.id, interface_one=interface) @@ -32,13 +32,13 @@ def example(options): # get nodes to run example first_node = session.get_node(2) - last_node = session.get_node(options.nodes + 1) + last_node = session.get_node(args.nodes + 1) logging.info("starting iperf server on node: %s", first_node.name) first_node.cmd("iperf -s -D") first_node_address = prefixes.ip4_address(first_node) logging.info("node %s connecting to %s", last_node.name, first_node_address) - output = last_node.cmd(f"iperf -t {options.time} -c {first_node_address}") + output = last_node.cmd(f"iperf -t {args.time} -c {first_node_address}") logging.info(output) first_node.cmd("killall -9 iperf") @@ -48,12 +48,10 @@ def example(options): def main(): logging.basicConfig(level=logging.INFO) - options = parser.parse_options("switch") + args = parser.parse("switch") start = datetime.datetime.now() - logging.info( - "running switch example: nodes(%s) time(%s)", options.nodes, options.time - ) - example(options) + logging.info("running switch example: nodes(%s) time(%s)", args.nodes, args.time) + example(args) logging.info("elapsed time: %s", datetime.datetime.now() - start) diff --git a/daemon/examples/python/wlan.py b/daemon/examples/python/wlan.py index 1ef1a5d1..7c23d411 100644 --- a/daemon/examples/python/wlan.py +++ b/daemon/examples/python/wlan.py @@ -8,7 +8,7 @@ from core.emulator.enumerations import EventTypes, NodeTypes from core.location.mobility import BasicRangeModel -def example(options): +def example(args): # ip generator for example prefixes = IpPrefixes("10.83.0.0/16") @@ -24,10 +24,10 @@ def example(options): session.mobility.set_model(wlan, BasicRangeModel) # create nodes, must set a position for wlan basic range model - node_options = NodeOptions() - node_options.set_position(0, 0) - for _ in range(options.nodes): - node = session.add_node(node_options=node_options) + options = NodeOptions(model="mdr") + options.set_position(0, 0) + for _ in range(args.nodes): + node = session.add_node(options=options) interface = prefixes.create_interface(node) session.add_link(node.id, wlan.id, interface_one=interface) @@ -36,13 +36,14 @@ def example(options): # get nodes for example run first_node = session.get_node(2) - last_node = session.get_node(options.nodes + 1) + last_node = session.get_node(args.nodes + 1) logging.info("starting iperf server on node: %s", first_node.name) first_node.cmd("iperf -s -D") address = prefixes.ip4_address(first_node) logging.info("node %s connecting to %s", last_node.name, address) - last_node.cmd(f"iperf -t {options.time} -c {address}") + output = last_node.cmd(f"iperf -t {args.time} -c {address}") + logging.info(output) first_node.cmd("killall -9 iperf") # shutdown session @@ -51,13 +52,11 @@ def example(options): def main(): logging.basicConfig(level=logging.INFO) - options = parser.parse_options("wlan") + args = parser.parse("wlan") start = datetime.datetime.now() - logging.info( - "running wlan example: nodes(%s) time(%s)", options.nodes, options.time - ) - example(options) + logging.info("running wlan example: nodes(%s) time(%s)", args.nodes, args.time) + example(args) logging.info("elapsed time: %s", datetime.datetime.now() - start) diff --git a/daemon/proto/core/api/grpc/core.proto b/daemon/proto/core/api/grpc/core.proto index af1768ce..19a1ff97 100644 --- a/daemon/proto/core/api/grpc/core.proto +++ b/daemon/proto/core/api/grpc/core.proto @@ -7,6 +7,10 @@ option java_outer_classname = "CoreProto"; service CoreApi { // session rpc + rpc StartSession (StartSessionRequest) returns (StartSessionResponse) { + } + rpc StopSession (StopSessionRequest) returns (StopSessionResponse) { + } rpc CreateSession (CreateSessionRequest) returns (CreateSessionResponse) { } rpc DeleteSession (DeleteSessionRequest) returns (DeleteSessionResponse) { @@ -19,6 +23,10 @@ service CoreApi { } rpc SetSessionOptions (SetSessionOptionsRequest) returns (SetSessionOptionsResponse) { } + rpc SetSessionMetadata (SetSessionMetadataRequest) returns (SetSessionMetadataResponse) { + } + rpc GetSessionMetadata (GetSessionMetadataRequest) returns (GetSessionMetadataResponse) { + } rpc GetSessionLocation (GetSessionLocationRequest) returns (GetSessionLocationResponse) { } rpc SetSessionLocation (SetSessionLocationRequest) returns (SetSessionLocationResponse) { @@ -126,6 +134,30 @@ service CoreApi { } // rpc request/response messages +message StartSessionRequest { + int32 session_id = 1; + repeated Node nodes = 2; + repeated Link links = 3; + repeated Hook hooks = 4; + SessionLocation location = 5; + map emane_config = 6; + repeated WlanConfig wlan_configs = 7; + repeated EmaneModelConfig emane_model_configs = 8; + repeated MobilityConfig mobility_configs = 9; +} + +message StartSessionResponse { + bool result = 1; +} + +message StopSessionRequest { + int32 session_id = 1; +} + +message StopSessionResponse { + bool result = 1; +} + message CreateSessionRequest { int32 session_id = 1; } @@ -175,19 +207,34 @@ message SetSessionOptionsResponse { bool result = 1; } +message SetSessionMetadataRequest { + int32 session_id = 1; + map config = 2; +} + +message SetSessionMetadataResponse { + bool result = 1; +} + +message GetSessionMetadataRequest { + int32 session_id = 1; +} + +message GetSessionMetadataResponse { + map config = 1; +} + message GetSessionLocationRequest { int32 session_id = 1; } message GetSessionLocationResponse { - SessionPosition position = 1; - float scale = 2; + SessionLocation location = 1; } message SetSessionLocationRequest { int32 session_id = 1; - SessionPosition position = 2; - float scale = 3; + SessionLocation location = 2; } message SetSessionLocationResponse { @@ -442,8 +489,7 @@ message GetMobilityConfigResponse { message SetMobilityConfigRequest { int32 session_id = 1; - int32 node_id = 2; - map config = 3; + MobilityConfig mobility_config = 2; } message SetMobilityConfigResponse { @@ -553,8 +599,7 @@ message GetWlanConfigResponse { message SetWlanConfigRequest { int32 session_id = 1; - int32 node_id = 2; - map config = 3; + WlanConfig wlan_config = 2; } message SetWlanConfigResponse { @@ -599,10 +644,7 @@ message GetEmaneModelConfigResponse { message SetEmaneModelConfigRequest { int32 session_id = 1; - int32 node_id = 2; - int32 interface_id = 3; - string model = 4; - map config = 5; + EmaneModelConfig emane_model_config = 2; } message SetEmaneModelConfigResponse { @@ -659,6 +701,23 @@ message EmaneLinkResponse { } // data structures for messages below +message WlanConfig { + int32 node_id = 1; + map config = 2; +} + +message MobilityConfig { + int32 node_id = 1; + map config = 2; +} + +message EmaneModelConfig { + int32 node_id = 1; + int32 interface_id = 2; + string model = 3; + map config = 4; +} + message MessageType { enum Enum { NONE = 0; @@ -695,18 +754,15 @@ message NodeType { enum Enum { DEFAULT = 0; PHYSICAL = 1; - TBD = 3; SWITCH = 4; HUB = 5; WIRELESS_LAN = 6; RJ45 = 7; TUNNEL = 8; - KTUNNEL = 9; EMANE = 10; TAP_BRIDGE = 11; PEER_TO_PEER = 12; CONTROL_NET = 13; - EMANE_NET = 14; DOCKER = 15; LXC = 16; } @@ -853,13 +909,14 @@ message Interface { int32 mtu = 10; } -message SessionPosition { +message SessionLocation { float x = 1; float y = 2; float z = 3; float lat = 4; float lon = 5; float alt = 6; + float scale = 7; } message Position { diff --git a/daemon/scripts/core-manage b/daemon/scripts/core-manage index d9b9de08..14e10e5b 100755 --- a/daemon/scripts/core-manage +++ b/daemon/scripts/core-manage @@ -14,7 +14,7 @@ from core import services from core.constants import CORE_CONF_DIR -class FileUpdater(object): +class FileUpdater: """ Helper class for changing configuration files. """ diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index 521a2432..2055820c 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -2,189 +2,140 @@ Unit test fixture module. """ -import os import threading import time +import mock import pytest from mock.mock import MagicMock from core.api.grpc.client import InterfaceHelper from core.api.grpc.server import CoreGrpcServer -from core.api.tlv.coreapi import CoreConfMessage, CoreEventMessage from core.api.tlv.corehandlers import CoreHandler -from core.api.tlv.coreserver import CoreServer +from core.emane.emanemanager import EmaneManager from core.emulator.coreemu import CoreEmu +from core.emulator.distributed import DistributedServer from core.emulator.emudata import IpPrefixes -from core.emulator.enumerations import CORE_API_PORT, ConfigTlvs, EventTlvs, EventTypes -from core.nodes import ipaddress -from core.services.coreservices import ServiceManager +from core.emulator.enumerations import EventTypes +from core.emulator.session import Session +from core.nodes.base import CoreNode EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward" -class CoreServerTest(object): - 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} - ) +class PatchManager: + def __init__(self): + self.patches = [] - self.distributed_server = "core2" - self.prefix = ipaddress.Ipv4Prefix("10.83.0.0/16") - self.session = None - self.request_handler = None + def patch_obj(self, _cls, attribute): + p = mock.patch.object(_cls, attribute) + p.start() + self.patches.append(p) - def setup_handler(self): - self.session = self.server.coreemu.create_session(1) - request_mock = MagicMock() - request_mock.fileno = MagicMock(return_value=1) - self.request_handler = CoreHandler(request_mock, "", self.server) - self.request_handler.session = self.session - self.request_handler.add_session_handlers() - - def setup(self, distributed_address): - # validate address - assert distributed_address, "distributed server address was not provided" - - # create session - self.session = self.server.coreemu.create_session(1) - - # create request handler - request_mock = MagicMock() - request_mock.fileno = MagicMock(return_value=1) - self.request_handler = CoreHandler(request_mock, "", self.server) - self.request_handler.session = self.session - self.request_handler.add_session_handlers() - - # have broker handle a configuration state change - self.session.set_state(EventTypes.DEFINITION_STATE) - message = CoreEventMessage.create( - 0, [(EventTlvs.TYPE, EventTypes.CONFIGURATION_STATE.value)] - ) - self.request_handler.handle_message(message) - - # add broker server for distributed core - distributed = f"{self.distributed_server}:{distributed_address}:{self.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.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 patch(self, func): + p = mock.patch(func) + p.start() + self.patches.append(p) def shutdown(self): - self.server.coreemu.shutdown() - self.server.server_close() + for p in self.patches: + p.stop() -@pytest.fixture -def grpc_server(): - coremu = CoreEmu() - grpc_server = CoreGrpcServer(coremu) +class MockServer: + def __init__(self, config, coreemu): + self.config = config + self.coreemu = coreemu + + +@pytest.fixture(scope="session") +def patcher(request): + patch_manager = PatchManager() + patch_manager.patch_obj(DistributedServer, "remote_cmd") + if request.config.getoption("mock"): + patch_manager.patch("os.mkdir") + patch_manager.patch("core.utils.cmd") + patch_manager.patch("core.nodes.netclient.get_net_client") + patch_manager.patch_obj(CoreNode, "nodefile") + patch_manager.patch_obj(Session, "write_state") + patch_manager.patch_obj(Session, "write_nodes") + patch_manager.patch_obj(EmaneManager, "buildxml") + yield patch_manager + patch_manager.shutdown() + + +@pytest.fixture(scope="session") +def global_coreemu(patcher): + coreemu = CoreEmu(config={"emane_prefix": "/usr"}) + yield coreemu + coreemu.shutdown() + + +@pytest.fixture(scope="session") +def global_session(request, patcher, global_coreemu): + mkdir = not request.config.getoption("mock") + session = Session(1000, {"emane_prefix": "/usr"}, mkdir) + yield session + session.shutdown() + + +@pytest.fixture(scope="session") +def ip_prefixes(): + return IpPrefixes(ip4_prefix="10.83.0.0/16") + + +@pytest.fixture(scope="session") +def interface_helper(): + return InterfaceHelper(ip4_prefix="10.83.0.0/16") + + +@pytest.fixture(scope="module") +def module_grpc(global_coreemu): + grpc_server = CoreGrpcServer(global_coreemu) thread = threading.Thread(target=grpc_server.listen, args=("localhost:50051",)) thread.daemon = True thread.start() time.sleep(0.1) yield grpc_server - coremu.shutdown() grpc_server.server.stop(None) +@pytest.fixture(scope="module") +def module_coretlv(patcher, global_coreemu, global_session): + request_mock = MagicMock() + request_mock.fileno = MagicMock(return_value=1) + server = MockServer({"numthreads": "1"}, global_coreemu) + request_handler = CoreHandler(request_mock, "", server) + request_handler.session = global_session + request_handler.add_session_handlers() + yield request_handler + + @pytest.fixture -def session(): - # use coreemu and create a session - coreemu = CoreEmu(config={"emane_prefix": "/usr"}) - session_fixture = coreemu.create_session() - session_fixture.set_state(EventTypes.CONFIGURATION_STATE) - assert os.path.exists(session_fixture.session_dir) +def grpc_server(module_grpc): + yield module_grpc + module_grpc.coreemu.shutdown() - # return created session - yield session_fixture - # clear session configurations - session_fixture.location.reset() - session_fixture.services.reset() - session_fixture.mobility.config_reset() - session_fixture.emane.config_reset() +@pytest.fixture +def session(global_session): + global_session.set_state(EventTypes.CONFIGURATION_STATE) + yield global_session + global_session.clear() - # shutdown coreemu + +@pytest.fixture +def coretlv(module_coretlv): + session = module_coretlv.session + coreemu = module_coretlv.coreemu + coreemu.sessions[session.id] = session + yield module_coretlv coreemu.shutdown() - # clear services, since they will be reloaded - ServiceManager.services.clear() - - -@pytest.fixture(scope="module") -def ip_prefixes(): - return IpPrefixes(ip4_prefix="10.83.0.0/16") - - -@pytest.fixture(scope="module") -def interface_helper(): - return InterfaceHelper(ip4_prefix="10.83.0.0/16") - - -@pytest.fixture() -def cored(): - # create and return server - server = CoreServerTest() - yield server - - # cleanup - server.shutdown() - - # cleanup services - ServiceManager.services.clear() - - -@pytest.fixture() -def coreserver(): - # create and return server - server = CoreServerTest() - server.setup_handler() - yield server - - # cleanup - server.shutdown() - - # cleanup services - ServiceManager.services.clear() - def pytest_addoption(parser): parser.addoption("--distributed", help="distributed server address") + parser.addoption("--mock", action="store_true", help="run without mocking") def pytest_generate_tests(metafunc): diff --git a/daemon/tests/distributed/test_distributed.py b/daemon/tests/distributed/test_distributed.py deleted file mode 100644 index 7078d6ed..00000000 --- a/daemon/tests/distributed/test_distributed.py +++ /dev/null @@ -1,354 +0,0 @@ -""" -Unit tests for testing CORE with distributed networks. -""" -from core.api.tlv.coreapi import ( - CoreConfMessage, - CoreEventMessage, - CoreExecMessage, - CoreLinkMessage, - CoreNodeMessage, -) -from core.emane.ieee80211abg import EmaneIeee80211abgModel -from core.emulator.enumerations import ( - ConfigFlags, - ConfigTlvs, - EventTlvs, - EventTypes, - ExecuteTlvs, - LinkTlvs, - LinkTypes, - MessageFlags, - NodeTlvs, - NodeTypes, -) -from core.nodes.ipaddress import IpAddress, MacAddress - - -def set_emane_model(node_id, model): - return CoreConfMessage.create( - 0, - [ - (ConfigTlvs.NODE, node_id), - (ConfigTlvs.OBJECT, model), - (ConfigTlvs.TYPE, ConfigFlags.UPDATE.value), - ], - ) - - -def node_message( - _id, name, emulation_server=None, node_type=NodeTypes.DEFAULT, model=None -): - """ - Convenience method for creating a node TLV messages. - - :param int _id: node id - :param str name: node name - :param str emulation_server: distributed server name, if desired - :param core.emulator.enumerations.NodeTypes node_type: node type - :param str model: model for node - :return: tlv message - :rtype: core.api.tlv.coreapi.CoreNodeMessage - """ - values = [ - (NodeTlvs.NUMBER, _id), - (NodeTlvs.TYPE, node_type.value), - (NodeTlvs.NAME, name), - (NodeTlvs.EMULATION_SERVER, emulation_server), - (NodeTlvs.X_POSITION, 0), - (NodeTlvs.Y_POSITION, 0), - ] - - 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, - key=None, - mask=24, -): - """ - 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.nodes.ipaddress.IpAddress address_one: node one ip4 address - :param int intf_two: node two interface id - :param core.nodes.ipaddress.IpAddress address_two: node two ip4 address - :param int key: tunnel key for link if needed - :param int mask: ip4 mask to use for link - :return: tlv mesage - :rtype: core.api.tlv.coreapi.CoreLinkMessage - """ - mac_one, mac_two = None, None - if address_one: - mac_one = MacAddress.random() - if address_two: - mac_two = MacAddress.random() - - values = [ - (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, mask), - (LinkTlvs.INTERFACE1_MAC, mac_one), - (LinkTlvs.INTERFACE2_NUMBER, intf_two), - (LinkTlvs.INTERFACE2_IP4, address_two), - (LinkTlvs.INTERFACE2_IP4_MASK, mask), - (LinkTlvs.INTERFACE2_MAC, mac_two), - ] - - if key: - values.append((LinkTlvs.KEY, key)) - - return CoreLinkMessage.create(MessageFlags.ADD.value, values) - - -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.tlv.coreapi.CoreExecMessage - """ - flags = MessageFlags.STRING.value | MessageFlags.TEXT.value - return CoreExecMessage.create( - flags, - [ - (ExecuteTlvs.NODE, node.id), - (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.tlv.coreapi.CoreEventMessage - """ - return CoreEventMessage.create(0, [(EventTlvs.TYPE, state.value)]) - - -def validate_response(replies, _): - """ - Patch method for handling dispatch replies within a CoreRequestHandler to validate a response. - - :param tuple replies: replies to handle - :param _: nothing - :return: nothing - """ - response = replies[0] - header = response[: CoreExecMessage.header_len] - tlv_data = response[CoreExecMessage.header_len :] - response = CoreExecMessage(MessageFlags.TEXT, header, tlv_data) - assert not response.get_tlv(ExecuteTlvs.STATUS.value) - - -class TestDistributed: - def test_switch(self, cored, distributed_address): - """ - Test creating a distributed switch network. - - :param core.api.tlv.coreserver.CoreServer conftest.Core cored: core daemon server to test with - :param str distributed_address: distributed server to test against - """ - # initialize server for testing - cored.setup(distributed_address) - - # create local node - message = node_message(_id=1, name="n1", model="host") - cored.request_handler.handle_message(message) - - # create distributed node and assign to distributed server - message = node_message( - _id=2, name="n2", emulation_server=cored.distributed_server, model="host" - ) - cored.request_handler.handle_message(message) - - # create distributed switch and assign to distributed server - message = node_message(_id=3, name="n3", node_type=NodeTypes.SWITCH) - cored.request_handler.handle_message(message) - - # link message one - ip4_address = cored.prefix.addr(1) - message = link_message(n1=1, n2=3, intf_one=0, address_one=ip4_address) - cored.request_handler.handle_message(message) - - # link message two - ip4_address = cored.prefix.addr(2) - message = link_message(n1=3, n2=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) - cored.request_handler.handle_message(message) - - # test a ping command - node_one = cored.session.get_node(1) - message = command_message(node_one, f"ping -c 5 {ip4_address}") - cored.request_handler.dispatch_replies = validate_response - cored.request_handler.handle_message(message) - - def test_emane(self, cored, distributed_address): - """ - Test creating a distributed emane network. - - :param core.api.tlv.coreserver.CoreServer conftest.Core cored: core daemon server to test with - :param str distributed_address: distributed server to test against - """ - # initialize server for testing - cored.setup(distributed_address) - - # configure required controlnet - cored.session.options.set_config( - "controlnet", "core1:172.16.1.0/24 core2:172.16.2.0/24" - ) - - # create local node - message = node_message(_id=1, name="n1", model="mdr") - cored.request_handler.handle_message(message) - - # create distributed node and assign to distributed server - message = node_message( - _id=2, name="n2", emulation_server=cored.distributed_server, model="mdr" - ) - cored.request_handler.handle_message(message) - - # create distributed switch and assign to distributed server - message = node_message(_id=3, name="n3", node_type=NodeTypes.EMANE) - cored.request_handler.handle_message(message) - - # set emane model - message = set_emane_model(3, EmaneIeee80211abgModel.name) - cored.request_handler.handle_message(message) - - # link message one - ip4_address = cored.prefix.addr(1) - message = link_message(n1=1, n2=3, intf_one=0, address_one=ip4_address, mask=32) - cored.request_handler.handle_message(message) - - # link message two - ip4_address = cored.prefix.addr(2) - message = link_message(n1=2, n2=3, intf_one=0, address_one=ip4_address, mask=32) - cored.request_handler.handle_message(message) - - # change session to instantiation state - message = state_message(EventTypes.INSTANTIATION_STATE) - cored.request_handler.handle_message(message) - - # test a ping command - node_one = cored.session.get_node(1) - message = command_message(node_one, f"ping -c 5 {ip4_address}") - cored.request_handler.dispatch_replies = validate_response - cored.request_handler.handle_message(message) - - def test_prouter(self, cored, distributed_address): - """ - Test creating a distributed prouter node. - - :param core.coreserver.CoreServer Core cored: core daemon server to test with - :param str distributed_address: distributed server to test against - """ - # initialize server for testing - cored.setup(distributed_address) - - # create local node - message = node_message(_id=1, name="n1", model="host") - cored.request_handler.handle_message(message) - - # create distributed node and assign to distributed server - message = node_message( - _id=2, - name="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 = node_message(_id=3, name="n3", node_type=NodeTypes.SWITCH) - cored.request_handler.handle_message(message) - - # link message one - ip4_address = cored.prefix.addr(1) - message = link_message(n1=1, n2=3, intf_one=0, address_one=ip4_address) - cored.request_handler.handle_message(message) - - # link message two - ip4_address = cored.prefix.addr(2) - message = link_message(n1=3, n2=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) - cored.request_handler.handle_message(message) - - # test a ping command - node_one = cored.session.get_node(1) - message = command_message(node_one, f"ping -c 5 {ip4_address}") - cored.request_handler.dispatch_replies = validate_response - cored.request_handler.handle_message(message) - cored.request_handler.handle_message(message) - - def test_tunnel(self, cored, distributed_address): - """ - Test session broker creation. - - :param core.coreserver.CoreServer Core cored: core daemon server to test with - :param str distributed_address: distributed server to test against - """ - # initialize server for testing - cored.setup(distributed_address) - - # create local node - message = node_message(_id=1, name="n1", model="host") - cored.request_handler.handle_message(message) - - # create distributed node and assign to distributed server - message = node_message( - _id=2, - name=distributed_address, - emulation_server=cored.distributed_server, - node_type=NodeTypes.TUNNEL, - ) - cored.request_handler.handle_message(message) - - # link message one - ip4_address = cored.prefix.addr(1) - address_two = IpAddress.from_string(distributed_address) - message = link_message( - n1=1, - n2=2, - intf_one=0, - address_one=ip4_address, - intf_two=0, - address_two=address_two, - key=1, - ) - cored.request_handler.handle_message(message) - - # change session to instantiation state - message = state_message(EventTypes.INSTANTIATION_STATE) - cored.request_handler.handle_message(message) diff --git a/daemon/tests/emane/test_emane.py b/daemon/tests/emane/test_emane.py index 3eb87596..4c507eee 100644 --- a/daemon/tests/emane/test_emane.py +++ b/daemon/tests/emane/test_emane.py @@ -12,6 +12,7 @@ from core.emane.ieee80211abg import EmaneIeee80211abgModel from core.emane.rfpipe import EmaneRfPipeModel from core.emane.tdma import EmaneTdmaModel from core.emulator.emudata import NodeOptions +from core.emulator.enumerations import NodeTypes from core.errors import CoreCommandError, CoreError _EMANE_MODELS = [ @@ -46,10 +47,11 @@ class TestEmane: """ # 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) + session.set_location(47.57917, -122.13232, 2.00000, 1.0) + options = NodeOptions() + options.set_position(80, 50) + emane_network = session.add_node(_type=NodeTypes.EMANE, options=options) + session.emane.set_model(emane_network, model) # configure tdma if model == EmaneTdmaModel: @@ -60,11 +62,11 @@ class TestEmane: ) # 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) + options = NodeOptions(model="mdr") + options.set_position(150, 150) + node_one = session.add_node(options=options) + options.set_position(300, 150) + node_two = session.add_node(options=options) for i, node in enumerate([node_one, node_two]): node.setposition(x=150 * (i + 1), y=150) @@ -87,19 +89,22 @@ class TestEmane: :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"}, + session.set_location(47.57917, -122.13232, 2.00000, 1.0) + options = NodeOptions() + options.set_position(80, 50) + emane_network = session.add_node(_type=NodeTypes.EMANE, options=options) + config_key = "txpower" + config_value = "10" + session.emane.set_model( + emane_network, EmaneIeee80211abgModel, {config_key: config_value} ) - 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) + options = NodeOptions(model="mdr") + options.set_position(150, 150) + node_one = session.add_node(options=options) + options.set_position(300, 150) + node_two = session.add_node(options=options) for i, node in enumerate([node_one, node_two]): node.setposition(x=150 * (i + 1), y=150) @@ -137,11 +142,11 @@ class TestEmane: # retrieve configuration we set originally value = str( - session.emane.get_config("test", emane_id, EmaneIeee80211abgModel.name) + session.emane.get_config(config_key, 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" + assert value == config_value diff --git a/daemon/tests/test_core.py b/daemon/tests/test_core.py index 47368740..80cf6787 100644 --- a/daemon/tests/test_core.py +++ b/daemon/tests/test_core.py @@ -20,7 +20,7 @@ _WIRED = [NodeTypes.PEER_TO_PEER, NodeTypes.HUB, NodeTypes.SWITCH] def ping(from_node, to_node, ip_prefixes): address = ip_prefixes.ip4_address(to_node) try: - from_node.cmd(f"ping -c 3 {address}") + from_node.cmd(f"ping -c 1 {address}") status = 0 except CoreCommandError as e: status = e.returncode @@ -57,14 +57,14 @@ class TestCore: status = ping(node_one, node_two, ip_prefixes) assert not status - def test_vnode_client(self, session, ip_prefixes): + def test_vnode_client(self, request, session, ip_prefixes): """ Test vnode client methods. + :param request: pytest request :param session: session for test :param ip_prefixes: generates ip addresses for nodes """ - # create ptp ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER) @@ -87,7 +87,8 @@ class TestCore: assert client.connected() # validate command - assert client.check_cmd("echo hello") == "hello" + if not request.config.getoption("mock"): + assert client.check_cmd("echo hello") == "hello" def test_netif(self, session, ip_prefixes): """ @@ -146,10 +147,10 @@ class TestCore: session.mobility.set_model(wlan_node, BasicRangeModel) # create nodes - 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) + options = NodeOptions(model="mdr") + options.set_position(0, 0) + node_one = session.add_node(options=options) + node_two = session.add_node(options=options) # link nodes for node in [node_one, node_two]: @@ -176,10 +177,10 @@ class TestCore: session.mobility.set_model(wlan_node, BasicRangeModel) # create nodes - 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) + options = NodeOptions(model="mdr") + options.set_position(0, 0) + node_one = session.add_node(options=options) + node_two = session.add_node(options=options) # link nodes for node in [node_one, node_two]: diff --git a/daemon/tests/test_distributed.py b/daemon/tests/test_distributed.py new file mode 100644 index 00000000..d6b251e0 --- /dev/null +++ b/daemon/tests/test_distributed.py @@ -0,0 +1,39 @@ +from core.emulator.emudata import NodeOptions +from core.emulator.enumerations import NodeTypes + + +class TestDistributed: + def test_remote_node(self, session): + # given + server_name = "core2" + host = "127.0.0.1" + + # when + session.distributed.add_server(server_name, host) + options = NodeOptions() + options.server = server_name + node = session.add_node(options=options) + + # then + assert node.server is not None + assert node.server.name == server_name + assert node.server.host == host + + def test_remote_bridge(self, session): + # given + server_name = "core2" + host = "127.0.0.1" + session.distributed.address = host + + # when + session.distributed.add_server(server_name, host) + options = NodeOptions() + options.server = server_name + node = session.add_node(_type=NodeTypes.HUB, options=options) + session.instantiate() + + # then + assert node.server is not None + assert node.server.name == server_name + assert node.server.host == host + assert len(session.distributed.tunnels) > 0 diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index cb837957..62ff3a22 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -3,9 +3,10 @@ from queue import Queue import grpc import pytest +from mock import patch from core.api.grpc import core_pb2 -from core.api.grpc.client import CoreGrpcClient +from core.api.grpc.client import CoreGrpcClient, InterfaceHelper from core.config import ConfigShim from core.emane.ieee80211abg import EmaneIeee80211abgModel from core.emulator.data import EventData @@ -18,9 +19,127 @@ from core.emulator.enumerations import ( ) from core.errors import CoreError from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility +from core.xml.corexml import CoreXmlWriter class TestGrpc: + def test_start_session(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + nodes = [] + position = core_pb2.Position(x=50, y=100) + node_one = core_pb2.Node(id=1, position=position, model="PC") + position = core_pb2.Position(x=100, y=100) + node_two = core_pb2.Node(id=2, position=position, model="PC") + position = core_pb2.Position(x=200, y=200) + wlan_node = core_pb2.Node( + id=3, type=NodeTypes.WIRELESS_LAN.value, position=position + ) + nodes.extend([node_one, node_two, wlan_node]) + links = [] + interface_helper = InterfaceHelper(ip4_prefix="10.83.0.0/16") + interface_one = interface_helper.create_interface(node_one.id, 0) + interface_two = interface_helper.create_interface(node_two.id, 0) + link = core_pb2.Link( + type=core_pb2.LinkType.WIRED, + node_one_id=node_one.id, + node_two_id=node_two.id, + interface_one=interface_one, + interface_two=interface_two, + ) + links.append(link) + hooks = [] + hook = core_pb2.Hook( + state=core_pb2.SessionState.RUNTIME, file="echo.sh", data="echo hello" + ) + hooks.append(hook) + location_x = 5 + location_y = 10 + location_z = 15 + location_lat = 20 + location_lon = 30 + location_alt = 40 + location_scale = 5 + location = core_pb2.SessionLocation( + x=location_x, + y=location_y, + z=location_z, + lat=location_lat, + lon=location_lon, + alt=location_alt, + scale=location_scale, + ) + emane_config_key = "platform_id_start" + emane_config_value = "2" + emane_config = {emane_config_key: emane_config_value} + model_configs = [] + model_node_id = 20 + model_config_key = "bandwidth" + model_config_value = "500000" + model_config = core_pb2.EmaneModelConfig( + node_id=model_node_id, + interface_id=-1, + model=EmaneIeee80211abgModel.name, + config={model_config_key: model_config_value}, + ) + model_configs.append(model_config) + wlan_configs = [] + wlan_config_key = "range" + wlan_config_value = "333" + wlan_config = core_pb2.WlanConfig( + node_id=wlan_node.id, config={wlan_config_key: wlan_config_value} + ) + wlan_configs.append(wlan_config) + mobility_config_key = "refresh_ms" + mobility_config_value = "60" + mobility_configs = [] + mobility_config = core_pb2.MobilityConfig( + node_id=wlan_node.id, config={mobility_config_key: mobility_config_value} + ) + mobility_configs.append(mobility_config) + + # when + with patch.object(CoreXmlWriter, "write"): + with client.context_connect(): + client.start_session( + session.id, + nodes, + links, + location, + hooks, + emane_config, + model_configs, + wlan_configs, + mobility_configs, + ) + + # then + assert node_one.id in session.nodes + assert node_two.id in session.nodes + assert wlan_node.id in session.nodes + assert session.nodes[node_one.id].netif(0) is not None + assert session.nodes[node_two.id].netif(0) is not None + hook_file, hook_data = session._hooks[core_pb2.SessionState.RUNTIME][0] + assert hook_file == hook.file + assert hook_data == hook.data + assert session.location.refxyz == (location_x, location_y, location_z) + assert session.location.refgeo == (location_lat, location_lon, location_alt) + assert session.location.refscale == location_scale + assert session.emane.get_config(emane_config_key) == emane_config_value + set_wlan_config = session.mobility.get_model_config( + wlan_node.id, BasicRangeModel.name + ) + assert set_wlan_config[wlan_config_key] == wlan_config_value + set_mobility_config = session.mobility.get_model_config( + wlan_node.id, Ns2ScriptedMobility.name + ) + assert set_mobility_config[mobility_config_key] == mobility_config_value + set_model_config = session.emane.get_model_config( + model_node_id, EmaneIeee80211abgModel.name + ) + assert set_model_config[model_config_key] == model_config_value + @pytest.mark.parametrize("session_id", [None, 6013]) def test_create_session(self, grpc_server, session_id): # given @@ -112,13 +231,13 @@ class TestGrpc: response = client.get_session_location(session.id) # then - assert response.scale == 1.0 - assert response.position.x == 0 - assert response.position.y == 0 - assert response.position.z == 0 - assert response.position.lat == 0 - assert response.position.lon == 0 - assert response.position.alt == 0 + assert response.location.scale == 1.0 + assert response.location.x == 0 + assert response.location.y == 0 + assert response.location.z == 0 + assert response.location.lat == 0 + assert response.location.lon == 0 + assert response.location.alt == 0 def test_set_session_location(self, grpc_server): # given @@ -164,6 +283,36 @@ class TestGrpc: config = session.options.get_configs() assert len(config) > 0 + def test_set_session_metadata(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + key = "meta1" + value = "value1" + with client.context_connect(): + response = client.set_session_metadata(session.id, {key: value}) + + # then + assert response.result is True + assert session.metadata[key] == value + + def test_get_session_metadata(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + key = "meta1" + value = "value1" + session.metadata[key] = value + + # then + with client.context_connect(): + response = client.get_session_metadata(session.id) + + # then + assert response.config[key] == value + def test_set_session_state(self, grpc_server): # given client = CoreGrpcClient() @@ -240,13 +389,16 @@ class TestGrpc: with pytest.raises(CoreError): assert session.get_node(node.id) - def test_node_command(self, grpc_server): + def test_node_command(self, request, grpc_server): + if request.config.getoption("mock"): + pytest.skip("mocking calls") + # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() session.set_state(EventTypes.CONFIGURATION_STATE) - node_options = NodeOptions(model="Host") - node = session.add_node(node_options=node_options) + options = NodeOptions(model="Host") + node = session.add_node(options=options) session.instantiate() output = "hello world" @@ -263,8 +415,8 @@ class TestGrpc: client = CoreGrpcClient() session = grpc_server.coreemu.create_session() session.set_state(EventTypes.CONFIGURATION_STATE) - node_options = NodeOptions(model="Host") - node = session.add_node(node_options=node_options) + options = NodeOptions(model="Host") + node = session.add_node(options=options) session.instantiate() # then @@ -524,9 +676,9 @@ class TestGrpc: # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() - emane_network = session.create_emane_network( - model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000) - ) + session.set_location(47.57917, -122.13232, 2.00000, 1.0) + emane_network = session.add_node(_type=NodeTypes.EMANE) + session.emane.set_model(emane_network, EmaneIeee80211abgModel) config_key = "platform_id_start" config_value = "2" session.emane.set_model_config( @@ -548,9 +700,9 @@ class TestGrpc: # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() - emane_network = session.create_emane_network( - model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000) - ) + session.set_location(47.57917, -122.13232, 2.00000, 1.0) + emane_network = session.add_node(_type=NodeTypes.EMANE) + session.emane.set_model(emane_network, EmaneIeee80211abgModel) config_key = "bandwidth" config_value = "900000" @@ -574,9 +726,9 @@ class TestGrpc: # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() - emane_network = session.create_emane_network( - model=EmaneIeee80211abgModel, geo_reference=(47.57917, -122.13232, 2.00000) - ) + session.set_location(47.57917, -122.13232, 2.00000, 1.0) + emane_network = session.add_node(_type=NodeTypes.EMANE) + session.emane.set_model(emane_network, EmaneIeee80211abgModel) # then with client.context_connect(): @@ -834,7 +986,10 @@ class TestGrpc: # then queue.get(timeout=5) - def test_throughputs(self, grpc_server): + def test_throughputs(self, request, grpc_server): + if request.config.getoption("mock"): + pytest.skip("mocking calls") + # given client = CoreGrpcClient() grpc_server.coreemu.create_session() diff --git a/daemon/tests/test_gui.py b/daemon/tests/test_gui.py index 40c025ee..c2a8c9fc 100644 --- a/daemon/tests/test_gui.py +++ b/daemon/tests/test_gui.py @@ -6,6 +6,7 @@ import time import mock import pytest +from mock import MagicMock from core.api.tlv import coreapi from core.emane.ieee80211abg import EmaneIeee80211abgModel @@ -42,10 +43,9 @@ class TestGui: (NodeTypes.SWITCH, None), (NodeTypes.WIRELESS_LAN, None), (NodeTypes.TUNNEL, None), - (NodeTypes.RJ45, None), ], ) - def test_node_add(self, coreserver, node_type, model): + def test_node_add(self, coretlv, node_type, model): node_id = 1 message = coreapi.CoreNodeMessage.create( MessageFlags.ADD.value, @@ -59,13 +59,13 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.get_node(node_id) is not None + assert coretlv.session.get_node(node_id) is not None - def test_node_update(self, coreserver): + def test_node_update(self, coretlv): node_id = 1 - coreserver.session.add_node(_id=node_id) + coretlv.session.add_node(_id=node_id) x = 50 y = 100 message = coreapi.CoreNodeMessage.create( @@ -77,30 +77,30 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - node = coreserver.session.get_node(node_id) + node = coretlv.session.get_node(node_id) assert node is not None assert node.position.x == x assert node.position.y == y - def test_node_delete(self, coreserver): + def test_node_delete(self, coretlv): node_id = 1 - coreserver.session.add_node(_id=node_id) + coretlv.session.add_node(_id=node_id) message = coreapi.CoreNodeMessage.create( MessageFlags.DELETE.value, [(NodeTlvs.NUMBER, node_id)] ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) with pytest.raises(CoreError): - coreserver.session.get_node(node_id) + coretlv.session.get_node(node_id) - def test_link_add_node_to_net(self, coreserver): + def test_link_add_node_to_net(self, coretlv): node_one = 1 - coreserver.session.add_node(_id=node_one) + coretlv.session.add_node(_id=node_one) switch = 2 - coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH) + coretlv.session.add_node(_id=switch, _type=NodeTypes.SWITCH) ip_prefix = Ipv4Prefix("10.0.0.0/24") interface_one = ip_prefix.addr(node_one) message = coreapi.CoreLinkMessage.create( @@ -114,17 +114,17 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - switch_node = coreserver.session.get_node(switch) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 1 - def test_link_add_net_to_node(self, coreserver): + def test_link_add_net_to_node(self, coretlv): node_one = 1 - coreserver.session.add_node(_id=node_one) + coretlv.session.add_node(_id=node_one) switch = 2 - coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH) + coretlv.session.add_node(_id=switch, _type=NodeTypes.SWITCH) ip_prefix = Ipv4Prefix("10.0.0.0/24") interface_one = ip_prefix.addr(node_one) message = coreapi.CoreLinkMessage.create( @@ -138,17 +138,17 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - switch_node = coreserver.session.get_node(switch) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 1 - def test_link_add_node_to_node(self, coreserver): + def test_link_add_node_to_node(self, coretlv): node_one = 1 - coreserver.session.add_node(_id=node_one) + coretlv.session.add_node(_id=node_one) node_two = 2 - coreserver.session.add_node(_id=node_two) + coretlv.session.add_node(_id=node_two) ip_prefix = Ipv4Prefix("10.0.0.0/24") interface_one = ip_prefix.addr(node_one) interface_two = ip_prefix.addr(node_two) @@ -166,19 +166,19 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) all_links = [] - for node_id in coreserver.session.nodes: - node = coreserver.session.nodes[node_id] + for node_id in coretlv.session.nodes: + node = coretlv.session.nodes[node_id] all_links += node.all_link_data(0) assert len(all_links) == 1 - def test_link_update(self, coreserver): + def test_link_update(self, coretlv): node_one = 1 - coreserver.session.add_node(_id=node_one) + coretlv.session.add_node(_id=node_one) switch = 2 - coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH) + coretlv.session.add_node(_id=switch, _type=NodeTypes.SWITCH) ip_prefix = Ipv4Prefix("10.0.0.0/24") interface_one = ip_prefix.addr(node_one) message = coreapi.CoreLinkMessage.create( @@ -191,8 +191,8 @@ class TestGui: (LinkTlvs.INTERFACE1_IP4_MASK, 24), ], ) - coreserver.request_handler.handle_message(message) - switch_node = coreserver.session.get_node(switch) + coretlv.handle_message(message) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 1 link = all_links[0] @@ -208,19 +208,19 @@ class TestGui: (LinkTlvs.BANDWIDTH, bandwidth), ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - switch_node = coreserver.session.get_node(switch) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 1 link = all_links[0] assert link.bandwidth == bandwidth - def test_link_delete_node_to_node(self, coreserver): + def test_link_delete_node_to_node(self, coretlv): node_one = 1 - coreserver.session.add_node(_id=node_one) + coretlv.session.add_node(_id=node_one) node_two = 2 - coreserver.session.add_node(_id=node_two) + coretlv.session.add_node(_id=node_two) ip_prefix = Ipv4Prefix("10.0.0.0/24") interface_one = ip_prefix.addr(node_one) interface_two = ip_prefix.addr(node_two) @@ -236,10 +236,10 @@ class TestGui: (LinkTlvs.INTERFACE2_IP4_MASK, 24), ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) all_links = [] - for node_id in coreserver.session.nodes: - node = coreserver.session.nodes[node_id] + for node_id in coretlv.session.nodes: + node = coretlv.session.nodes[node_id] all_links += node.all_link_data(0) assert len(all_links) == 1 @@ -252,19 +252,19 @@ class TestGui: (LinkTlvs.INTERFACE2_NUMBER, 0), ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) all_links = [] - for node_id in coreserver.session.nodes: - node = coreserver.session.nodes[node_id] + for node_id in coretlv.session.nodes: + node = coretlv.session.nodes[node_id] all_links += node.all_link_data(0) assert len(all_links) == 0 - def test_link_delete_node_to_net(self, coreserver): + def test_link_delete_node_to_net(self, coretlv): node_one = 1 - coreserver.session.add_node(_id=node_one) + coretlv.session.add_node(_id=node_one) switch = 2 - coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH) + coretlv.session.add_node(_id=switch, _type=NodeTypes.SWITCH) ip_prefix = Ipv4Prefix("10.0.0.0/24") interface_one = ip_prefix.addr(node_one) message = coreapi.CoreLinkMessage.create( @@ -277,8 +277,8 @@ class TestGui: (LinkTlvs.INTERFACE1_IP4_MASK, 24), ], ) - coreserver.request_handler.handle_message(message) - switch_node = coreserver.session.get_node(switch) + coretlv.handle_message(message) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 1 @@ -290,17 +290,17 @@ class TestGui: (LinkTlvs.INTERFACE1_NUMBER, 0), ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - switch_node = coreserver.session.get_node(switch) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 0 - def test_link_delete_net_to_node(self, coreserver): + def test_link_delete_net_to_node(self, coretlv): node_one = 1 - coreserver.session.add_node(_id=node_one) + coretlv.session.add_node(_id=node_one) switch = 2 - coreserver.session.add_node(_id=switch, _type=NodeTypes.SWITCH) + coretlv.session.add_node(_id=switch, _type=NodeTypes.SWITCH) ip_prefix = Ipv4Prefix("10.0.0.0/24") interface_one = ip_prefix.addr(node_one) message = coreapi.CoreLinkMessage.create( @@ -313,8 +313,8 @@ class TestGui: (LinkTlvs.INTERFACE1_IP4_MASK, 24), ], ) - coreserver.request_handler.handle_message(message) - switch_node = coreserver.session.get_node(switch) + coretlv.handle_message(message) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 1 @@ -326,58 +326,58 @@ class TestGui: (LinkTlvs.INTERFACE2_NUMBER, 0), ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - switch_node = coreserver.session.get_node(switch) + switch_node = coretlv.session.get_node(switch) all_links = switch_node.all_link_data(0) assert len(all_links) == 0 - def test_session_update(self, coreserver): - session_id = coreserver.session.id + def test_session_update(self, coretlv): + session_id = coretlv.session.id name = "test" message = coreapi.CoreSessionMessage.create( 0, [(SessionTlvs.NUMBER, str(session_id)), (SessionTlvs.NAME, name)] ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.name == name + assert coretlv.session.name == name - def test_session_query(self, coreserver): - coreserver.request_handler.dispatch_replies = mock.MagicMock() + def test_session_query(self, coretlv): + coretlv.dispatch_replies = mock.MagicMock() message = coreapi.CoreSessionMessage.create(MessageFlags.STRING.value, []) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - args, _ = coreserver.request_handler.dispatch_replies.call_args + args, _ = coretlv.dispatch_replies.call_args replies = args[0] assert len(replies) == 1 - def test_session_join(self, coreserver): - coreserver.request_handler.dispatch_replies = mock.MagicMock() - session_id = coreserver.session.id + def test_session_join(self, coretlv): + coretlv.dispatch_replies = mock.MagicMock() + session_id = coretlv.session.id message = coreapi.CoreSessionMessage.create( MessageFlags.ADD.value, [(SessionTlvs.NUMBER, str(session_id))] ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.request_handler.session.id == session_id + assert coretlv.session.id == session_id - def test_session_delete(self, coreserver): - assert len(coreserver.server.coreemu.sessions) == 1 - session_id = coreserver.session.id + def test_session_delete(self, coretlv): + assert len(coretlv.coreemu.sessions) == 1 + session_id = coretlv.session.id message = coreapi.CoreSessionMessage.create( MessageFlags.DELETE.value, [(SessionTlvs.NUMBER, str(session_id))] ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert len(coreserver.server.coreemu.sessions) == 0 + assert len(coretlv.coreemu.sessions) == 0 - def test_file_hook_add(self, coreserver): + def test_file_hook_add(self, coretlv): state = EventTypes.DATACOLLECT_STATE.value - assert coreserver.session._hooks.get(state) is None + assert coretlv.session._hooks.get(state) is None file_name = "test.sh" file_data = "echo hello" message = coreapi.CoreFileMessage.create( @@ -389,16 +389,16 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - hooks = coreserver.session._hooks.get(state) + hooks = coretlv.session._hooks.get(state) assert len(hooks) == 1 name, data = hooks[0] assert file_name == name assert file_data == data - def test_file_service_file_set(self, coreserver): - node = coreserver.session.add_node() + def test_file_service_file_set(self, coretlv): + node = coretlv.session.add_node() service = "DefaultRoute" file_name = "defaultroute.sh" file_data = "echo hello" @@ -412,16 +412,16 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - service_file = coreserver.session.services.get_service_file( + service_file = coretlv.session.services.get_service_file( node, service, file_name ) assert file_data == service_file.data - def test_file_node_file_copy(self, coreserver): + def test_file_node_file_copy(self, request, coretlv): file_name = "/var/log/test/node.log" - node = coreserver.session.add_node() + node = coretlv.session.add_node() node.makenodedir() file_data = "echo hello" message = coreapi.CoreFileMessage.create( @@ -433,17 +433,17 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - directory, basename = os.path.split(file_name) - created_directory = directory[1:].replace("/", ".") - create_path = os.path.join(node.nodedir, created_directory, basename) - assert os.path.exists(create_path) + if not request.config.getoption("mock"): + directory, basename = os.path.split(file_name) + created_directory = directory[1:].replace("/", ".") + create_path = os.path.join(node.nodedir, created_directory, basename) + assert os.path.exists(create_path) - def test_exec_node_tty(self, coreserver): - coreserver.request_handler.dispatch_replies = mock.MagicMock() - node = coreserver.session.add_node() - node.startup() + def test_exec_node_tty(self, coretlv): + coretlv.dispatch_replies = mock.MagicMock() + node = coretlv.session.add_node() message = coreapi.CoreExecMessage.create( MessageFlags.TTY.value, [ @@ -453,49 +453,51 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - args, _ = coreserver.request_handler.dispatch_replies.call_args + args, _ = coretlv.dispatch_replies.call_args replies = args[0] assert len(replies) == 1 - def test_exec_local_command(self, coreserver): - coreserver.request_handler.dispatch_replies = mock.MagicMock() - node = coreserver.session.add_node() - node.startup() + def test_exec_local_command(self, request, coretlv): + if request.config.getoption("mock"): + pytest.skip("mocking calls") + + coretlv.dispatch_replies = mock.MagicMock() + node = coretlv.session.add_node() + cmd = "echo hello" message = coreapi.CoreExecMessage.create( MessageFlags.TEXT.value | MessageFlags.LOCAL.value, [ (ExecuteTlvs.NODE, node.id), (ExecuteTlvs.NUMBER, 1), - (ExecuteTlvs.COMMAND, "echo hello"), + (ExecuteTlvs.COMMAND, cmd), ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - args, _ = coreserver.request_handler.dispatch_replies.call_args + args, _ = coretlv.dispatch_replies.call_args replies = args[0] assert len(replies) == 1 - def test_exec_node_command(self, coreserver): - coreserver.request_handler.dispatch_replies = mock.MagicMock() - node = coreserver.session.add_node() - node.startup() + def test_exec_node_command(self, coretlv): + coretlv.dispatch_replies = mock.MagicMock() + node = coretlv.session.add_node() + cmd = "echo hello" message = coreapi.CoreExecMessage.create( MessageFlags.TEXT.value, [ (ExecuteTlvs.NODE, node.id), (ExecuteTlvs.NUMBER, 1), - (ExecuteTlvs.COMMAND, "echo hello"), + (ExecuteTlvs.COMMAND, cmd), ], ) + node.cmd = MagicMock(return_value="hello") - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - args, _ = coreserver.request_handler.dispatch_replies.call_args - replies = args[0] - assert len(replies) == 1 + node.cmd.assert_called_with(cmd) @pytest.mark.parametrize( "state", @@ -507,16 +509,16 @@ class TestGui: EventTypes.DEFINITION_STATE, ], ) - def test_event_state(self, coreserver, state): + def test_event_state(self, coretlv, state): message = coreapi.CoreEventMessage.create(0, [(EventTlvs.TYPE, state.value)]) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.state == state.value + assert coretlv.session.state == state.value - def test_event_schedule(self, coreserver): - coreserver.session.add_event = mock.MagicMock() - node = coreserver.session.add_node() + def test_event_schedule(self, coretlv): + coretlv.session.add_event = mock.MagicMock() + node = coretlv.session.add_node() message = coreapi.CoreEventMessage.create( MessageFlags.ADD.value, [ @@ -528,37 +530,37 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.session.add_event.assert_called_once() + coretlv.session.add_event.assert_called_once() - def test_event_save_xml(self, coreserver, tmpdir): - xml_file = tmpdir.join("session.xml") + def test_event_save_xml(self, coretlv, tmpdir): + xml_file = tmpdir.join("coretlv.session.xml") file_path = xml_file.strpath - coreserver.session.add_node() + coretlv.session.add_node() message = coreapi.CoreEventMessage.create( 0, [(EventTlvs.TYPE, EventTypes.FILE_SAVE.value), (EventTlvs.NAME, file_path)], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) assert os.path.exists(file_path) - def test_event_open_xml(self, coreserver, tmpdir): - xml_file = tmpdir.join("session.xml") + def test_event_open_xml(self, coretlv, tmpdir): + xml_file = tmpdir.join("coretlv.session.xml") file_path = xml_file.strpath - node = coreserver.session.add_node() - coreserver.session.save_xml(file_path) - coreserver.session.delete_node(node.id) + node = coretlv.session.add_node() + coretlv.session.save_xml(file_path) + coretlv.session.delete_node(node.id) message = coreapi.CoreEventMessage.create( 0, [(EventTlvs.TYPE, EventTypes.FILE_OPEN.value), (EventTlvs.NAME, file_path)], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.get_node(node.id) + assert coretlv.session.get_node(node.id) @pytest.mark.parametrize( "state", @@ -570,10 +572,9 @@ class TestGui: EventTypes.RECONFIGURE, ], ) - def test_event_service(self, coreserver, state): - coreserver.session.broadcast_event = mock.MagicMock() - node = coreserver.session.add_node() - node.startup() + def test_event_service(self, coretlv, state): + coretlv.session.broadcast_event = mock.MagicMock() + node = coretlv.session.add_node() message = coreapi.CoreEventMessage.create( 0, [ @@ -583,9 +584,9 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.session.broadcast_event.assert_called_once() + coretlv.session.broadcast_event.assert_called_once() @pytest.mark.parametrize( "state", @@ -597,69 +598,60 @@ class TestGui: EventTypes.RECONFIGURE, ], ) - def test_event_mobility(self, coreserver, state): + def test_event_mobility(self, coretlv, state): message = coreapi.CoreEventMessage.create( 0, [(EventTlvs.TYPE, state.value), (EventTlvs.NAME, "mobility:ns2script")] ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - def test_register_gui(self, coreserver): - coreserver.request_handler.master = False + def test_register_gui(self, coretlv): message = coreapi.CoreRegMessage.create(0, [(RegisterTlvs.GUI, "gui")]) + coretlv.handle_message(message) - coreserver.request_handler.handle_message(message) - - assert coreserver.request_handler.master is True - - def test_register_xml(self, coreserver, tmpdir): - xml_file = tmpdir.join("session.xml") + def test_register_xml(self, coretlv, tmpdir): + xml_file = tmpdir.join("coretlv.session.xml") file_path = xml_file.strpath - node = coreserver.session.add_node() - coreserver.session.save_xml(file_path) - coreserver.session.delete_node(node.id) + node = coretlv.session.add_node() + coretlv.session.save_xml(file_path) + coretlv.session.delete_node(node.id) message = coreapi.CoreRegMessage.create( 0, [(RegisterTlvs.EXECUTE_SERVER, file_path)] ) - coreserver.session.instantiate() + coretlv.session.instantiate() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.server.coreemu.sessions[2].get_node(node.id) + assert coretlv.coreemu.sessions[1].get_node(node.id) - def test_register_python(self, coreserver, tmpdir): + def test_register_python(self, coretlv, tmpdir): xml_file = tmpdir.join("test.py") file_path = xml_file.strpath with open(file_path, "w") as f: f.write("coreemu = globals()['coreemu']\n") - f.write("session = coreemu.sessions[1]\n") + f.write(f"session = coreemu.sessions[{coretlv.session.id}]\n") f.write("session.add_node()\n") message = coreapi.CoreRegMessage.create( 0, [(RegisterTlvs.EXECUTE_SERVER, file_path)] ) - coreserver.session.instantiate() + coretlv.session.instantiate() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert len(coreserver.session.nodes) == 1 + assert len(coretlv.session.nodes) == 1 - def test_config_all(self, coreserver): - node = coreserver.session.add_node() + def test_config_all(self, coretlv): message = coreapi.CoreConfMessage.create( MessageFlags.ADD.value, - [ - (ConfigTlvs.OBJECT, "all"), - (ConfigTlvs.NODE, node.id), - (ConfigTlvs.TYPE, ConfigFlags.RESET.value), - ], + [(ConfigTlvs.OBJECT, "all"), (ConfigTlvs.TYPE, ConfigFlags.RESET.value)], ) - coreserver.session.location.reset = mock.MagicMock() + coretlv.session.location.refxyz = (10, 10, 10) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.session.location.reset.assert_called_once() + assert coretlv.session.location.refxyz == (0, 0, 0) - def test_config_options_request(self, coreserver): + def test_config_options_request(self, coretlv): message = coreapi.CoreConfMessage.create( 0, [ @@ -667,13 +659,13 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.REQUEST.value), ], ) - coreserver.request_handler.handle_broadcast_config = mock.MagicMock() + coretlv.handle_broadcast_config = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.request_handler.handle_broadcast_config.assert_called_once() + coretlv.handle_broadcast_config.assert_called_once() - def test_config_options_update(self, coreserver): + def test_config_options_update(self, coretlv): test_key = "test" test_value = "test" values = {test_key: test_value} @@ -686,11 +678,11 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.options.get_config(test_key) == test_value + assert coretlv.session.options.get_config(test_key) == test_value - def test_config_location_reset(self, coreserver): + def test_config_location_reset(self, coretlv): message = coreapi.CoreConfMessage.create( 0, [ @@ -698,13 +690,13 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.RESET.value), ], ) - coreserver.session.location.refxyz = (10, 10, 10) + coretlv.session.location.refxyz = (10, 10, 10) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.location.refxyz == (0, 0, 0) + assert coretlv.session.location.refxyz == (0, 0, 0) - def test_config_location_update(self, coreserver): + def test_config_location_update(self, coretlv): message = coreapi.CoreConfMessage.create( 0, [ @@ -714,13 +706,13 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.location.refxyz == (10, 10, 0.0) - assert coreserver.session.location.refgeo == (70, 50, 0) - assert coreserver.session.location.refscale == 0.5 + assert coretlv.session.location.refxyz == (10, 10, 0.0) + assert coretlv.session.location.refgeo == (70, 50, 0) + assert coretlv.session.location.refscale == 0.5 - def test_config_metadata_request(self, coreserver): + def test_config_metadata_request(self, coretlv): message = coreapi.CoreConfMessage.create( 0, [ @@ -728,13 +720,13 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.REQUEST.value), ], ) - coreserver.request_handler.handle_broadcast_config = mock.MagicMock() + coretlv.handle_broadcast_config = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.request_handler.handle_broadcast_config.assert_called_once() + coretlv.handle_broadcast_config.assert_called_once() - def test_config_metadata_update(self, coreserver): + def test_config_metadata_update(self, coretlv): test_key = "test" test_value = "test" values = {test_key: test_value} @@ -747,11 +739,11 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.metadata.get_config(test_key) == test_value + assert coretlv.session.metadata[test_key] == test_value - def test_config_broker_request(self, coreserver): + def test_config_broker_request(self, coretlv): server = "test" host = "10.0.0.1" port = 50000 @@ -763,13 +755,13 @@ class TestGui: (ConfigTlvs.VALUES, f"{server}:{host}:{port}"), ], ) - coreserver.session.distributed.add_server = mock.MagicMock() + coretlv.session.distributed.add_server = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.session.distributed.add_server.assert_called_once_with(server, host) + coretlv.session.distributed.add_server.assert_called_once_with(server, host) - def test_config_services_request_all(self, coreserver): + def test_config_services_request_all(self, coretlv): message = coreapi.CoreConfMessage.create( 0, [ @@ -777,14 +769,14 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.REQUEST.value), ], ) - coreserver.request_handler.handle_broadcast_config = mock.MagicMock() + coretlv.handle_broadcast_config = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.request_handler.handle_broadcast_config.assert_called_once() + coretlv.handle_broadcast_config.assert_called_once() - def test_config_services_request_specific(self, coreserver): - node = coreserver.session.add_node() + def test_config_services_request_specific(self, coretlv): + node = coretlv.session.add_node() message = coreapi.CoreConfMessage.create( 0, [ @@ -794,14 +786,14 @@ class TestGui: (ConfigTlvs.OPAQUE, "service:DefaultRoute"), ], ) - coreserver.request_handler.handle_broadcast_config = mock.MagicMock() + coretlv.handle_broadcast_config = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.request_handler.handle_broadcast_config.assert_called_once() + coretlv.handle_broadcast_config.assert_called_once() - def test_config_services_request_specific_file(self, coreserver): - node = coreserver.session.add_node() + def test_config_services_request_specific_file(self, coretlv): + node = coretlv.session.add_node() message = coreapi.CoreConfMessage.create( 0, [ @@ -811,16 +803,16 @@ class TestGui: (ConfigTlvs.OPAQUE, "service:DefaultRoute:defaultroute.sh"), ], ) - coreserver.session.broadcast_file = mock.MagicMock() + coretlv.session.broadcast_file = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.session.broadcast_file.assert_called_once() + coretlv.session.broadcast_file.assert_called_once() - def test_config_services_reset(self, coreserver): - node = coreserver.session.add_node() + def test_config_services_reset(self, coretlv): + node = coretlv.session.add_node() service = "DefaultRoute" - coreserver.session.services.set_service(node.id, service) + coretlv.session.services.set_service(node.id, service) message = coreapi.CoreConfMessage.create( 0, [ @@ -828,14 +820,14 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.RESET.value), ], ) - assert coreserver.session.services.get_service(node.id, service) is not None + assert coretlv.session.services.get_service(node.id, service) is not None - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.services.get_service(node.id, service) is None + assert coretlv.session.services.get_service(node.id, service) is None - def test_config_services_set(self, coreserver): - node = coreserver.session.add_node() + def test_config_services_set(self, coretlv): + node = coretlv.session.add_node() service = "DefaultRoute" values = {"meta": "metadata"} message = coreapi.CoreConfMessage.create( @@ -848,14 +840,14 @@ class TestGui: (ConfigTlvs.VALUES, dict_to_str(values)), ], ) - assert coreserver.session.services.get_service(node.id, service) is None + assert coretlv.session.services.get_service(node.id, service) is None - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert coreserver.session.services.get_service(node.id, service) is not None + assert coretlv.session.services.get_service(node.id, service) is not None - def test_config_mobility_reset(self, coreserver): - wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN) + def test_config_mobility_reset(self, coretlv): + wlan = coretlv.session.add_node(_type=NodeTypes.WIRELESS_LAN) message = coreapi.CoreConfMessage.create( 0, [ @@ -863,15 +855,15 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.RESET.value), ], ) - coreserver.session.mobility.set_model_config(wlan.id, BasicRangeModel.name, {}) - assert len(coreserver.session.mobility.node_configurations) == 1 + coretlv.session.mobility.set_model_config(wlan.id, BasicRangeModel.name, {}) + assert len(coretlv.session.mobility.node_configurations) == 1 - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - assert len(coreserver.session.mobility.node_configurations) == 0 + assert len(coretlv.session.mobility.node_configurations) == 0 - def test_config_mobility_model_request(self, coreserver): - wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN) + def test_config_mobility_model_request(self, coretlv): + wlan = coretlv.session.add_node(_type=NodeTypes.WIRELESS_LAN) message = coreapi.CoreConfMessage.create( 0, [ @@ -880,14 +872,14 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.REQUEST.value), ], ) - coreserver.request_handler.handle_broadcast_config = mock.MagicMock() + coretlv.handle_broadcast_config = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.request_handler.handle_broadcast_config.assert_called_once() + coretlv.handle_broadcast_config.assert_called_once() - def test_config_mobility_model_update(self, coreserver): - wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN) + def test_config_mobility_model_update(self, coretlv): + wlan = coretlv.session.add_node(_type=NodeTypes.WIRELESS_LAN) config_key = "range" config_value = "1000" values = {config_key: config_value} @@ -901,15 +893,15 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - config = coreserver.session.mobility.get_model_config( + config = coretlv.session.mobility.get_model_config( wlan.id, BasicRangeModel.name ) assert config[config_key] == config_value - def test_config_emane_model_request(self, coreserver): - wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN) + def test_config_emane_model_request(self, coretlv): + wlan = coretlv.session.add_node(_type=NodeTypes.WIRELESS_LAN) message = coreapi.CoreConfMessage.create( 0, [ @@ -918,14 +910,14 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.REQUEST.value), ], ) - coreserver.request_handler.handle_broadcast_config = mock.MagicMock() + coretlv.handle_broadcast_config = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.request_handler.handle_broadcast_config.assert_called_once() + coretlv.handle_broadcast_config.assert_called_once() - def test_config_emane_model_update(self, coreserver): - wlan = coreserver.session.add_node(_type=NodeTypes.WIRELESS_LAN) + def test_config_emane_model_update(self, coretlv): + wlan = coretlv.session.add_node(_type=NodeTypes.WIRELESS_LAN) config_key = "distance" config_value = "50051" values = {config_key: config_value} @@ -939,14 +931,14 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - config = coreserver.session.emane.get_model_config( + config = coretlv.session.emane.get_model_config( wlan.id, EmaneIeee80211abgModel.name ) assert config[config_key] == config_value - def test_config_emane_request(self, coreserver): + def test_config_emane_request(self, coretlv): message = coreapi.CoreConfMessage.create( 0, [ @@ -954,13 +946,13 @@ class TestGui: (ConfigTlvs.TYPE, ConfigFlags.REQUEST.value), ], ) - coreserver.request_handler.handle_broadcast_config = mock.MagicMock() + coretlv.handle_broadcast_config = mock.MagicMock() - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - coreserver.request_handler.handle_broadcast_config.assert_called_once() + coretlv.handle_broadcast_config.assert_called_once() - def test_config_emane_update(self, coreserver): + def test_config_emane_update(self, coretlv): config_key = "eventservicedevice" config_value = "eth4" values = {config_key: config_value} @@ -973,7 +965,7 @@ class TestGui: ], ) - coreserver.request_handler.handle_message(message) + coretlv.handle_message(message) - config = coreserver.session.emane.get_configs() + config = coretlv.session.emane.get_configs() assert config[config_key] == config_value diff --git a/daemon/tests/test_nodes.py b/daemon/tests/test_nodes.py index 70525f70..71d1a157 100644 --- a/daemon/tests/test_nodes.py +++ b/daemon/tests/test_nodes.py @@ -1,15 +1,10 @@ -import os -import time - import pytest -from core import utils from core.emulator.emudata import NodeOptions from core.emulator.enumerations import NodeTypes from core.errors import CoreError MODELS = ["router", "host", "PC", "mdr"] - NET_TYPES = [NodeTypes.SWITCH, NodeTypes.HUB, NodeTypes.WIRELESS_LAN] @@ -17,20 +12,15 @@ class TestNodes: @pytest.mark.parametrize("model", MODELS) def test_node_add(self, session, model): # given - node_options = NodeOptions(model=model) + options = NodeOptions(model=model) # when - node = session.add_node(node_options=node_options) - - # give time for node services to boot - time.sleep(1) + node = session.add_node(options=options) # then assert node - assert os.path.exists(node.nodedir) assert node.alive() assert node.up - assert node.cmd("ip address show lo") def test_node_update(self, session): # given @@ -40,7 +30,7 @@ class TestNodes: update_options.set_position(x=position_value, y=position_value) # when - session.update_node(node.id, update_options) + session.edit_node(node.id, update_options) # then assert node.position.x == position_value @@ -67,4 +57,3 @@ class TestNodes: # then assert node assert node.up - assert utils.cmd(f"brctl show {node.brname}") diff --git a/daemon/tests/test_services.py b/daemon/tests/test_services.py index e2a3f32a..489a9ab7 100644 --- a/daemon/tests/test_services.py +++ b/daemon/tests/test_services.py @@ -1,7 +1,9 @@ import os import pytest +from mock import MagicMock +from core.errors import CoreCommandError from core.services.coreservices import CoreService, ServiceDependencies, ServiceManager _PATH = os.path.abspath(os.path.dirname(__file__)) @@ -88,7 +90,7 @@ class TestServices: assert node.services assert len(node.services) == total_service + 2 - def test_service_file(self, session): + def test_service_file(self, request, session): # given ServiceManager.add_services(_SERVICES_PATH) my_service = ServiceManager.get(SERVICE_ONE) @@ -100,7 +102,8 @@ class TestServices: session.services.create_service_files(node, my_service) # then - assert os.path.exists(file_path) + if not request.config.getoption("mock"): + assert os.path.exists(file_path) def test_service_validate(self, session): # given @@ -121,6 +124,7 @@ class TestServices: my_service = ServiceManager.get(SERVICE_TWO) node = session.add_node() session.services.create_service_files(node, my_service) + node.cmd = MagicMock(side_effect=CoreCommandError(-1, "invalid")) # when status = session.services.validate_service(node, my_service) @@ -147,6 +151,7 @@ class TestServices: my_service = ServiceManager.get(SERVICE_TWO) node = session.add_node() session.services.create_service_files(node, my_service) + node.cmd = MagicMock(side_effect=CoreCommandError(-1, "invalid")) # when status = session.services.startup_service(node, my_service, wait=True) @@ -173,6 +178,7 @@ class TestServices: my_service = ServiceManager.get(SERVICE_TWO) node = session.add_node() session.services.create_service_files(node, my_service) + node.cmd = MagicMock(side_effect=CoreCommandError(-1, "invalid")) # when status = session.services.stop_service(node, my_service) @@ -216,17 +222,6 @@ class TestServices: custom_service_two = session.services.get_service(node_two.id, my_service.name) session.services.create_service_files(node_two, custom_service_two) - # then - file_path_one = node_one.hostfilename(file_name) - assert os.path.exists(file_path_one) - with open(file_path_one, "r") as custom_file: - assert custom_file.read() == file_data_one - - file_path_two = node_two.hostfilename(file_name) - assert os.path.exists(file_path_two) - with open(file_path_two, "r") as custom_file: - assert custom_file.read() == file_data_two - def test_service_import(self): """ Test importing a custom service. diff --git a/daemon/tests/test_xml.py b/daemon/tests/test_xml.py index dc01c09d..496623a6 100644 --- a/daemon/tests/test_xml.py +++ b/daemon/tests/test_xml.py @@ -108,8 +108,8 @@ class TestXml: ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER) # create nodes - node_options = NodeOptions(model="host") - node_one = session.add_node(node_options=node_options) + options = NodeOptions(model="host") + node_one = session.add_node(options=options) node_two = session.add_node() # link nodes to ptp net @@ -174,10 +174,10 @@ class TestXml: session.mobility.set_model(wlan_node, BasicRangeModel, {"test": "1"}) # create nodes - 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) + options = NodeOptions(model="mdr") + options.set_position(0, 0) + node_one = session.add_node(options=options) + node_two = session.add_node(options=options) # link nodes for node in [node_one, node_two]: diff --git a/docs/scripting.md b/docs/scripting.md index 6985b7d8..38eaa901 100644 --- a/docs/scripting.md +++ b/docs/scripting.md @@ -108,13 +108,12 @@ Examples for configuring custom emane model settings. # create session and emane network coreemu = CoreEmu() session = coreemu.create_session() -emane_network = session.create_emane_network( - model=EmaneIeee80211abgModel, - geo_reference=(47.57917, -122.13232, 2.00000) -) -emane_network.setposition(x=80, y=50) +session.set_location(47.57917, -122.13232, 2.00000, 1.0) +options = NodeOptions() +options.set_position(80, 50) +emane_network = session.add_node(_type=NodeTypes.EMANE, options=options) # set custom emane model config config = {} -session.emane.set_model_config(emane_network.id, EmaneIeee80211abgModel.name, config) +session.emane.set_model(emane_network, EmaneIeee80211abgModel, config) ``` diff --git a/ns3/corens3/obj.py b/ns3/corens3/obj.py index c1907f03..750dc7a5 100644 --- a/ns3/corens3/obj.py +++ b/ns3/corens3/obj.py @@ -60,7 +60,7 @@ class CoreNs3Node(CoreNode, ns.network.Node): if not isinstance(net, CoreNs3Net): return CoreNode.newnetif(self, net, addrlist, hwaddr, ifindex, ifname) - ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname, net=net) + ifindex = self.newtuntap(ifindex, ifname) self.attachnet(ifindex, net) netif = self.netif(ifindex) netif.sethwaddr(hwaddr) @@ -68,7 +68,7 @@ class CoreNs3Node(CoreNode, ns.network.Node): netif.addaddr(addr) addrstr = netif.addrlist[0] - (addr, mask) = addrstr.split('/') + addr, mask = addrstr.split('/') tap = net._tapdevs[netif] tap.SetAttribute( "IpAddress", @@ -76,9 +76,9 @@ class CoreNs3Node(CoreNode, ns.network.Node): ) tap.SetAttribute( "Netmask", - ns.network.Ipv4MaskValue(ns.network.Ipv4Mask("/" + mask)) + ns.network.Ipv4MaskValue(ns.network.Ipv4Mask(f"/{mask}")) ) - ns.core.Simulator.Schedule(ns.core.Time('0'), netif.install) + ns.core.Simulator.Schedule(ns.core.Time("0"), netif.install) return ifindex def getns3position(self): @@ -118,7 +118,7 @@ class CoreNs3Net(CoreNetworkBase): type = "wlan" def __init__( - self, session, _id=None, name=None, start=True, server=None, policy=None + self, session, _id=None, name=None, start=True, server=None ): CoreNetworkBase.__init__(self, session, _id, name, start, server) self.tapbridge = ns.tap_bridge.TapBridgeHelper()