daemon: adjustments to revamp how core nodes are created in session.add_node, nodes now provide a create_options function for node specific options that are type hinted

This commit is contained in:
Blake Harnden 2022-05-25 10:51:42 -07:00
parent 03e646031c
commit 2e3e085522
35 changed files with 646 additions and 478 deletions

View file

@ -17,15 +17,22 @@ from core.api.grpc.services_pb2 import (
ServiceDefaults,
)
from core.config import ConfigurableOptions
from core.emane.nodes import EmaneNet
from core.emulator.data import InterfaceData, LinkData, LinkOptions, NodeOptions
from core.emane.nodes import EmaneNet, EmaneOptions
from core.emulator.data import InterfaceData, LinkData, LinkOptions
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.emulator.links import CoreLink
from core.emulator.session import Session
from core.errors import CoreError
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.nodes.base import CoreNode, CoreNodeBase, NodeBase
from core.nodes.docker import DockerNode
from core.nodes.base import (
CoreNode,
CoreNodeBase,
CoreNodeOptions,
NodeBase,
NodeOptions,
Position,
)
from core.nodes.docker import DockerNode, DockerOptions
from core.nodes.interface import CoreInterface
from core.nodes.lxd import LxcNode
from core.nodes.network import CoreNetwork, CtrlNet, PtpNet, WlanNode
@ -55,34 +62,33 @@ class CpuUsage:
return (total_diff - idle_diff) / total_diff
def add_node_data(node_proto: core_pb2.Node) -> Tuple[NodeTypes, int, NodeOptions]:
def add_node_data(
_class: Type[NodeBase], node_proto: core_pb2.Node
) -> Tuple[Position, NodeOptions]:
"""
Convert node protobuf message to data for creating a node.
:param _class: node class to create options from
:param node_proto: node proto message
:return: node type, id, and options
"""
_id = node_proto.id
_type = NodeTypes(node_proto.type)
options = NodeOptions(
name=node_proto.name,
model=node_proto.model,
icon=node_proto.icon,
image=node_proto.image,
services=node_proto.services,
config_services=node_proto.config_services,
canvas=node_proto.canvas,
)
if node_proto.emane:
options.emane = node_proto.emane
if node_proto.server:
options.server = node_proto.server
position = node_proto.position
options.set_position(position.x, position.y)
options = _class.create_options()
options.icon = node_proto.icon
options.canvas = node_proto.canvas
if isinstance(options, CoreNodeOptions):
options.model = node_proto.model
options.services = node_proto.services
options.config_services = node_proto.config_services
if isinstance(options, EmaneOptions):
options.emane_model = node_proto.emane
if isinstance(options, DockerOptions):
options.image = node_proto.image
position = Position()
position.set(node_proto.position.x, node_proto.position.y)
if node_proto.HasField("geo"):
geo = node_proto.geo
options.set_location(geo.lat, geo.lon, geo.alt)
return _type, _id, options
position.set_geo(geo.lon, geo.lat, geo.alt)
return position, options
def link_iface(iface_proto: core_pb2.Interface) -> InterfaceData:
@ -150,9 +156,17 @@ def create_nodes(
"""
funcs = []
for node_proto in node_protos:
_type, _id, options = add_node_data(node_proto)
_type = NodeTypes(node_proto.type)
_class = session.get_node_class(_type)
args = (_class, _id, options)
position, options = add_node_data(_class, node_proto)
args = (
_class,
node_proto.id or None,
node_proto.name or None,
node_proto.server or None,
position,
options,
)
funcs.append((session.add_node, args, {}))
start = time.monotonic()
results, exceptions = utils.threadpool(funcs)

View file

@ -88,7 +88,12 @@ from core.configservice.base import ConfigServiceBootError
from core.emane.modelmanager import EmaneModelManager
from core.emulator.coreemu import CoreEmu
from core.emulator.data import InterfaceData, LinkData, LinkOptions
from core.emulator.enumerations import EventTypes, ExceptionLevels, MessageFlags
from core.emulator.enumerations import (
EventTypes,
ExceptionLevels,
MessageFlags,
NodeTypes,
)
from core.emulator.session import NT, Session
from core.errors import CoreCommandError, CoreError
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
@ -548,9 +553,17 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logger.debug("add node: %s", request)
session = self.get_session(request.session_id, context)
_type, _id, options = grpcutils.add_node_data(request.node)
_type = NodeTypes(request.node.type)
_class = session.get_node_class(_type)
node = session.add_node(_class, _id, options)
position, options = grpcutils.add_node_data(_class, request.node)
node = session.add_node(
_class,
request.node.id or None,
request.node.name or None,
request.node.server or None,
position,
options,
)
grpcutils.configure_node(session, request.node, node, context)
source = request.source if request.source else None
session.broadcast_node(node, MessageFlags.ADD, source)

View file

@ -603,7 +603,7 @@ class EmaneManager:
node = iface.node
loglevel = str(DEFAULT_LOG_LEVEL)
cfgloglevel = self.session.options.get_int("emane_log_level", 2)
realtime = self.session.options.get_bool("emane_realtime")
realtime = self.session.options.get_bool("emane_realtime", True)
if cfgloglevel:
logger.info("setting user-defined emane log level: %d", cfgloglevel)
loglevel = str(cfgloglevel)

View file

@ -5,13 +5,14 @@ share the same MAC+PHY model.
import logging
import time
from dataclasses import dataclass
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type, Union
from core.emulator.data import InterfaceData, LinkData, LinkOptions
from core.emulator.distributed import DistributedServer
from core.emulator.enumerations import EventTypes, MessageFlags, RegisterTlvs
from core.errors import CoreCommandError, CoreError
from core.nodes.base import CoreNetworkBase, CoreNode
from core.nodes.base import CoreNetworkBase, CoreNode, NodeOptions
from core.nodes.interface import CoreInterface
logger = logging.getLogger(__name__)
@ -139,6 +140,12 @@ class TunTap(CoreInterface):
self.node.node_net_client.create_address(self.name, str(ip))
@dataclass
class EmaneOptions(NodeOptions):
emane_model: str = None
"""name of emane model to associate an emane network to"""
class EmaneNet(CoreNetworkBase):
"""
EMANE node contains NEM configuration and causes connected nodes
@ -152,11 +159,20 @@ class EmaneNet(CoreNetworkBase):
_id: int = None,
name: str = None,
server: DistributedServer = None,
options: EmaneOptions = None,
) -> None:
super().__init__(session, _id, name, server)
options = options or EmaneOptions()
super().__init__(session, _id, name, server, options)
self.conf: str = ""
self.wireless_model: Optional["EmaneModel"] = None
self.mobility: Optional[WayPointMobility] = None
model_class = self.session.emane.get_model(options.emane_model)
self.wireless_model: Optional["EmaneModel"] = model_class(self.session, self.id)
if self.session.state == EventTypes.RUNTIME_STATE:
self.session.emane.add_node(self)
@classmethod
def create_options(cls) -> EmaneOptions:
return EmaneOptions()
def linkconfig(
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None

View file

@ -14,7 +14,7 @@ import tempfile
import threading
import time
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, TypeVar, Union
from typing import Callable, Dict, List, Optional, Set, Tuple, Type, TypeVar, Union
from core import constants, utils
from core.configservice.manager import ConfigServiceManager
@ -29,7 +29,6 @@ from core.emulator.data import (
LinkData,
LinkOptions,
NodeData,
NodeOptions,
)
from core.emulator.distributed import DistributedController
from core.emulator.enumerations import (
@ -44,7 +43,7 @@ from core.errors import CoreError
from core.location.event import EventLoop
from core.location.geo import GeoLocation
from core.location.mobility import BasicRangeModel, MobilityManager
from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase
from core.nodes.base import CoreNode, CoreNodeBase, NodeBase, NodeOptions, Position
from core.nodes.docker import DockerNode
from core.nodes.interface import DEFAULT_MTU, CoreInterface
from core.nodes.lxd import LxcNode
@ -476,14 +475,23 @@ class Session:
return _id
def add_node(
self, _class: Type[NT], _id: int = None, options: NodeOptions = None
self,
_class: Type[NT],
_id: int = None,
name: str = None,
server: str = None,
position: Position = None,
options: NodeOptions = None,
) -> NT:
"""
Add a node to the session, based on the provided node data.
:param _class: node class to create
:param _id: id for node, defaults to None for generated id
:param options: data to create node with
:param name: name to assign to node
:param server: distributed server for node, if desired
:param position: geo or x/y/z position to set
:param options: options to create node with
:return: created node
:raises core.CoreError: when an invalid node type is given
"""
@ -492,89 +500,31 @@ class Session:
enable_rj45 = self.options.get_int("enablerj45") == 1
if _class == Rj45Node and not enable_rj45:
start = False
# determine node id
if not _id:
_id = self.next_node_id()
# generate name if not provided
if not options:
options = NodeOptions()
options.set_position(0, 0)
name = options.name
if not name:
name = f"{_class.__name__}{_id}"
# generate options if not provided
options = options if options else _class.create_options()
# verify distributed 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}")
dist_server = None
if server is not None:
dist_server = self.distributed.servers.get(server)
if not dist_server:
raise CoreError(f"invalid distributed server: {server}")
# create node
logger.info(
"creating node(%s) id(%s) name(%s) start(%s)",
_class.__name__,
_id,
name,
start,
)
kwargs = dict(_id=_id, name=name, server=server)
if _class in CONTAINER_NODES:
kwargs["image"] = options.image
kwargs["binds"] = options.binds
kwargs["volumes"] = options.volumes
node = self.create_node(_class, start, **kwargs)
# set node attributes
node.icon = options.icon
node.canvas = options.canvas
# set node position and broadcast it
has_geo = all(i is not None for i in [options.lon, options.lat, options.alt])
if has_geo:
self.set_node_geo(node, options.lon, options.lat, options.alt)
node = self.create_node(_class, start, _id, name, dist_server, options)
# set node position
position = position or Position()
if position.has_geo():
self.set_node_geo(node, position.lon, position.lat, position.alt)
else:
self.set_node_pos(node, options.x, options.y)
# add services to needed nodes
if isinstance(node, (CoreNode, PhysicalNode)):
node.model = options.model
if options.legacy or options.services:
logger.debug("set node type: %s", node.model)
self.services.add_services(node, node.model, options.services)
# add config services
config_services = options.config_services
if not options.legacy and not config_services and not node.services:
config_services = self.services.default_services.get(node.model, [])
logger.info("setting node config services: %s", config_services)
for name in config_services:
service_class = self.service_manager.get_service(name)
node.add_config_service(service_class)
# set network mtu, if configured
mtu = self.options.get_int("mtu")
if isinstance(node, CoreNetworkBase) and mtu > 0:
node.mtu = mtu
# ensure default emane configuration
if isinstance(node, EmaneNet) and options.emane:
model_class = self.emane.get_model(options.emane)
node.wireless_model = model_class(self, node.id)
if self.state == EventTypes.RUNTIME_STATE:
self.emane.add_node(node)
# set default wlan config if needed
self.set_node_pos(node, position.x, position.y)
# setup default wlan
if isinstance(node, WlanNode):
self.mobility.set_model_config(_id, BasicRangeModel.name)
# boot nodes after runtime CoreNodes and PhysicalNodes
is_boot_node = isinstance(node, (CoreNode, PhysicalNode))
if self.state == EventTypes.RUNTIME_STATE and is_boot_node:
self.mobility.set_model_config(self.id, BasicRangeModel.name)
# boot core nodes after runtime
is_runtime = self.state == EventTypes.RUNTIME_STATE
if is_runtime and isinstance(node, CoreNode):
self.write_nodes()
self.add_remove_control_iface(node, remove=False)
self.boot_node(node)
self.sdt.add_node(node)
return node
@ -980,24 +930,39 @@ class Session:
logger.exception("failed to set permission on %s", self.directory)
def create_node(
self, _class: Type[NT], start: bool, *args: Any, **kwargs: Any
self,
_class: Type[NT],
start: bool,
_id: int = None,
name: str = None,
server: str = None,
options: NodeOptions = None,
) -> NT:
"""
Create an emulation node.
:param _class: node class to create
:param start: True to start node, False otherwise
:param args: list of arguments for the class to create
:param kwargs: dictionary of arguments for the class to create
:param _id: id for node, defaults to None for generated id
:param name: name to assign to node
:param server: distributed server for node, if desired
:param options: options to create node with
:return: the created node instance
:raises core.CoreError: when id of the node to create already exists
"""
with self.nodes_lock:
node = _class(self, *args, **kwargs)
node = _class(self, _id=_id, name=name, server=server, options=options)
if node.id in self.nodes:
node.shutdown()
raise CoreError(f"duplicate node id {node.id} for {node.name}")
self.nodes[node.id] = node
logger.info(
"created node(%s) id(%s) name(%s) start(%s)",
_class.__name__,
node.id,
node.name,
start,
)
if start:
node.startup()
return node
@ -1219,7 +1184,7 @@ class Session:
funcs = []
start = time.monotonic()
for node in self.nodes.values():
if isinstance(node, (CoreNode, PhysicalNode)):
if isinstance(node, CoreNode):
self.add_remove_control_iface(node, remove=False)
funcs.append((self.boot_node, (node,), {}))
results, exceptions = utils.threadpool(funcs)
@ -1354,21 +1319,18 @@ class Session:
updown_script,
server_iface,
)
control_net = self.create_node(
CtrlNet,
start=False,
prefix=prefix,
_id=_id,
updown_script=updown_script,
serverintf=server_iface,
)
options = CtrlNet.create_options()
options.prefix = prefix
options.updown_script = updown_script
options.serverintf = server_iface
control_net = self.create_node(CtrlNet, False, _id, options=options)
control_net.brname = f"ctrl{net_index}.{self.short_session_id()}"
control_net.startup()
return control_net
def add_remove_control_iface(
self,
node: Union[CoreNode, PhysicalNode],
node: CoreNode,
net_index: int = 0,
remove: bool = False,
conf_required: bool = True,

View file

@ -5,6 +5,7 @@ import abc
import logging
import shutil
import threading
from dataclasses import dataclass, field
from pathlib import Path
from threading import RLock
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type, Union
@ -33,6 +34,94 @@ if TYPE_CHECKING:
PRIVATE_DIRS: List[Path] = [Path("/var/run"), Path("/var/log")]
@dataclass
class Position:
"""
Helper class for Cartesian coordinate position
"""
x: float = 0.0
y: float = 0.0
z: float = 0.0
lon: float = None
lat: float = None
alt: float = None
def set(self, x: float = None, y: float = None, z: float = None) -> bool:
"""
Returns True if the position has actually changed.
:param x: x position
:param y: y position
:param z: z position
:return: True if position changed, False otherwise
"""
if self.x == x and self.y == y and self.z == z:
return False
self.x = x
self.y = y
self.z = z
return True
def get(self) -> Tuple[float, float, float]:
"""
Retrieve x,y,z position.
:return: x,y,z position tuple
"""
return self.x, self.y, self.z
def has_geo(self) -> bool:
return all(x is not None for x in [self.lon, self.lat, self.alt])
def set_geo(self, lon: float, lat: float, alt: float) -> None:
"""
Set geo position lon, lat, alt.
:param lon: longitude value
:param lat: latitude value
:param alt: altitude value
:return: nothing
"""
self.lon = lon
self.lat = lat
self.alt = alt
def get_geo(self) -> Tuple[float, float, float]:
"""
Retrieve current geo position lon, lat, alt.
:return: lon, lat, alt position tuple
"""
return self.lon, self.lat, self.alt
@dataclass
class NodeOptions:
"""
Base options for configuring a node.
"""
canvas: int = None
"""id of canvas for display within gui"""
icon: str = None
"""custom icon for display, None for default"""
@dataclass
class CoreNodeOptions(NodeOptions):
model: str = "PC"
"""model is used for providing a default set of services"""
services: List[str] = field(default_factory=list)
"""services to start within node"""
config_services: List[str] = field(default_factory=list)
"""config services to start within node"""
directory: Path = None
"""directory to define node, defaults to path under the session directory"""
legacy: bool = False
"""legacy nodes default to standard services"""
class NodeBase(abc.ABC):
"""
Base class for CORE nodes (nodes and networks)
@ -44,6 +133,7 @@ class NodeBase(abc.ABC):
_id: int = None,
name: str = None,
server: "DistributedServer" = None,
options: NodeOptions = None,
) -> None:
"""
Creates a NodeBase instance.
@ -53,26 +143,29 @@ class NodeBase(abc.ABC):
:param name: object name
:param server: remote server node
will run on, default is None for localhost
:param options: options to create node with
"""
self.session: "Session" = session
if _id is None:
_id = session.next_node_id()
self.id: int = _id
self.name: str = name or f"o{self.id}"
self.id: int = _id if _id is not None else self.session.next_node_id()
self.name: str = name or f"{self.__class__.__name__}{self.id}"
self.server: "DistributedServer" = server
self.model: Optional[str] = None
self.services: CoreServices = []
self.ifaces: Dict[int, CoreInterface] = {}
self.iface_id: int = 0
self.canvas: Optional[int] = None
self.icon: Optional[str] = None
self.position: Position = Position()
self.up: bool = False
self.lock: RLock = RLock()
self.net_client: LinuxNetClient = get_net_client(
self.session.use_ovs(), self.host_cmd
)
options = options if options else NodeOptions()
self.canvas: Optional[int] = options.canvas
self.icon: Optional[str] = options.icon
@classmethod
def create_options(cls) -> NodeOptions:
return NodeOptions()
@abc.abstractmethod
def startup(self) -> None:
@ -288,6 +381,7 @@ class CoreNodeBase(NodeBase):
_id: int = None,
name: str = None,
server: "DistributedServer" = None,
options: NodeOptions = None,
) -> None:
"""
Create a CoreNodeBase instance.
@ -298,7 +392,7 @@ class CoreNodeBase(NodeBase):
:param server: remote server node
will run on, default is None for localhost
"""
super().__init__(session, _id, name, server)
super().__init__(session, _id, name, server, options)
self.config_services: Dict[str, "ConfigService"] = {}
self.directory: Optional[Path] = None
self.tmpnodedir: bool = False
@ -460,8 +554,8 @@ class CoreNode(CoreNodeBase):
session: "Session",
_id: int = None,
name: str = None,
directory: Path = None,
server: "DistributedServer" = None,
options: CoreNodeOptions = None,
) -> None:
"""
Create a CoreNode instance.
@ -469,18 +563,37 @@ class CoreNode(CoreNodeBase):
:param session: core session instance
:param _id: object id
:param name: object name
:param directory: node directory
:param server: remote server node
will run on, default is None for localhost
:param options: options to create node with
"""
super().__init__(session, _id, name, server)
self.directory: Optional[Path] = directory
options = options or CoreNodeOptions()
super().__init__(session, _id, name, server, options)
self.directory: Optional[Path] = options.directory
self.ctrlchnlname: Path = self.session.directory / self.name
self.pid: Optional[int] = None
self._mounts: List[Tuple[Path, Path]] = []
self.node_net_client: LinuxNetClient = self.create_node_net_client(
self.session.use_ovs()
)
options = options or CoreNodeOptions()
self.model: Optional[str] = options.model
# setup services
if options.legacy or options.services:
logger.debug("set node type: %s", self.model)
self.session.services.add_services(self, self.model, options.services)
# add config services
config_services = options.config_services
if not options.legacy and not config_services and not options.services:
config_services = self.session.services.default_services.get(self.model, [])
logger.info("setting node config services: %s", config_services)
for name in config_services:
service_class = self.session.service_manager.get_service(name)
self.add_config_service(service_class)
@classmethod
def create_options(cls) -> CoreNodeOptions:
return CoreNodeOptions()
def create_node_net_client(self, use_ovs: bool) -> LinuxNetClient:
"""
@ -797,6 +910,7 @@ class CoreNetworkBase(NodeBase):
_id: int,
name: str,
server: "DistributedServer" = None,
options: NodeOptions = None,
) -> None:
"""
Create a CoreNetworkBase instance.
@ -806,9 +920,11 @@ class CoreNetworkBase(NodeBase):
:param name: object name
:param server: remote server node
will run on, default is None for localhost
:param options: options to create node with
"""
super().__init__(session, _id, name, server)
self.mtu: int = DEFAULT_MTU
super().__init__(session, _id, name, server, options)
mtu = self.session.options.get_int("mtu")
self.mtu: int = mtu if mtu > 0 else DEFAULT_MTU
self.brname: Optional[str] = None
self.linked: Dict[CoreInterface, Dict[CoreInterface, bool]] = {}
self.linked_lock: threading.Lock = threading.Lock()
@ -839,69 +955,3 @@ class CoreNetworkBase(NodeBase):
iface.net_id = None
with self.linked_lock:
del self.linked[iface]
class Position:
"""
Helper class for Cartesian coordinate position
"""
def __init__(self, x: float = None, y: float = None, z: float = None) -> None:
"""
Creates a Position instance.
:param x: x position
:param y: y position
:param z: z position
"""
self.x: float = x
self.y: float = y
self.z: float = z
self.lon: Optional[float] = None
self.lat: Optional[float] = None
self.alt: Optional[float] = None
def set(self, x: float = None, y: float = None, z: float = None) -> bool:
"""
Returns True if the position has actually changed.
:param x: x position
:param y: y position
:param z: z position
:return: True if position changed, False otherwise
"""
if self.x == x and self.y == y and self.z == z:
return False
self.x = x
self.y = y
self.z = z
return True
def get(self) -> Tuple[float, float, float]:
"""
Retrieve x,y,z position.
:return: x,y,z position tuple
"""
return self.x, self.y, self.z
def set_geo(self, lon: float, lat: float, alt: float) -> None:
"""
Set geo position lon, lat, alt.
:param lon: longitude value
:param lat: latitude value
:param alt: altitude value
:return: nothing
"""
self.lon = lon
self.lat = lat
self.alt = alt
def get_geo(self) -> Tuple[float, float, float]:
"""
Retrieve current geo position lon, lat, alt.
:return: lon, lat, alt position tuple
"""
return self.lon, self.lat, self.alt

View file

@ -1,7 +1,7 @@
import json
import logging
import shlex
from dataclasses import dataclass
from dataclasses import dataclass, field
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING, Dict, List, Tuple
@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Dict, List, Tuple
from core.emulator.distributed import DistributedServer
from core.errors import CoreCommandError, CoreError
from core.executables import BASH
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, CoreNodeOptions
logger = logging.getLogger(__name__)
@ -19,6 +19,21 @@ if TYPE_CHECKING:
DOCKER: str = "docker"
@dataclass
class DockerOptions(CoreNodeOptions):
image: str = "ubuntu"
"""image used when creating container"""
binds: List[Tuple[str, str]] = field(default_factory=list)
"""bind mount source and destinations to setup within container"""
volumes: List[Tuple[str, str, bool, bool]] = field(default_factory=list)
"""
volume mount source, destination, unique, delete to setup within container
unique is True for node unique volume naming
delete is True for deleting volume mount during shutdown
"""
@dataclass
class DockerVolume:
src: str
@ -38,11 +53,8 @@ class DockerNode(CoreNode):
session: "Session",
_id: int = None,
name: str = None,
directory: str = None,
server: DistributedServer = None,
image: str = None,
binds: List[Tuple[str, str]] = None,
volumes: List[Tuple[str, str, bool, bool]] = None,
options: DockerOptions = None,
) -> None:
"""
Create a DockerNode instance.
@ -50,22 +62,23 @@ class DockerNode(CoreNode):
:param session: core session instance
:param _id: object id
:param name: object name
:param directory: node directory
:param server: remote server node
will run on, default is None for localhost
:param image: image to start container with
:param binds: bind mounts to set for the created container
:param volumes: volume mount settings to set for the created container
:param options: options for creating node
"""
super().__init__(session, _id, name, directory, server)
self.image: str = image if image is not None else "ubuntu"
self.binds: List[Tuple[str, str]] = binds or []
options = options or DockerOptions()
super().__init__(session, _id, name, server, options)
self.image: str = options.image
self.binds: List[Tuple[str, str]] = options.binds
self.volumes: Dict[str, DockerVolume] = {}
volumes = volumes or []
for src, dst, unique, delete in volumes:
for src, dst, unique, delete in options.volumes:
src_name = self._unique_name(src) if unique else src
self.volumes[src] = DockerVolume(src_name, dst, unique, delete)
@classmethod
def create_options(cls) -> DockerOptions:
return DockerOptions()
def _create_cmd(self, args: str, shell: bool = False) -> str:
"""
Create command used to run commands within the context of a node.

View file

@ -1,15 +1,16 @@
import json
import logging
import time
from dataclasses import dataclass, field
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING, Callable, Dict, Optional
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
from core import utils
from core.emulator.data import InterfaceData, LinkOptions
from core.emulator.distributed import DistributedServer
from core.errors import CoreCommandError
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, CoreNodeOptions
from core.nodes.interface import CoreInterface
logger = logging.getLogger(__name__)
@ -66,15 +67,29 @@ class LxdClient:
self.run(args)
@dataclass
class LxcOptions(CoreNodeOptions):
image: str = "ubuntu"
"""image used when creating container"""
binds: List[Tuple[str, str]] = field(default_factory=list)
"""bind mount source and destinations to setup within container"""
volumes: List[Tuple[str, str, bool, bool]] = field(default_factory=list)
"""
volume mount source, destination, unique, delete to setup within container
unique is True for node unique volume naming
delete is True for deleting volume mount during shutdown
"""
class LxcNode(CoreNode):
def __init__(
self,
session: "Session",
_id: int = None,
name: str = None,
directory: str = None,
server: DistributedServer = None,
image: str = None,
options: LxcOptions = None,
) -> None:
"""
Create a LxcNode instance.
@ -82,15 +97,19 @@ class LxcNode(CoreNode):
:param session: core session instance
:param _id: object id
:param name: object name
:param directory: node directory
:param server: remote server node
will run on, default is None for localhost
:param image: image to start container with
:param options: option to create node with
"""
super().__init__(session, _id, name, directory, server)
self.image: str = image if image is not None else "ubuntu"
options = options or LxcOptions()
super().__init__(session, _id, name, server, options)
self.image: str = options.image
self.client: Optional[LxdClient] = None
@classmethod
def create_options(cls) -> LxcOptions:
return LxcOptions()
def alive(self) -> bool:
"""
Check if the node is alive.

View file

@ -4,6 +4,7 @@ Defines network nodes used within core.
import logging
import threading
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Optional, Type
@ -14,7 +15,7 @@ from core.emulator.data import InterfaceData, LinkData
from core.emulator.enumerations import MessageFlags, NetworkPolicy, RegisterTlvs
from core.errors import CoreCommandError, CoreError
from core.executables import NFTABLES
from core.nodes.base import CoreNetworkBase
from core.nodes.base import CoreNetworkBase, NodeOptions
from core.nodes.interface import CoreInterface, GreTap
from core.nodes.netclient import get_net_client
@ -180,6 +181,12 @@ class NftablesQueue:
nft_queue: NftablesQueue = NftablesQueue()
@dataclass
class NetworkOptions(NodeOptions):
policy: NetworkPolicy = None
"""allows overriding the network policy, otherwise uses class defined default"""
class CoreNetwork(CoreNetworkBase):
"""
Provides linux bridge network functionality for core nodes.
@ -193,7 +200,7 @@ class CoreNetwork(CoreNetworkBase):
_id: int = None,
name: str = None,
server: "DistributedServer" = None,
policy: NetworkPolicy = None,
options: NetworkOptions = None,
) -> None:
"""
Creates a CoreNetwork instance.
@ -203,18 +210,19 @@ class CoreNetwork(CoreNetworkBase):
:param name: object name
:param server: remote server node
will run on, default is None for localhost
:param policy: network policy
:param options: options to create node with
"""
super().__init__(session, _id, name, server)
if name is None:
name = str(self.id)
if policy is not None:
self.policy: NetworkPolicy = policy
self.name: Optional[str] = name
options = options or NetworkOptions()
super().__init__(session, _id, name, server, options)
self.policy: NetworkPolicy = options.policy if options.policy else self.policy
sessionid = self.session.short_session_id()
self.brname: str = f"b.{self.id}.{sessionid}"
self.has_nftables_chain: bool = False
@classmethod
def create_options(cls) -> NetworkOptions:
return NetworkOptions()
def host_cmd(
self,
args: str,
@ -482,6 +490,20 @@ class GreTapBridge(CoreNetwork):
self.add_ips(ips)
@dataclass
class CtrlNetOptions(NetworkOptions):
prefix: str = None
"""ip4 network prefix to use for generating an address"""
updown_script: str = None
"""script to execute during startup and shutdown"""
serverintf: str = None
"""used to associate an interface with the control network bridge"""
assign_address: bool = True
"""used to determine if a specific address should be assign using hostid"""
hostid: int = None
"""used with assign address to """
class CtrlNet(CoreNetwork):
"""
Control network functionality.
@ -500,36 +522,32 @@ class CtrlNet(CoreNetwork):
def __init__(
self,
session: "Session",
prefix: str,
_id: int = None,
name: str = None,
hostid: int = None,
server: "DistributedServer" = None,
assign_address: bool = True,
updown_script: str = None,
serverintf: str = None,
options: CtrlNetOptions = None,
) -> None:
"""
Creates a CtrlNet instance.
:param session: core session instance
:param _id: node id
:param name: node namee
:param prefix: control network ipv4 prefix
:param hostid: host id
:param name: node name
:param server: remote server node
will run on, default is None for localhost
:param assign_address: assigned address
:param updown_script: updown script
:param serverintf: server interface
:return:
:param options: node options for creation
"""
self.prefix: netaddr.IPNetwork = netaddr.IPNetwork(prefix).cidr
self.hostid: Optional[int] = hostid
self.assign_address: bool = assign_address
self.updown_script: Optional[str] = updown_script
self.serverintf: Optional[str] = serverintf
super().__init__(session, _id, name, server)
options = options or CtrlNetOptions()
super().__init__(session, _id, name, server, options)
self.prefix: netaddr.IPNetwork = netaddr.IPNetwork(options.prefix).cidr
self.hostid: Optional[int] = options.hostid
self.assign_address: bool = options.assign_address
self.updown_script: Optional[str] = options.updown_script
self.serverintf: Optional[str] = options.serverintf
@classmethod
def create_options(cls) -> CtrlNetOptions:
return CtrlNetOptions()
def add_addresses(self, index: int) -> None:
"""
@ -669,7 +687,7 @@ class WlanNode(CoreNetwork):
_id: int = None,
name: str = None,
server: "DistributedServer" = None,
policy: NetworkPolicy = None,
options: NetworkOptions = None,
) -> None:
"""
Create a WlanNode instance.
@ -679,9 +697,9 @@ class WlanNode(CoreNetwork):
:param name: node name
:param server: remote server node
will run on, default is None for localhost
:param policy: wlan policy
:param options: options to create node with
"""
super().__init__(session, _id, name, server, policy)
super().__init__(session, _id, name, server, options)
# wireless and mobility models (BasicRangeModel, Ns2WaypointMobility)
self.wireless_model: Optional[WirelessModel] = None
self.mobility: Optional[WayPointMobility] = None

View file

@ -13,7 +13,7 @@ from core.emulator.distributed import DistributedServer
from core.emulator.enumerations import TransportType
from core.errors import CoreCommandError, CoreError
from core.executables import BASH, TEST, UMOUNT
from core.nodes.base import CoreNode, CoreNodeBase
from core.nodes.base import CoreNode, CoreNodeBase, CoreNodeOptions, NodeOptions
from core.nodes.interface import CoreInterface
logger = logging.getLogger(__name__)
@ -34,6 +34,7 @@ class Rj45Node(CoreNodeBase):
_id: int = None,
name: str = None,
server: DistributedServer = None,
options: NodeOptions = None,
) -> None:
"""
Create an RJ45Node instance.
@ -43,8 +44,9 @@ class Rj45Node(CoreNodeBase):
:param name: node name
:param server: remote server node
will run on, default is None for localhost
:param options: option to create node with
"""
super().__init__(session, _id, name, server)
super().__init__(session, _id, name, server, options)
self.iface: CoreInterface = CoreInterface(
self.iface_id, name, name, session.use_ovs(), node=self, server=server
)
@ -224,12 +226,12 @@ class PhysicalNode(CoreNode):
session: "Session",
_id: int = None,
name: str = None,
directory: Path = None,
server: DistributedServer = None,
options: CoreNodeOptions = None,
) -> None:
if not self.server:
raise CoreError("physical nodes must be assigned to a remote server")
super().__init__(session, _id, name, directory, server)
super().__init__(session, _id, name, server, options)
def startup(self) -> None:
with self.lock:

View file

@ -14,7 +14,7 @@ from core.emulator.data import LinkData, LinkOptions
from core.emulator.enumerations import LinkTypes, MessageFlags
from core.errors import CoreError
from core.executables import NFTABLES
from core.nodes.base import CoreNetworkBase
from core.nodes.base import CoreNetworkBase, NodeOptions
from core.nodes.interface import CoreInterface
if TYPE_CHECKING:
@ -108,8 +108,9 @@ class WirelessNode(CoreNetworkBase):
_id: int,
name: str,
server: "DistributedServer" = None,
options: NodeOptions = None,
):
super().__init__(session, _id, name, server)
super().__init__(session, _id, name, server, options)
self.bridges: Dict[int, Tuple[CoreInterface, str]] = {}
self.links: Dict[Tuple[int, int], WirelessLink] = {}
self.position_enabled: bool = CONFIG_ENABLED

View file

@ -8,12 +8,12 @@ import core.nodes.base
import core.nodes.physical
from core import utils
from core.config import Configuration
from core.emane.nodes import EmaneNet
from core.emulator.data import InterfaceData, LinkOptions, NodeOptions
from core.emane.nodes import EmaneNet, EmaneOptions
from core.emulator.data import InterfaceData, LinkOptions
from core.emulator.enumerations import EventTypes, NodeTypes
from core.errors import CoreXmlError
from core.nodes.base import CoreNodeBase, NodeBase
from core.nodes.docker import DockerNode
from core.nodes.base import CoreNodeBase, CoreNodeOptions, NodeBase, Position
from core.nodes.docker import DockerNode, DockerOptions
from core.nodes.interface import CoreInterface
from core.nodes.lxd import LxcNode
from core.nodes.network import CtrlNet, GreTapBridge, PtpNet, WlanNode
@ -802,68 +802,76 @@ class CoreXmlReader:
clazz = device_element.get("class")
image = device_element.get("image")
server = device_element.get("server")
options = NodeOptions(
name=name, model=model, image=image, icon=icon, server=server
)
canvas = get_int(device_element, "canvas")
node_type = NodeTypes.DEFAULT
if clazz == "docker":
node_type = NodeTypes.DOCKER
elif clazz == "lxc":
node_type = NodeTypes.LXC
_class = self.session.get_node_class(node_type)
options = _class.create_options()
options.icon = icon
options.canvas = canvas
# check for special options
if isinstance(options, CoreNodeOptions):
options.model = model
service_elements = device_element.find("services")
if service_elements is not None:
options.services = [x.get("name") for x in service_elements.iterchildren()]
options.services.extend(
x.get("name") for x in service_elements.iterchildren()
)
config_service_elements = device_element.find("configservices")
if config_service_elements is not None:
options.config_services = [
options.config_services.extend(
x.get("name") for x in config_service_elements.iterchildren()
]
)
if isinstance(options, DockerOptions):
options.image = image
# get position information
position_element = device_element.find("position")
position = None
if position_element is not None:
position = Position()
x = get_float(position_element, "x")
y = get_float(position_element, "y")
if all([x, y]):
options.set_position(x, y)
position.set(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]):
options.set_location(lat, lon, alt)
position.set_geo(lon, lat, alt)
logger.info("reading node id(%s) model(%s) name(%s)", node_id, model, name)
self.session.add_node(_class, node_id, options)
self.session.add_node(_class, node_id, name, server, position, options)
def read_network(self, network_element: etree.Element) -> None:
node_id = get_int(network_element, "id")
name = network_element.get("name")
server = network_element.get("server")
node_type = NodeTypes[network_element.get("type")]
_class = self.session.get_node_class(node_type)
icon = network_element.get("icon")
server = network_element.get("server")
options = NodeOptions(name=name, icon=icon, server=server)
if node_type == NodeTypes.EMANE:
model = network_element.get("model")
options.emane = model
options = _class.create_options()
options.canvas = get_int(network_element, "canvas")
options.icon = network_element.get("icon")
if isinstance(options, EmaneOptions):
options.emane_model = network_element.get("model")
position_element = network_element.find("position")
position = None
if position_element is not None:
position = Position()
x = get_float(position_element, "x")
y = get_float(position_element, "y")
if all([x, y]):
options.set_position(x, y)
position.set(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]):
options.set_location(lat, lon, alt)
position.set_geo(lon, lat, alt)
logger.info(
"reading node id(%s) node_type(%s) name(%s)", node_id, node_type, name
)
node = self.session.add_node(_class, node_id, options)
node = self.session.add_node(_class, node_id, name, server, position, options)
if isinstance(node, WirelessNode):
wireless_element = network_element.find("wireless")
if wireless_element:

View file

@ -1,7 +1,7 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.network import SwitchNode
@ -11,13 +11,13 @@ if __name__ == "__main__":
# setup basic network
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(model=None)
coreemu = CoreEmu()
session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
switch = session.add_node(SwitchNode)
# node one
options = CoreNode.create_options()
options.config_services = ["DefaultRoute", "IPForward"]
node1 = session.add_node(CoreNode, options=options)
interface = prefixes.create_iface(node1)

View file

@ -1,7 +1,7 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.docker import DockerNode
@ -14,9 +14,10 @@ if __name__ == "__main__":
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(model=None, image="ubuntu")
# create node one
options = DockerNode.create_options()
options.image = "ubuntu"
node1 = session.add_node(DockerNode, options=options)
interface1_data = prefixes.create_iface(node1)

View file

@ -1,7 +1,7 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.docker import DockerNode
@ -15,9 +15,10 @@ if __name__ == "__main__":
# create nodes and interfaces
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(model=None, image="ubuntu")
# create node one
options = DockerNode.create_options()
options.image = "ubuntu"
node1 = session.add_node(DockerNode, options=options)
interface1_data = prefixes.create_iface(node1)

View file

@ -1,9 +1,8 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.docker import DockerNode
from core.nodes.network import SwitchNode
@ -16,12 +15,15 @@ if __name__ == "__main__":
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(model=None, image="ubuntu")
# create switch
switch = session.add_node(SwitchNode)
# node one
options = DockerNode.create_options()
options.image = "core"
options.binds.append(("/tmp/testbind", "/tmp/bind"))
options.volumes.append(("var.log", "/tmp/var.log", True, True))
node1 = session.add_node(DockerNode, options=options)
interface1_data = prefixes.create_iface(node1)
@ -30,16 +32,18 @@ if __name__ == "__main__":
interface2_data = prefixes.create_iface(node2)
# node three
node_three = session.add_node(CoreNode)
interface_three = prefixes.create_iface(node_three)
# node_three = session.add_node(CoreNode)
# interface_three = prefixes.create_iface(node_three)
# add links
session.add_link(node1.id, switch.id, interface1_data)
session.add_link(node2.id, switch.id, interface2_data)
session.add_link(node_three.id, switch.id, interface_three)
# session.add_link(node_three.id, switch.id, interface_three)
# instantiate
session.instantiate()
print(f"{node2.name}: {node2.volumes.values()}")
finally:
input("continue to shutdown")
coreemu.shutdown()

View file

@ -1,7 +1,7 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.lxd import LxcNode
@ -14,9 +14,10 @@ if __name__ == "__main__":
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(image="ubuntu")
# create node one
options = LxcNode.create_options()
options.image = "ubuntu"
node1 = session.add_node(LxcNode, options=options)
interface1_data = prefixes.create_iface(node1)

View file

@ -1,7 +1,7 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.lxd import LxcNode
@ -15,9 +15,10 @@ if __name__ == "__main__":
# create nodes and interfaces
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(image="ubuntu:18.04")
# create node one
options = LxcNode.create_options()
options.image = "ubuntu:18.04"
node1 = session.add_node(LxcNode, options=options)
interface1_data = prefixes.create_iface(node1)

View file

@ -1,7 +1,7 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.lxd import LxcNode
@ -16,12 +16,13 @@ if __name__ == "__main__":
try:
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
options = NodeOptions(image="ubuntu")
# create switch
switch = session.add_node(SwitchNode)
# node one
options = LxcNode.create_options()
options.image = "ubuntu"
node1 = session.add_node(LxcNode, options=options)
interface1_data = prefixes.create_iface(node1)

View file

@ -9,7 +9,7 @@ import logging
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
@ -50,11 +50,13 @@ def main(args):
session.set_state(EventTypes.CONFIGURATION_STATE)
# create local node, switch, and remote nodes
options = NodeOptions(model="mdr")
options.set_position(0, 0)
options = CoreNode.create_options()
options.model = "mdr"
node1 = session.add_node(CoreNode, options=options)
emane_net = session.add_node(EmaneNet)
session.emane.set_model(emane_net, EmaneIeee80211abgModel)
options = EmaneNet.create_options()
options.emane_model = EmaneIeee80211abgModel.name
emane_net = session.add_node(EmaneNet, options=options)
options = CoreNode.create_options()
options.server = server_name
node2 = session.add_node(CoreNode, options=options)

View file

@ -7,7 +7,7 @@ import argparse
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.lxd import LxcNode
@ -42,7 +42,8 @@ def main(args):
session.set_state(EventTypes.CONFIGURATION_STATE)
# create local node, switch, and remote nodes
options = NodeOptions(image="ubuntu:18.04")
options = LxcNode.create_options()
options.image = "ubuntu:18.04"
node1 = session.add_node(LxcNode, options=options)
options.server = server_name
node2 = session.add_node(LxcNode, options=options)

View file

@ -7,7 +7,7 @@ import argparse
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
@ -42,10 +42,8 @@ def main(args):
session.set_state(EventTypes.CONFIGURATION_STATE)
# create local node, switch, and remote nodes
options = NodeOptions()
node1 = session.add_node(CoreNode, options=options)
options.server = server_name
node2 = session.add_node(CoreNode, options=options)
node1 = session.add_node(CoreNode)
node2 = session.add_node(CoreNode, server=server_name)
# create node interfaces and link
interface1_data = prefixes.create_iface(node1)

View file

@ -7,7 +7,7 @@ import argparse
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.network import SwitchNode
@ -47,7 +47,7 @@ def main(args):
# create local node, switch, and remote nodes
node1 = session.add_node(CoreNode)
switch = session.add_node(SwitchNode)
options = NodeOptions()
options = CoreNode.create_options()
options.server = server_name
node2 = session.add_node(CoreNode, options=options)

View file

@ -2,9 +2,9 @@
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
# ip nerator for example
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
@ -21,14 +21,20 @@ session.location.refscale = 150.0
session.set_state(EventTypes.CONFIGURATION_STATE)
# create emane
options = NodeOptions(x=200, y=200, emane=EmaneIeee80211abgModel.name)
emane = session.add_node(EmaneNet, options=options)
options = EmaneNet.create_options()
options.emane_model = EmaneIeee80211abgModel.name
position = Position(x=200, y=200)
emane = session.add_node(EmaneNet, position=position, options=options)
# create nodes
options = NodeOptions(model="mdr", x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(model="mdr", x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position, options=options)
# configure general emane settings
config = session.emane.get_configs()

View file

@ -1,8 +1,8 @@
# required imports
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
# ip nerator for example
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
@ -15,10 +15,10 @@ session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create nodes
options = NodeOptions(x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position)
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position)
# link nodes together
iface1 = ip_prefixes.create_iface(n1)

View file

@ -1,8 +1,8 @@
# required imports
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
from core.nodes.network import SwitchNode
# ip nerator for example
@ -16,14 +16,14 @@ session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create switch
options = NodeOptions(x=200, y=200)
switch = session.add_node(SwitchNode, options=options)
position = Position(x=200, y=200)
switch = session.add_node(SwitchNode, position=position)
# create nodes
options = NodeOptions(x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position)
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position)
# link nodes to switch
iface1 = ip_prefixes.create_iface(n1)

View file

@ -2,9 +2,9 @@
import logging
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
from core.nodes.network import WlanNode
# enable info logging
@ -21,14 +21,18 @@ session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create wireless
options = NodeOptions(x=200, y=200)
wireless = session.add_node(WlanNode, options=options)
position = Position(x=200, y=200)
wireless = session.add_node(WlanNode, position=position)
# create nodes
options = NodeOptions(model="mdr", x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(model="mdr", x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position, options=options)
# link nodes to wireless
iface1 = ip_prefixes.create_iface(n1)

View file

@ -1,9 +1,9 @@
# required imports
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.location.mobility import BasicRangeModel
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
from core.nodes.network import WlanNode
# ip nerator for example
@ -17,14 +17,18 @@ session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create wlan
options = NodeOptions(x=200, y=200)
wlan = session.add_node(WlanNode, options=options)
position = Position(x=200, y=200)
wlan = session.add_node(WlanNode, position=position)
# create nodes
options = NodeOptions(model="mdr", x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(model="mdr", x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position, options=options)
# configuring wlan
session.mobility.set_model_config(

View file

@ -16,10 +16,10 @@ from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.rfpipe import EmaneRfPipeModel
from core.emane.models.tdma import EmaneTdmaModel
from core.emane.nodes import EmaneNet
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.session import Session
from core.errors import CoreCommandError, CoreError
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
_EMANE_MODELS = [
EmaneIeee80211abgModel,
@ -53,19 +53,22 @@ class TestEmane:
"""
# create emane node for networking the core nodes
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions()
options.set_position(80, 50)
options.emane = EmaneIeee80211abgModel.name
emane_net1 = session.add_node(EmaneNet, options=options)
options.emane = EmaneRfPipeModel.name
emane_net2 = session.add_node(EmaneNet, options=options)
options = EmaneNet.create_options()
options.emane_model = EmaneIeee80211abgModel.name
position = Position(x=80, y=50)
emane_net1 = session.add_node(EmaneNet, position=position, options=options)
options = EmaneNet.create_options()
options.emane_model = EmaneRfPipeModel.name
position = Position(x=80, y=50)
emane_net2 = session.add_node(EmaneNet, position=position, options=options)
# create nodes
options = NodeOptions(model="mdr")
options.set_position(150, 150)
node1 = session.add_node(CoreNode, options=options)
options.set_position(300, 150)
node2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=150, y=150)
node1 = session.add_node(CoreNode, position=position, options=options)
position = Position(x=300, y=150)
node2 = session.add_node(CoreNode, position=position, options=options)
# create interfaces
ip_prefix1 = IpPrefixes("10.0.0.0/24")
@ -100,9 +103,10 @@ class TestEmane:
# create emane node for networking the core nodes
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions(emane=model.name)
options.set_position(80, 50)
emane_network = session.add_node(EmaneNet, options=options)
options = EmaneNet.create_options()
options.emane_model = model.name
position = Position(x=80, y=50)
emane_network = session.add_node(EmaneNet, position=position, options=options)
# configure tdma
if model == EmaneTdmaModel:
@ -111,11 +115,12 @@ class TestEmane:
)
# create nodes
options = NodeOptions(model="mdr")
options.set_position(150, 150)
node1 = session.add_node(CoreNode, options=options)
options.set_position(300, 150)
node2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=150, y=150)
node1 = session.add_node(CoreNode, position=position, options=options)
position = Position(x=300, y=150)
node2 = session.add_node(CoreNode, position=position, options=options)
for i, node in enumerate([node1, node2]):
node.setposition(x=150 * (i + 1), y=150)
@ -141,9 +146,10 @@ class TestEmane:
"""
# create emane node for networking the core nodes
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions(emane=EmaneIeee80211abgModel.name)
options.set_position(80, 50)
emane_network = session.add_node(EmaneNet, options=options)
options = EmaneNet.create_options()
options.emane_model = EmaneIeee80211abgModel.name
position = Position(x=80, y=50)
emane_network = session.add_node(EmaneNet, position=position, options=options)
config_key = "txpower"
config_value = "10"
session.emane.set_config(
@ -151,11 +157,12 @@ class TestEmane:
)
# create nodes
options = NodeOptions(model="mdr")
options.set_position(150, 150)
node1 = session.add_node(CoreNode, options=options)
options.set_position(300, 150)
node2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=150, y=150)
node1 = session.add_node(CoreNode, position=position, options=options)
position = Position(x=300, y=150)
node2 = session.add_node(CoreNode, position=position, options=options)
for i, node in enumerate([node1, node2]):
node.setposition(x=150 * (i + 1), y=150)
@ -205,14 +212,17 @@ class TestEmane:
self, session: Session, tmpdir: TemporaryFile, ip_prefixes: IpPrefixes
):
# create nodes
options = NodeOptions(model="mdr", x=50, y=50)
node1 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=50, y=50)
node1 = session.add_node(CoreNode, position=position, options=options)
iface1_data = ip_prefixes.create_iface(node1)
node2 = session.add_node(CoreNode, options=options)
node2 = session.add_node(CoreNode, position=position, options=options)
iface2_data = ip_prefixes.create_iface(node2)
# create emane node
options = NodeOptions(model=None, emane=EmaneRfPipeModel.name)
options = EmaneNet.create_options()
options.emane_model = EmaneRfPipeModel.name
emane_node = session.add_node(EmaneNet, options=options)
# create links
@ -255,11 +265,7 @@ class TestEmane:
assert session.get_node(node1.id, CoreNode)
assert session.get_node(node2.id, CoreNode)
assert session.get_node(emane_node.id, EmaneNet)
links = []
for node_id in session.nodes:
node = session.nodes[node_id]
links += node.links()
assert len(links) == 2
assert len(session.link_manager.links()) == 2
config = session.emane.get_config(node1.id, EmaneRfPipeModel.name)
assert config["datarate"] == datarate
@ -267,14 +273,17 @@ class TestEmane:
self, session: Session, tmpdir: TemporaryFile, ip_prefixes: IpPrefixes
):
# create nodes
options = NodeOptions(model="mdr", x=50, y=50)
node1 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=50, y=50)
node1 = session.add_node(CoreNode, position=position, options=options)
iface1_data = ip_prefixes.create_iface(node1)
node2 = session.add_node(CoreNode, options=options)
node2 = session.add_node(CoreNode, position=position, options=options)
iface2_data = ip_prefixes.create_iface(node2)
# create emane node
options = NodeOptions(model=None, emane=EmaneRfPipeModel.name)
options = EmaneNet.create_options()
options.emane_model = EmaneRfPipeModel.name
emane_node = session.add_node(EmaneNet, options=options)
# create links
@ -318,10 +327,6 @@ class TestEmane:
assert session.get_node(node1.id, CoreNode)
assert session.get_node(node2.id, CoreNode)
assert session.get_node(emane_node.id, EmaneNet)
links = []
for node_id in session.nodes:
node = session.nodes[node_id]
links += node.links()
assert len(links) == 2
assert len(session.link_manager.links()) == 2
config = session.emane.get_config(config_id, EmaneRfPipeModel.name)
assert config["datarate"] == datarate

View file

@ -8,7 +8,7 @@ from typing import List, Type
import pytest
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.session import Session
from core.errors import CoreCommandError
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
@ -75,8 +75,8 @@ class TestCore:
session.mobility.set_model(wlan_node, BasicRangeModel)
# create nodes
options = NodeOptions(model="mdr")
options.set_position(0, 0)
options = CoreNode.create_options()
options.model = "mdr"
node1 = session.add_node(CoreNode, options=options)
node2 = session.add_node(CoreNode, options=options)
@ -105,8 +105,8 @@ class TestCore:
session.mobility.set_model(wlan_node, BasicRangeModel)
# create nodes
options = NodeOptions(model="mdr")
options.set_position(0, 0)
options = CoreNode.create_options()
options.model = "mdr"
node1 = session.add_node(CoreNode, options=options)
node2 = session.add_node(CoreNode, options=options)

View file

@ -1,4 +1,3 @@
from core.emulator.data import NodeOptions
from core.emulator.session import Session
from core.nodes.base import CoreNode
from core.nodes.network import HubNode
@ -12,8 +11,7 @@ class TestDistributed:
# when
session.distributed.add_server(server_name, host)
options = NodeOptions(server=server_name)
node = session.add_node(CoreNode, options=options)
node = session.add_node(CoreNode, server=server_name)
session.instantiate()
# then
@ -30,8 +28,7 @@ class TestDistributed:
# when
session.distributed.add_server(server_name, host)
node1 = session.add_node(HubNode)
options = NodeOptions(server=server_name)
node2 = session.add_node(HubNode, options=options)
node2 = session.add_node(HubNode, server=server_name)
session.add_link(node1.id, node2.id)
session.instantiate()

View file

@ -34,7 +34,7 @@ from core.api.grpc.wrappers import (
)
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.data import EventData, IpPrefixes, NodeData, NodeOptions
from core.emulator.data import EventData, IpPrefixes, NodeData
from core.emulator.enumerations import EventTypes, ExceptionLevels, MessageFlags
from core.errors import CoreError
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
@ -350,8 +350,7 @@ class TestGrpc:
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
options = NodeOptions(model="Host")
node = session.add_node(CoreNode, options=options)
node = session.add_node(CoreNode)
session.instantiate()
expected_output = "hello world"
expected_status = 0
@ -369,8 +368,7 @@ class TestGrpc:
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
options = NodeOptions(model="Host")
node = session.add_node(CoreNode, options=options)
node = session.add_node(CoreNode)
session.instantiate()
# then
@ -444,10 +442,12 @@ class TestGrpc:
# given
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
switch = session.add_node(SwitchNode)
node = session.add_node(CoreNode)
iface_data = ip_prefixes.create_iface(node)
iface, _ = session.add_link(node.id, switch.id, iface_data)
session.instantiate()
options = LinkOptions(bandwidth=30000)
assert iface.options.bandwidth != options.bandwidth
link = Link(node.id, switch.id, iface1=Interface(id=iface.id), options=options)
@ -535,7 +535,8 @@ class TestGrpc:
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions(emane=EmaneIeee80211abgModel.name)
options = EmaneNet.create_options()
options.emane_model = EmaneIeee80211abgModel.name
emane_network = session.add_node(EmaneNet, options=options)
session.emane.node_models[emane_network.id] = EmaneIeee80211abgModel.name
config_key = "bandwidth"
@ -565,7 +566,8 @@ class TestGrpc:
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions(emane=EmaneIeee80211abgModel.name)
options = EmaneNet.create_options()
options.emane_model = EmaneIeee80211abgModel.name
emane_network = session.add_node(EmaneNet, options=options)
session.emane.node_models[emane_network.id] = EmaneIeee80211abgModel.name
@ -685,7 +687,8 @@ class TestGrpc:
# given
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
options = NodeOptions(legacy=True)
options = CoreNode.create_options()
options.legacy = True
node = session.add_node(CoreNode, options=options)
service_name = "DefaultRoute"
@ -932,6 +935,7 @@ class TestGrpc:
# given
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
wlan = session.add_node(WlanNode)
node1 = session.add_node(CoreNode)
node2 = session.add_node(CoreNode)

View file

@ -1,6 +1,6 @@
import pytest
from core.emulator.data import InterfaceData, NodeOptions
from core.emulator.data import InterfaceData
from core.emulator.session import Session
from core.errors import CoreError
from core.nodes.base import CoreNode
@ -14,7 +14,8 @@ class TestNodes:
@pytest.mark.parametrize("model", MODELS)
def test_node_add(self, session: Session, model: str):
# given
options = NodeOptions(model=model)
options = CoreNode.create_options()
options.model = model
# when
node = session.add_node(CoreNode, options=options)

View file

@ -4,7 +4,7 @@ from xml.etree import ElementTree
import pytest
from core.emulator.data import IpPrefixes, LinkOptions, NodeOptions
from core.emulator.data import IpPrefixes, LinkOptions
from core.emulator.enumerations import EventTypes
from core.emulator.session import Session
from core.errors import CoreError
@ -116,8 +116,7 @@ class TestXml:
:param ip_prefixes: generates ip addresses for nodes
"""
# create nodes
options = NodeOptions(model="host")
node1 = session.add_node(CoreNode, options=options)
node1 = session.add_node(CoreNode)
node2 = session.add_node(CoreNode)
# link nodes to ptp net
@ -180,8 +179,8 @@ class TestXml:
session.mobility.set_model(wlan, BasicRangeModel, {"test": "1"})
# create nodes
options = NodeOptions(model="mdr")
options.set_position(0, 0)
options = CoreNode.create_options()
options.model = "mdr"
node1 = session.add_node(CoreNode, options=options)
node2 = session.add_node(CoreNode, options=options)

View file

@ -36,8 +36,10 @@ when creating interface data for nodes. Alternatively one can manually create
a `core.emulator.data.InterfaceData` class instead with appropriate information.
Manually creating interface data:
```python
from core.emulator.data import InterfaceData
# id is optional and will set to the next available id
# name is optional and will default to eth<id>
# mac is optional and will result in a randomly generated mac
@ -52,6 +54,7 @@ iface_data = InterfaceData(
```
Leveraging the interface prefixes helper class:
```python
from core.emulator.data import IpPrefixes
@ -69,6 +72,7 @@ iface_data = ip_prefixes.create_iface(
Various events that can occur within a session can be listened to.
Event types:
* session - events for changes in session state and mobility start/stop/pause
* node - events for node movements and icon changes
* link - events for link configuration changes and wireless link add/delete
@ -80,6 +84,7 @@ Event types:
def event_listener(event):
print(event)
# add an event listener to event type you want to listen to
# each handler will receive an object unique to that type
session.event_handlers.append(event_listener)
@ -95,6 +100,7 @@ session.config_handlers.append(event_listener)
Links can be configured at the time of creation or during runtime.
Currently supported configuration options:
* bandwidth (bps)
* delay (us)
* dup (%)
@ -119,12 +125,13 @@ session.update_link(n1_id, n2_id, iface1_id, iface2_id, options)
```
### Peer to Peer Example
```python
# required imports
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
# ip nerator for example
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
@ -137,10 +144,10 @@ session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create nodes
options = NodeOptions(x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position)
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position)
# link nodes together
iface1 = ip_prefixes.create_iface(n1)
@ -158,12 +165,13 @@ session.shutdown()
```
### Switch/Hub Example
```python
# required imports
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
from core.nodes.network import SwitchNode
# ip nerator for example
@ -177,14 +185,14 @@ session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create switch
options = NodeOptions(x=200, y=200)
switch = session.add_node(SwitchNode, options=options)
position = Position(x=200, y=200)
switch = session.add_node(SwitchNode, position=position)
# create nodes
options = NodeOptions(x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position)
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position)
# link nodes to switch
iface1 = ip_prefixes.create_iface(n1)
@ -203,13 +211,14 @@ session.shutdown()
```
### WLAN Example
```python
# required imports
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.location.mobility import BasicRangeModel
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
from core.nodes.network import WlanNode
# ip nerator for example
@ -223,14 +232,16 @@ session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE)
# create wlan
options = NodeOptions(x=200, y=200)
wlan = session.add_node(WlanNode, options=options)
position = Position(x=200, y=200)
wlan = session.add_node(WlanNode, position=position)
# create nodes
options = NodeOptions(model="mdr", x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(model="mdr", x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position, options=options)
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position, options=options)
# configuring wlan
session.mobility.set_model_config(wlan.id, BasicRangeModel.name, {
@ -263,6 +274,7 @@ For EMANE you can import and use one of the existing models and
use its name for configuration.
Current models:
* core.emane.ieee80211abg.EmaneIeee80211abgModel
* core.emane.rfpipe.EmaneRfPipeModel
* core.emane.tdma.EmaneTdmaModel
@ -281,9 +293,9 @@ will use the defaults. When no configuration is used, the defaults are used.
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.data import IpPrefixes
from core.emulator.enumerations import EventTypes
from core.nodes.base import CoreNode
from core.nodes.base import CoreNode, Position
# ip nerator for example
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
@ -300,14 +312,18 @@ session.location.refscale = 150.0
session.set_state(EventTypes.CONFIGURATION_STATE)
# create emane
options = NodeOptions(x=200, y=200, emane=EmaneIeee80211abgModel.name)
emane = session.add_node(EmaneNet, options=options)
options = EmaneNet.create_options()
options.emane_model = EmaneIeee80211abgModel.name
position = Position(x=200, y=200)
emane = session.add_node(EmaneNet, position=position, options=options)
# create nodes
options = NodeOptions(model="mdr", x=100, y=100)
n1 = session.add_node(CoreNode, options=options)
options = NodeOptions(model="mdr", x=300, y=100)
n2 = session.add_node(CoreNode, options=options)
options = CoreNode.create_options()
options.model = "mdr"
position = Position(x=100, y=100)
n1 = session.add_node(CoreNode, position=position, options=options)
position = Position(x=300, y=100)
n2 = session.add_node(CoreNode, position=position, options=options)
# configure general emane settings
config = session.emane.get_configs()
@ -338,6 +354,7 @@ session.shutdown()
```
EMANE Model Configuration:
```python
from core import utils
@ -358,6 +375,7 @@ Configuring the files of a service results in a specific hard coded script being
generated, instead of the default scripts, that may leverage dynamic generation.
The following features can be configured for a service:
* configs - files that will be generated
* dirs - directories that will be mounted unique to the node
* startup - commands to run start a service
@ -365,6 +383,7 @@ The following features can be configured for a service:
* shutdown - commands to run to stop a service
Editing service properties:
```python
# configure a service, for a node, for a given session
session.services.set_service(node_id, service_name)
@ -380,6 +399,7 @@ When editing a service file, it must be the name of `config`
file that the service will generate.
Editing a service file:
```python
# to edit the contents of a generated file you can specify
# the service, the file name, and its contents
@ -397,10 +417,12 @@ File versions of the network examples can be found
[here](https://github.com/coreemu/core/tree/master/daemon/examples/python).
## Executing Scripts from GUI
To execute a python script from a GUI you need have the following.
The builtin name check here to know it is being executed from the GUI, this can
be avoided if your script does not use a name check.
```python
if __name__ in ["__main__", "__builtin__"]:
main()