merged latest from 5.2
This commit is contained in:
commit
b18d5b5805
90 changed files with 5060 additions and 3665 deletions
21
Changelog
21
Changelog
|
@ -1,17 +1,34 @@
|
||||||
2018-XX-XX CORE 5.1
|
2018-05-22 CORE 5.1
|
||||||
* DAEMON:
|
* DAEMON:
|
||||||
|
- removed and cleared out code that is either legacy or no longer supported (Xen, BSD, Kernel patching, RPM/DEB specific files)
|
||||||
- default nodes are now set in the node map
|
- default nodes are now set in the node map
|
||||||
- moved ns3 and netns directories to the top of the repo
|
- moved ns3 and netns directories to the top of the repo
|
||||||
- changes to make use of fpm as the tool for building packages
|
- changes to make use of fpm as the tool for building packages
|
||||||
- removed usage of logzero to avoid dependency issues for built packages
|
- removed usage of logzero to avoid dependency issues for built packages
|
||||||
- removed daemon addons directory
|
- removed daemon addons directory
|
||||||
|
- added CoreEmu to core.emulator.coreemu to help begin serving as the basis for a more formal API for scripting and creating new external APIs out of
|
||||||
|
- cleaned up logging, moved more logging to DEBUG from INFO, tried to mold INFO message to be more simple and informative
|
||||||
|
- EMANE 1.0.1-1.21 supported
|
||||||
|
- updates to leverage EMANE python bindings for dynamically parsing phy/mac manifest files
|
||||||
|
- example custom EMANE model lives under /usr/share/core/examples/myemane/examplemodel.py
|
||||||
|
- EMANE TDMA model now supports an option to start a TDMA schedule when running
|
||||||
|
- fixed issues with coresendmsg script due to code refactoring
|
||||||
|
- added make target for generating documentation "make doc"
|
||||||
|
- Python 2.7+ is now required
|
||||||
|
- ns3 is no longer bundled by default, but will be produced as a separate package for installation
|
||||||
|
* GUI
|
||||||
|
- updated broken help links in GUI Help->About
|
||||||
|
* Packaging
|
||||||
|
- fixed PYTHON_PATH to PYTHONPATH in sysv script
|
||||||
|
- added make command to leverage FPM as the tool for creating deb/rpm packages going forward, there is documentation within README.md to try it out
|
||||||
* TEST:
|
* TEST:
|
||||||
- fixed some broken tests
|
- fixed some broken tests
|
||||||
|
- new test cases based on CoreEmu usage
|
||||||
* BUGFIXES:
|
* BUGFIXES:
|
||||||
- #142 - duplication of custom services
|
- #142 - duplication of custom services
|
||||||
- #136 - sphinx-apidoc command not found
|
- #136 - sphinx-apidoc command not found
|
||||||
- #137 - make command fails when using distclean
|
- #137 - make command fails when using distclean
|
||||||
|
|
||||||
2017-09-01 CORE 5.0
|
2017-09-01 CORE 5.0
|
||||||
* DEVELOPMENT:
|
* DEVELOPMENT:
|
||||||
- support for editorconfig to help standardize development across IDEs, from the defined configuration file
|
- support for editorconfig to help standardize development across IDEs, from the defined configuration file
|
||||||
|
|
20
Jenkinsfile
vendored
Normal file
20
Jenkinsfile
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
stages {
|
||||||
|
stage('build core') {
|
||||||
|
steps {
|
||||||
|
sh './bootstrap.sh'
|
||||||
|
sh './configure'
|
||||||
|
sh 'make'
|
||||||
|
sh 'sudo make install'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('test core') {
|
||||||
|
steps {
|
||||||
|
sh 'pytest daemon/tests/test_core.py'
|
||||||
|
sh 'pytest daemon/tests/test_gui.py'
|
||||||
|
sh 'pytest daemon/tests/test_emane.py'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
# this defines the CORE version number, must be static for AC_INIT
|
# this defines the CORE version number, must be static for AC_INIT
|
||||||
AC_INIT(core, 5.1, core-dev@nrl.navy.mil)
|
AC_INIT(core, 5.2, core-dev@nrl.navy.mil)
|
||||||
|
|
||||||
# autoconf and automake initialization
|
# autoconf and automake initialization
|
||||||
AC_CONFIG_SRCDIR([netns/version.h.in])
|
AC_CONFIG_SRCDIR([netns/version.h.in])
|
||||||
|
@ -36,7 +36,7 @@ AC_ARG_WITH([guiconfdir],
|
||||||
[AS_HELP_STRING([--with-guiconfdir=dir],
|
[AS_HELP_STRING([--with-guiconfdir=dir],
|
||||||
[specify GUI configuration directory])],
|
[specify GUI configuration directory])],
|
||||||
[CORE_GUI_CONF_DIR="$with_guiconfdir"],
|
[CORE_GUI_CONF_DIR="$with_guiconfdir"],
|
||||||
[CORE_GUI_CONF_DIR="\${HOME}/.core"])
|
[CORE_GUI_CONF_DIR="\$\${HOME}/.core"])
|
||||||
AC_SUBST(CORE_GUI_CONF_DIR)
|
AC_SUBST(CORE_GUI_CONF_DIR)
|
||||||
AC_ARG_ENABLE([gui],
|
AC_ARG_ENABLE([gui],
|
||||||
[AS_HELP_STRING([--enable-gui[=ARG]],
|
[AS_HELP_STRING([--enable-gui[=ARG]],
|
||||||
|
@ -66,7 +66,7 @@ AC_PROG_SED
|
||||||
|
|
||||||
want_python=no
|
want_python=no
|
||||||
want_linux_netns=no
|
want_linux_netns=no
|
||||||
if test "x$enable_daemon" = "xyes"; then
|
if test "x$enable_daemon" = "xyes"; then
|
||||||
want_python=yes
|
want_python=yes
|
||||||
want_linux_netns=yes
|
want_linux_netns=yes
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,19 @@
|
||||||
Converts CORE data objects into legacy API messages.
|
Converts CORE data objects into legacy API messages.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from core import logger
|
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
|
from core.enumerations import ConfigTlvs
|
||||||
from core.enumerations import NodeTlvs
|
from core.enumerations import NodeTlvs
|
||||||
from core.misc import structutils
|
from core.misc import structutils
|
||||||
|
|
||||||
|
|
||||||
def convert_node(node_data):
|
def convert_node(node_data):
|
||||||
"""
|
"""
|
||||||
Callback to handle an node broadcast out from a session.
|
Convenience method for converting NodeData to a packed TLV message.
|
||||||
|
|
||||||
:param core.data.NodeData node_data: node data to handle
|
:param core.data.NodeData node_data: node data to convert
|
||||||
:return: packed node message
|
:return: packed node message
|
||||||
"""
|
"""
|
||||||
logger.debug("converting node data to message: %s", node_data)
|
|
||||||
|
|
||||||
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
|
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
|
||||||
(NodeTlvs.NUMBER, node_data.id),
|
(NodeTlvs.NUMBER, node_data.id),
|
||||||
(NodeTlvs.TYPE, node_data.node_type),
|
(NodeTlvs.TYPE, node_data.node_type),
|
||||||
|
@ -39,5 +37,29 @@ def convert_node(node_data):
|
||||||
(NodeTlvs.ICON, node_data.icon),
|
(NodeTlvs.ICON, node_data.icon),
|
||||||
(NodeTlvs.OPAQUE, node_data.opaque)
|
(NodeTlvs.OPAQUE, node_data.opaque)
|
||||||
])
|
])
|
||||||
|
|
||||||
return coreapi.CoreNodeMessage.pack(node_data.message_type, tlv_data)
|
return coreapi.CoreNodeMessage.pack(node_data.message_type, tlv_data)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_config(config_data):
|
||||||
|
"""
|
||||||
|
Convenience method for converting ConfigData to a packed TLV message.
|
||||||
|
|
||||||
|
:param core.data.ConfigData config_data: config data to convert
|
||||||
|
:return: packed message
|
||||||
|
"""
|
||||||
|
tlv_data = structutils.pack_values(coreapi.CoreConfigTlv, [
|
||||||
|
(ConfigTlvs.NODE, config_data.node),
|
||||||
|
(ConfigTlvs.OBJECT, config_data.object),
|
||||||
|
(ConfigTlvs.TYPE, config_data.type),
|
||||||
|
(ConfigTlvs.DATA_TYPES, config_data.data_types),
|
||||||
|
(ConfigTlvs.VALUES, config_data.data_values),
|
||||||
|
(ConfigTlvs.CAPTIONS, config_data.captions),
|
||||||
|
(ConfigTlvs.BITMAP, config_data.bitmap),
|
||||||
|
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
|
||||||
|
(ConfigTlvs.GROUPS, config_data.groups),
|
||||||
|
(ConfigTlvs.SESSION, config_data.session),
|
||||||
|
(ConfigTlvs.INTERFACE_NUMBER, config_data.interface_number),
|
||||||
|
(ConfigTlvs.NETWORK_ID, config_data.network_id),
|
||||||
|
(ConfigTlvs.OPAQUE, config_data.opaque),
|
||||||
|
])
|
||||||
|
return coreapi.CoreConfMessage.pack(config_data.message_type, tlv_data)
|
||||||
|
|
|
@ -11,7 +11,6 @@ import threading
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.conf import ConfigurableManager
|
|
||||||
from core.coreobj import PyCoreNet
|
from core.coreobj import PyCoreNet
|
||||||
from core.coreobj import PyCoreNode
|
from core.coreobj import PyCoreNode
|
||||||
from core.enumerations import ConfigDataTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
|
@ -81,7 +80,7 @@ class CoreDistributedServer(object):
|
||||||
self.sock = None
|
self.sock = None
|
||||||
|
|
||||||
|
|
||||||
class CoreBroker(ConfigurableManager):
|
class CoreBroker(object):
|
||||||
"""
|
"""
|
||||||
Helps with brokering messages between CORE daemon servers.
|
Helps with brokering messages between CORE daemon servers.
|
||||||
"""
|
"""
|
||||||
|
@ -100,7 +99,7 @@ class CoreBroker(ConfigurableManager):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ConfigurableManager.__init__(self)
|
# ConfigurableManager.__init__(self)
|
||||||
self.session = session
|
self.session = session
|
||||||
self.session_clients = []
|
self.session_clients = []
|
||||||
self.session_id_master = None
|
self.session_id_master = None
|
||||||
|
@ -611,62 +610,6 @@ class CoreBroker(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
self.physical_nodes.add(nodenum)
|
self.physical_nodes.add(nodenum)
|
||||||
|
|
||||||
def configure_reset(self, config_data):
|
|
||||||
"""
|
|
||||||
Ignore reset messages, because node delete responses may still
|
|
||||||
arrive and require the use of nodecounts.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def configure_values(self, config_data):
|
|
||||||
"""
|
|
||||||
Receive configuration message with a list of server:host:port
|
|
||||||
combinations that we"ll need to connect with.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
values = config_data.data_values
|
|
||||||
session_id = config_data.session
|
|
||||||
|
|
||||||
if values is None:
|
|
||||||
logger.info("emulation server data missing")
|
|
||||||
return None
|
|
||||||
values = values.split("|")
|
|
||||||
|
|
||||||
# string of "server:ip:port,server:ip:port,..."
|
|
||||||
server_strings = values[0]
|
|
||||||
server_list = server_strings.split(",")
|
|
||||||
|
|
||||||
for server in server_list:
|
|
||||||
server_items = server.split(":")
|
|
||||||
(name, host, port) = server_items[:3]
|
|
||||||
|
|
||||||
if host == "":
|
|
||||||
host = None
|
|
||||||
|
|
||||||
if port == "":
|
|
||||||
port = None
|
|
||||||
else:
|
|
||||||
port = int(port)
|
|
||||||
|
|
||||||
if session_id is not None:
|
|
||||||
# receive session ID and my IP from master
|
|
||||||
self.session_id_master = int(session_id.split("|")[0])
|
|
||||||
self.myip = host
|
|
||||||
host = None
|
|
||||||
port = None
|
|
||||||
|
|
||||||
# this connects to the server immediately; maybe we should wait
|
|
||||||
# or spin off a new "client" thread here
|
|
||||||
self.addserver(name, host, port)
|
|
||||||
self.setupserver(name)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def handle_message(self, message):
|
def handle_message(self, message):
|
||||||
"""
|
"""
|
||||||
Handle an API message. Determine whether this needs to be handled
|
Handle an API message. Determine whether this needs to be handled
|
||||||
|
@ -733,6 +676,7 @@ class CoreBroker(ConfigurableManager):
|
||||||
if server is None:
|
if server is None:
|
||||||
logger.warn("ignoring unknown server: %s", servername)
|
logger.warn("ignoring unknown server: %s", servername)
|
||||||
return
|
return
|
||||||
|
|
||||||
if server.sock is None or server.host is None or server.port is None:
|
if server.sock is None or server.host is None or server.port is None:
|
||||||
logger.info("ignoring disconnected server: %s", servername)
|
logger.info("ignoring disconnected server: %s", servername)
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,492 +2,397 @@
|
||||||
Common support for configurable CORE objects.
|
Common support for configurable CORE objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import string
|
from collections import OrderedDict
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.data import ConfigData
|
from core.data import ConfigData
|
||||||
from core.enumerations import ConfigDataTypes
|
|
||||||
from core.enumerations import ConfigFlags
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigurableManager(object):
|
class ConfigShim(object):
|
||||||
"""
|
"""
|
||||||
A generic class for managing Configurables. This class can register
|
Provides helper methods for converting newer configuration values into TLV compatible formats.
|
||||||
with a session to receive Config Messages for setting some parameters
|
|
||||||
for itself or for the Configurables that it manages.
|
|
||||||
"""
|
"""
|
||||||
# name corresponds to configuration object field
|
|
||||||
name = ""
|
|
||||||
|
|
||||||
# type corresponds with register message types
|
|
||||||
config_type = None
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""
|
|
||||||
Creates a ConfigurableManager instance.
|
|
||||||
"""
|
|
||||||
# configurable key=values, indexed by node number
|
|
||||||
self.configs = {}
|
|
||||||
|
|
||||||
# TODO: fix the need for this and isolate to the mobility class that wants it
|
|
||||||
self._modelclsmap = {}
|
|
||||||
|
|
||||||
def configure(self, session, config_data):
|
|
||||||
"""
|
|
||||||
Handle configure messages. The configuration message sent to a
|
|
||||||
ConfigurableManager usually is used to:
|
|
||||||
1. Request a list of Configurables (request flag)
|
|
||||||
2. Reset manager and clear configs (reset flag)
|
|
||||||
3. Send values that configure the manager or one of its Configurables
|
|
||||||
|
|
||||||
:param core.session.Session session: CORE session object
|
|
||||||
:param ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: response messages
|
|
||||||
"""
|
|
||||||
|
|
||||||
if config_data.type == ConfigFlags.REQUEST.value:
|
|
||||||
return self.configure_request(config_data)
|
|
||||||
elif config_data.type == ConfigFlags.RESET.value:
|
|
||||||
return self.configure_reset(config_data)
|
|
||||||
else:
|
|
||||||
return self.configure_values(config_data)
|
|
||||||
|
|
||||||
def configure_request(self, config_data):
|
|
||||||
"""
|
|
||||||
Request configuration data.
|
|
||||||
|
|
||||||
:param ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def configure_reset(self, config_data):
|
|
||||||
"""
|
|
||||||
By default, resets this manager to clear configs.
|
|
||||||
|
|
||||||
:param ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: reset response messages, or None
|
|
||||||
"""
|
|
||||||
return self.reset()
|
|
||||||
|
|
||||||
def configure_values(self, config_data):
|
|
||||||
"""
|
|
||||||
Values have been sent to this manager.
|
|
||||||
|
|
||||||
:param ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def configure_values_keyvalues(self, config_data, target, keys):
|
|
||||||
"""
|
|
||||||
Helper that can be used for configure_values for parsing in
|
|
||||||
'key=value' strings from a values field. The key name must be
|
|
||||||
in the keys list, and target.key=value is set.
|
|
||||||
|
|
||||||
:param ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:param target: target to set attribute values on
|
|
||||||
:param keys: list of keys to verify validity
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
values = config_data.data_values
|
|
||||||
|
|
||||||
if values is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
kvs = values.split('|')
|
|
||||||
for kv in kvs:
|
|
||||||
try:
|
|
||||||
key, value = kv.split('=', 1)
|
|
||||||
if value is not None and not value.strip():
|
|
||||||
value = None
|
|
||||||
except ValueError:
|
|
||||||
# value only
|
|
||||||
key = keys[kvs.index(kv)]
|
|
||||||
value = kv
|
|
||||||
if key not in keys:
|
|
||||||
raise ValueError("invalid key: %s" % key)
|
|
||||||
if value is not None:
|
|
||||||
setattr(target, key, value)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""
|
|
||||||
Reset functionality for the configurable class.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def setconfig(self, nodenum, conftype, values):
|
|
||||||
"""
|
|
||||||
Add configuration values for a node to a dictionary; values are
|
|
||||||
usually received from a Configuration Message, and may refer to a
|
|
||||||
node for which no object exists yet
|
|
||||||
|
|
||||||
:param int nodenum: node id
|
|
||||||
:param conftype: configuration types
|
|
||||||
:param values: configuration values
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
logger.info("setting config for node(%s): %s - %s", nodenum, conftype, values)
|
|
||||||
conflist = []
|
|
||||||
if nodenum in self.configs:
|
|
||||||
oldlist = self.configs[nodenum]
|
|
||||||
found = False
|
|
||||||
for t, v in oldlist:
|
|
||||||
if t == conftype:
|
|
||||||
# replace existing config
|
|
||||||
found = True
|
|
||||||
conflist.append((conftype, values))
|
|
||||||
else:
|
|
||||||
conflist.append((t, v))
|
|
||||||
if not found:
|
|
||||||
conflist.append((conftype, values))
|
|
||||||
else:
|
|
||||||
conflist.append((conftype, values))
|
|
||||||
self.configs[nodenum] = conflist
|
|
||||||
|
|
||||||
def getconfig(self, nodenum, conftype, defaultvalues):
|
|
||||||
"""
|
|
||||||
Get configuration values for a node; if the values don't exist in
|
|
||||||
our dictionary then return the default values supplied
|
|
||||||
|
|
||||||
:param int nodenum: node id
|
|
||||||
:param conftype: configuration type
|
|
||||||
:param defaultvalues: default values
|
|
||||||
:return: configuration type and default values
|
|
||||||
:type: tuple
|
|
||||||
"""
|
|
||||||
logger.info("getting config for node(%s): %s - default(%s)",
|
|
||||||
nodenum, conftype, defaultvalues)
|
|
||||||
if nodenum in self.configs:
|
|
||||||
# return configured values
|
|
||||||
conflist = self.configs[nodenum]
|
|
||||||
for t, v in conflist:
|
|
||||||
if conftype is None or t == conftype:
|
|
||||||
return t, v
|
|
||||||
# return default values provided (may be None)
|
|
||||||
return conftype, defaultvalues
|
|
||||||
|
|
||||||
def getallconfigs(self, use_clsmap=True):
|
|
||||||
"""
|
|
||||||
Return (nodenum, conftype, values) tuples for all stored configs.
|
|
||||||
Used when reconnecting to a session.
|
|
||||||
|
|
||||||
:param bool use_clsmap: should a class map be used, default to True
|
|
||||||
:return: list of all configurations
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
r = []
|
|
||||||
for nodenum in self.configs:
|
|
||||||
for t, v in self.configs[nodenum]:
|
|
||||||
if use_clsmap:
|
|
||||||
t = self._modelclsmap[t]
|
|
||||||
r.append((nodenum, t, v))
|
|
||||||
return r
|
|
||||||
|
|
||||||
def clearconfig(self, nodenum):
|
|
||||||
"""
|
|
||||||
remove configuration values for the specified node;
|
|
||||||
when nodenum is None, remove all configuration values
|
|
||||||
|
|
||||||
:param int nodenum: node id
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if nodenum is None:
|
|
||||||
self.configs = {}
|
|
||||||
return
|
|
||||||
if nodenum in self.configs:
|
|
||||||
self.configs.pop(nodenum)
|
|
||||||
|
|
||||||
def setconfig_keyvalues(self, nodenum, conftype, keyvalues):
|
|
||||||
"""
|
|
||||||
Key values list of tuples for a node.
|
|
||||||
|
|
||||||
:param int nodenum: node id
|
|
||||||
:param conftype: configuration type
|
|
||||||
:param keyvalues: key valyes
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if conftype not in self._modelclsmap:
|
|
||||||
logger.warn("unknown model type '%s'", conftype)
|
|
||||||
return
|
|
||||||
model = self._modelclsmap[conftype]
|
|
||||||
keys = model.getnames()
|
|
||||||
# defaults are merged with supplied values here
|
|
||||||
values = list(model.getdefaultvalues())
|
|
||||||
for key, value in keyvalues:
|
|
||||||
if key not in keys:
|
|
||||||
logger.warn("Skipping unknown configuration key for %s: '%s'", conftype, key)
|
|
||||||
continue
|
|
||||||
i = keys.index(key)
|
|
||||||
values[i] = value
|
|
||||||
self.setconfig(nodenum, conftype, values)
|
|
||||||
|
|
||||||
def getmodels(self, n):
|
|
||||||
"""
|
|
||||||
Return a list of model classes and values for a net if one has been
|
|
||||||
configured. This is invoked when exporting a session to XML.
|
|
||||||
This assumes self.configs contains an iterable of (model-names, values)
|
|
||||||
and a self._modelclsmapdict exists.
|
|
||||||
|
|
||||||
:param n: network node to get models for
|
|
||||||
:return: list of model and values tuples for the network node
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
r = []
|
|
||||||
if n.objid in self.configs:
|
|
||||||
v = self.configs[n.objid]
|
|
||||||
for model in v:
|
|
||||||
cls = self._modelclsmap[model[0]]
|
|
||||||
vals = model[1]
|
|
||||||
r.append((cls, vals))
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
class Configurable(object):
|
|
||||||
"""
|
|
||||||
A generic class for managing configuration parameters.
|
|
||||||
Parameters are sent via Configuration Messages, which allow the GUI
|
|
||||||
to build dynamic dialogs depending on what is being configured.
|
|
||||||
"""
|
|
||||||
name = ""
|
|
||||||
# Configuration items:
|
|
||||||
# ('name', 'type', 'default', 'possible-value-list', 'caption')
|
|
||||||
config_matrix = []
|
|
||||||
config_groups = None
|
|
||||||
bitmap = None
|
|
||||||
|
|
||||||
def __init__(self, session=None, object_id=None):
|
|
||||||
"""
|
|
||||||
Creates a Configurable instance.
|
|
||||||
|
|
||||||
:param core.session.Session session: session for this configurable
|
|
||||||
:param object_id:
|
|
||||||
"""
|
|
||||||
self.session = session
|
|
||||||
self.object_id = object_id
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""
|
|
||||||
Reset method.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def register(self):
|
|
||||||
"""
|
|
||||||
Register method.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getdefaultvalues(cls):
|
def str_to_dict(cls, key_values):
|
||||||
"""
|
"""
|
||||||
Retrieve default values from configuration matrix.
|
Converts a TLV key/value string into an ordered mapping.
|
||||||
|
|
||||||
:return: tuple of default values
|
:param str key_values:
|
||||||
:rtype: tuple
|
:return: ordered mapping of key/value pairs
|
||||||
|
:rtype: OrderedDict
|
||||||
"""
|
"""
|
||||||
return tuple(map(lambda x: x[2], cls.config_matrix))
|
key_values = key_values.split("|")
|
||||||
|
values = OrderedDict()
|
||||||
|
for key_value in key_values:
|
||||||
|
key, value = key_value.split("=", 1)
|
||||||
|
values[key] = value
|
||||||
|
return values
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getnames(cls):
|
def groups_to_str(cls, config_groups):
|
||||||
"""
|
"""
|
||||||
Retrieve name values from configuration matrix.
|
Converts configuration groups to a TLV formatted string.
|
||||||
|
|
||||||
:return: tuple of name values
|
:param list[ConfigGroup] config_groups: configuration groups to format
|
||||||
:rtype: tuple
|
:return: TLV configuration group string
|
||||||
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return tuple(map(lambda x: x[0], cls.config_matrix))
|
group_strings = []
|
||||||
|
for config_group in config_groups:
|
||||||
|
group_string = "%s:%s-%s" % (config_group.name, config_group.start, config_group.stop)
|
||||||
|
group_strings.append(group_string)
|
||||||
|
return "|".join(group_strings)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configure(cls, manager, config_data):
|
def config_data(cls, flags, node_id, type_flags, configurable_options, config):
|
||||||
"""
|
|
||||||
Handle configuration messages for this object.
|
|
||||||
|
|
||||||
:param ConfigurableManager manager: configuration manager
|
|
||||||
:param config_data: configuration data
|
|
||||||
:return: configuration data object
|
|
||||||
:rtype: ConfigData
|
|
||||||
"""
|
|
||||||
reply = None
|
|
||||||
node_id = config_data.node
|
|
||||||
object_name = config_data.object
|
|
||||||
config_type = config_data.type
|
|
||||||
interface_id = config_data.interface_number
|
|
||||||
values_str = config_data.data_values
|
|
||||||
|
|
||||||
if interface_id is not None:
|
|
||||||
node_id = node_id * 1000 + interface_id
|
|
||||||
|
|
||||||
logger.debug("received configure message for %s nodenum:%s", cls.name, str(node_id))
|
|
||||||
if config_type == ConfigFlags.REQUEST.value:
|
|
||||||
logger.info("replying to configure request for %s model", cls.name)
|
|
||||||
# when object name is "all", the reply to this request may be None
|
|
||||||
# if this node has not been configured for this model; otherwise we
|
|
||||||
# reply with the defaults for this model
|
|
||||||
if object_name == "all":
|
|
||||||
defaults = None
|
|
||||||
typeflags = ConfigFlags.UPDATE.value
|
|
||||||
else:
|
|
||||||
defaults = cls.getdefaultvalues()
|
|
||||||
typeflags = ConfigFlags.NONE.value
|
|
||||||
values = manager.getconfig(node_id, cls.name, defaults)[1]
|
|
||||||
if values is None:
|
|
||||||
logger.warn("no active configuration for node (%s), ignoring request")
|
|
||||||
# node has no active config for this model (don't send defaults)
|
|
||||||
return None
|
|
||||||
# reply with config options
|
|
||||||
reply = cls.config_data(0, node_id, typeflags, values)
|
|
||||||
elif config_type == ConfigFlags.RESET.value:
|
|
||||||
if object_name == "all":
|
|
||||||
manager.clearconfig(node_id)
|
|
||||||
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
|
|
||||||
else:
|
|
||||||
# store the configuration values for later use, when the node
|
|
||||||
# object has been created
|
|
||||||
if object_name is None:
|
|
||||||
logger.info("no configuration object for node %s", node_id)
|
|
||||||
return None
|
|
||||||
defaults = cls.getdefaultvalues()
|
|
||||||
if values_str is None:
|
|
||||||
# use default or preconfigured values
|
|
||||||
values = manager.getconfig(node_id, cls.name, defaults)[1]
|
|
||||||
else:
|
|
||||||
# use new values supplied from the conf message
|
|
||||||
values = values_str.split('|')
|
|
||||||
# determine new or old style config
|
|
||||||
new = cls.haskeyvalues(values)
|
|
||||||
if new:
|
|
||||||
new_values = list(defaults)
|
|
||||||
keys = cls.getnames()
|
|
||||||
for v in values:
|
|
||||||
key, value = v.split('=', 1)
|
|
||||||
try:
|
|
||||||
new_values[keys.index(key)] = value
|
|
||||||
except ValueError:
|
|
||||||
logger.info("warning: ignoring invalid key '%s'" % key)
|
|
||||||
values = new_values
|
|
||||||
manager.setconfig(node_id, object_name, values)
|
|
||||||
|
|
||||||
return reply
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def config_data(cls, flags, node_id, type_flags, values):
|
|
||||||
"""
|
"""
|
||||||
Convert this class to a Config API message. Some TLVs are defined
|
Convert this class to a Config API message. Some TLVs are defined
|
||||||
by the class, but node number, conf type flags, and values must
|
by the class, but node number, conf type flags, and values must
|
||||||
be passed in.
|
be passed in.
|
||||||
|
|
||||||
:param flags: message flags
|
:param int flags: message flags
|
||||||
:param int node_id: node id
|
:param int node_id: node id
|
||||||
:param type_flags: type flags
|
:param int type_flags: type flags
|
||||||
:param values: values
|
:param ConfigurableOptions configurable_options: options to create config data for
|
||||||
|
:param dict config: configuration values for options
|
||||||
:return: configuration data object
|
:return: configuration data object
|
||||||
:rtype: ConfigData
|
:rtype: ConfigData
|
||||||
"""
|
"""
|
||||||
keys = cls.getnames()
|
key_values = None
|
||||||
keyvalues = map(lambda a, b: "%s=%s" % (a, b), keys, values)
|
captions = None
|
||||||
values_str = string.join(keyvalues, '|')
|
data_types = []
|
||||||
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
|
possible_values = []
|
||||||
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
|
logger.debug("configurable: %s", configurable_options)
|
||||||
possible_valuess = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
|
logger.debug("configuration options: %s", configurable_options.configurations)
|
||||||
|
logger.debug("configuration data: %s", config)
|
||||||
|
for configuration in configurable_options.configurations():
|
||||||
|
if not captions:
|
||||||
|
captions = configuration.label
|
||||||
|
else:
|
||||||
|
captions += "|%s" % configuration.label
|
||||||
|
|
||||||
|
data_types.append(configuration.type.value)
|
||||||
|
|
||||||
|
options = ",".join(configuration.options)
|
||||||
|
possible_values.append(options)
|
||||||
|
|
||||||
|
_id = configuration.id
|
||||||
|
config_value = config.get(_id, configuration.default)
|
||||||
|
key_value = "%s=%s" % (_id, config_value)
|
||||||
|
if not key_values:
|
||||||
|
key_values = key_value
|
||||||
|
else:
|
||||||
|
key_values += "|%s" % key_value
|
||||||
|
|
||||||
|
groups_str = cls.groups_to_str(configurable_options.config_groups())
|
||||||
return ConfigData(
|
return ConfigData(
|
||||||
message_type=flags,
|
message_type=flags,
|
||||||
node=node_id,
|
node=node_id,
|
||||||
object=cls.name,
|
object=configurable_options.name,
|
||||||
type=type_flags,
|
type=type_flags,
|
||||||
data_types=datatypes,
|
data_types=tuple(data_types),
|
||||||
data_values=values_str,
|
data_values=key_values,
|
||||||
captions=captions,
|
captions=captions,
|
||||||
possible_values=possible_valuess,
|
possible_values="|".join(possible_values),
|
||||||
bitmap=cls.bitmap,
|
bitmap=configurable_options.bitmap,
|
||||||
groups=cls.config_groups
|
groups=groups_str
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def booltooffon(value):
|
|
||||||
"""
|
|
||||||
Convenience helper turns bool into on (True) or off (False) string.
|
|
||||||
|
|
||||||
:param str value: value to retrieve on/off value for
|
class Configuration(object):
|
||||||
:return: on or off string
|
"""
|
||||||
:rtype: str
|
Represents a configuration options.
|
||||||
"""
|
"""
|
||||||
if value == "1" or value == "true" or value == "on":
|
|
||||||
return "on"
|
|
||||||
else:
|
|
||||||
return "off"
|
|
||||||
|
|
||||||
@staticmethod
|
def __init__(self, _id, _type, label=None, default="", options=None):
|
||||||
def offontobool(value):
|
|
||||||
"""
|
"""
|
||||||
Convenience helper for converting an on/off string to a integer.
|
Creates a Configuration object.
|
||||||
|
|
||||||
:param str value: on/off string
|
:param str _id: unique name for configuration
|
||||||
:return: on/off integer value
|
:param core.enumerations.ConfigDataTypes _type: configuration data type
|
||||||
:rtype: int
|
:param str label: configuration label for display
|
||||||
|
:param str default: default value for configuration
|
||||||
|
:param list options: list options if this is a configuration with a combobox
|
||||||
"""
|
"""
|
||||||
if type(value) == str:
|
self.id = _id
|
||||||
if value.lower() == "on":
|
self.type = _type
|
||||||
return 1
|
self.default = default
|
||||||
elif value.lower() == "off":
|
if not options:
|
||||||
return 0
|
options = []
|
||||||
return value
|
self.options = options
|
||||||
|
if not label:
|
||||||
|
label = _id
|
||||||
|
self.label = label
|
||||||
|
|
||||||
@classmethod
|
def __str__(self):
|
||||||
def valueof(cls, name, values):
|
return "%s(id=%s, type=%s, default=%s, options=%s)" % (
|
||||||
|
self.__class__.__name__, self.id, self.type, self.default, self.options)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurableManager(object):
|
||||||
|
"""
|
||||||
|
Provides convenience methods for storing and retrieving configuration options for nodes.
|
||||||
|
"""
|
||||||
|
_default_node = -1
|
||||||
|
_default_type = _default_node
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Helper to return a value by the name defined in confmatrix.
|
Creates a ConfigurableManager object.
|
||||||
Checks if it is boolean
|
|
||||||
|
|
||||||
:param str name: name to get value of
|
|
||||||
:param values: values to get value from
|
|
||||||
:return: value for name
|
|
||||||
"""
|
"""
|
||||||
i = cls.getnames().index(name)
|
self.node_configurations = {}
|
||||||
if cls.config_matrix[i][1] == ConfigDataTypes.BOOL.value and values[i] != "":
|
|
||||||
return cls.booltooffon(values[i])
|
|
||||||
else:
|
|
||||||
return values[i]
|
|
||||||
|
|
||||||
@staticmethod
|
def nodes(self):
|
||||||
def haskeyvalues(values):
|
|
||||||
"""
|
"""
|
||||||
Helper to check for list of key=value pairs versus a plain old
|
Retrieves the ids of all node configurations known by this manager.
|
||||||
list of values. Returns True if all elements are "key=value".
|
|
||||||
|
|
||||||
:param values: items to check for key/value pairs
|
:return: list of node ids
|
||||||
:return: True if all values are key/value pairs, False otherwise
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
if len(values) == 0:
|
|
||||||
return False
|
|
||||||
for v in values:
|
|
||||||
if "=" not in v:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def getkeyvaluelist(self):
|
|
||||||
"""
|
|
||||||
Helper to return a list of (key, value) tuples. Keys come from
|
|
||||||
configuration matrix and values are instance attributes.
|
|
||||||
|
|
||||||
:return: tuples of key value pairs
|
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
key_values = []
|
return [node_id for node_id in self.node_configurations.iterkeys() if node_id != self._default_node]
|
||||||
|
|
||||||
for name in self.getnames():
|
def config_reset(self, node_id=None):
|
||||||
if hasattr(self, name):
|
"""
|
||||||
value = getattr(self, name)
|
Clears all configurations or configuration for a specific node.
|
||||||
key_values.append((name, value))
|
|
||||||
|
|
||||||
return key_values
|
:param int node_id: node id to clear configurations for, default is None and clears all configurations
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.debug("resetting all configurations: %s", self.__class__.__name__)
|
||||||
|
if not node_id:
|
||||||
|
self.node_configurations.clear()
|
||||||
|
elif node_id in self.node_configurations:
|
||||||
|
self.node_configurations.pop(node_id)
|
||||||
|
|
||||||
|
def set_config(self, _id, value, node_id=_default_node, config_type=_default_type):
|
||||||
|
"""
|
||||||
|
Set a specific configuration value for a node and configuration type.
|
||||||
|
|
||||||
|
:param str _id: configuration key
|
||||||
|
:param str value: configuration value
|
||||||
|
:param int node_id: node id to store configuration for
|
||||||
|
:param str config_type: configuration type to store configuration for
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.debug("setting config for node(%s) type(%s): %s=%s", node_id, config_type, _id, value)
|
||||||
|
node_configs = self.node_configurations.setdefault(node_id, OrderedDict())
|
||||||
|
node_type_configs = node_configs.setdefault(config_type, OrderedDict())
|
||||||
|
node_type_configs[_id] = value
|
||||||
|
|
||||||
|
def set_configs(self, config, node_id=_default_node, config_type=_default_type):
|
||||||
|
"""
|
||||||
|
Set configurations for a node and configuration type.
|
||||||
|
|
||||||
|
:param dict config: configurations to set
|
||||||
|
:param int node_id: node id to store configuration for
|
||||||
|
:param str config_type: configuration type to store configuration for
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.debug("setting config for node(%s) type(%s): %s", node_id, config_type, config)
|
||||||
|
node_configs = self.node_configurations.setdefault(node_id, OrderedDict())
|
||||||
|
node_configs[config_type] = config
|
||||||
|
|
||||||
|
def get_config(self, _id, node_id=_default_node, config_type=_default_type, default=None):
|
||||||
|
"""
|
||||||
|
Retrieves a specific configuration for a node and configuration type.
|
||||||
|
|
||||||
|
:param str _id: specific configuration to retrieve
|
||||||
|
:param int node_id: node id to store configuration for
|
||||||
|
:param str config_type: configuration type to store configuration for
|
||||||
|
:param default: default value to return when value is not found
|
||||||
|
:return: configuration value
|
||||||
|
:rtype str
|
||||||
|
"""
|
||||||
|
logger.debug("getting config for node(%s) type(%s): %s", node_id, config_type, _id)
|
||||||
|
result = default
|
||||||
|
node_type_configs = self.get_configs(node_id, config_type)
|
||||||
|
if node_type_configs:
|
||||||
|
result = node_type_configs.get(_id, default)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_configs(self, node_id=_default_node, config_type=_default_type):
|
||||||
|
"""
|
||||||
|
Retrieve configurations for a node and configuration type.
|
||||||
|
|
||||||
|
:param int node_id: node id to store configuration for
|
||||||
|
:param str config_type: configuration type to store configuration for
|
||||||
|
:return: configurations
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
logger.debug("getting configs for node(%s) type(%s)", node_id, config_type)
|
||||||
|
result = None
|
||||||
|
node_configs = self.node_configurations.get(node_id)
|
||||||
|
if node_configs:
|
||||||
|
result = node_configs.get(config_type)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_all_configs(self, node_id=_default_node):
|
||||||
|
"""
|
||||||
|
Retrieve all current configuration types for a node.
|
||||||
|
|
||||||
|
:param int node_id: node id to retrieve configurations for
|
||||||
|
:return: all configuration types for a node
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
logger.debug("getting all configs for node(%s)", node_id)
|
||||||
|
return self.node_configurations.get(node_id)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigGroup(object):
|
||||||
|
"""
|
||||||
|
Defines configuration group tabs used for display by ConfigurationOptions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, start, stop):
|
||||||
|
"""
|
||||||
|
Creates a ConfigGroup object.
|
||||||
|
|
||||||
|
:param str name: configuration group display name
|
||||||
|
:param int start: configurations start index for this group
|
||||||
|
:param int stop: configurations stop index for this group
|
||||||
|
"""
|
||||||
|
self.name = name
|
||||||
|
self.start = start
|
||||||
|
self.stop = stop
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurableOptions(object):
|
||||||
|
"""
|
||||||
|
Provides a base for defining configuration options within CORE.
|
||||||
|
"""
|
||||||
|
name = None
|
||||||
|
bitmap = None
|
||||||
|
options = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def configurations(cls):
|
||||||
|
"""
|
||||||
|
Provides the configurations for this class.
|
||||||
|
|
||||||
|
:return: configurations
|
||||||
|
:rtype: list[Configuration]
|
||||||
|
"""
|
||||||
|
return cls.options
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def config_groups(cls):
|
||||||
|
"""
|
||||||
|
Defines how configurations are grouped.
|
||||||
|
|
||||||
|
:return: configuration group definition
|
||||||
|
:rtype: list[ConfigGroup]
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
ConfigGroup("Options", 1, len(cls.configurations()))
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_values(cls):
|
||||||
|
"""
|
||||||
|
Provides an ordered mapping of configuration keys to default values.
|
||||||
|
|
||||||
|
:return: ordered configuration mapping default values
|
||||||
|
:rtype: OrderedDict
|
||||||
|
"""
|
||||||
|
return OrderedDict([(config.id, config.default) for config in cls.configurations()])
|
||||||
|
|
||||||
|
|
||||||
|
class ModelManager(ConfigurableManager):
|
||||||
|
"""
|
||||||
|
Helps handle setting models for nodes and managing their model configurations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Creates a ModelManager object.
|
||||||
|
"""
|
||||||
|
super(ModelManager, self).__init__()
|
||||||
|
self.models = {}
|
||||||
|
self.node_models = {}
|
||||||
|
|
||||||
|
def set_model_config(self, node_id, model_name, config=None):
|
||||||
|
"""
|
||||||
|
Set configuration data for a model.
|
||||||
|
|
||||||
|
:param int node_id: node id to set model configuration for
|
||||||
|
:param str model_name: model to set configuration for
|
||||||
|
:param dict config: configuration data to set for model
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# get model class to configure
|
||||||
|
model_class = self.models.get(model_name)
|
||||||
|
if not model_class:
|
||||||
|
raise ValueError("%s is an invalid model" % model_name)
|
||||||
|
|
||||||
|
# retrieve default values
|
||||||
|
model_config = self.get_model_config(node_id, model_name)
|
||||||
|
if not config:
|
||||||
|
config = {}
|
||||||
|
for key, value in config.iteritems():
|
||||||
|
model_config[key] = value
|
||||||
|
|
||||||
|
# set as node model for startup
|
||||||
|
self.node_models[node_id] = model_name
|
||||||
|
|
||||||
|
# set configuration
|
||||||
|
self.set_configs(model_config, node_id=node_id, config_type=model_name)
|
||||||
|
|
||||||
|
def get_model_config(self, node_id, model_name):
|
||||||
|
"""
|
||||||
|
Set configuration data for a model.
|
||||||
|
|
||||||
|
:param int node_id: node id to set model configuration for
|
||||||
|
:param str model_name: model to set configuration for
|
||||||
|
:return: current model configuration for node
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
# get model class to configure
|
||||||
|
model_class = self.models.get(model_name)
|
||||||
|
if not model_class:
|
||||||
|
raise ValueError("%s is an invalid model" % model_name)
|
||||||
|
|
||||||
|
config = self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
|
if not config:
|
||||||
|
# set default values, when not already set
|
||||||
|
config = model_class.default_values()
|
||||||
|
self.set_configs(config, node_id=node_id, config_type=model_name)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
def set_model(self, node, model_class, config=None):
|
||||||
|
"""
|
||||||
|
Set model and model configuration for node.
|
||||||
|
|
||||||
|
:param node: node to set model for
|
||||||
|
:param model_class: model class to set for node
|
||||||
|
:param dict config: model configuration, None for default configuration
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.info("setting mobility model(%s) for node(%s): %s", model_class.name, node.objid, config)
|
||||||
|
self.set_model_config(node.objid, model_class.name, config)
|
||||||
|
config = self.get_model_config(node.objid, model_class.name)
|
||||||
|
node.setmodel(model_class, config)
|
||||||
|
|
||||||
|
def get_models(self, node):
|
||||||
|
"""
|
||||||
|
Return a list of model classes and values for a net if one has been
|
||||||
|
configured. This is invoked when exporting a session to XML.
|
||||||
|
|
||||||
|
:param node: network node to get models for
|
||||||
|
:return: list of model and values tuples for the network node
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
all_configs = self.get_all_configs(node.objid)
|
||||||
|
if not all_configs:
|
||||||
|
all_configs = {}
|
||||||
|
|
||||||
|
models = []
|
||||||
|
for model_name, config in all_configs.iteritems():
|
||||||
|
if model_name == ModelManager._default_node:
|
||||||
|
continue
|
||||||
|
model_class = self.models[model_name]
|
||||||
|
models.append((model_class, config))
|
||||||
|
|
||||||
|
logger.debug("models for node(%s): %s", node.objid, models)
|
||||||
|
return models
|
||||||
|
|
|
@ -10,14 +10,20 @@ import shutil
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from itertools import repeat
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.data import ConfigData
|
from core.api import dataconversion
|
||||||
|
from core.conf import ConfigShim
|
||||||
|
from core.data import ConfigData, ExceptionData
|
||||||
from core.data import EventData
|
from core.data import EventData
|
||||||
|
from core.data import FileData
|
||||||
from core.emulator.emudata import InterfaceData
|
from core.emulator.emudata import InterfaceData
|
||||||
from core.emulator.emudata import LinkOptions
|
from core.emulator.emudata import LinkOptions
|
||||||
from core.emulator.emudata import NodeOptions
|
from core.emulator.emudata import NodeOptions
|
||||||
|
from core.enumerations import ConfigDataTypes, ExceptionLevels
|
||||||
|
from core.enumerations import ConfigFlags
|
||||||
from core.enumerations import ConfigTlvs
|
from core.enumerations import ConfigTlvs
|
||||||
from core.enumerations import EventTlvs
|
from core.enumerations import EventTlvs
|
||||||
from core.enumerations import EventTypes
|
from core.enumerations import EventTypes
|
||||||
|
@ -35,6 +41,8 @@ from core.enumerations import SessionTlvs
|
||||||
from core.misc import nodeutils
|
from core.misc import nodeutils
|
||||||
from core.misc import structutils
|
from core.misc import structutils
|
||||||
from core.misc import utils
|
from core.misc import utils
|
||||||
|
from core.service import ServiceManager
|
||||||
|
from core.service import ServiceShim
|
||||||
|
|
||||||
|
|
||||||
class CoreHandler(SocketServer.BaseRequestHandler):
|
class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
|
@ -261,24 +269,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logger.debug("handling broadcast config: %s", config_data)
|
logger.debug("handling broadcast config: %s", config_data)
|
||||||
|
message = dataconversion.convert_config(config_data)
|
||||||
tlv_data = structutils.pack_values(coreapi.CoreConfigTlv, [
|
|
||||||
(ConfigTlvs.NODE, config_data.node),
|
|
||||||
(ConfigTlvs.OBJECT, config_data.object),
|
|
||||||
(ConfigTlvs.TYPE, config_data.type),
|
|
||||||
(ConfigTlvs.DATA_TYPES, config_data.data_types),
|
|
||||||
(ConfigTlvs.VALUES, config_data.data_values),
|
|
||||||
(ConfigTlvs.CAPTIONS, config_data.captions),
|
|
||||||
(ConfigTlvs.BITMAP, config_data.bitmap),
|
|
||||||
(ConfigTlvs.POSSIBLE_VALUES, config_data.possible_values),
|
|
||||||
(ConfigTlvs.GROUPS, config_data.groups),
|
|
||||||
(ConfigTlvs.SESSION, config_data.session),
|
|
||||||
(ConfigTlvs.INTERFACE_NUMBER, config_data.interface_number),
|
|
||||||
(ConfigTlvs.NETWORK_ID, config_data.network_id),
|
|
||||||
(ConfigTlvs.OPAQUE, config_data.opaque),
|
|
||||||
])
|
|
||||||
message = coreapi.CoreConfMessage.pack(config_data.message_type, tlv_data)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sendall(message)
|
self.sendall(message)
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -315,31 +306,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logger.debug("handling broadcast node: %s", node_data)
|
logger.debug("handling broadcast node: %s", node_data)
|
||||||
|
message = dataconversion.convert_node(node_data)
|
||||||
tlv_data = structutils.pack_values(coreapi.CoreNodeTlv, [
|
|
||||||
(NodeTlvs.NUMBER, node_data.id),
|
|
||||||
(NodeTlvs.TYPE, node_data.node_type),
|
|
||||||
(NodeTlvs.NAME, node_data.name),
|
|
||||||
(NodeTlvs.IP_ADDRESS, node_data.ip_address),
|
|
||||||
(NodeTlvs.MAC_ADDRESS, node_data.mac_address),
|
|
||||||
(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.SESSION, node_data.session),
|
|
||||||
(NodeTlvs.X_POSITION, node_data.x_position),
|
|
||||||
(NodeTlvs.Y_POSITION, node_data.y_position),
|
|
||||||
(NodeTlvs.CANVAS, node_data.canvas),
|
|
||||||
(NodeTlvs.NETWORK_ID, node_data.network_id),
|
|
||||||
(NodeTlvs.SERVICES, node_data.services),
|
|
||||||
(NodeTlvs.LATITUDE, node_data.latitude),
|
|
||||||
(NodeTlvs.LONGITUDE, node_data.longitude),
|
|
||||||
(NodeTlvs.ALTITUDE, node_data.altitude),
|
|
||||||
(NodeTlvs.ICON, node_data.icon),
|
|
||||||
(NodeTlvs.OPAQUE, node_data.opaque)
|
|
||||||
])
|
|
||||||
message = coreapi.CoreNodeMessage.pack(node_data.message_type, tlv_data)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sendall(message)
|
self.sendall(message)
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -407,12 +374,17 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
tlv_data = ""
|
tlv_data = ""
|
||||||
tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.EXECUTE_SERVER.value, "core-daemon")
|
tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.EXECUTE_SERVER.value, "core-daemon")
|
||||||
tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.EMULATION_SERVER.value, "core-daemon")
|
tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.EMULATION_SERVER.value, "core-daemon")
|
||||||
|
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.broker.config_type, self.session.broker.name)
|
||||||
# get config objects for session
|
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.location.config_type, self.session.location.name)
|
||||||
for name in self.session.config_objects:
|
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.mobility.config_type, self.session.mobility.name)
|
||||||
config_type, callback = self.session.config_objects[name]
|
for model_class in self.session.mobility.models.itervalues():
|
||||||
# type must be in coreapi.reg_tlvs
|
tlv_data += coreapi.CoreRegisterTlv.pack(model_class.config_type, model_class.name)
|
||||||
tlv_data += coreapi.CoreRegisterTlv.pack(config_type, name)
|
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.services.config_type, self.session.services.name)
|
||||||
|
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.emane.config_type, self.session.emane.name)
|
||||||
|
for model_class in self.session.emane.models.itervalues():
|
||||||
|
tlv_data += coreapi.CoreRegisterTlv.pack(model_class.config_type, model_class.name)
|
||||||
|
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)
|
||||||
|
|
||||||
return coreapi.CoreRegMessage.pack(MessageFlags.ADD.value, tlv_data)
|
return coreapi.CoreRegMessage.pack(MessageFlags.ADD.value, tlv_data)
|
||||||
|
|
||||||
|
@ -606,6 +578,26 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
logger.debug("BROADCAST TO OTHER CLIENT: %s", client)
|
logger.debug("BROADCAST TO OTHER CLIENT: %s", client)
|
||||||
client.sendall(message.raw_message)
|
client.sendall(message.raw_message)
|
||||||
|
|
||||||
|
def send_exception(self, level, source, text, node=None):
|
||||||
|
"""
|
||||||
|
Sends an exception for display within the GUI.
|
||||||
|
|
||||||
|
:param core.enumerations.ExceptionLevel level: level for exception
|
||||||
|
:param str source: source where exception came from
|
||||||
|
:param str text: details about exception
|
||||||
|
:param int node: node id, if related to a specific node
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
exception_data = ExceptionData(
|
||||||
|
session=str(self.session.session_id),
|
||||||
|
node=node,
|
||||||
|
date=time.ctime(),
|
||||||
|
level=level.value,
|
||||||
|
source=source,
|
||||||
|
text=text
|
||||||
|
)
|
||||||
|
self.handle_broadcast_exception(exception_data)
|
||||||
|
|
||||||
def add_session_handlers(self):
|
def add_session_handlers(self):
|
||||||
logger.debug("adding session broadcast handlers")
|
logger.debug("adding session broadcast handlers")
|
||||||
self.session.event_handlers.append(self.handle_broadcast_event)
|
self.session.event_handlers.append(self.handle_broadcast_event)
|
||||||
|
@ -941,15 +933,395 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
opaque=message.get_tlv(ConfigTlvs.OPAQUE.value)
|
opaque=message.get_tlv(ConfigTlvs.OPAQUE.value)
|
||||||
)
|
)
|
||||||
logger.debug("configuration message for %s node %s", config_data.object, config_data.node)
|
logger.debug("configuration message for %s node %s", config_data.object, config_data.node)
|
||||||
|
message_type = ConfigFlags(config_data.type)
|
||||||
|
|
||||||
# dispatch to any registered callback for this object type
|
replies = []
|
||||||
replies = self.session.config_object(config_data)
|
|
||||||
|
# handle session configuration
|
||||||
|
if config_data.object == "all":
|
||||||
|
replies = self.handle_config_all(message_type, config_data)
|
||||||
|
elif config_data.object == self.session.options.name:
|
||||||
|
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:
|
||||||
|
replies = self.handle_config_metadata(message_type, config_data)
|
||||||
|
elif config_data.object == self.session.broker.name:
|
||||||
|
self.handle_config_broker(message_type, config_data)
|
||||||
|
elif config_data.object == self.session.services.name:
|
||||||
|
replies = self.handle_config_services(message_type, config_data)
|
||||||
|
elif config_data.object == self.session.mobility.name:
|
||||||
|
self.handle_config_mobility(message_type, config_data)
|
||||||
|
elif config_data.object in self.session.mobility.models:
|
||||||
|
replies = self.handle_config_mobility_models(message_type, config_data)
|
||||||
|
elif config_data.object == self.session.emane.name:
|
||||||
|
replies = self.handle_config_emane(message_type, config_data)
|
||||||
|
elif config_data.object in self.session.emane.models:
|
||||||
|
replies = self.handle_config_emane_models(message_type, config_data)
|
||||||
|
else:
|
||||||
|
raise Exception("no handler for configuration: %s", config_data.object)
|
||||||
|
|
||||||
for reply in replies:
|
for reply in replies:
|
||||||
self.handle_broadcast_config(reply)
|
self.handle_broadcast_config(reply)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def handle_config_all(self, message_type, config_data):
|
||||||
|
replies = []
|
||||||
|
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
raise Exception("cant handle config all: %s" % message_type)
|
||||||
|
|
||||||
|
return replies
|
||||||
|
|
||||||
|
def handle_config_session(self, message_type, config_data):
|
||||||
|
replies = []
|
||||||
|
if message_type == ConfigFlags.REQUEST:
|
||||||
|
type_flags = ConfigFlags.NONE.value
|
||||||
|
config = self.session.options.get_configs()
|
||||||
|
config_response = ConfigShim.config_data(0, None, type_flags, self.session.options, config)
|
||||||
|
replies.append(config_response)
|
||||||
|
elif message_type != ConfigFlags.RESET and config_data.data_values:
|
||||||
|
values = ConfigShim.str_to_dict(config_data.data_values)
|
||||||
|
for key, value in values.iteritems():
|
||||||
|
self.session.options.set_config(key, value)
|
||||||
|
return replies
|
||||||
|
|
||||||
|
def handle_config_location(self, message_type, config_data):
|
||||||
|
if message_type == ConfigFlags.RESET:
|
||||||
|
self.session.location.reset()
|
||||||
|
else:
|
||||||
|
if not config_data.data_values:
|
||||||
|
logger.warn("location data missing")
|
||||||
|
else:
|
||||||
|
values = config_data.data_values.split("|")
|
||||||
|
|
||||||
|
# Cartesian coordinate reference point
|
||||||
|
refx, refy = map(lambda x: float(x), values[0:2])
|
||||||
|
refz = 0.0
|
||||||
|
lat, lon, alt = map(lambda x: float(x), values[2:5])
|
||||||
|
# xyz point
|
||||||
|
self.session.location.refxyz = (refx, refy, refz)
|
||||||
|
# geographic reference point
|
||||||
|
self.session.location.setrefgeo(lat, lon, alt)
|
||||||
|
self.session.location.refscale = float(values[5])
|
||||||
|
logger.info("location configured: %s = %s scale=%s", self.session.location.refxyz,
|
||||||
|
self.session.location.refgeo, self.session.location.refscale)
|
||||||
|
logger.info("location configured: UTM%s", self.session.location.refutm)
|
||||||
|
|
||||||
|
def handle_config_metadata(self, message_type, config_data):
|
||||||
|
replies = []
|
||||||
|
if message_type == ConfigFlags.REQUEST:
|
||||||
|
node_id = config_data.node
|
||||||
|
data_values = "|".join(["%s=%s" % item for item in self.session.metadata.get_configs().iteritems()])
|
||||||
|
data_types = tuple(ConfigDataTypes.STRING.value for _ in self.session.metadata.get_configs())
|
||||||
|
config_response = ConfigData(
|
||||||
|
message_type=0,
|
||||||
|
node=node_id,
|
||||||
|
object=self.session.metadata.name,
|
||||||
|
type=ConfigFlags.NONE.value,
|
||||||
|
data_types=data_types,
|
||||||
|
data_values=data_values
|
||||||
|
)
|
||||||
|
replies.append(config_response)
|
||||||
|
elif message_type != ConfigFlags.RESET and config_data.data_values:
|
||||||
|
values = ConfigShim.str_to_dict(config_data.data_values)
|
||||||
|
for key, value in values.iteritems():
|
||||||
|
self.session.metadata.set_config(key, value)
|
||||||
|
return replies
|
||||||
|
|
||||||
|
def handle_config_broker(self, message_type, config_data):
|
||||||
|
if message_type not in [ConfigFlags.REQUEST, ConfigFlags.RESET]:
|
||||||
|
session_id = config_data.session
|
||||||
|
if not config_data.data_values:
|
||||||
|
logger.info("emulation server data missing")
|
||||||
|
else:
|
||||||
|
values = config_data.data_values.split("|")
|
||||||
|
|
||||||
|
# string of "server:ip:port,server:ip:port,..."
|
||||||
|
server_strings = values[0]
|
||||||
|
server_list = server_strings.split(",")
|
||||||
|
|
||||||
|
for server in server_list:
|
||||||
|
server_items = server.split(":")
|
||||||
|
name, host, port = server_items[:3]
|
||||||
|
|
||||||
|
if host == "":
|
||||||
|
host = None
|
||||||
|
|
||||||
|
if port == "":
|
||||||
|
port = None
|
||||||
|
else:
|
||||||
|
port = int(port)
|
||||||
|
|
||||||
|
if session_id is not None:
|
||||||
|
# receive session ID and my IP from master
|
||||||
|
self.session.broker.session_id_master = int(session_id.split("|")[0])
|
||||||
|
self.session.broker.myip = host
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
|
||||||
|
# this connects to the server immediately; maybe we should wait
|
||||||
|
# or spin off a new "client" thread here
|
||||||
|
self.session.broker.addserver(name, host, port)
|
||||||
|
self.session.broker.setupserver(name)
|
||||||
|
|
||||||
|
def handle_config_services(self, message_type, config_data):
|
||||||
|
replies = []
|
||||||
|
node_id = config_data.node
|
||||||
|
opaque = config_data.opaque
|
||||||
|
|
||||||
|
if message_type == ConfigFlags.REQUEST:
|
||||||
|
session_id = config_data.session
|
||||||
|
opaque = config_data.opaque
|
||||||
|
|
||||||
|
logger.debug("configuration request: node(%s) session(%s) opaque(%s)", node_id, session_id, opaque)
|
||||||
|
|
||||||
|
# send back a list of available services
|
||||||
|
if opaque is None:
|
||||||
|
type_flag = ConfigFlags.NONE.value
|
||||||
|
data_types = tuple(repeat(ConfigDataTypes.BOOL.value, len(ServiceManager.services)))
|
||||||
|
|
||||||
|
# sort groups by name and map services to groups
|
||||||
|
groups = set()
|
||||||
|
group_map = {}
|
||||||
|
for service_name in ServiceManager.services.itervalues():
|
||||||
|
group = service_name.group
|
||||||
|
groups.add(group)
|
||||||
|
group_map.setdefault(group, []).append(service_name)
|
||||||
|
groups = sorted(groups, key=lambda x: x.lower())
|
||||||
|
|
||||||
|
# define tlv values in proper order
|
||||||
|
captions = []
|
||||||
|
possible_values = []
|
||||||
|
values = []
|
||||||
|
group_strings = []
|
||||||
|
start_index = 1
|
||||||
|
logger.info("sorted groups: %s", groups)
|
||||||
|
for group in groups:
|
||||||
|
services = sorted(group_map[group], key=lambda x: x.name.lower())
|
||||||
|
logger.info("sorted services for group(%s): %s", group, services)
|
||||||
|
end_index = start_index + len(services) - 1
|
||||||
|
group_strings.append("%s:%s-%s" % (group, start_index, end_index))
|
||||||
|
start_index += len(services)
|
||||||
|
for service_name in services:
|
||||||
|
captions.append(service_name.name)
|
||||||
|
values.append("0")
|
||||||
|
if service_name.custom_needed:
|
||||||
|
possible_values.append("1")
|
||||||
|
else:
|
||||||
|
possible_values.append("")
|
||||||
|
|
||||||
|
# format for tlv
|
||||||
|
captions = "|".join(captions)
|
||||||
|
possible_values = "|".join(possible_values)
|
||||||
|
values = "|".join(values)
|
||||||
|
groups = "|".join(group_strings)
|
||||||
|
# send back the properties for this service
|
||||||
|
else:
|
||||||
|
if not node_id:
|
||||||
|
return replies
|
||||||
|
|
||||||
|
node = self.session.get_object(node_id)
|
||||||
|
if node is None:
|
||||||
|
logger.warn("request to configure service for unknown node %s", node_id)
|
||||||
|
return replies
|
||||||
|
|
||||||
|
services = ServiceShim.servicesfromopaque(opaque)
|
||||||
|
if not services:
|
||||||
|
return replies
|
||||||
|
|
||||||
|
servicesstring = opaque.split(":")
|
||||||
|
if len(servicesstring) == 3:
|
||||||
|
# a file request: e.g. "service:zebra:quagga.conf"
|
||||||
|
file_name = servicesstring[2]
|
||||||
|
service_name = services[0]
|
||||||
|
file_data = self.session.services.get_service_file(node, service_name, file_name)
|
||||||
|
self.session.broadcast_file(file_data)
|
||||||
|
# short circuit this request early to avoid returning response below
|
||||||
|
return replies
|
||||||
|
|
||||||
|
# the first service in the list is the one being configured
|
||||||
|
service_name = services[0]
|
||||||
|
# send back:
|
||||||
|
# dirs, configs, startindex, startup, shutdown, metadata, config
|
||||||
|
type_flag = ConfigFlags.UPDATE.value
|
||||||
|
data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(ServiceShim.keys)))
|
||||||
|
service = self.session.services.get_service(node_id, service_name, default_service=True)
|
||||||
|
values = ServiceShim.tovaluelist(node, service)
|
||||||
|
captions = None
|
||||||
|
possible_values = None
|
||||||
|
groups = None
|
||||||
|
|
||||||
|
config_response = ConfigData(
|
||||||
|
message_type=0,
|
||||||
|
node=node_id,
|
||||||
|
object=self.session.services.name,
|
||||||
|
type=type_flag,
|
||||||
|
data_types=data_types,
|
||||||
|
data_values=values,
|
||||||
|
captions=captions,
|
||||||
|
possible_values=possible_values,
|
||||||
|
groups=groups,
|
||||||
|
session=session_id,
|
||||||
|
opaque=opaque
|
||||||
|
)
|
||||||
|
replies.append(config_response)
|
||||||
|
elif message_type == ConfigFlags.RESET:
|
||||||
|
self.session.services.reset()
|
||||||
|
else:
|
||||||
|
data_types = config_data.data_types
|
||||||
|
values = config_data.data_values
|
||||||
|
|
||||||
|
error_message = "services config message that I don't know how to handle"
|
||||||
|
if values is None:
|
||||||
|
logger.error(error_message)
|
||||||
|
else:
|
||||||
|
if opaque is None:
|
||||||
|
values = values.split("|")
|
||||||
|
# store default services for a node type in self.defaultservices[]
|
||||||
|
if data_types is None or data_types[0] != ConfigDataTypes.STRING.value:
|
||||||
|
logger.info(error_message)
|
||||||
|
return None
|
||||||
|
key = values.pop(0)
|
||||||
|
self.session.services.default_services[key] = values
|
||||||
|
logger.debug("default services for type %s set to %s", key, values)
|
||||||
|
elif node_id:
|
||||||
|
services = ServiceShim.servicesfromopaque(opaque)
|
||||||
|
if services:
|
||||||
|
service_name = services[0]
|
||||||
|
|
||||||
|
# set custom service for node
|
||||||
|
self.session.services.set_service(node_id, service_name)
|
||||||
|
|
||||||
|
# set custom values for custom service
|
||||||
|
service = self.session.services.get_service(node_id, service_name)
|
||||||
|
if not service:
|
||||||
|
raise ValueError("custom service(%s) for node(%s) does not exist", service_name, node_id)
|
||||||
|
|
||||||
|
values = ConfigShim.str_to_dict(values)
|
||||||
|
for name, value in values.iteritems():
|
||||||
|
ServiceShim.setvalue(service, name, value)
|
||||||
|
|
||||||
|
return replies
|
||||||
|
|
||||||
|
def handle_config_mobility(self, message_type, _):
|
||||||
|
if message_type == ConfigFlags.RESET:
|
||||||
|
self.session.mobility.reset()
|
||||||
|
|
||||||
|
def handle_config_mobility_models(self, message_type, config_data):
|
||||||
|
replies = []
|
||||||
|
node_id = config_data.node
|
||||||
|
object_name = config_data.object
|
||||||
|
interface_id = config_data.interface_number
|
||||||
|
values_str = config_data.data_values
|
||||||
|
|
||||||
|
if interface_id is not None:
|
||||||
|
node_id = node_id * 1000 + interface_id
|
||||||
|
|
||||||
|
logger.debug("received configure message for %s nodenum: %s", object_name, node_id)
|
||||||
|
if message_type == ConfigFlags.REQUEST:
|
||||||
|
logger.info("replying to configure request for model: %s", object_name)
|
||||||
|
typeflags = ConfigFlags.NONE.value
|
||||||
|
|
||||||
|
model_class = self.session.mobility.models.get(object_name)
|
||||||
|
if not model_class:
|
||||||
|
logger.warn("model class does not exist: %s", object_name)
|
||||||
|
return []
|
||||||
|
|
||||||
|
config = self.session.mobility.get_model_config(node_id, object_name)
|
||||||
|
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
|
||||||
|
replies.append(config_response)
|
||||||
|
elif message_type != ConfigFlags.RESET:
|
||||||
|
# store the configuration values for later use, when the node
|
||||||
|
if not object_name:
|
||||||
|
logger.warn("no configuration object for node: %s", node_id)
|
||||||
|
return []
|
||||||
|
|
||||||
|
parsed_config = {}
|
||||||
|
if values_str:
|
||||||
|
parsed_config = ConfigShim.str_to_dict(values_str)
|
||||||
|
|
||||||
|
self.session.mobility.set_model_config(node_id, object_name, parsed_config)
|
||||||
|
|
||||||
|
return replies
|
||||||
|
|
||||||
|
def handle_config_emane(self, message_type, config_data):
|
||||||
|
replies = []
|
||||||
|
node_id = config_data.node
|
||||||
|
object_name = config_data.object
|
||||||
|
interface_id = config_data.interface_number
|
||||||
|
values_str = config_data.data_values
|
||||||
|
|
||||||
|
if interface_id is not None:
|
||||||
|
node_id = node_id * 1000 + interface_id
|
||||||
|
|
||||||
|
logger.debug("received configure message for %s nodenum: %s", object_name, node_id)
|
||||||
|
if message_type == ConfigFlags.REQUEST:
|
||||||
|
logger.info("replying to configure request for %s model", object_name)
|
||||||
|
typeflags = ConfigFlags.NONE.value
|
||||||
|
config = self.session.emane.get_configs()
|
||||||
|
config_response = ConfigShim.config_data(0, node_id, typeflags, self.session.emane.emane_config, config)
|
||||||
|
replies.append(config_response)
|
||||||
|
elif message_type != ConfigFlags.RESET:
|
||||||
|
if not object_name:
|
||||||
|
logger.info("no configuration object for node %s", node_id)
|
||||||
|
return []
|
||||||
|
|
||||||
|
if values_str:
|
||||||
|
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):
|
||||||
|
replies = []
|
||||||
|
node_id = config_data.node
|
||||||
|
object_name = config_data.object
|
||||||
|
interface_id = config_data.interface_number
|
||||||
|
values_str = config_data.data_values
|
||||||
|
|
||||||
|
if interface_id is not None:
|
||||||
|
node_id = node_id * 1000 + interface_id
|
||||||
|
|
||||||
|
logger.debug("received configure message for %s nodenum: %s", object_name, node_id)
|
||||||
|
if message_type == ConfigFlags.REQUEST:
|
||||||
|
logger.info("replying to configure request for model: %s", object_name)
|
||||||
|
typeflags = ConfigFlags.NONE.value
|
||||||
|
|
||||||
|
model_class = self.session.emane.models.get(object_name)
|
||||||
|
if not model_class:
|
||||||
|
logger.warn("model class does not exist: %s", object_name)
|
||||||
|
return []
|
||||||
|
|
||||||
|
config = self.session.emane.get_model_config(node_id, object_name)
|
||||||
|
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
|
||||||
|
replies.append(config_response)
|
||||||
|
elif message_type != ConfigFlags.RESET:
|
||||||
|
# store the configuration values for later use, when the node
|
||||||
|
if not object_name:
|
||||||
|
logger.warn("no configuration object for node: %s", node_id)
|
||||||
|
return []
|
||||||
|
|
||||||
|
parsed_config = {}
|
||||||
|
if values_str:
|
||||||
|
parsed_config = ConfigShim.str_to_dict(values_str)
|
||||||
|
|
||||||
|
self.session.emane.set_model_config(node_id, object_name, parsed_config)
|
||||||
|
|
||||||
|
return replies
|
||||||
|
|
||||||
def handle_file_message(self, message):
|
def handle_file_message(self, message):
|
||||||
"""
|
"""
|
||||||
File Message handler
|
File Message handler
|
||||||
|
@ -958,7 +1330,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
:return: reply messages
|
:return: reply messages
|
||||||
"""
|
"""
|
||||||
if message.flags & MessageFlags.ADD.value:
|
if message.flags & MessageFlags.ADD.value:
|
||||||
node_num = message.get_tlv(FileTlvs.NUMBER.value)
|
node_num = message.get_tlv(FileTlvs.NODE.value)
|
||||||
file_name = message.get_tlv(FileTlvs.NAME.value)
|
file_name = message.get_tlv(FileTlvs.NAME.value)
|
||||||
file_type = message.get_tlv(FileTlvs.TYPE.value)
|
file_type = message.get_tlv(FileTlvs.TYPE.value)
|
||||||
source_name = message.get_tlv(FileTlvs.SOURCE_NAME.value)
|
source_name = message.get_tlv(FileTlvs.SOURCE_NAME.value)
|
||||||
|
@ -978,7 +1350,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
if file_type is not None:
|
if file_type is not None:
|
||||||
if file_type.startswith("service:"):
|
if file_type.startswith("service:"):
|
||||||
_, service_name = file_type.split(':')[:2]
|
_, service_name = file_type.split(':')[:2]
|
||||||
self.session.add_node_service_file(node_num, service_name, file_name, source_name, data)
|
self.session.services.set_service_file(node_num, service_name, file_name, data)
|
||||||
return ()
|
return ()
|
||||||
elif file_type.startswith("hook:"):
|
elif file_type.startswith("hook:"):
|
||||||
_, state = file_type.split(':')[:2]
|
_, state = file_type.split(':')[:2]
|
||||||
|
@ -1084,7 +1456,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
# TODO: register system for event message handlers,
|
# TODO: register system for event message handlers,
|
||||||
# like confobjs
|
# like confobjs
|
||||||
if name.startswith("service:"):
|
if name.startswith("service:"):
|
||||||
self.session.services_event(event_data)
|
self.handle_service_event(event_data)
|
||||||
handled = True
|
handled = True
|
||||||
elif name.startswith("mobility:"):
|
elif name.startswith("mobility:"):
|
||||||
self.session.mobility_event(event_data)
|
self.session.mobility_event(event_data)
|
||||||
|
@ -1094,11 +1466,12 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
elif event_type == EventTypes.FILE_OPEN:
|
elif event_type == EventTypes.FILE_OPEN:
|
||||||
filename = event_data.name
|
filename = event_data.name
|
||||||
self.session.open_xml(filename, start=False)
|
self.session.open_xml(filename, start=False)
|
||||||
self.session.send_objects()
|
self.send_objects()
|
||||||
return ()
|
return ()
|
||||||
elif event_type == EventTypes.FILE_SAVE:
|
elif event_type == EventTypes.FILE_SAVE:
|
||||||
filename = event_data.name
|
filename = event_data.name
|
||||||
self.session.save_xml(filename, self.session.config["xmlfilever"])
|
xml_version = self.session.options.get_config("xmlfilever")
|
||||||
|
self.session.save_xml(filename, xml_version)
|
||||||
elif event_type == EventTypes.SCHEDULED:
|
elif event_type == EventTypes.SCHEDULED:
|
||||||
etime = event_data.time
|
etime = event_data.time
|
||||||
node = event_data.node
|
node = event_data.node
|
||||||
|
@ -1116,6 +1489,72 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
|
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
def handle_service_event(self, event_data):
|
||||||
|
"""
|
||||||
|
Handle an Event Message used to start, stop, restart, or validate
|
||||||
|
a service on a given node.
|
||||||
|
|
||||||
|
:param EventData event_data: event data to handle
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
event_type = event_data.event_type
|
||||||
|
node_id = event_data.node
|
||||||
|
name = event_data.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
node = self.session.get_object(node_id)
|
||||||
|
except KeyError:
|
||||||
|
logger.warn("ignoring event for service '%s', unknown node '%s'", name, node_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
fail = ""
|
||||||
|
unknown = []
|
||||||
|
services = ServiceShim.servicesfromopaque(name)
|
||||||
|
for service_name in services:
|
||||||
|
service = self.session.services.get_service(node_id, service_name, default_service=True)
|
||||||
|
if not service:
|
||||||
|
unknown.append(service_name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if event_type == EventTypes.STOP.value or event_type == EventTypes.RESTART.value:
|
||||||
|
status = self.session.services.stop_service(node, service)
|
||||||
|
if status:
|
||||||
|
fail += "Stop %s," % service.name
|
||||||
|
if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value:
|
||||||
|
status = self.session.services.startup_service(node, service)
|
||||||
|
if status:
|
||||||
|
fail += "Start %s(%s)," % service.name
|
||||||
|
if event_type == EventTypes.PAUSE.value:
|
||||||
|
status = self.session.services.validate_service(node, service)
|
||||||
|
if status:
|
||||||
|
fail += "%s," % service.name
|
||||||
|
if event_type == EventTypes.RECONFIGURE.value:
|
||||||
|
self.session.services.service_reconfigure(node, service)
|
||||||
|
|
||||||
|
fail_data = ""
|
||||||
|
if len(fail) > 0:
|
||||||
|
fail_data += "Fail:" + fail
|
||||||
|
unknown_data = ""
|
||||||
|
num = len(unknown)
|
||||||
|
if num > 0:
|
||||||
|
for u in unknown:
|
||||||
|
unknown_data += u
|
||||||
|
if num > 1:
|
||||||
|
unknown_data += ", "
|
||||||
|
num -= 1
|
||||||
|
logger.warn("Event requested for unknown service(s): %s", unknown_data)
|
||||||
|
unknown_data = "Unknown:" + unknown_data
|
||||||
|
|
||||||
|
event_data = EventData(
|
||||||
|
node=node_id,
|
||||||
|
event_type=event_type,
|
||||||
|
name=name,
|
||||||
|
data=fail_data + ";" + unknown_data,
|
||||||
|
time="%s" % time.time()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.session.broadcast_event(event_data)
|
||||||
|
|
||||||
def handle_session_message(self, message):
|
def handle_session_message(self, message):
|
||||||
"""
|
"""
|
||||||
Session Message handler
|
Session Message handler
|
||||||
|
@ -1196,7 +1635,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
self.session.set_user(user)
|
self.session.set_user(user)
|
||||||
|
|
||||||
if message.flags & MessageFlags.STRING.value:
|
if message.flags & MessageFlags.STRING.value:
|
||||||
self.session.send_objects()
|
self.send_objects()
|
||||||
elif message.flags & MessageFlags.DELETE.value:
|
elif message.flags & MessageFlags.DELETE.value:
|
||||||
# shut down the specified session(s)
|
# shut down the specified session(s)
|
||||||
logger.info("request to terminate session %s" % session_id)
|
logger.info("request to terminate session %s" % session_id)
|
||||||
|
@ -1225,3 +1664,105 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
logger.exception("error sending node emulation id message: %s", node_id)
|
logger.exception("error sending node emulation id message: %s", node_id)
|
||||||
|
|
||||||
del self.node_status_request[node_id]
|
del self.node_status_request[node_id]
|
||||||
|
|
||||||
|
def send_objects(self):
|
||||||
|
"""
|
||||||
|
Return API messages that describe the current session.
|
||||||
|
"""
|
||||||
|
# find all nodes and links
|
||||||
|
|
||||||
|
nodes_data = []
|
||||||
|
links_data = []
|
||||||
|
with self.session._objects_lock:
|
||||||
|
for obj in self.session.objects.itervalues():
|
||||||
|
node_data = obj.data(message_type=MessageFlags.ADD.value)
|
||||||
|
if node_data:
|
||||||
|
nodes_data.append(node_data)
|
||||||
|
|
||||||
|
node_links = obj.all_link_data(flags=MessageFlags.ADD.value)
|
||||||
|
for link_data in node_links:
|
||||||
|
links_data.append(link_data)
|
||||||
|
|
||||||
|
# send all nodes first, so that they will exist for any links
|
||||||
|
for node_data in nodes_data:
|
||||||
|
self.session.broadcast_node(node_data)
|
||||||
|
|
||||||
|
for link_data in links_data:
|
||||||
|
self.session.broadcast_link(link_data)
|
||||||
|
|
||||||
|
# send mobility model info
|
||||||
|
for node_id in self.session.mobility.nodes():
|
||||||
|
node = self.session.get_object(node_id)
|
||||||
|
for model_class, config in self.session.mobility.get_models(node):
|
||||||
|
logger.debug("mobility config: node(%s) class(%s) values(%s)", node_id, model_class, config)
|
||||||
|
config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config)
|
||||||
|
self.session.broadcast_config(config_data)
|
||||||
|
|
||||||
|
# send emane model info
|
||||||
|
for node_id in self.session.emane.nodes():
|
||||||
|
node = self.session.get_object(node_id)
|
||||||
|
for model_class, config in self.session.emane.get_models(node):
|
||||||
|
logger.debug("emane config: node(%s) class(%s) values(%s)", node_id, model_class, config)
|
||||||
|
config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config)
|
||||||
|
self.session.broadcast_config(config_data)
|
||||||
|
|
||||||
|
# service customizations
|
||||||
|
service_configs = self.session.services.all_configs()
|
||||||
|
for node_id, service in service_configs:
|
||||||
|
opaque = "service:%s" % service.name
|
||||||
|
data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(ServiceShim.keys)))
|
||||||
|
node = self.session.get_object(node_id)
|
||||||
|
values = ServiceShim.tovaluelist(node, service)
|
||||||
|
config_data = ConfigData(
|
||||||
|
message_type=0,
|
||||||
|
node=node_id,
|
||||||
|
object=self.session.services.name,
|
||||||
|
type=ConfigFlags.UPDATE.value,
|
||||||
|
data_types=data_types,
|
||||||
|
data_values=values,
|
||||||
|
session=str(self.session.session_id),
|
||||||
|
opaque=opaque
|
||||||
|
)
|
||||||
|
self.session.broadcast_config(config_data)
|
||||||
|
|
||||||
|
for file_name, config_data in self.session.services.all_files(service):
|
||||||
|
file_data = FileData(
|
||||||
|
message_type=MessageFlags.ADD.value,
|
||||||
|
node=node_id,
|
||||||
|
name=str(file_name),
|
||||||
|
type=opaque,
|
||||||
|
data=str(config_data)
|
||||||
|
)
|
||||||
|
self.session.broadcast_file(file_data)
|
||||||
|
|
||||||
|
# TODO: send location info
|
||||||
|
|
||||||
|
# send hook scripts
|
||||||
|
for state in sorted(self.session._hooks.keys()):
|
||||||
|
for file_name, config_data in self.session._hooks[state]:
|
||||||
|
file_data = FileData(
|
||||||
|
message_type=MessageFlags.ADD.value,
|
||||||
|
name=str(file_name),
|
||||||
|
type="hook:%s" % state,
|
||||||
|
data=str(config_data)
|
||||||
|
)
|
||||||
|
self.session.broadcast_file(file_data)
|
||||||
|
|
||||||
|
# send session configuration
|
||||||
|
session_config = self.session.options.get_configs()
|
||||||
|
config_data = ConfigShim.config_data(0, None, ConfigFlags.UPDATE.value, self.session.options, session_config)
|
||||||
|
self.session.broadcast_config(config_data)
|
||||||
|
|
||||||
|
# send session metadata
|
||||||
|
data_values = "|".join(["%s=%s" % item for item in self.session.metadata.get_configs().iteritems()])
|
||||||
|
data_types = tuple(ConfigDataTypes.STRING.value for _ in self.session.metadata.get_configs())
|
||||||
|
config_data = ConfigData(
|
||||||
|
message_type=0,
|
||||||
|
object=self.session.metadata.name,
|
||||||
|
type=ConfigFlags.NONE.value,
|
||||||
|
data_types=data_types,
|
||||||
|
data_values=data_values
|
||||||
|
)
|
||||||
|
self.session.broadcast_config(config_data)
|
||||||
|
|
||||||
|
logger.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data))
|
||||||
|
|
|
@ -218,7 +218,7 @@ class PyCoreObj(object):
|
||||||
if hasattr(self, "services") and len(self.services) != 0:
|
if hasattr(self, "services") and len(self.services) != 0:
|
||||||
nodeservices = []
|
nodeservices = []
|
||||||
for s in self.services:
|
for s in self.services:
|
||||||
nodeservices.append(s._name)
|
nodeservices.append(s.name)
|
||||||
services = "|".join(nodeservices)
|
services = "|".join(nodeservices)
|
||||||
|
|
||||||
node_data = NodeData(
|
node_data = NodeData(
|
||||||
|
@ -305,8 +305,8 @@ class PyCoreNode(PyCoreObj):
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
preserve = getattr(self.session.options, "preservedir", None)
|
preserve = self.session.options.get_config("preservedir") == "1"
|
||||||
if preserve == "1":
|
if preserve:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.tmpnodedir:
|
if self.tmpnodedir:
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
"""
|
"""
|
||||||
EMANE Bypass model for CORE
|
EMANE Bypass model for CORE
|
||||||
"""
|
"""
|
||||||
|
from core.conf import ConfigGroup
|
||||||
|
from core.conf import Configuration
|
||||||
from core.emane import emanemodel
|
from core.emane import emanemodel
|
||||||
from core.enumerations import ConfigDataTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
|
|
||||||
|
@ -15,13 +16,22 @@ class EmaneBypassModel(emanemodel.EmaneModel):
|
||||||
# mac definitions
|
# mac definitions
|
||||||
mac_library = "bypassmaclayer"
|
mac_library = "bypassmaclayer"
|
||||||
mac_config = [
|
mac_config = [
|
||||||
("none", ConfigDataTypes.BOOL.value, "0", "True,False",
|
Configuration(
|
||||||
"There are no parameters for the bypass model."),
|
_id="none",
|
||||||
|
_type=ConfigDataTypes.BOOL,
|
||||||
|
default="0",
|
||||||
|
options=["True", "False"],
|
||||||
|
label="There are no parameters for the bypass model."
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# phy definitions
|
# phy definitions
|
||||||
phy_library = "bypassphylayer"
|
phy_library = "bypassphylayer"
|
||||||
phy_config = []
|
phy_config = []
|
||||||
|
|
||||||
# override gui display tabs
|
# override config groups
|
||||||
config_groups_override = "Bypass Parameters:1-1"
|
@classmethod
|
||||||
|
def config_groups(cls):
|
||||||
|
return [
|
||||||
|
ConfigGroup("Bypass Parameters", 1, 1),
|
||||||
|
]
|
||||||
|
|
|
@ -2,9 +2,15 @@
|
||||||
commeffect.py: EMANE CommEffect model for CORE
|
commeffect.py: EMANE CommEffect model for CORE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
|
from core.conf import ConfigGroup
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emane import emanemodel
|
from core.emane import emanemodel
|
||||||
|
from core.xml import emanexml
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from emane.events.commeffectevent import CommEffectEvent
|
from emane.events.commeffectevent import CommEffectEvent
|
||||||
|
@ -35,58 +41,67 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
shim_defaults = {}
|
shim_defaults = {}
|
||||||
config_shim = emanemanifest.parse(shim_xml, shim_defaults)
|
config_shim = emanemanifest.parse(shim_xml, shim_defaults)
|
||||||
|
|
||||||
config_groups_override = "CommEffect SHIM Parameters:1-%d" % len(config_shim)
|
# comm effect does not need the default phy and external configurations
|
||||||
config_matrix_override = config_shim
|
phy_config = ()
|
||||||
|
external_config = ()
|
||||||
|
|
||||||
def build_xml_files(self, emane_manager, interface):
|
@classmethod
|
||||||
|
def configurations(cls):
|
||||||
|
return cls.config_shim
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def config_groups(cls):
|
||||||
|
return [
|
||||||
|
ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))
|
||||||
|
]
|
||||||
|
|
||||||
|
def build_xml_files(self, config, interface=None):
|
||||||
"""
|
"""
|
||||||
Build the necessary nem and commeffect XMLs in the given path.
|
Build the necessary nem and commeffect XMLs in the given path.
|
||||||
If an individual NEM has a nonstandard config, we need to build
|
If an individual NEM has a nonstandard config, we need to build
|
||||||
that file also. Otherwise the WLAN-wide
|
that file also. Otherwise the WLAN-wide
|
||||||
nXXemane_commeffectnem.xml, nXXemane_commeffectshim.xml are used.
|
nXXemane_commeffectnem.xml, nXXemane_commeffectshim.xml are used.
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
|
:param dict config: emane model configuration for the node and interface
|
||||||
:param interface: interface for the emane node
|
:param interface: interface for the emane node
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
values = emane_manager.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), interface)
|
|
||||||
if values is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
# retrieve xml names
|
# retrieve xml names
|
||||||
nem_name = self.nem_name(interface)
|
nem_name = emanexml.nem_file_name(self, interface)
|
||||||
shim_name = self.shim_name(interface)
|
shim_name = emanexml.shim_file_name(self, interface)
|
||||||
|
|
||||||
nem_document = emane_manager.xmldoc("nem")
|
# create and write nem document
|
||||||
nem_element = nem_document.getElementsByTagName("nem").pop()
|
nem_element = etree.Element("nem", name="%s NEM" % self.name, type="unstructured")
|
||||||
nem_element.setAttribute("name", "%s NEM" % self.name)
|
transport_type = "virtual"
|
||||||
nem_element.setAttribute("type", "unstructured")
|
if interface and interface.transport_type == "raw":
|
||||||
emane_manager.appendtransporttonem(nem_document, nem_element, self.object_id, interface)
|
transport_type = "raw"
|
||||||
|
transport_file = emanexml.transport_file_name(self.object_id, transport_type)
|
||||||
|
etree.SubElement(nem_element, "transport", definition=transport_file)
|
||||||
|
|
||||||
shim_xml = emane_manager.xmlshimdefinition(nem_document, shim_name)
|
# set shim configuration
|
||||||
nem_element.appendChild(shim_xml)
|
etree.SubElement(nem_element, "shim", definition=shim_name)
|
||||||
emane_manager.xmlwrite(nem_document, nem_name)
|
|
||||||
|
|
||||||
names = self.getnames()
|
nem_file = os.path.join(self.session.session_dir, nem_name)
|
||||||
shim_names = list(names)
|
emanexml.create_file(nem_element, "nem", nem_file)
|
||||||
shim_names.remove("filterfile")
|
|
||||||
|
|
||||||
shim_document = emane_manager.xmldoc("shim")
|
# create and write shim document
|
||||||
shim_element = shim_document.getElementsByTagName("shim").pop()
|
shim_element = etree.Element("shim", name="%s SHIM" % self.name, library=self.shim_library)
|
||||||
shim_element.setAttribute("name", "%s SHIM" % self.name)
|
|
||||||
shim_element.setAttribute("library", self.shim_library)
|
|
||||||
|
|
||||||
# append all shim options (except filterfile) to shimdoc
|
# append all shim options (except filterfile) to shimdoc
|
||||||
for name in shim_names:
|
for configuration in self.config_shim:
|
||||||
value = self.valueof(name, values)
|
name = configuration.id
|
||||||
param = emane_manager.xmlparam(shim_document, name, value)
|
if name == "filterfile":
|
||||||
shim_element.appendChild(param)
|
continue
|
||||||
|
value = config[name]
|
||||||
|
emanexml.add_param(shim_element, name, value)
|
||||||
|
|
||||||
# empty filterfile is not allowed
|
# empty filterfile is not allowed
|
||||||
ff = self.valueof("filterfile", values)
|
ff = config["filterfile"]
|
||||||
if ff.strip() != "":
|
if ff.strip() != "":
|
||||||
shim_element.appendChild(emane_manager.xmlparam(shim_document, "filterfile", ff))
|
emanexml.add_param(shim_element, "filterfile", ff)
|
||||||
emane_manager.xmlwrite(shim_document, shim_name)
|
|
||||||
|
shim_file = os.path.join(self.session.session_dir, shim_name)
|
||||||
|
emanexml.create_file(shim_element, "shim", shim_file)
|
||||||
|
|
||||||
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None, jitter=None, netif2=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -4,13 +4,16 @@ emane.py: definition of an Emane class for implementing configuration control of
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
from xml.dom.minidom import parseString
|
|
||||||
|
|
||||||
from core import CoreCommandError
|
from core import CoreCommandError
|
||||||
from core import constants
|
from core import constants
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.conf import ConfigurableManager
|
from core.api import dataconversion
|
||||||
|
from core.conf import ConfigGroup
|
||||||
|
from core.conf import ConfigShim
|
||||||
|
from core.conf import Configuration
|
||||||
|
from core.conf import ModelManager
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emane.bypass import EmaneBypassModel
|
from core.emane.bypass import EmaneBypassModel
|
||||||
from core.emane.commeffect import EmaneCommEffectModel
|
from core.emane.commeffect import EmaneCommEffectModel
|
||||||
|
@ -18,16 +21,16 @@ from core.emane.emanemodel import EmaneModel
|
||||||
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
from core.emane.rfpipe import EmaneRfPipeModel
|
from core.emane.rfpipe import EmaneRfPipeModel
|
||||||
from core.emane.tdma import EmaneTdmaModel
|
from core.emane.tdma import EmaneTdmaModel
|
||||||
from core.enumerations import ConfigDataTypes, NodeTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
from core.enumerations import ConfigFlags
|
from core.enumerations import ConfigFlags
|
||||||
from core.enumerations import ConfigTlvs
|
from core.enumerations import ConfigTlvs
|
||||||
from core.enumerations import MessageFlags
|
from core.enumerations import MessageFlags
|
||||||
from core.enumerations import MessageTypes
|
from core.enumerations import MessageTypes
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
from core.enumerations import RegisterTlvs
|
from core.enumerations import RegisterTlvs
|
||||||
from core.misc import nodeutils
|
from core.misc import nodeutils
|
||||||
from core.misc import utils
|
from core.misc import utils
|
||||||
from core.misc.ipaddress import MacAddress
|
from core.xml import emanexml
|
||||||
from core.xml import xmlutils
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from emane.events import EventService
|
from emane.events import EventService
|
||||||
|
@ -50,7 +53,7 @@ EMANE_MODELS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class EmaneManager(ConfigurableManager):
|
class EmaneManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
EMANE controller object. Lives in a Session instance and is used for
|
EMANE controller object. Lives in a Session instance and is used for
|
||||||
building EMANE config files from all of the EmaneNode objects in this
|
building EMANE config files from all of the EmaneNode objects in this
|
||||||
|
@ -58,7 +61,6 @@ class EmaneManager(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
name = "emane"
|
name = "emane"
|
||||||
config_type = RegisterTlvs.EMULATION_SERVER.value
|
config_type = RegisterTlvs.EMULATION_SERVER.value
|
||||||
_hwaddr_prefix = "02:02"
|
|
||||||
SUCCESS, NOT_NEEDED, NOT_READY = (0, 1, 2)
|
SUCCESS, NOT_NEEDED, NOT_READY = (0, 1, 2)
|
||||||
EVENTCFGVAR = "LIBEMANEEVENTSERVICECONFIG"
|
EVENTCFGVAR = "LIBEMANEEVENTSERVICECONFIG"
|
||||||
DEFAULT_LOG_LEVEL = 3
|
DEFAULT_LOG_LEVEL = 3
|
||||||
|
@ -70,30 +72,72 @@ class EmaneManager(ConfigurableManager):
|
||||||
:param core.session.Session session: session this manager is tied to
|
:param core.session.Session session: session this manager is tied to
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
ConfigurableManager.__init__(self)
|
super(EmaneManager, self).__init__()
|
||||||
self.session = session
|
self.session = session
|
||||||
self._emane_nodes = {}
|
self._emane_nodes = {}
|
||||||
self._emane_node_lock = threading.Lock()
|
self._emane_node_lock = threading.Lock()
|
||||||
self._ifccounts = {}
|
self._ifccounts = {}
|
||||||
self._ifccountslock = threading.Lock()
|
self._ifccountslock = threading.Lock()
|
||||||
# Port numbers are allocated from these counters
|
# port numbers are allocated from these counters
|
||||||
self.platformport = self.session.get_config_item_int("emane_platform_port", 8100)
|
self.platformport = self.session.options.get_config_int("emane_platform_port", 8100)
|
||||||
self.transformport = self.session.get_config_item_int("emane_transform_port", 8200)
|
self.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
|
||||||
self.doeventloop = False
|
self.doeventloop = False
|
||||||
self.eventmonthread = None
|
self.eventmonthread = None
|
||||||
|
|
||||||
# model for global EMANE configuration options
|
# model for global EMANE configuration options
|
||||||
self.emane_config = EmaneGlobalModel(session, None)
|
self.emane_config = EmaneGlobalModel(session)
|
||||||
|
self.set_configs(self.emane_config.default_values())
|
||||||
|
|
||||||
session.broker.handlers.add(self.handledistributed)
|
session.broker.handlers.add(self.handledistributed)
|
||||||
self.service = None
|
self.service = None
|
||||||
self.event_device = None
|
self.event_device = None
|
||||||
self._modelclsmap = {
|
|
||||||
self.emane_config.name: self.emane_config
|
|
||||||
}
|
|
||||||
|
|
||||||
self.service = None
|
|
||||||
self.emane_check()
|
self.emane_check()
|
||||||
|
|
||||||
|
def getifcconfig(self, node_id, interface, model_name):
|
||||||
|
"""
|
||||||
|
Retrieve interface configuration or node configuration if not provided.
|
||||||
|
|
||||||
|
:param int node_id: node id
|
||||||
|
:param interface: node interface
|
||||||
|
:param str model_name: model to get configuration for
|
||||||
|
:return: node/interface model configuration
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
# use the network-wide config values or interface(NEM)-specific values?
|
||||||
|
if interface is None:
|
||||||
|
return self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
|
else:
|
||||||
|
# don"t use default values when interface config is the same as net
|
||||||
|
# note here that using ifc.node.objid as key allows for only one type
|
||||||
|
# of each model per node;
|
||||||
|
# TODO: use both node and interface as key
|
||||||
|
|
||||||
|
# Adamson change: first check for iface config keyed by "node:ifc.name"
|
||||||
|
# (so that nodes w/ multiple interfaces of same conftype can have
|
||||||
|
# different configs for each separate interface)
|
||||||
|
key = 1000 * interface.node.objid
|
||||||
|
if interface.netindex is not None:
|
||||||
|
key += interface.netindex
|
||||||
|
|
||||||
|
# try retrieve interface specific configuration, avoid getting defaults
|
||||||
|
config = self.get_configs(node_id=key, config_type=model_name)
|
||||||
|
|
||||||
|
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
||||||
|
if not config:
|
||||||
|
config = self.get_configs(node_id=interface.node.objid, config_type=model_name)
|
||||||
|
|
||||||
|
# get non interface config, when none found
|
||||||
|
if not config:
|
||||||
|
# with EMANE 0.9.2+, we need an extra NEM XML from
|
||||||
|
# model.buildnemxmlfiles(), so defaults are returned here
|
||||||
|
config = self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
def config_reset(self, node_id=None):
|
||||||
|
super(EmaneManager, self).config_reset(node_id)
|
||||||
|
self.set_configs(self.emane_config.default_values())
|
||||||
|
|
||||||
def emane_check(self):
|
def emane_check(self):
|
||||||
"""
|
"""
|
||||||
Check if emane is installed and load models.
|
Check if emane is installed and load models.
|
||||||
|
@ -109,7 +153,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
self.load_models(EMANE_MODELS)
|
self.load_models(EMANE_MODELS)
|
||||||
|
|
||||||
# load custom models
|
# load custom models
|
||||||
custom_models_path = self.session.config.get("emane_models_dir")
|
custom_models_path = self.session.options.get_config("emane_models_dir")
|
||||||
if custom_models_path:
|
if custom_models_path:
|
||||||
emane_models = utils.load_classes(custom_models_path, EmaneModel)
|
emane_models = utils.load_classes(custom_models_path, EmaneModel)
|
||||||
self.load_models(emane_models)
|
self.load_models(emane_models)
|
||||||
|
@ -138,9 +182,8 @@ class EmaneManager(ConfigurableManager):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get the control network to be used for events
|
# Get the control network to be used for events
|
||||||
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
|
group, port = self.get_config("eventservicegroup").split(":")
|
||||||
group, port = self.emane_config.valueof("eventservicegroup", values).split(":")
|
self.event_device = self.get_config("eventservicedevice")
|
||||||
self.event_device = self.emane_config.valueof("eventservicedevice", values)
|
|
||||||
eventnetidx = self.session.get_control_net_index(self.event_device)
|
eventnetidx = self.session.get_control_net_index(self.event_device)
|
||||||
if eventnetidx < 0:
|
if eventnetidx < 0:
|
||||||
logger.error("invalid emane event service device provided: %s", self.event_device)
|
logger.error("invalid emane event service device provided: %s", self.event_device)
|
||||||
|
@ -165,12 +208,11 @@ class EmaneManager(ConfigurableManager):
|
||||||
|
|
||||||
def load_models(self, emane_models):
|
def load_models(self, emane_models):
|
||||||
"""
|
"""
|
||||||
load EMANE models and make them available.
|
Load EMANE models and make them available.
|
||||||
"""
|
"""
|
||||||
for emane_model in emane_models:
|
for emane_model in emane_models:
|
||||||
logger.info("loading emane model: %s", emane_model.__name__)
|
logger.info("loading emane model: %s", emane_model.__name__)
|
||||||
self._modelclsmap[emane_model.name] = emane_model
|
self.models[emane_model.name] = emane_model
|
||||||
self.session.add_config_object(emane_model.name, emane_model.config_type, emane_model.configure_emane)
|
|
||||||
|
|
||||||
def add_node(self, emane_node):
|
def add_node(self, emane_node):
|
||||||
"""
|
"""
|
||||||
|
@ -196,50 +238,6 @@ class EmaneManager(ConfigurableManager):
|
||||||
nodes.add(netif.node)
|
nodes.add(netif.node)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def getmodels(self, n):
|
|
||||||
"""
|
|
||||||
Used with XML export; see ConfigurableManager.getmodels()
|
|
||||||
"""
|
|
||||||
r = ConfigurableManager.getmodels(self, n)
|
|
||||||
# EMANE global params are stored with first EMANE node (if non-default
|
|
||||||
# values are configured)
|
|
||||||
sorted_ids = sorted(self.configs.keys())
|
|
||||||
if None in self.configs and len(sorted_ids) > 1 and n.objid == sorted_ids[1]:
|
|
||||||
v = self.configs[None]
|
|
||||||
for model in v:
|
|
||||||
cls = self._modelclsmap[model[0]]
|
|
||||||
vals = model[1]
|
|
||||||
r.append((cls, vals))
|
|
||||||
return r
|
|
||||||
|
|
||||||
def getifcconfig(self, nodenum, conftype, defaultvalues, ifc):
|
|
||||||
# use the network-wide config values or interface(NEM)-specific values?
|
|
||||||
if ifc is None:
|
|
||||||
return self.getconfig(nodenum, conftype, defaultvalues)[1]
|
|
||||||
else:
|
|
||||||
# don"t use default values when interface config is the same as net
|
|
||||||
# note here that using ifc.node.objid as key allows for only one type
|
|
||||||
# of each model per node;
|
|
||||||
# TODO: use both node and interface as key
|
|
||||||
|
|
||||||
# Adamson change: first check for iface config keyed by "node:ifc.name"
|
|
||||||
# (so that nodes w/ multiple interfaces of same conftype can have
|
|
||||||
# different configs for each separate interface)
|
|
||||||
key = 1000 * ifc.node.objid
|
|
||||||
if ifc.netindex is not None:
|
|
||||||
key += ifc.netindex
|
|
||||||
|
|
||||||
values = self.getconfig(key, conftype, None)[1]
|
|
||||||
if not values:
|
|
||||||
values = self.getconfig(ifc.node.objid, conftype, None)[1]
|
|
||||||
|
|
||||||
if not values and ifc.transport_type == "raw":
|
|
||||||
# with EMANE 0.9.2+, we need an extra NEM XML from
|
|
||||||
# model.buildnemxmlfiles(), so defaults are returned here
|
|
||||||
values = self.getconfig(nodenum, conftype, defaultvalues)[1]
|
|
||||||
|
|
||||||
return values
|
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
"""
|
"""
|
||||||
Populate self._objs with EmaneNodes; perform distributed setup;
|
Populate self._objs with EmaneNodes; perform distributed setup;
|
||||||
|
@ -264,9 +262,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
# - needs to be configured before checkdistributed() for distributed
|
# - needs to be configured before checkdistributed() for distributed
|
||||||
# - needs to exist when eventservice binds to it (initeventservice)
|
# - needs to exist when eventservice binds to it (initeventservice)
|
||||||
if self.session.master:
|
if self.session.master:
|
||||||
values = self.getconfig(None, self.emane_config.name, self.emane_config.getdefaultvalues())[1]
|
otadev = self.get_config("otamanagerdevice")
|
||||||
logger.debug("emane config default values: %s", values)
|
|
||||||
otadev = self.emane_config.valueof("otamanagerdevice", values)
|
|
||||||
netidx = self.session.get_control_net_index(otadev)
|
netidx = self.session.get_control_net_index(otadev)
|
||||||
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
|
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
|
||||||
if netidx < 0:
|
if netidx < 0:
|
||||||
|
@ -275,7 +271,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
|
|
||||||
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
||||||
self.distributedctrlnet(ctrlnet)
|
self.distributedctrlnet(ctrlnet)
|
||||||
eventdev = self.emane_config.valueof("eventservicedevice", values)
|
eventdev = self.get_config("eventservicedevice")
|
||||||
logger.debug("emane event service device: eventdev(%s)", eventdev)
|
logger.debug("emane event service device: eventdev(%s)", eventdev)
|
||||||
if eventdev != otadev:
|
if eventdev != otadev:
|
||||||
netidx = self.session.get_control_net_index(eventdev)
|
netidx = self.session.get_control_net_index(eventdev)
|
||||||
|
@ -288,13 +284,14 @@ class EmaneManager(ConfigurableManager):
|
||||||
self.distributedctrlnet(ctrlnet)
|
self.distributedctrlnet(ctrlnet)
|
||||||
|
|
||||||
if self.checkdistributed():
|
if self.checkdistributed():
|
||||||
# we are slave, but haven"t received a platformid yet
|
# we are slave, but haven't received a platformid yet
|
||||||
cfgval = self.getconfig(None, self.emane_config.name, self.emane_config.getdefaultvalues())[1]
|
platform_id_start = "platform_id_start"
|
||||||
i = self.emane_config.getnames().index("platform_id_start")
|
default_values = self.emane_config.default_values()
|
||||||
if cfgval[i] == self.emane_config.getdefaultvalues()[i]:
|
value = self.get_config(platform_id_start)
|
||||||
|
if value == default_values[platform_id_start]:
|
||||||
return EmaneManager.NOT_READY
|
return EmaneManager.NOT_READY
|
||||||
|
|
||||||
self.setnodemodels()
|
self.check_node_models()
|
||||||
return EmaneManager.SUCCESS
|
return EmaneManager.SUCCESS
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
|
@ -318,7 +315,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
|
|
||||||
if self.numnems() > 0:
|
if self.numnems() > 0:
|
||||||
self.startdaemons()
|
self.startdaemons()
|
||||||
self.installnetifs(do_netns=False)
|
self.installnetifs()
|
||||||
|
|
||||||
for emane_node in self._emane_nodes.itervalues():
|
for emane_node in self._emane_nodes.itervalues():
|
||||||
for netif in emane_node.netifs():
|
for netif in emane_node.netifs():
|
||||||
|
@ -346,7 +343,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nodes.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
logger.debug("post startup for emane node: %s - %s", emane_node.objid, emane_node.name)
|
logger.debug("post startup for emane node: %s - %s", emane_node.objid, emane_node.name)
|
||||||
emane_node.model.post_startup(self)
|
emane_node.model.post_startup()
|
||||||
for netif in emane_node.netifs():
|
for netif in emane_node.netifs():
|
||||||
x, y, z = netif.node.position.get()
|
x, y, z = netif.node.position.get()
|
||||||
emane_node.setnemposition(netif, x, y, z)
|
emane_node.setnemposition(netif, x, y, z)
|
||||||
|
@ -359,9 +356,9 @@ class EmaneManager(ConfigurableManager):
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
self._emane_nodes.clear()
|
self._emane_nodes.clear()
|
||||||
|
|
||||||
# don"t clear self._ifccounts here; NEM counts are needed for buildxml
|
# don't clear self._ifccounts here; NEM counts are needed for buildxml
|
||||||
self.platformport = self.session.get_config_item_int("emane_platform_port", 8100)
|
self.platformport = self.session.options.get_config_int("emane_platform_port", 8100)
|
||||||
self.transformport = self.session.get_config_item_int("emane_transform_port", 8200)
|
self.transformport = self.session.options.get_config_int("emane_transform_port", 8200)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""
|
"""
|
||||||
|
@ -416,20 +413,16 @@ class EmaneManager(ConfigurableManager):
|
||||||
if not master:
|
if not master:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
cfgval = self.getconfig(None, self.emane_config.name, self.emane_config.getdefaultvalues())[1]
|
|
||||||
values = list(cfgval)
|
|
||||||
|
|
||||||
nemcount = 0
|
nemcount = 0
|
||||||
with self._emane_node_lock:
|
with self._emane_node_lock:
|
||||||
for key in self._emane_nodes:
|
for key in self._emane_nodes:
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
nemcount += emane_node.numnetif()
|
nemcount += emane_node.numnetif()
|
||||||
|
|
||||||
nemid = int(self.emane_config.valueof("nem_id_start", values))
|
nemid = int(self.get_config("nem_id_start"))
|
||||||
nemid += nemcount
|
nemid += nemcount
|
||||||
|
|
||||||
platformid = int(self.emane_config.valueof("platform_id_start", values))
|
platformid = int(self.get_config("platform_id_start"))
|
||||||
names = list(self.emane_config.getnames())
|
|
||||||
|
|
||||||
# build an ordered list of servers so platform ID is deterministic
|
# build an ordered list of servers so platform ID is deterministic
|
||||||
servers = []
|
servers = []
|
||||||
|
@ -448,10 +441,11 @@ class EmaneManager(ConfigurableManager):
|
||||||
|
|
||||||
platformid += 1
|
platformid += 1
|
||||||
typeflags = ConfigFlags.UPDATE.value
|
typeflags = ConfigFlags.UPDATE.value
|
||||||
values[names.index("platform_id_start")] = str(platformid)
|
self.set_config("platform_id_start", str(platformid))
|
||||||
values[names.index("nem_id_start")] = str(nemid)
|
self.set_config("nem_id_start", str(nemid))
|
||||||
msg = EmaneGlobalModel.config_data(flags=0, node_id=None, type_flags=typeflags, values=values)
|
config_data = ConfigShim.config_data(0, None, typeflags, self.emane_config, self.get_configs())
|
||||||
server.sock.send(msg)
|
message = dataconversion.convert_config(config_data)
|
||||||
|
server.sock.send(message)
|
||||||
# increment nemid for next server by number of interfaces
|
# increment nemid for next server by number of interfaces
|
||||||
with self._ifccountslock:
|
with self._ifccountslock:
|
||||||
if server in self._ifccounts:
|
if server in self._ifccounts:
|
||||||
|
@ -473,6 +467,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
self.buildnemxml()
|
self.buildnemxml()
|
||||||
self.buildeventservicexml()
|
self.buildeventservicexml()
|
||||||
|
|
||||||
|
# TODO: remove need for tlv messaging
|
||||||
def distributedctrlnet(self, ctrlnet):
|
def distributedctrlnet(self, ctrlnet):
|
||||||
"""
|
"""
|
||||||
Distributed EMANE requires multiple control network prefixes to
|
Distributed EMANE requires multiple control network prefixes to
|
||||||
|
@ -489,8 +484,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
if len(servers) < 2:
|
if len(servers) < 2:
|
||||||
return
|
return
|
||||||
|
|
||||||
prefix = session.config.get("controlnet")
|
prefix = session.options.get_config("controlnet")
|
||||||
prefix = getattr(session.options, "controlnet", prefix)
|
|
||||||
prefixes = prefix.split()
|
prefixes = prefix.split()
|
||||||
# normal Config messaging will distribute controlnets
|
# normal Config messaging will distribute controlnets
|
||||||
if len(prefixes) >= len(servers):
|
if len(prefixes) >= len(servers):
|
||||||
|
@ -509,76 +503,30 @@ class EmaneManager(ConfigurableManager):
|
||||||
msg = coreapi.CoreConfMessage(flags=0, hdr=msghdr, data=rawmsg[coreapi.CoreMessage.header_len:])
|
msg = coreapi.CoreConfMessage(flags=0, hdr=msghdr, data=rawmsg[coreapi.CoreMessage.header_len:])
|
||||||
self.session.broker.handle_message(msg)
|
self.session.broker.handle_message(msg)
|
||||||
|
|
||||||
def xmldoc(self, doctype):
|
def check_node_models(self):
|
||||||
"""
|
|
||||||
Returns an XML xml.minidom.Document with a DOCTYPE tag set to the
|
|
||||||
provided doctype string, and an initial element having the same
|
|
||||||
name.
|
|
||||||
"""
|
|
||||||
# we hack in the DOCTYPE using the parser
|
|
||||||
docstr = """<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE %s SYSTEM "file:///usr/share/emane/dtd/%s.dtd">
|
|
||||||
<%s/>""" % (doctype, doctype, doctype)
|
|
||||||
# normally this would be: doc = Document()
|
|
||||||
return parseString(docstr)
|
|
||||||
|
|
||||||
def xmlparam(self, doc, name, value):
|
|
||||||
"""
|
|
||||||
Convenience function for building a parameter tag of the format:
|
|
||||||
<param name="name" value="value" />
|
|
||||||
"""
|
|
||||||
p = doc.createElement("param")
|
|
||||||
p.setAttribute("name", name)
|
|
||||||
p.setAttribute("value", value)
|
|
||||||
return p
|
|
||||||
|
|
||||||
def xmlshimdefinition(self, doc, name):
|
|
||||||
"""
|
|
||||||
Convenience function for building a definition tag of the format:
|
|
||||||
<shim definition="name" />
|
|
||||||
"""
|
|
||||||
p = doc.createElement("shim")
|
|
||||||
p.setAttribute("definition", name)
|
|
||||||
return p
|
|
||||||
|
|
||||||
def xmlwrite(self, doc, filename):
|
|
||||||
"""
|
|
||||||
Write the given XML document to the specified filename.
|
|
||||||
"""
|
|
||||||
pathname = os.path.join(self.session.session_dir, filename)
|
|
||||||
with open(pathname, "w") as xml_file:
|
|
||||||
doc.writexml(writer=xml_file, indent="", addindent=" ", newl="\n", encoding="UTF-8")
|
|
||||||
|
|
||||||
def setnodemodels(self):
|
|
||||||
"""
|
"""
|
||||||
Associate EmaneModel classes with EmaneNode nodes. The model
|
Associate EmaneModel classes with EmaneNode nodes. The model
|
||||||
configurations are stored in self.configs.
|
configurations are stored in self.configs.
|
||||||
"""
|
"""
|
||||||
for key in self._emane_nodes:
|
for node_id in self._emane_nodes:
|
||||||
self.setnodemodel(key)
|
emane_node = self._emane_nodes[node_id]
|
||||||
|
logger.debug("checking emane model for node: %s", node_id)
|
||||||
|
|
||||||
def setnodemodel(self, key):
|
# skip nodes that already have a model set
|
||||||
logger.debug("setting emane node model: %s", key)
|
if emane_node.model:
|
||||||
emane_node = self._emane_nodes[key]
|
logger.debug("node(%s) already has model(%s)", emane_node.objid, emane_node.model.name)
|
||||||
if key not in self.configs:
|
|
||||||
logger.debug("no emane node model configuration, leaving")
|
|
||||||
return False
|
|
||||||
|
|
||||||
for t, v in self.configs[key]:
|
|
||||||
logger.debug("configuration: key(%s) value(%s)", t, v)
|
|
||||||
if t is None:
|
|
||||||
continue
|
|
||||||
if t == self.emane_config.name:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# only use the first valid EmaneModel
|
# set model configured for node, due to legacy messaging configuration before nodes exist
|
||||||
# convert model name to class (e.g. emane_rfpipe -> EmaneRfPipe)
|
model_name = self.node_models.get(node_id)
|
||||||
cls = self._modelclsmap[t]
|
if not model_name:
|
||||||
emane_node.setmodel(cls, v)
|
logger.error("emane node(%s) has no node model", node_id)
|
||||||
return True
|
raise ValueError("emane node has no model set")
|
||||||
|
|
||||||
# no model has been configured for this EmaneNode
|
config = self.get_model_config(node_id=node_id, model_name=model_name)
|
||||||
return False
|
logger.debug("setting emane model(%s) config(%s)", model_name, config)
|
||||||
|
model_class = self.models[model_name]
|
||||||
|
emane_node.setmodel(model_class, config)
|
||||||
|
|
||||||
def nemlookup(self, nemid):
|
def nemlookup(self, nemid):
|
||||||
"""
|
"""
|
||||||
|
@ -588,8 +536,8 @@ class EmaneManager(ConfigurableManager):
|
||||||
emane_node = None
|
emane_node = None
|
||||||
netif = None
|
netif = None
|
||||||
|
|
||||||
for key in self._emane_nodes:
|
for node_id in self._emane_nodes:
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[node_id]
|
||||||
netif = emane_node.getnemnetif(nemid)
|
netif = emane_node.getnemnetif(nemid)
|
||||||
if netif is not None:
|
if netif is not None:
|
||||||
break
|
break
|
||||||
|
@ -607,75 +555,17 @@ class EmaneManager(ConfigurableManager):
|
||||||
count += len(emane_node.netifs())
|
count += len(emane_node.netifs())
|
||||||
return count
|
return count
|
||||||
|
|
||||||
def newplatformxmldoc(self, values, otadev=None, eventdev=None):
|
|
||||||
"""
|
|
||||||
Start a new platform XML file. Use global EMANE config values
|
|
||||||
as keys. Override OTA manager and event service devices if
|
|
||||||
specified (in order to support Raw Transport).
|
|
||||||
"""
|
|
||||||
doc = self.xmldoc("platform")
|
|
||||||
plat = doc.getElementsByTagName("platform").pop()
|
|
||||||
names = list(self.emane_config.getnames())
|
|
||||||
platform_names = names[:len(self.emane_config.emulator_config)]
|
|
||||||
platform_names.remove("platform_id_start")
|
|
||||||
platform_values = list(values)
|
|
||||||
if otadev:
|
|
||||||
i = platform_names.index("otamanagerdevice")
|
|
||||||
platform_values[i] = otadev
|
|
||||||
|
|
||||||
if eventdev:
|
|
||||||
i = platform_names.index("eventservicedevice")
|
|
||||||
platform_values[i] = eventdev
|
|
||||||
|
|
||||||
# append all platform options (except starting id) to doc
|
|
||||||
for name in platform_names:
|
|
||||||
value = self.emane_config.valueof(name, platform_values)
|
|
||||||
param = self.xmlparam(doc, name, value)
|
|
||||||
plat.appendChild(param)
|
|
||||||
|
|
||||||
return doc
|
|
||||||
|
|
||||||
def buildplatformxml(self, ctrlnet):
|
def buildplatformxml(self, ctrlnet):
|
||||||
"""
|
"""
|
||||||
Build a platform.xml file now that all nodes are configured.
|
Build a platform.xml file now that all nodes are configured.
|
||||||
"""
|
"""
|
||||||
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
|
nemid = int(self.get_config("nem_id_start"))
|
||||||
nemid = int(self.emane_config.valueof("nem_id_start", values))
|
platform_xmls = {}
|
||||||
platformxmls = {}
|
|
||||||
|
|
||||||
# assume self._objslock is already held here
|
# assume self._objslock is already held here
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nodes.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
nems = emane_node.buildplatformxmlentry(self.xmldoc("platform"))
|
nemid = emanexml.build_node_platform_xml(self, ctrlnet, emane_node, nemid, platform_xmls)
|
||||||
for netif in sorted(nems, key=lambda x: x.node.objid):
|
|
||||||
nementry = nems[netif]
|
|
||||||
nementry.setAttribute("id", "%d" % nemid)
|
|
||||||
key = netif.node.objid
|
|
||||||
if netif.transport_type == "raw":
|
|
||||||
key = "host"
|
|
||||||
otadev = ctrlnet.brname
|
|
||||||
eventdev = ctrlnet.brname
|
|
||||||
else:
|
|
||||||
otadev = None
|
|
||||||
eventdev = None
|
|
||||||
|
|
||||||
if key not in platformxmls:
|
|
||||||
platformxmls[key] = self.newplatformxmldoc(values, otadev, eventdev)
|
|
||||||
|
|
||||||
doc = platformxmls[key]
|
|
||||||
plat = doc.getElementsByTagName("platform").pop()
|
|
||||||
plat.appendChild(nementry)
|
|
||||||
emane_node.setnemid(netif, nemid)
|
|
||||||
macstr = self._hwaddr_prefix + ":00:00:"
|
|
||||||
macstr += "%02X:%02X" % ((nemid >> 8) & 0xFF, nemid & 0xFF)
|
|
||||||
netif.sethwaddr(MacAddress.from_string(macstr))
|
|
||||||
nemid += 1
|
|
||||||
|
|
||||||
for key in sorted(platformxmls.keys()):
|
|
||||||
if key == "host":
|
|
||||||
self.xmlwrite(platformxmls["host"], "platform.xml")
|
|
||||||
continue
|
|
||||||
self.xmlwrite(platformxmls[key], "platform%d.xml" % key)
|
|
||||||
|
|
||||||
def buildnemxml(self):
|
def buildnemxml(self):
|
||||||
"""
|
"""
|
||||||
|
@ -684,23 +574,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nodes.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
emane_node.build_xml_files(self)
|
emanexml.build_xml_files(self, emane_node)
|
||||||
|
|
||||||
def appendtransporttonem(self, doc, nem, nodenum, ifc=None):
|
|
||||||
"""
|
|
||||||
Given a nem XML node and EMANE WLAN node number, append
|
|
||||||
a <transport/> tag to the NEM definition, required for using
|
|
||||||
EMANE"s internal transport.
|
|
||||||
"""
|
|
||||||
emane_node = self._emane_nodes[nodenum]
|
|
||||||
transtag = doc.createElement("transport")
|
|
||||||
transtypestr = "virtual"
|
|
||||||
|
|
||||||
if ifc and ifc.transport_type == "raw":
|
|
||||||
transtypestr = "raw"
|
|
||||||
|
|
||||||
transtag.setAttribute("definition", emane_node.transportxmlname(transtypestr))
|
|
||||||
nem.appendChild(transtag)
|
|
||||||
|
|
||||||
def buildtransportxml(self):
|
def buildtransportxml(self):
|
||||||
"""
|
"""
|
||||||
|
@ -713,13 +587,11 @@ class EmaneManager(ConfigurableManager):
|
||||||
Build the libemaneeventservice.xml file if event service options
|
Build the libemaneeventservice.xml file if event service options
|
||||||
were changed in the global config.
|
were changed in the global config.
|
||||||
"""
|
"""
|
||||||
defaults = self.emane_config.getdefaultvalues()
|
|
||||||
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
|
|
||||||
need_xml = False
|
need_xml = False
|
||||||
keys = ("eventservicegroup", "eventservicedevice")
|
default_values = self.emane_config.default_values()
|
||||||
for k in keys:
|
for name in ["eventservicegroup", "eventservicedevice"]:
|
||||||
a = self.emane_config.valueof(k, defaults)
|
a = default_values[name]
|
||||||
b = self.emane_config.valueof(k, values)
|
b = self.get_config(name)
|
||||||
if a != b:
|
if a != b:
|
||||||
need_xml = True
|
need_xml = True
|
||||||
|
|
||||||
|
@ -729,20 +601,14 @@ class EmaneManager(ConfigurableManager):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
group, port = self.emane_config.valueof("eventservicegroup", values).split(":")
|
group, port = self.get_config("eventservicegroup").split(":")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.exception("invalid eventservicegroup in EMANE config")
|
logger.exception("invalid eventservicegroup in EMANE config")
|
||||||
return
|
return
|
||||||
|
|
||||||
dev = self.emane_config.valueof("eventservicedevice", values)
|
dev = self.get_config("eventservicedevice")
|
||||||
doc = self.xmldoc("emaneeventmsgsvc")
|
|
||||||
es = doc.getElementsByTagName("emaneeventmsgsvc").pop()
|
emanexml.create_event_service_xml(group, port, dev, self.session.session_dir)
|
||||||
kvs = (("group", group), ("port", port), ("device", dev), ("mcloop", "1"), ("ttl", "32"))
|
|
||||||
xmlutils.add_text_elements_from_tuples(doc, es, kvs)
|
|
||||||
filename = "libemaneeventservice.xml"
|
|
||||||
self.xmlwrite(doc, filename)
|
|
||||||
pathname = os.path.join(self.session.session_dir, filename)
|
|
||||||
self.initeventservice(filename=pathname)
|
|
||||||
|
|
||||||
def startdaemons(self):
|
def startdaemons(self):
|
||||||
"""
|
"""
|
||||||
|
@ -751,8 +617,8 @@ class EmaneManager(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
logger.info("starting emane daemons...")
|
logger.info("starting emane daemons...")
|
||||||
loglevel = str(EmaneManager.DEFAULT_LOG_LEVEL)
|
loglevel = str(EmaneManager.DEFAULT_LOG_LEVEL)
|
||||||
cfgloglevel = self.session.get_config_item_int("emane_log_level")
|
cfgloglevel = self.session.options.get_config_int("emane_log_level")
|
||||||
realtime = self.session.get_config_item_bool("emane_realtime", True)
|
realtime = self.session.options.get_config_bool("emane_realtime", default=True)
|
||||||
if cfgloglevel:
|
if cfgloglevel:
|
||||||
logger.info("setting user-defined EMANE log level: %d", cfgloglevel)
|
logger.info("setting user-defined EMANE log level: %d", cfgloglevel)
|
||||||
loglevel = str(cfgloglevel)
|
loglevel = str(cfgloglevel)
|
||||||
|
@ -761,13 +627,12 @@ class EmaneManager(ConfigurableManager):
|
||||||
if realtime:
|
if realtime:
|
||||||
emanecmd += "-r",
|
emanecmd += "-r",
|
||||||
|
|
||||||
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
|
otagroup, otaport = self.get_config("otamanagergroup").split(":")
|
||||||
otagroup, otaport = self.emane_config.valueof("otamanagergroup", values).split(":")
|
otadev = self.get_config("otamanagerdevice")
|
||||||
otadev = self.emane_config.valueof("otamanagerdevice", values)
|
|
||||||
otanetidx = self.session.get_control_net_index(otadev)
|
otanetidx = self.session.get_control_net_index(otadev)
|
||||||
|
|
||||||
eventgroup, eventport = self.emane_config.valueof("eventservicegroup", values).split(":")
|
eventgroup, eventport = self.get_config("eventservicegroup").split(":")
|
||||||
eventdev = self.emane_config.valueof("eventservicedevice", values)
|
eventdev = self.get_config("eventservicedevice")
|
||||||
eventservicenetidx = self.session.get_control_net_index(eventdev)
|
eventservicenetidx = self.session.get_control_net_index(eventdev)
|
||||||
|
|
||||||
run_emane_on_host = False
|
run_emane_on_host = False
|
||||||
|
@ -799,8 +664,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
node.check_cmd(args)
|
node.check_cmd(args)
|
||||||
|
|
||||||
# start emane
|
# start emane
|
||||||
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n),
|
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n), os.path.join(path, "platform%d.xml" % n)]
|
||||||
os.path.join(path, "platform%d.xml" % n)]
|
|
||||||
output = node.check_cmd(args)
|
output = node.check_cmd(args)
|
||||||
logger.info("node(%s) emane daemon running: %s", node.name, args)
|
logger.info("node(%s) emane daemon running: %s", node.name, args)
|
||||||
logger.info("node(%s) emane daemon output: %s", node.name, output)
|
logger.info("node(%s) emane daemon output: %s", node.name, output)
|
||||||
|
@ -837,7 +701,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
except CoreCommandError:
|
except CoreCommandError:
|
||||||
logger.exception("error shutting down emane daemons")
|
logger.exception("error shutting down emane daemons")
|
||||||
|
|
||||||
def installnetifs(self, do_netns=True):
|
def installnetifs(self):
|
||||||
"""
|
"""
|
||||||
Install TUN/TAP virtual interfaces into their proper namespaces
|
Install TUN/TAP virtual interfaces into their proper namespaces
|
||||||
now that the EMANE daemons are running.
|
now that the EMANE daemons are running.
|
||||||
|
@ -845,7 +709,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
for key in sorted(self._emane_nodes.keys()):
|
for key in sorted(self._emane_nodes.keys()):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
logger.info("emane install netifs for node: %d", key)
|
logger.info("emane install netifs for node: %d", key)
|
||||||
emane_node.installnetifs(do_netns)
|
emane_node.installnetifs()
|
||||||
|
|
||||||
def deinstallnetifs(self):
|
def deinstallnetifs(self):
|
||||||
"""
|
"""
|
||||||
|
@ -855,30 +719,13 @@ class EmaneManager(ConfigurableManager):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
emane_node.deinstallnetifs()
|
emane_node.deinstallnetifs()
|
||||||
|
|
||||||
def configure(self, session, config_data):
|
|
||||||
"""
|
|
||||||
Handle configuration messages for global EMANE config.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
"""
|
|
||||||
r = self.emane_config.configure_emane(session, config_data)
|
|
||||||
|
|
||||||
# extra logic to start slave Emane object after nemid has been configured from the master
|
|
||||||
config_type = config_data.type
|
|
||||||
if config_type == ConfigFlags.UPDATE.value and self.session.master is False:
|
|
||||||
# instantiation was previously delayed by self.setup()
|
|
||||||
# returning Emane.NOT_READY
|
|
||||||
self.session.instantiate()
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
def doeventmonitor(self):
|
def doeventmonitor(self):
|
||||||
"""
|
"""
|
||||||
Returns boolean whether or not EMANE events will be monitored.
|
Returns boolean whether or not EMANE events will be monitored.
|
||||||
"""
|
"""
|
||||||
# this support must be explicitly turned on; by default, CORE will
|
# this support must be explicitly turned on; by default, CORE will
|
||||||
# generate the EMANE events when nodes are moved
|
# generate the EMANE events when nodes are moved
|
||||||
return self.session.get_config_item_bool("emane_event_monitor", False)
|
return self.session.options.get_config_bool("emane_event_monitor")
|
||||||
|
|
||||||
def genlocationevents(self):
|
def genlocationevents(self):
|
||||||
"""
|
"""
|
||||||
|
@ -886,7 +733,7 @@ class EmaneManager(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
# By default, CORE generates EMANE location events when nodes
|
# By default, CORE generates EMANE location events when nodes
|
||||||
# are moved; this can be explicitly disabled in core.conf
|
# are moved; this can be explicitly disabled in core.conf
|
||||||
tmp = self.session.get_config_item_bool("emane_event_generate")
|
tmp = self.session.options.get_config_bool("emane_event_generate")
|
||||||
if tmp is None:
|
if tmp is None:
|
||||||
tmp = not self.doeventmonitor()
|
tmp = not self.doeventmonitor()
|
||||||
return tmp
|
return tmp
|
||||||
|
@ -900,11 +747,10 @@ class EmaneManager(ConfigurableManager):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.service is None:
|
if self.service is None:
|
||||||
errmsg = "Warning: EMANE events will not be generated " \
|
logger.error("Warning: EMANE events will not be generated "
|
||||||
"because the emaneeventservice\n binding was " \
|
"because the emaneeventservice\n binding was "
|
||||||
"unable to load " \
|
"unable to load "
|
||||||
"(install the python-emaneeventservice bindings)"
|
"(install the python-emaneeventservice bindings)")
|
||||||
logger.error(errmsg)
|
|
||||||
return
|
return
|
||||||
self.doeventloop = True
|
self.doeventloop = True
|
||||||
self.eventmonthread = threading.Thread(target=self.eventmonitorloop)
|
self.eventmonthread = threading.Thread(target=self.eventmonitorloop)
|
||||||
|
@ -1019,6 +865,7 @@ class EmaneGlobalModel(EmaneModel):
|
||||||
"""
|
"""
|
||||||
Global EMANE configuration options.
|
Global EMANE configuration options.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_DEFAULT_DEV = "ctrl0"
|
_DEFAULT_DEV = "ctrl0"
|
||||||
|
|
||||||
name = "emane"
|
name = "emane"
|
||||||
|
@ -1033,22 +880,30 @@ class EmaneGlobalModel(EmaneModel):
|
||||||
emulator_config = emanemanifest.parse(emulator_xml, emulator_defaults)
|
emulator_config = emanemanifest.parse(emulator_xml, emulator_defaults)
|
||||||
emulator_config.insert(
|
emulator_config.insert(
|
||||||
0,
|
0,
|
||||||
("platform_id_start", ConfigDataTypes.INT32.value, "1", "", "Starting Platform ID (core)")
|
Configuration(_id="platform_id_start", _type=ConfigDataTypes.INT32, default="1",
|
||||||
|
label="Starting Platform ID (core)")
|
||||||
)
|
)
|
||||||
|
|
||||||
nem_config = [
|
nem_config = [
|
||||||
("nem_id_start", ConfigDataTypes.INT32.value, "1", "", "Starting NEM ID (core)"),
|
Configuration(_id="nem_id_start", _type=ConfigDataTypes.INT32, default="1",
|
||||||
|
label="Starting NEM ID (core)")
|
||||||
]
|
]
|
||||||
|
|
||||||
config_matrix_override = emulator_config + nem_config
|
@classmethod
|
||||||
config_groups_override = "Platform Attributes:1-%d|NEM Parameters:%d-%d" % (
|
def configurations(cls):
|
||||||
len(emulator_config), len(emulator_config) + 1, len(config_matrix_override))
|
return cls.emulator_config + cls.nem_config
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def config_groups(cls):
|
||||||
|
emulator_len = len(cls.emulator_config)
|
||||||
|
config_len = len(cls.configurations())
|
||||||
|
return [
|
||||||
|
ConfigGroup("Platform Attributes", 1, emulator_len),
|
||||||
|
ConfigGroup("NEM Parameters", emulator_len + 1, config_len)
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, session, object_id=None):
|
def __init__(self, session, object_id=None):
|
||||||
EmaneModel.__init__(self, session, object_id)
|
super(EmaneGlobalModel, self).__init__(session, object_id)
|
||||||
|
|
||||||
def build_xml_files(self, emane_manager, interface):
|
def build_xml_files(self, config, interface=None):
|
||||||
"""
|
|
||||||
Build the necessary nem, mac, and phy XMLs in the given path.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from core import logger
|
from core import logger
|
||||||
|
from core.conf import Configuration
|
||||||
from core.enumerations import ConfigDataTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
|
|
||||||
manifest = None
|
manifest = None
|
||||||
|
@ -23,7 +24,7 @@ def _type_value(config_type):
|
||||||
config_type = "FLOAT"
|
config_type = "FLOAT"
|
||||||
elif config_type == "INETADDR":
|
elif config_type == "INETADDR":
|
||||||
config_type = "STRING"
|
config_type = "STRING"
|
||||||
return ConfigDataTypes[config_type].value
|
return ConfigDataTypes[config_type]
|
||||||
|
|
||||||
|
|
||||||
def _get_possible(config_type, config_regex):
|
def _get_possible(config_type, config_regex):
|
||||||
|
@ -33,17 +34,16 @@ def _get_possible(config_type, config_regex):
|
||||||
:param str config_type: emane configuration type
|
:param str config_type: emane configuration type
|
||||||
:param str config_regex: emane configuration regex
|
:param str config_regex: emane configuration regex
|
||||||
:return: a string listing comma delimited values, if needed, empty string otherwise
|
:return: a string listing comma delimited values, if needed, empty string otherwise
|
||||||
:rtype: str
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
if config_type == "bool":
|
if config_type == "bool":
|
||||||
return "On,Off"
|
return ["On", "Off"]
|
||||||
|
|
||||||
if config_type == "string" and config_regex:
|
if config_type == "string" and config_regex:
|
||||||
possible = config_regex[2:-2]
|
possible = config_regex[2:-2]
|
||||||
possible = possible.replace("|", ",")
|
return possible.split("|")
|
||||||
return possible
|
|
||||||
|
|
||||||
return ""
|
return []
|
||||||
|
|
||||||
|
|
||||||
def _get_default(config_type_name, config_value):
|
def _get_default(config_type_name, config_value):
|
||||||
|
@ -116,7 +116,13 @@ def parse(manifest_path, defaults):
|
||||||
if config_name.endswith("uri"):
|
if config_name.endswith("uri"):
|
||||||
config_descriptions = "%s file" % config_descriptions
|
config_descriptions = "%s file" % config_descriptions
|
||||||
|
|
||||||
config_tuple = (config_name, config_type_value, config_default, possible, config_descriptions)
|
configuration = Configuration(
|
||||||
configurations.append(config_tuple)
|
_id=config_name,
|
||||||
|
_type=config_type_value,
|
||||||
|
default=config_default,
|
||||||
|
options=possible,
|
||||||
|
label=config_descriptions
|
||||||
|
)
|
||||||
|
configurations.append(configuration)
|
||||||
|
|
||||||
return configurations
|
return configurations
|
||||||
|
|
|
@ -1,74 +1,15 @@
|
||||||
"""
|
"""
|
||||||
Defines Emane Models used within CORE.
|
Defines Emane Models used within CORE.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
|
from core.conf import ConfigGroup
|
||||||
|
from core.conf import Configuration
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.misc import utils
|
from core.enumerations import ConfigDataTypes
|
||||||
from core.mobility import WirelessModel
|
from core.mobility import WirelessModel
|
||||||
from core.xml import xmlutils
|
from core.xml import emanexml
|
||||||
|
|
||||||
|
|
||||||
def value_to_params(doc, name, value):
|
|
||||||
"""
|
|
||||||
Helper to convert a parameter to a paramlist. Returns an XML paramlist, or None if the value does not expand to
|
|
||||||
multiple values.
|
|
||||||
|
|
||||||
:param xml.dom.minidom.Document doc: xml document
|
|
||||||
:param name: name of element for params
|
|
||||||
:param str value: value string to convert to tuple
|
|
||||||
:return: xml document with added params or None, when an invalid value has been provided
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
values = utils.make_tuple_fromstr(value, str)
|
|
||||||
except SyntaxError:
|
|
||||||
logger.exception("error in value string to param list")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not hasattr(values, "__iter__"):
|
|
||||||
return None
|
|
||||||
|
|
||||||
if len(values) < 2:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return xmlutils.add_param_list_to_parent(doc, parent=None, name=name, values=values)
|
|
||||||
|
|
||||||
|
|
||||||
class EmaneModelMetaClass(type):
|
|
||||||
"""
|
|
||||||
Hack into making class level properties to streamline emane model creation, until the Configurable class is
|
|
||||||
removed or refactored.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def config_matrix(cls):
|
|
||||||
"""
|
|
||||||
Convenience method for creating the config matrix, allow for a custom override.
|
|
||||||
|
|
||||||
:param EmaneModel cls: emane class
|
|
||||||
:return: config matrix value
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
if cls.config_matrix_override:
|
|
||||||
return cls.config_matrix_override
|
|
||||||
else:
|
|
||||||
return cls.mac_config + cls.phy_config
|
|
||||||
|
|
||||||
@property
|
|
||||||
def config_groups(cls):
|
|
||||||
"""
|
|
||||||
Convenience method for creating the config groups, allow for a custom override.
|
|
||||||
|
|
||||||
:param EmaneModel cls: emane class
|
|
||||||
:return: config groups value
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
if cls.config_groups_override:
|
|
||||||
return cls.config_groups_override
|
|
||||||
else:
|
|
||||||
mac_len = len(cls.mac_config)
|
|
||||||
config_len = len(cls.config_matrix)
|
|
||||||
return "MAC Parameters:1-%d|PHY Parameters:%d-%d" % (mac_len, mac_len + 1, config_len)
|
|
||||||
|
|
||||||
|
|
||||||
class EmaneModel(WirelessModel):
|
class EmaneModel(WirelessModel):
|
||||||
|
@ -77,8 +18,6 @@ class EmaneModel(WirelessModel):
|
||||||
handling configuration messages based on the list of
|
handling configuration messages based on the list of
|
||||||
configurable parameters. Helper functions also live here.
|
configurable parameters. Helper functions also live here.
|
||||||
"""
|
"""
|
||||||
__metaclass__ = EmaneModelMetaClass
|
|
||||||
|
|
||||||
# default mac configuration settings
|
# default mac configuration settings
|
||||||
mac_library = None
|
mac_library = None
|
||||||
mac_xml = None
|
mac_xml = None
|
||||||
|
@ -95,277 +34,69 @@ class EmaneModel(WirelessModel):
|
||||||
}
|
}
|
||||||
phy_config = emanemanifest.parse(phy_xml, phy_defaults)
|
phy_config = emanemanifest.parse(phy_xml, phy_defaults)
|
||||||
|
|
||||||
|
# support for external configurations
|
||||||
|
external_config = [
|
||||||
|
Configuration("external", ConfigDataTypes.BOOL, default="0"),
|
||||||
|
Configuration("platformendpoint", ConfigDataTypes.STRING, default="127.0.0.1:40001"),
|
||||||
|
Configuration("transportendpoint", ConfigDataTypes.STRING, default="127.0.0.1:50002")
|
||||||
|
]
|
||||||
|
|
||||||
config_ignore = set()
|
config_ignore = set()
|
||||||
config_groups_override = None
|
|
||||||
config_matrix_override = None
|
|
||||||
|
|
||||||
def __init__(self, session, object_id=None):
|
@classmethod
|
||||||
WirelessModel.__init__(self, session, object_id)
|
def configurations(cls):
|
||||||
|
return cls.mac_config + cls.phy_config + cls.external_config
|
||||||
|
|
||||||
def build_xml_files(self, emane_manager, interface):
|
@classmethod
|
||||||
|
def config_groups(cls):
|
||||||
|
mac_len = len(cls.mac_config)
|
||||||
|
phy_len = len(cls.phy_config) + mac_len
|
||||||
|
config_len = len(cls.configurations())
|
||||||
|
return [
|
||||||
|
ConfigGroup("MAC Parameters", 1, mac_len),
|
||||||
|
ConfigGroup("PHY Parameters", mac_len + 1, phy_len),
|
||||||
|
ConfigGroup("External Parameters", phy_len + 1, config_len)
|
||||||
|
]
|
||||||
|
|
||||||
|
def build_xml_files(self, config, interface=None):
|
||||||
"""
|
"""
|
||||||
Builds xml files for emane. Includes a nem.xml file that points to both mac.xml and phy.xml definitions.
|
Builds xml files for this emane model. Creates a nem.xml file that points to both mac.xml and phy.xml
|
||||||
|
definitions.
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
|
:param dict config: emane model configuration for the node and interface
|
||||||
:param interface: interface for the emane node
|
:param interface: interface for the emane node
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# retrieve configuration values
|
nem_name = emanexml.nem_file_name(self, interface)
|
||||||
values = emane_manager.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), interface)
|
mac_name = emanexml.mac_file_name(self, interface)
|
||||||
if values is None:
|
phy_name = emanexml.phy_file_name(self, interface)
|
||||||
return
|
|
||||||
|
|
||||||
# create document and write to disk
|
# check if this is external
|
||||||
nem_name = self.nem_name(interface)
|
transport_type = "virtual"
|
||||||
nem_document = self.create_nem_doc(emane_manager, interface)
|
if interface and interface.transport_type == "raw":
|
||||||
emane_manager.xmlwrite(nem_document, nem_name)
|
transport_type = "raw"
|
||||||
|
transport_name = emanexml.transport_file_name(self.object_id, transport_type)
|
||||||
|
|
||||||
# create mac document and write to disk
|
# create nem xml file
|
||||||
mac_name = self.mac_name(interface)
|
nem_file = os.path.join(self.session.session_dir, nem_name)
|
||||||
mac_document = self.create_mac_doc(emane_manager, values)
|
emanexml.create_nem_xml(self, config, nem_file, transport_name, mac_name, phy_name)
|
||||||
emane_manager.xmlwrite(mac_document, mac_name)
|
|
||||||
|
|
||||||
# create phy document and write to disk
|
# create mac xml file
|
||||||
phy_name = self.phy_name(interface)
|
mac_file = os.path.join(self.session.session_dir, mac_name)
|
||||||
phy_document = self.create_phy_doc(emane_manager, values)
|
emanexml.create_mac_xml(self, config, mac_file)
|
||||||
emane_manager.xmlwrite(phy_document, phy_name)
|
|
||||||
|
|
||||||
def create_nem_doc(self, emane_manager, interface):
|
# create phy xml file
|
||||||
"""
|
phy_file = os.path.join(self.session.session_dir, phy_name)
|
||||||
Create the nem xml document.
|
emanexml.create_phy_xml(self, config, phy_file)
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
|
def post_startup(self):
|
||||||
:param interface: interface for the emane node
|
|
||||||
:return: nem document
|
|
||||||
:rtype: xml.dom.minidom.Document
|
|
||||||
"""
|
|
||||||
mac_name = self.mac_name(interface)
|
|
||||||
phy_name = self.phy_name(interface)
|
|
||||||
|
|
||||||
nem_document = emane_manager.xmldoc("nem")
|
|
||||||
nem_element = nem_document.getElementsByTagName("nem").pop()
|
|
||||||
nem_element.setAttribute("name", "%s NEM" % self.name)
|
|
||||||
emane_manager.appendtransporttonem(nem_document, nem_element, self.object_id, interface)
|
|
||||||
|
|
||||||
mac_element = nem_document.createElement("mac")
|
|
||||||
mac_element.setAttribute("definition", mac_name)
|
|
||||||
nem_element.appendChild(mac_element)
|
|
||||||
|
|
||||||
phy_element = nem_document.createElement("phy")
|
|
||||||
phy_element.setAttribute("definition", phy_name)
|
|
||||||
nem_element.appendChild(phy_element)
|
|
||||||
|
|
||||||
return nem_document
|
|
||||||
|
|
||||||
def create_mac_doc(self, emane_manager, values):
|
|
||||||
"""
|
|
||||||
Create the mac xml document.
|
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
|
|
||||||
:param tuple values: all current configuration values, mac + phy
|
|
||||||
:return: nem document
|
|
||||||
:rtype: xml.dom.minidom.Document
|
|
||||||
"""
|
|
||||||
names = list(self.getnames())
|
|
||||||
mac_names = names[:len(self.mac_config)]
|
|
||||||
|
|
||||||
mac_document = emane_manager.xmldoc("mac")
|
|
||||||
mac_element = mac_document.getElementsByTagName("mac").pop()
|
|
||||||
mac_element.setAttribute("name", "%s MAC" % self.name)
|
|
||||||
|
|
||||||
if not self.mac_library:
|
|
||||||
raise ValueError("must define emane model library")
|
|
||||||
mac_element.setAttribute("library", self.mac_library)
|
|
||||||
|
|
||||||
for name in mac_names:
|
|
||||||
# ignore custom configurations
|
|
||||||
if name in self.config_ignore:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# check if value is a multi param
|
|
||||||
value = self.valueof(name, values)
|
|
||||||
param = value_to_params(mac_document, name, value)
|
|
||||||
if not param:
|
|
||||||
param = emane_manager.xmlparam(mac_document, name, value)
|
|
||||||
|
|
||||||
mac_element.appendChild(param)
|
|
||||||
|
|
||||||
return mac_document
|
|
||||||
|
|
||||||
def create_phy_doc(self, emane_manager, values):
|
|
||||||
"""
|
|
||||||
Create the phy xml document.
|
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
|
|
||||||
:param tuple values: all current configuration values, mac + phy
|
|
||||||
:return: nem document
|
|
||||||
:rtype: xml.dom.minidom.Document
|
|
||||||
"""
|
|
||||||
names = list(self.getnames())
|
|
||||||
phy_names = names[len(self.mac_config):]
|
|
||||||
|
|
||||||
phy_document = emane_manager.xmldoc("phy")
|
|
||||||
phy_element = phy_document.getElementsByTagName("phy").pop()
|
|
||||||
phy_element.setAttribute("name", "%s PHY" % self.name)
|
|
||||||
|
|
||||||
if self.phy_library:
|
|
||||||
phy_element.setAttribute("library", self.phy_library)
|
|
||||||
|
|
||||||
# append all phy options
|
|
||||||
for name in phy_names:
|
|
||||||
# ignore custom configurations
|
|
||||||
if name in self.config_ignore:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# check if value is a multi param
|
|
||||||
value = self.valueof(name, values)
|
|
||||||
param = value_to_params(phy_document, name, value)
|
|
||||||
if not param:
|
|
||||||
param = emane_manager.xmlparam(phy_document, name, value)
|
|
||||||
|
|
||||||
phy_element.appendChild(param)
|
|
||||||
|
|
||||||
return phy_document
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def configure_emane(cls, session, config_data):
|
|
||||||
"""
|
|
||||||
Handle configuration messages for configuring an emane model.
|
|
||||||
|
|
||||||
:param core.session.Session session: session to configure emane
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
"""
|
|
||||||
return cls.configure(session.emane, config_data)
|
|
||||||
|
|
||||||
def post_startup(self, emane_manager):
|
|
||||||
"""
|
"""
|
||||||
Logic to execute after the emane manager is finished with startup.
|
Logic to execute after the emane manager is finished with startup.
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager for the session
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logger.info("emane model(%s) has no post setup tasks", self.name)
|
logger.info("emane model(%s) has no post setup tasks", self.name)
|
||||||
|
|
||||||
def build_nem_xml(self, doc, emane_node, interface):
|
|
||||||
"""
|
|
||||||
Build the NEM definition that goes into the platform.xml file.
|
|
||||||
|
|
||||||
This returns an XML element that will be added to the <platform/> element.
|
|
||||||
|
|
||||||
This default method supports per-interface config (e.g. <nem definition="n2_0_63emane_rfpipe.xml" id="1">
|
|
||||||
or per-EmaneNode config (e.g. <nem definition="n1emane_rfpipe.xml" id="1">.
|
|
||||||
|
|
||||||
This can be overriden by a model for NEM flexibility; n is the EmaneNode.
|
|
||||||
|
|
||||||
<nem name="NODE-001" definition="rfpipenem.xml">
|
|
||||||
|
|
||||||
:param xml.dom.minidom.Document doc: xml document
|
|
||||||
:param core.emane.nodes.EmaneNode emane_node: emane node to get information from
|
|
||||||
:param interface: interface for the emane node
|
|
||||||
:return: created platform xml
|
|
||||||
"""
|
|
||||||
# if this netif contains a non-standard (per-interface) config,
|
|
||||||
# then we need to use a more specific xml file here
|
|
||||||
nem_name = self.nem_name(interface)
|
|
||||||
nem = doc.createElement("nem")
|
|
||||||
nem.setAttribute("name", interface.localname)
|
|
||||||
nem.setAttribute("definition", nem_name)
|
|
||||||
return nem
|
|
||||||
|
|
||||||
def build_transport_xml(self, doc, emane_node, interface):
|
|
||||||
"""
|
|
||||||
Build the transport definition that goes into the platform.xml file.
|
|
||||||
This returns an XML element that will be added to the nem definition.
|
|
||||||
This default method supports raw and virtual transport types, but may be
|
|
||||||
overridden by a model to support the e.g. pluggable virtual transport.
|
|
||||||
|
|
||||||
<transport definition="transvirtual.xml" group="1">
|
|
||||||
<param name="device" value="n1.0.158" />
|
|
||||||
</transport>
|
|
||||||
|
|
||||||
:param xml.dom.minidom.Document doc: xml document
|
|
||||||
:param core.emane.nodes.EmaneNode emane_node: emane node to get information from
|
|
||||||
:param interface: interface for the emane node
|
|
||||||
:return: created transport xml
|
|
||||||
"""
|
|
||||||
transport_type = interface.transport_type
|
|
||||||
if not transport_type:
|
|
||||||
logger.info("warning: %s interface type unsupported!", interface.name)
|
|
||||||
transport_type = "raw"
|
|
||||||
transport_name = emane_node.transportxmlname(transport_type)
|
|
||||||
|
|
||||||
transport = doc.createElement("transport")
|
|
||||||
transport.setAttribute("definition", transport_name)
|
|
||||||
|
|
||||||
param = doc.createElement("param")
|
|
||||||
param.setAttribute("name", "device")
|
|
||||||
param.setAttribute("value", interface.name)
|
|
||||||
|
|
||||||
transport.appendChild(param)
|
|
||||||
return transport
|
|
||||||
|
|
||||||
def _basename(self, interface=None):
|
|
||||||
"""
|
|
||||||
Create name that is leveraged for configuration file creation.
|
|
||||||
|
|
||||||
:param interface: interface for this model
|
|
||||||
:return: basename used for file creation
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
name = "n%s" % self.object_id
|
|
||||||
emane_manager = self.session.emane
|
|
||||||
|
|
||||||
if interface:
|
|
||||||
node_id = interface.node.objid
|
|
||||||
if emane_manager.getifcconfig(node_id, self.name, None, interface) is not None:
|
|
||||||
name = interface.localname.replace(".", "_")
|
|
||||||
|
|
||||||
return "%s%s" % (name, self.name)
|
|
||||||
|
|
||||||
def nem_name(self, interface=None):
|
|
||||||
"""
|
|
||||||
Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml"
|
|
||||||
|
|
||||||
:param interface: interface for this model
|
|
||||||
:return: nem xml filename
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
basename = self._basename(interface)
|
|
||||||
append = ""
|
|
||||||
if interface and interface.transport_type == "raw":
|
|
||||||
append = "_raw"
|
|
||||||
return "%snem%s.xml" % (basename, append)
|
|
||||||
|
|
||||||
def shim_name(self, interface=None):
|
|
||||||
"""
|
|
||||||
Return the string name for the SHIM XML file, e.g. "commeffectshim.xml"
|
|
||||||
|
|
||||||
:param interface: interface for this model
|
|
||||||
:return: shim xml filename
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
return "%sshim.xml" % self._basename(interface)
|
|
||||||
|
|
||||||
def mac_name(self, interface=None):
|
|
||||||
"""
|
|
||||||
Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml"
|
|
||||||
|
|
||||||
:param interface: interface for this model
|
|
||||||
:return: mac xml filename
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
return "%smac.xml" % self._basename(interface)
|
|
||||||
|
|
||||||
def phy_name(self, interface=None):
|
|
||||||
"""
|
|
||||||
Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml"
|
|
||||||
|
|
||||||
:param interface: interface for this model
|
|
||||||
:return: phy xml filename
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
return "%sphy.xml" % self._basename(interface)
|
|
||||||
|
|
||||||
def update(self, moved, moved_netifs):
|
def update(self, moved, moved_netifs):
|
||||||
"""
|
"""
|
||||||
Invoked from MobilityModel when nodes are moved; this causes
|
Invoked from MobilityModel when nodes are moved; this causes
|
||||||
|
|
|
@ -4,8 +4,6 @@ control of an EMANE emulation. An EmaneNode has several attached NEMs that
|
||||||
share the same MAC+PHY model.
|
share the same MAC+PHY model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.coreobj import PyCoreNet
|
from core.coreobj import PyCoreNet
|
||||||
from core.enumerations import LinkTypes
|
from core.enumerations import LinkTypes
|
||||||
|
@ -67,6 +65,12 @@ class EmaneNode(EmaneNet):
|
||||||
def unlink(self, netif1, netif2):
|
def unlink(self, netif1, netif2):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def updatemodel(self, config):
|
||||||
|
if not self.model:
|
||||||
|
raise ValueError("no model set to update for node(%s)", self.objid)
|
||||||
|
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
|
||||||
|
self.model.set_configs(config, node_id=self.objid)
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model, config):
|
||||||
"""
|
"""
|
||||||
set the EmaneModel associated with this node
|
set the EmaneModel associated with this node
|
||||||
|
@ -76,8 +80,10 @@ class EmaneNode(EmaneNet):
|
||||||
# EmaneModel really uses values from ConfigurableManager
|
# EmaneModel really uses values from ConfigurableManager
|
||||||
# when buildnemxml() is called, not during init()
|
# when buildnemxml() is called, not during init()
|
||||||
self.model = model(session=self.session, object_id=self.objid)
|
self.model = model(session=self.session, object_id=self.objid)
|
||||||
|
self.model.update_config(config)
|
||||||
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, object_id=self.objid, values=config)
|
self.mobility = model(session=self.session, object_id=self.objid)
|
||||||
|
self.mobility.update_config(config)
|
||||||
|
|
||||||
def setnemid(self, netif, nemid):
|
def setnemid(self, netif, nemid):
|
||||||
"""
|
"""
|
||||||
|
@ -111,95 +117,7 @@ class EmaneNode(EmaneNet):
|
||||||
"""
|
"""
|
||||||
return sorted(self._netif.values(), key=lambda ifc: ifc.node.objid)
|
return sorted(self._netif.values(), key=lambda ifc: ifc.node.objid)
|
||||||
|
|
||||||
def buildplatformxmlentry(self, doc):
|
def installnetifs(self):
|
||||||
"""
|
|
||||||
Return a dictionary of XML elements describing the NEMs
|
|
||||||
connected to this EmaneNode for inclusion in the platform.xml file.
|
|
||||||
"""
|
|
||||||
ret = {}
|
|
||||||
if self.model is None:
|
|
||||||
logger.info("warning: EmaneNode %s has no associated model", self.name)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
for netif in self.netifs():
|
|
||||||
nementry = self.model.build_nem_xml(doc, self, netif)
|
|
||||||
trans = self.model.build_transport_xml(doc, self, netif)
|
|
||||||
nementry.appendChild(trans)
|
|
||||||
ret[netif] = nementry
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def build_xml_files(self, emane_manager):
|
|
||||||
"""
|
|
||||||
Let the configured model build the necessary nem, mac, and phy XMLs.
|
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if self.model is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
# build XML for overall network (EmaneNode) configs
|
|
||||||
self.model.build_xml_files(emane_manager, interface=None)
|
|
||||||
|
|
||||||
# build XML for specific interface (NEM) configs
|
|
||||||
need_virtual = False
|
|
||||||
need_raw = False
|
|
||||||
vtype = "virtual"
|
|
||||||
rtype = "raw"
|
|
||||||
|
|
||||||
for netif in self.netifs():
|
|
||||||
self.model.build_xml_files(emane_manager, netif)
|
|
||||||
if "virtual" in netif.transport_type:
|
|
||||||
need_virtual = True
|
|
||||||
vtype = netif.transport_type
|
|
||||||
else:
|
|
||||||
need_raw = True
|
|
||||||
rtype = netif.transport_type
|
|
||||||
|
|
||||||
# build transport XML files depending on type of interfaces involved
|
|
||||||
if need_virtual:
|
|
||||||
self.buildtransportxml(emane_manager, vtype)
|
|
||||||
|
|
||||||
if need_raw:
|
|
||||||
self.buildtransportxml(emane_manager, rtype)
|
|
||||||
|
|
||||||
def buildtransportxml(self, emane, transport_type):
|
|
||||||
"""
|
|
||||||
Write a transport XML file for the Virtual or Raw Transport.
|
|
||||||
"""
|
|
||||||
transdoc = emane.xmldoc("transport")
|
|
||||||
trans = transdoc.getElementsByTagName("transport").pop()
|
|
||||||
trans.setAttribute("name", "%s Transport" % transport_type.capitalize())
|
|
||||||
trans.setAttribute("library", "trans%s" % transport_type.lower())
|
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0"))
|
|
||||||
|
|
||||||
flowcontrol = False
|
|
||||||
names = self.model.getnames()
|
|
||||||
values = emane.getconfig(self.objid, self.model.name, self.model.getdefaultvalues())[1]
|
|
||||||
|
|
||||||
if "flowcontrolenable" in names and values:
|
|
||||||
i = names.index("flowcontrolenable")
|
|
||||||
if self.model.booltooffon(values[i]) == "on":
|
|
||||||
flowcontrol = True
|
|
||||||
|
|
||||||
if "virtual" in transport_type.lower():
|
|
||||||
if os.path.exists("/dev/net/tun_flowctl"):
|
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "devicepath", "/dev/net/tun_flowctl"))
|
|
||||||
else:
|
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "devicepath", "/dev/net/tun"))
|
|
||||||
if flowcontrol:
|
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "flowcontrolenable", "on"))
|
|
||||||
|
|
||||||
emane.xmlwrite(transdoc, self.transportxmlname(transport_type.lower()))
|
|
||||||
|
|
||||||
def transportxmlname(self, type):
|
|
||||||
"""
|
|
||||||
Return the string name for the Transport XML file, e.g. 'n3transvirtual.xml'
|
|
||||||
"""
|
|
||||||
return "n%strans%s.xml" % (self.objid, type)
|
|
||||||
|
|
||||||
def installnetifs(self, do_netns=True):
|
|
||||||
"""
|
"""
|
||||||
Install TAP devices into their namespaces. This is done after
|
Install TAP devices into their namespaces. This is done after
|
||||||
EMANE daemons have been started, because that is their only chance
|
EMANE daemons have been started, because that is their only chance
|
||||||
|
@ -211,12 +129,14 @@ class EmaneNode(EmaneNet):
|
||||||
logger.error(warntxt)
|
logger.error(warntxt)
|
||||||
|
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
if do_netns and "virtual" in netif.transport_type.lower():
|
external = self.session.emane.get_config("external", self.objid, self.model.name)
|
||||||
netif.install()
|
if external == "0":
|
||||||
netif.setaddrs()
|
netif.setaddrs()
|
||||||
|
|
||||||
if not self.session.emane.genlocationevents():
|
if not self.session.emane.genlocationevents():
|
||||||
netif.poshook = None
|
netif.poshook = None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# at this point we register location handlers for generating
|
# at this point we register location handlers for generating
|
||||||
# EMANE location events
|
# EMANE location events
|
||||||
netif.poshook = self.setnemposition
|
netif.poshook = self.setnemposition
|
||||||
|
|
|
@ -6,6 +6,7 @@ import os
|
||||||
|
|
||||||
from core import constants
|
from core import constants
|
||||||
from core import logger
|
from core import logger
|
||||||
|
from core.conf import Configuration
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emane import emanemodel
|
from core.emane import emanemodel
|
||||||
from core.enumerations import ConfigDataTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
|
@ -29,24 +30,29 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
||||||
default_schedule = os.path.join(constants.CORE_DATA_DIR, "examples", "tdma", "schedule.xml")
|
default_schedule = os.path.join(constants.CORE_DATA_DIR, "examples", "tdma", "schedule.xml")
|
||||||
mac_config.insert(
|
mac_config.insert(
|
||||||
0,
|
0,
|
||||||
(schedule_name, ConfigDataTypes.STRING.value, default_schedule, "", "TDMA schedule file (core)")
|
Configuration(
|
||||||
|
_id=schedule_name,
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
default=default_schedule,
|
||||||
|
label="TDMA schedule file (core)"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
config_ignore = {schedule_name}
|
config_ignore = {schedule_name}
|
||||||
|
|
||||||
def post_startup(self, emane_manager):
|
def post_startup(self):
|
||||||
"""
|
"""
|
||||||
Logic to execute after the emane manager is finished with startup.
|
Logic to execute after the emane manager is finished with startup.
|
||||||
|
|
||||||
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager for the session
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# get configured schedule
|
# get configured schedule
|
||||||
values = emane_manager.getconfig(self.object_id, self.name, self.getdefaultvalues())[1]
|
config = self.session.emane.get_configs(node_id=self.object_id, config_type=self.name)
|
||||||
if values is None:
|
if not config:
|
||||||
return
|
return
|
||||||
schedule = self.valueof(self.schedule_name, values)
|
schedule = config[self.schedule_name]
|
||||||
|
|
||||||
event_device = emane_manager.event_device
|
# get the set event device
|
||||||
|
event_device = self.session.emane.event_device
|
||||||
|
|
||||||
# initiate tdma schedule
|
# initiate tdma schedule
|
||||||
logger.info("setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device)
|
logger.info("setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device)
|
||||||
|
|
|
@ -15,9 +15,9 @@ from core.enumerations import LinkTypes
|
||||||
from core.enumerations import NodeTypes
|
from core.enumerations import NodeTypes
|
||||||
from core.misc import nodemaps
|
from core.misc import nodemaps
|
||||||
from core.misc import nodeutils
|
from core.misc import nodeutils
|
||||||
|
from core.service import ServiceManager
|
||||||
from core.session import Session
|
from core.session import Session
|
||||||
from core.xml.xmlparser import core_document_parser
|
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
|
||||||
from core.xml.xmlwriter import core_document_writer
|
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(signal_number, _):
|
def signal_handler(signal_number, _):
|
||||||
|
@ -126,7 +126,7 @@ class EmuSession(Session):
|
||||||
self.node_id_gen = IdGen()
|
self.node_id_gen = IdGen()
|
||||||
|
|
||||||
# set default services
|
# set default services
|
||||||
self.services.defaultservices = {
|
self.services.default_services = {
|
||||||
"mdr": ("zebra", "OSPFv3MDR", "IPForward"),
|
"mdr": ("zebra", "OSPFv3MDR", "IPForward"),
|
||||||
"PC": ("DefaultRoute",),
|
"PC": ("DefaultRoute",),
|
||||||
"prouter": ("zebra", "OSPFv2", "OSPFv3", "IPForward"),
|
"prouter": ("zebra", "OSPFv2", "OSPFv3", "IPForward"),
|
||||||
|
@ -199,7 +199,7 @@ class EmuSession(Session):
|
||||||
objects = [x for x in objects if x]
|
objects = [x for x in objects if x]
|
||||||
if len(objects) < 2:
|
if len(objects) < 2:
|
||||||
raise ValueError("wireless link failure: %s", objects)
|
raise ValueError("wireless link failure: %s", objects)
|
||||||
logger.debug("handling wireless linking objects(%) connect(%s)", objects, connect)
|
logger.debug("handling wireless linking objects(%s) connect(%s)", objects, connect)
|
||||||
common_networks = objects[0].commonnets(objects[1])
|
common_networks = objects[0].commonnets(objects[1])
|
||||||
if not common_networks:
|
if not common_networks:
|
||||||
raise ValueError("no common network found for wireless link/unlink")
|
raise ValueError("no common network found for wireless link/unlink")
|
||||||
|
@ -326,9 +326,6 @@ class EmuSession(Session):
|
||||||
:param core.enumerations.LinkTypes link_type: link type to delete
|
:param core.enumerations.LinkTypes link_type: link type to delete
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# interface data
|
|
||||||
# interface_one_data, interface_two_data = get_interfaces(link_data)
|
|
||||||
|
|
||||||
# get node objects identified by link data
|
# get node objects identified by link data
|
||||||
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
|
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
|
||||||
|
|
||||||
|
@ -379,7 +376,8 @@ class EmuSession(Session):
|
||||||
if node_two:
|
if node_two:
|
||||||
node_two.lock.release()
|
node_two.lock.release()
|
||||||
|
|
||||||
def update_link(self, node_one_id, node_two_id, link_options, interface_one_id=None, interface_two_id=None):
|
def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None,
|
||||||
|
link_options=LinkOptions()):
|
||||||
"""
|
"""
|
||||||
Update link information between nodes.
|
Update link information between nodes.
|
||||||
|
|
||||||
|
@ -390,9 +388,6 @@ class EmuSession(Session):
|
||||||
:param core.emulator.emudata.LinkOptions link_options: data to update link with
|
:param core.emulator.emudata.LinkOptions link_options: data to update link with
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# interface data
|
|
||||||
# interface_one_data, interface_two_data = get_interfaces(link_data)
|
|
||||||
|
|
||||||
# get node objects identified by link data
|
# get node objects identified by link data
|
||||||
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
|
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
|
||||||
|
|
||||||
|
@ -481,7 +476,7 @@ class EmuSession(Session):
|
||||||
|
|
||||||
# set node start based on current session state, override and check when rj45
|
# set node start based on current session state, override and check when rj45
|
||||||
start = self.state > EventTypes.DEFINITION_STATE.value
|
start = self.state > EventTypes.DEFINITION_STATE.value
|
||||||
enable_rj45 = getattr(self.options, "enablerj45", "0") == "1"
|
enable_rj45 = self.options.get_config("enablerj45") == "1"
|
||||||
if _type == NodeTypes.RJ45 and not enable_rj45:
|
if _type == NodeTypes.RJ45 and not enable_rj45:
|
||||||
start = False
|
start = False
|
||||||
|
|
||||||
|
@ -512,18 +507,15 @@ class EmuSession(Session):
|
||||||
# add services to default and physical nodes only
|
# add services to default and physical nodes only
|
||||||
if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL]:
|
if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL]:
|
||||||
node.type = node_options.model
|
node.type = node_options.model
|
||||||
services = "|".join(node_options.services) or None
|
logger.debug("set node type: %s", node.type)
|
||||||
logger.debug("set node type: %s - services(%s)", node.type, services)
|
self.services.add_services(node, node.type, node_options.services)
|
||||||
self.services.addservicestonode(node, node.type, services)
|
|
||||||
|
|
||||||
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
|
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
|
||||||
is_boot_node = isinstance(node, PyCoreNode) and not nodeutils.is_node(node, NodeTypes.RJ45)
|
is_boot_node = isinstance(node, PyCoreNode) and not nodeutils.is_node(node, NodeTypes.RJ45)
|
||||||
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
|
||||||
self.write_objects()
|
self.write_objects()
|
||||||
self.add_remove_control_interface(node=node, remove=False)
|
self.add_remove_control_interface(node=node, remove=False)
|
||||||
|
self.services.boot_services(node)
|
||||||
# TODO: common method to both Physical and LxcNodes, but not the common PyCoreNode
|
|
||||||
node.boot()
|
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
@ -669,10 +661,10 @@ class EmuSession(Session):
|
||||||
# clear out existing session
|
# clear out existing session
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
# set default node class when one is not provided
|
# write out xml file
|
||||||
node_class = nodeutils.get_node_class(NodeTypes.DEFAULT)
|
CoreXmlReader(self).read(file_name)
|
||||||
options = {"start": start, "nodecls": node_class}
|
|
||||||
core_document_parser(self, file_name, options)
|
# start session if needed
|
||||||
if start:
|
if start:
|
||||||
self.name = os.path.basename(file_name)
|
self.name = os.path.basename(file_name)
|
||||||
self.file_name = file_name
|
self.file_name = file_name
|
||||||
|
@ -686,8 +678,7 @@ class EmuSession(Session):
|
||||||
:param str version: xml version type
|
:param str version: xml version type
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
doc = core_document_writer(self, version)
|
CoreXmlWriter(self).write(file_name)
|
||||||
doc.writexml(file_name)
|
|
||||||
|
|
||||||
def add_hook(self, state, file_name, source_name, data):
|
def add_hook(self, state, file_name, source_name, data):
|
||||||
"""
|
"""
|
||||||
|
@ -703,21 +694,6 @@ class EmuSession(Session):
|
||||||
state = ":%s" % state
|
state = ":%s" % state
|
||||||
self.set_hook(state, file_name, source_name, data)
|
self.set_hook(state, file_name, source_name, data)
|
||||||
|
|
||||||
def add_node_service_file(self, node_id, service_name, file_name, source_name, data):
|
|
||||||
"""
|
|
||||||
Add a service file for a node.
|
|
||||||
|
|
||||||
:param int node_id: node to add service file to
|
|
||||||
:param str service_name: service file to add
|
|
||||||
:param str file_name: file name to use
|
|
||||||
:param str source_name: source file
|
|
||||||
:param str data: file data to save
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
# hack to conform with old logic until updated
|
|
||||||
service_name = ":%s" % service_name
|
|
||||||
self.services.setservicefile(node_id, service_name, file_name, source_name, data)
|
|
||||||
|
|
||||||
def add_node_file(self, node_id, source_name, file_name, data):
|
def add_node_file(self, node_id, source_name, file_name, data):
|
||||||
"""
|
"""
|
||||||
Add a file to a node.
|
Add a file to a node.
|
||||||
|
@ -745,6 +721,7 @@ class EmuSession(Session):
|
||||||
self.delete_objects()
|
self.delete_objects()
|
||||||
self.del_hooks()
|
self.del_hooks()
|
||||||
self.broker.reset()
|
self.broker.reset()
|
||||||
|
self.emane.reset()
|
||||||
|
|
||||||
def start_events(self):
|
def start_events(self):
|
||||||
"""
|
"""
|
||||||
|
@ -754,15 +731,6 @@ class EmuSession(Session):
|
||||||
"""
|
"""
|
||||||
self.event_loop.run()
|
self.event_loop.run()
|
||||||
|
|
||||||
def services_event(self, event_data):
|
|
||||||
"""
|
|
||||||
Handle a service event.
|
|
||||||
|
|
||||||
:param core.data.EventData event_data: event data to handle
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
self.services.handleevent(event_data)
|
|
||||||
|
|
||||||
def mobility_event(self, event_data):
|
def mobility_event(self, event_data):
|
||||||
"""
|
"""
|
||||||
Handle a mobility event.
|
Handle a mobility event.
|
||||||
|
@ -784,7 +752,7 @@ class EmuSession(Session):
|
||||||
node_options.model = "mdr"
|
node_options.model = "mdr"
|
||||||
return self.add_node(_type=NodeTypes.DEFAULT, _id=_id, node_options=node_options)
|
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()):
|
def create_emane_network(self, model, geo_reference, geo_scale=None, node_options=NodeOptions(), config=None):
|
||||||
"""
|
"""
|
||||||
Convenience method for creating an emane network.
|
Convenience method for creating an emane network.
|
||||||
|
|
||||||
|
@ -792,6 +760,7 @@ class EmuSession(Session):
|
||||||
:param geo_reference: geo reference point to use for emane node locations
|
: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 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 core.emulator.emudata.NodeOptions node_options: options for emane node being created
|
||||||
|
:param dict config: emane model configuration
|
||||||
:return: create emane network
|
:return: create emane network
|
||||||
"""
|
"""
|
||||||
# required to be set for emane to function properly
|
# required to be set for emane to function properly
|
||||||
|
@ -801,31 +770,9 @@ class EmuSession(Session):
|
||||||
|
|
||||||
# create and return network
|
# create and return network
|
||||||
emane_network = self.add_node(_type=NodeTypes.EMANE, node_options=node_options)
|
emane_network = self.add_node(_type=NodeTypes.EMANE, node_options=node_options)
|
||||||
self.set_emane_model(emane_network, model)
|
self.emane.set_model(emane_network, model, config)
|
||||||
return emane_network
|
return emane_network
|
||||||
|
|
||||||
def set_emane_model(self, emane_node, emane_model):
|
|
||||||
"""
|
|
||||||
Set emane model for a given emane node.
|
|
||||||
|
|
||||||
:param emane_node: emane node to set model for
|
|
||||||
:param emane_model: emane model to set
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
values = list(emane_model.getdefaultvalues())
|
|
||||||
self.emane.setconfig(emane_node.objid, emane_model.name, values)
|
|
||||||
|
|
||||||
def set_wireless_model(self, node, model):
|
|
||||||
"""
|
|
||||||
Convenience method for setting a wireless model.
|
|
||||||
|
|
||||||
:param node: node to set wireless model for
|
|
||||||
:param core.mobility.WirelessModel model: wireless model to set node to
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
values = list(model.getdefaultvalues())
|
|
||||||
node.setmodel(model, values)
|
|
||||||
|
|
||||||
def wireless_link_all(self, network, nodes):
|
def wireless_link_all(self, network, nodes):
|
||||||
"""
|
"""
|
||||||
Link all nodes to the provided wireless network.
|
Link all nodes to the provided wireless network.
|
||||||
|
@ -850,7 +797,12 @@ class CoreEmu(object):
|
||||||
|
|
||||||
:param dict config: configuration options
|
:param dict config: configuration options
|
||||||
"""
|
"""
|
||||||
|
# set umask 0
|
||||||
|
os.umask(0)
|
||||||
|
|
||||||
# configuration
|
# configuration
|
||||||
|
if not config:
|
||||||
|
config = {}
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
# session management
|
# session management
|
||||||
|
@ -861,12 +813,26 @@ class CoreEmu(object):
|
||||||
node_map = nodemaps.NODES
|
node_map = nodemaps.NODES
|
||||||
nodeutils.set_node_map(node_map)
|
nodeutils.set_node_map(node_map)
|
||||||
|
|
||||||
# load default services
|
# load services
|
||||||
core.services.load()
|
self.service_errors = []
|
||||||
|
self.load_services()
|
||||||
|
|
||||||
# catch exit event
|
# catch exit event
|
||||||
atexit.register(self.shutdown)
|
atexit.register(self.shutdown)
|
||||||
|
|
||||||
|
def load_services(self):
|
||||||
|
# load default services
|
||||||
|
self.service_errors = core.services.load()
|
||||||
|
|
||||||
|
# load custom services
|
||||||
|
service_paths = self.config.get("custom_services_dir")
|
||||||
|
logger.debug("custom service paths: %s", service_paths)
|
||||||
|
if service_paths:
|
||||||
|
for service_path in service_paths.split(','):
|
||||||
|
service_path = service_path.strip()
|
||||||
|
custom_service_errors = ServiceManager.add_services(service_path)
|
||||||
|
self.service_errors.extend(custom_service_errors)
|
||||||
|
|
||||||
def update_nodes(self, node_map):
|
def update_nodes(self, node_map):
|
||||||
"""
|
"""
|
||||||
Updates node map used by core.
|
Updates node map used by core.
|
||||||
|
|
|
@ -34,8 +34,8 @@ class NodeOptions(object):
|
||||||
"""
|
"""
|
||||||
Convenience method for setting position.
|
Convenience method for setting position.
|
||||||
|
|
||||||
:param int x: x position
|
:param float x: x position
|
||||||
:param int y: y position
|
:param float y: y position
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
self.x = x
|
self.x = x
|
||||||
|
@ -161,7 +161,7 @@ class IpPrefixes(object):
|
||||||
|
|
||||||
# random mac
|
# random mac
|
||||||
if not mac:
|
if not mac:
|
||||||
mac = str(MacAddress.random())
|
mac = MacAddress.random()
|
||||||
|
|
||||||
return InterfaceData(
|
return InterfaceData(
|
||||||
_id=inteface_id,
|
_id=inteface_id,
|
||||||
|
|
|
@ -6,19 +6,15 @@ https://pypi.python.org/pypi/utm (version 0.3.0).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.conf import ConfigurableManager
|
|
||||||
from core.enumerations import RegisterTlvs
|
from core.enumerations import RegisterTlvs
|
||||||
from core.misc import utm
|
from core.misc import utm
|
||||||
|
|
||||||
|
|
||||||
class CoreLocation(ConfigurableManager):
|
class CoreLocation(object):
|
||||||
"""
|
"""
|
||||||
Member of session class for handling global location data. This keeps
|
Member of session class for handling global location data. This keeps
|
||||||
track of a latitude/longitude/altitude reference point and scale in
|
track of a latitude/longitude/altitude reference point and scale in
|
||||||
order to convert between X,Y and geo coordinates.
|
order to convert between X,Y and geo coordinates.
|
||||||
|
|
||||||
TODO: this could be updated to use more generic
|
|
||||||
Configurable/ConfigurableManager code like other Session objects
|
|
||||||
"""
|
"""
|
||||||
name = "location"
|
name = "location"
|
||||||
config_type = RegisterTlvs.UTILITY.value
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
@ -29,7 +25,7 @@ class CoreLocation(ConfigurableManager):
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
ConfigurableManager.__init__(self)
|
# ConfigurableManager.__init__(self)
|
||||||
self.reset()
|
self.reset()
|
||||||
self.zonemap = {}
|
self.zonemap = {}
|
||||||
self.refxyz = (0.0, 0.0, 0.0)
|
self.refxyz = (0.0, 0.0, 0.0)
|
||||||
|
@ -52,35 +48,6 @@ class CoreLocation(ConfigurableManager):
|
||||||
# cached distance to refpt in other zones
|
# cached distance to refpt in other zones
|
||||||
self.zoneshifts = {}
|
self.zoneshifts = {}
|
||||||
|
|
||||||
def configure_values(self, config_data):
|
|
||||||
"""
|
|
||||||
Receive configuration message for setting the reference point
|
|
||||||
and scale.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
values = config_data.data_values
|
|
||||||
|
|
||||||
if values is None:
|
|
||||||
logger.warn("location data missing")
|
|
||||||
return None
|
|
||||||
values = values.split('|')
|
|
||||||
|
|
||||||
# Cartesian coordinate reference point
|
|
||||||
refx, refy = map(lambda x: float(x), values[0:2])
|
|
||||||
refz = 0.0
|
|
||||||
self.refxyz = (refx, refy, refz)
|
|
||||||
# Geographic reference point
|
|
||||||
lat, lon, alt = map(lambda x: float(x), values[2:5])
|
|
||||||
self.setrefgeo(lat, lon, alt)
|
|
||||||
self.refscale = float(values[5])
|
|
||||||
logger.info("location configured: (%.2f,%.2f,%.2f) = (%.5f,%.5f,%.5f) scale=%.2f" %
|
|
||||||
(self.refxyz[0], self.refxyz[1], self.refxyz[2], self.refgeo[0],
|
|
||||||
self.refgeo[1], self.refgeo[2], self.refscale))
|
|
||||||
logger.info("location configured: UTM(%.5f,%.5f,%.5f)" %
|
|
||||||
(self.refutm[1], self.refutm[2], self.refutm[3]))
|
|
||||||
|
|
||||||
def px2m(self, val):
|
def px2m(self, val):
|
||||||
"""
|
"""
|
||||||
Convert the specified value in pixels to meters using the
|
Convert the specified value in pixels to meters using the
|
||||||
|
|
|
@ -9,10 +9,13 @@ import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.conf import Configurable
|
from core.conf import ConfigGroup
|
||||||
from core.conf import ConfigurableManager
|
from core.conf import ConfigurableOptions
|
||||||
|
from core.conf import Configuration
|
||||||
|
from core.conf import ModelManager
|
||||||
from core.coreobj import PyCoreNode
|
from core.coreobj import PyCoreNode
|
||||||
from core.data import EventData, LinkData
|
from core.data import EventData
|
||||||
|
from core.data import LinkData
|
||||||
from core.enumerations import ConfigDataTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
from core.enumerations import EventTypes
|
from core.enumerations import EventTypes
|
||||||
from core.enumerations import LinkTypes
|
from core.enumerations import LinkTypes
|
||||||
|
@ -24,7 +27,7 @@ from core.misc import utils
|
||||||
from core.misc.ipaddress import IpAddress
|
from core.misc.ipaddress import IpAddress
|
||||||
|
|
||||||
|
|
||||||
class MobilityManager(ConfigurableManager):
|
class MobilityManager(ModelManager):
|
||||||
"""
|
"""
|
||||||
Member of session class for handling configuration data for mobility and
|
Member of session class for handling configuration data for mobility and
|
||||||
range models.
|
range models.
|
||||||
|
@ -38,15 +41,11 @@ class MobilityManager(ConfigurableManager):
|
||||||
|
|
||||||
:param core.session.Session session: session this manager is tied to
|
:param core.session.Session session: session this manager is tied to
|
||||||
"""
|
"""
|
||||||
ConfigurableManager.__init__(self)
|
super(MobilityManager, self).__init__()
|
||||||
self.session = session
|
self.session = session
|
||||||
# configurations for basic range, indexed by WLAN node number, are
|
self.models[BasicRangeModel.name] = BasicRangeModel
|
||||||
# stored in self.configs
|
self.models[Ns2ScriptedMobility.name] = Ns2ScriptedMobility
|
||||||
# mapping from model names to their classes
|
|
||||||
self._modelclsmap = {
|
|
||||||
BasicRangeModel.name: BasicRangeModel,
|
|
||||||
Ns2ScriptedMobility.name: Ns2ScriptedMobility
|
|
||||||
}
|
|
||||||
# dummy node objects for tracking position of nodes on other servers
|
# dummy node objects for tracking position of nodes on other servers
|
||||||
self.phys = {}
|
self.phys = {}
|
||||||
self.physnets = {}
|
self.physnets = {}
|
||||||
|
@ -61,31 +60,24 @@ class MobilityManager(ConfigurableManager):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if node_ids is None:
|
if node_ids is None:
|
||||||
node_ids = self.configs.keys()
|
node_ids = self.nodes()
|
||||||
|
|
||||||
for node_id in node_ids:
|
for node_id in node_ids:
|
||||||
logger.info("checking mobility startup for node: %s", node_id)
|
logger.info("checking mobility startup for node: %s", node_id)
|
||||||
|
logger.info("node mobility configurations: %s", self.get_all_configs(node_id))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node = self.session.get_object(node_id)
|
node = self.session.get_object(node_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.warn("skipping mobility configuration for unknown node %d." % node_id)
|
logger.warn("skipping mobility configuration for unknown node: %s", node_id)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if node_id not in self.configs:
|
for model_name in self.models.iterkeys():
|
||||||
logger.warn("missing mobility configuration for node %d." % node_id)
|
config = self.get_configs(node_id, model_name)
|
||||||
continue
|
if not config:
|
||||||
|
|
||||||
v = self.configs[node_id]
|
|
||||||
|
|
||||||
for model in v:
|
|
||||||
try:
|
|
||||||
logger.info("setting mobility model to node: %s", model)
|
|
||||||
cls = self._modelclsmap[model[0]]
|
|
||||||
node.setmodel(cls, model[1])
|
|
||||||
except KeyError:
|
|
||||||
logger.warn("skipping mobility configuration for unknown model '%s'" % model[0])
|
|
||||||
continue
|
continue
|
||||||
|
model_class = self.models[model_name]
|
||||||
|
self.set_model(node, model_class, config)
|
||||||
|
|
||||||
if self.session.master:
|
if self.session.master:
|
||||||
self.installphysnodes(node)
|
self.installphysnodes(node)
|
||||||
|
@ -93,33 +85,6 @@ class MobilityManager(ConfigurableManager):
|
||||||
if node.mobility:
|
if node.mobility:
|
||||||
self.session.event_loop.add_event(0.0, node.mobility.startup)
|
self.session.event_loop.add_event(0.0, node.mobility.startup)
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""
|
|
||||||
Reset all configs.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
self.clearconfig(nodenum=None)
|
|
||||||
|
|
||||||
def setconfig(self, node_id, config_type, values):
|
|
||||||
"""
|
|
||||||
Normal setconfig() with check for run-time updates for WLANs.
|
|
||||||
|
|
||||||
:param int node_id: node id
|
|
||||||
:param config_type: configuration type
|
|
||||||
:param values: configuration value
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
super(MobilityManager, self).setconfig(node_id, config_type, values)
|
|
||||||
if self.session is None:
|
|
||||||
return
|
|
||||||
if self.session.state == EventTypes.RUNTIME_STATE.value:
|
|
||||||
try:
|
|
||||||
node = self.session.get_object(node_id)
|
|
||||||
node.updatemodel(config_type, values)
|
|
||||||
except KeyError:
|
|
||||||
logger.exception("Skipping mobility configuration for unknown node %d.", node_id)
|
|
||||||
|
|
||||||
def handleevent(self, event_data):
|
def handleevent(self, event_data):
|
||||||
"""
|
"""
|
||||||
Handle an Event Message used to start, stop, or pause
|
Handle an Event Message used to start, stop, or pause
|
||||||
|
@ -142,7 +107,7 @@ class MobilityManager(ConfigurableManager):
|
||||||
models = name[9:].split(',')
|
models = name[9:].split(',')
|
||||||
for model in models:
|
for model in models:
|
||||||
try:
|
try:
|
||||||
cls = self._modelclsmap[model]
|
cls = self.models[model]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.warn("Ignoring event for unknown model '%s'", model)
|
logger.warn("Ignoring event for unknown model '%s'", model)
|
||||||
continue
|
continue
|
||||||
|
@ -206,13 +171,13 @@ class MobilityManager(ConfigurableManager):
|
||||||
:param list moved_netifs: moved network interfaces
|
:param list moved_netifs: moved network interfaces
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
for nodenum in self.configs:
|
for node_id in self.nodes():
|
||||||
try:
|
try:
|
||||||
n = self.session.get_object(nodenum)
|
node = self.session.get_object(node_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
if n.model:
|
if node.model:
|
||||||
n.model.update(moved, moved_netifs)
|
node.model.update(moved, moved_netifs)
|
||||||
|
|
||||||
def addphys(self, netnum, node):
|
def addphys(self, netnum, node):
|
||||||
"""
|
"""
|
||||||
|
@ -222,14 +187,15 @@ class MobilityManager(ConfigurableManager):
|
||||||
:param core.coreobj.PyCoreNode node: node to add physical network to
|
:param core.coreobj.PyCoreNode node: node to add physical network to
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
nodenum = node.objid
|
node_id = node.objid
|
||||||
self.phys[nodenum] = node
|
self.phys[node_id] = node
|
||||||
if netnum not in self.physnets:
|
if netnum not in self.physnets:
|
||||||
self.physnets[netnum] = [nodenum, ]
|
self.physnets[netnum] = [node_id, ]
|
||||||
else:
|
else:
|
||||||
self.physnets[netnum].append(nodenum)
|
self.physnets[netnum].append(node_id)
|
||||||
|
|
||||||
|
# TODO: remove need for handling old style message
|
||||||
|
|
||||||
# TODO: remove need for handling old style message
|
|
||||||
def physnodehandlelink(self, message):
|
def physnodehandlelink(self, message):
|
||||||
"""
|
"""
|
||||||
Broker handler. Snoop Link add messages to get
|
Broker handler. Snoop Link add messages to get
|
||||||
|
@ -247,8 +213,7 @@ class MobilityManager(ConfigurableManager):
|
||||||
return
|
return
|
||||||
if nn[1] in self.session.broker.physical_nodes:
|
if nn[1] in self.session.broker.physical_nodes:
|
||||||
# record the fact that this PhysicalNode is linked to a net
|
# record the fact that this PhysicalNode is linked to a net
|
||||||
dummy = PyCoreNode(session=self.session, objid=nn[1],
|
dummy = PyCoreNode(session=self.session, objid=nn[1], name="n%d" % nn[1], start=False)
|
||||||
name="n%d" % nn[1], start=False)
|
|
||||||
self.addphys(nn[0], dummy)
|
self.addphys(nn[0], dummy)
|
||||||
|
|
||||||
# TODO: remove need to handling old style messages
|
# TODO: remove need to handling old style messages
|
||||||
|
@ -291,7 +256,7 @@ class MobilityManager(ConfigurableManager):
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
|
|
||||||
|
|
||||||
class WirelessModel(Configurable):
|
class WirelessModel(ConfigurableOptions):
|
||||||
"""
|
"""
|
||||||
Base class used by EMANE models and the basic range model.
|
Base class used by EMANE models and the basic range model.
|
||||||
Used for managing arbitrary configuration parameters.
|
Used for managing arbitrary configuration parameters.
|
||||||
|
@ -300,17 +265,16 @@ class WirelessModel(Configurable):
|
||||||
bitmap = None
|
bitmap = None
|
||||||
position_callback = None
|
position_callback = None
|
||||||
|
|
||||||
def __init__(self, session, object_id, values=None):
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Create a WirelessModel instance.
|
Create a WirelessModel instance.
|
||||||
|
|
||||||
:param core.session.Session session: core session we are tied to
|
:param core.session.Session session: core session we are tied to
|
||||||
:param int object_id: object id
|
:param int object_id: object id
|
||||||
:param values: values
|
:param dict config: values
|
||||||
"""
|
"""
|
||||||
Configurable.__init__(self, session, object_id)
|
self.session = session
|
||||||
# 'values' can be retrieved from a ConfigurableManager, or used here
|
self.object_id = object_id
|
||||||
# during initialization, depending on the model.
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags):
|
||||||
"""
|
"""
|
||||||
|
@ -333,16 +297,15 @@ class WirelessModel(Configurable):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def updateconfig(self, values):
|
def update_config(self, config):
|
||||||
"""
|
"""
|
||||||
For run-time updates of model config. Returns True when position callback and set link
|
For run-time updates of model config. Returns True when position callback and set link
|
||||||
parameters should be invoked.
|
parameters should be invoked.
|
||||||
|
|
||||||
:param values: value to update
|
:param dict config: configuration values to update
|
||||||
:return: False
|
:return: nothing
|
||||||
:rtype: bool
|
|
||||||
"""
|
"""
|
||||||
return False
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BasicRangeModel(WirelessModel):
|
class BasicRangeModel(WirelessModel):
|
||||||
|
@ -352,82 +315,63 @@ class BasicRangeModel(WirelessModel):
|
||||||
the GUI.
|
the GUI.
|
||||||
"""
|
"""
|
||||||
name = "basic_range"
|
name = "basic_range"
|
||||||
|
options = [
|
||||||
# configuration parameters are
|
Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"),
|
||||||
# ( 'name', 'type', 'default', 'possible-value-list', 'caption')
|
Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT32, default="54000", label="bandwidth (bps)"),
|
||||||
config_matrix = [
|
Configuration(_id="jitter", _type=ConfigDataTypes.FLOAT, default="0.0", label="transmission jitter (usec)"),
|
||||||
("range", ConfigDataTypes.UINT32.value, '275',
|
Configuration(_id="delay", _type=ConfigDataTypes.FLOAT, default="5000.0",
|
||||||
'', 'wireless range (pixels)'),
|
label="transmission delay (usec)"),
|
||||||
("bandwidth", ConfigDataTypes.UINT32.value, '54000',
|
Configuration(_id="error", _type=ConfigDataTypes.FLOAT, default="0.0", label="error rate (%)")
|
||||||
'', 'bandwidth (bps)'),
|
|
||||||
("jitter", ConfigDataTypes.FLOAT.value, '0.0',
|
|
||||||
'', 'transmission jitter (usec)'),
|
|
||||||
("delay", ConfigDataTypes.FLOAT.value, '5000.0',
|
|
||||||
'', 'transmission delay (usec)'),
|
|
||||||
("error", ConfigDataTypes.FLOAT.value, '0.0',
|
|
||||||
'', 'error rate (%)'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# value groupings
|
@classmethod
|
||||||
config_groups = "Basic Range Parameters:1-%d" % len(config_matrix)
|
def config_groups(cls):
|
||||||
|
return [
|
||||||
|
ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, session, object_id, values=None):
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Create a BasicRangeModel instance.
|
Create a BasicRangeModel instance.
|
||||||
|
|
||||||
:param core.session.Session session: related core session
|
:param core.session.Session session: related core session
|
||||||
:param int object_id: object id
|
:param int object_id: object id
|
||||||
:param values: values
|
:param dict config: values
|
||||||
"""
|
"""
|
||||||
super(BasicRangeModel, self).__init__(session=session, object_id=object_id)
|
super(BasicRangeModel, self).__init__(session=session, object_id=object_id)
|
||||||
|
self.session = session
|
||||||
self.wlan = session.get_object(object_id)
|
self.wlan = session.get_object(object_id)
|
||||||
self._netifs = {}
|
self._netifs = {}
|
||||||
self._netifslock = threading.Lock()
|
self._netifslock = threading.Lock()
|
||||||
if values is None:
|
|
||||||
values = session.mobility.getconfig(object_id, self.name, self.getdefaultvalues())[1]
|
|
||||||
self.range = float(self.valueof("range", values))
|
|
||||||
logger.info("Basic range model configured for WLAN %d using range %d", object_id, self.range)
|
|
||||||
self.valuestolinkparams(values)
|
|
||||||
|
|
||||||
# link parameters
|
self.range = None
|
||||||
self.bw = None
|
self.bw = None
|
||||||
self.delay = None
|
self.delay = None
|
||||||
self.loss = None
|
self.loss = None
|
||||||
self.jitter = None
|
self.jitter = None
|
||||||
|
|
||||||
def valuestolinkparams(self, values):
|
def values_from_config(self, config):
|
||||||
"""
|
"""
|
||||||
Values to convert to link parameters.
|
Values to convert to link parameters.
|
||||||
|
|
||||||
:param values: values to convert
|
:param dict config: values to convert
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
self.bw = int(self.valueof("bandwidth", values))
|
self.range = float(config["range"])
|
||||||
|
logger.info("basic range model configured for WLAN %d using range %d", self.wlan.objid, self.range)
|
||||||
|
self.bw = int(config["bandwidth"])
|
||||||
if self.bw == 0.0:
|
if self.bw == 0.0:
|
||||||
self.bw = None
|
self.bw = None
|
||||||
self.delay = float(self.valueof("delay", values))
|
self.delay = float(config["delay"])
|
||||||
if self.delay == 0.0:
|
if self.delay == 0.0:
|
||||||
self.delay = None
|
self.delay = None
|
||||||
self.loss = float(self.valueof("error", values))
|
self.loss = float(config["error"])
|
||||||
if self.loss == 0.0:
|
if self.loss == 0.0:
|
||||||
self.loss = None
|
self.loss = None
|
||||||
self.jitter = float(self.valueof("jitter", values))
|
self.jitter = float(config["jitter"])
|
||||||
if self.jitter == 0.0:
|
if self.jitter == 0.0:
|
||||||
self.jitter = None
|
self.jitter = None
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def configure_mob(cls, session, config_data):
|
|
||||||
"""
|
|
||||||
Handle configuration messages for setting up a model.
|
|
||||||
Pass the MobilityManager object as the manager object.
|
|
||||||
|
|
||||||
:param core.session.Session session: current session calling function
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: configuration data
|
|
||||||
:rtype: core.data.ConfigData
|
|
||||||
"""
|
|
||||||
return cls.configure(session.mobility, config_data)
|
|
||||||
|
|
||||||
def setlinkparams(self):
|
def setlinkparams(self):
|
||||||
"""
|
"""
|
||||||
Apply link parameters to all interfaces. This is invoked from
|
Apply link parameters to all interfaces. This is invoked from
|
||||||
|
@ -435,8 +379,7 @@ class BasicRangeModel(WirelessModel):
|
||||||
"""
|
"""
|
||||||
with self._netifslock:
|
with self._netifslock:
|
||||||
for netif in self._netifs:
|
for netif in self._netifs:
|
||||||
self.wlan.linkconfig(netif, bw=self.bw, delay=self.delay,
|
self.wlan.linkconfig(netif, bw=self.bw, delay=self.delay, loss=self.loss, duplicate=None,
|
||||||
loss=self.loss, duplicate=None,
|
|
||||||
jitter=self.jitter)
|
jitter=self.jitter)
|
||||||
|
|
||||||
def get_position(self, netif):
|
def get_position(self, netif):
|
||||||
|
@ -461,7 +404,6 @@ class BasicRangeModel(WirelessModel):
|
||||||
:param z: z position
|
:param z: z position
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# print "set_position(%s, x=%s, y=%s, z=%s)" % (netif.localname, x, y, z)
|
|
||||||
self._netifslock.acquire()
|
self._netifslock.acquire()
|
||||||
self._netifs[netif] = (x, y, z)
|
self._netifs[netif] = (x, y, z)
|
||||||
if x is None or y is None:
|
if x is None or y is None:
|
||||||
|
@ -487,7 +429,7 @@ class BasicRangeModel(WirelessModel):
|
||||||
with self._netifslock:
|
with self._netifslock:
|
||||||
while len(moved_netifs):
|
while len(moved_netifs):
|
||||||
netif = moved_netifs.pop()
|
netif = moved_netifs.pop()
|
||||||
(nx, ny, nz) = netif.node.getposition()
|
nx, ny, nz = netif.node.getposition()
|
||||||
if netif in self._netifs:
|
if netif in self._netifs:
|
||||||
self._netifs[netif] = (nx, ny, nz)
|
self._netifs[netif] = (nx, ny, nz)
|
||||||
for netif2 in self._netifs:
|
for netif2 in self._netifs:
|
||||||
|
@ -529,12 +471,12 @@ class BasicRangeModel(WirelessModel):
|
||||||
a.name, b.name, linked, d, self.range)
|
a.name, b.name, linked, d, self.range)
|
||||||
if d > self.range:
|
if d > self.range:
|
||||||
if linked:
|
if linked:
|
||||||
logger.info("was linked, unlinking")
|
logger.debug("was linked, unlinking")
|
||||||
self.wlan.unlink(a, b)
|
self.wlan.unlink(a, b)
|
||||||
self.sendlinkmsg(a, b, unlink=True)
|
self.sendlinkmsg(a, b, unlink=True)
|
||||||
else:
|
else:
|
||||||
if not linked:
|
if not linked:
|
||||||
logger.info("was not linked, linking")
|
logger.debug("was not linked, linking")
|
||||||
self.wlan.link(a, b)
|
self.wlan.link(a, b)
|
||||||
self.sendlinkmsg(a, b)
|
self.sendlinkmsg(a, b)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -557,18 +499,15 @@ class BasicRangeModel(WirelessModel):
|
||||||
c = p1[2] - p2[2]
|
c = p1[2] - p2[2]
|
||||||
return math.hypot(math.hypot(a, b), c)
|
return math.hypot(math.hypot(a, b), c)
|
||||||
|
|
||||||
def updateconfig(self, values):
|
def update_config(self, config):
|
||||||
"""
|
"""
|
||||||
Configuration has changed during runtime.
|
Configuration has changed during runtime.
|
||||||
MobilityManager.setconfig() -> WlanNode.updatemodel() ->
|
|
||||||
WirelessModel.updateconfig()
|
|
||||||
|
|
||||||
:param values: values to update configuration
|
:param dict config: values to update configuration
|
||||||
:return: was update successful
|
:return: nothing
|
||||||
:rtype: bool
|
|
||||||
"""
|
"""
|
||||||
self.valuestolinkparams(values)
|
self.values_from_config(config)
|
||||||
self.range = float(self.valueof("range", values))
|
self.setlinkparams()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def create_link_data(self, interface1, interface2, message_type):
|
def create_link_data(self, interface1, interface2, message_type):
|
||||||
|
@ -581,7 +520,6 @@ class BasicRangeModel(WirelessModel):
|
||||||
:return: link data
|
:return: link data
|
||||||
:rtype: LinkData
|
:rtype: LinkData
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return LinkData(
|
return LinkData(
|
||||||
message_type=message_type,
|
message_type=message_type,
|
||||||
node1_id=interface1.node.objid,
|
node1_id=interface1.node.objid,
|
||||||
|
@ -668,16 +606,16 @@ class WayPointMobility(WirelessModel):
|
||||||
STATE_RUNNING = 1
|
STATE_RUNNING = 1
|
||||||
STATE_PAUSED = 2
|
STATE_PAUSED = 2
|
||||||
|
|
||||||
def __init__(self, session, object_id, values=None):
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Create a WayPointMobility instance.
|
Create a WayPointMobility instance.
|
||||||
|
|
||||||
:param core.session.Session session: CORE session instance
|
:param core.session.Session session: CORE session instance
|
||||||
:param int object_id: object id
|
:param int object_id: object id
|
||||||
:param values: values for this model
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
super(WayPointMobility, self).__init__(session=session, object_id=object_id, values=values)
|
super(WayPointMobility, self).__init__(session=session, object_id=object_id)
|
||||||
|
|
||||||
self.state = self.STATE_STOPPED
|
self.state = self.STATE_STOPPED
|
||||||
self.queue = []
|
self.queue = []
|
||||||
self.queue_copy = []
|
self.queue_copy = []
|
||||||
|
@ -705,7 +643,6 @@ class WayPointMobility(WirelessModel):
|
||||||
self.lasttime = time.time()
|
self.lasttime = time.time()
|
||||||
now = self.lasttime - self.timezero
|
now = self.lasttime - self.timezero
|
||||||
dt = self.lasttime - t
|
dt = self.lasttime - t
|
||||||
# print "runround(now=%.2f, dt=%.2f)" % (now, dt)
|
|
||||||
|
|
||||||
# keep current waypoints up-to-date
|
# keep current waypoints up-to-date
|
||||||
self.updatepoints(now)
|
self.updatepoints(now)
|
||||||
|
@ -741,7 +678,6 @@ class WayPointMobility(WirelessModel):
|
||||||
moved_netifs.append(netif)
|
moved_netifs.append(netif)
|
||||||
|
|
||||||
# calculate all ranges after moving nodes; this saves calculations
|
# calculate all ranges after moving nodes; this saves calculations
|
||||||
# self.wlan.model.update(moved)
|
|
||||||
self.session.mobility.updatewlans(moved, moved_netifs)
|
self.session.mobility.updatewlans(moved, moved_netifs)
|
||||||
|
|
||||||
# TODO: check session state
|
# TODO: check session state
|
||||||
|
@ -806,7 +742,6 @@ class WayPointMobility(WirelessModel):
|
||||||
self.endtime = self.lasttime - self.timezero
|
self.endtime = self.lasttime - self.timezero
|
||||||
del self.points[node.objid]
|
del self.points[node.objid]
|
||||||
return False
|
return False
|
||||||
# print "node %s dx,dy= <%s, %d>" % (node.name, dx, dy)
|
|
||||||
if (x1 + dx) < 0.0:
|
if (x1 + dx) < 0.0:
|
||||||
dx = 0.0 - x1
|
dx = 0.0 - x1
|
||||||
if (y1 + dy) < 0.0:
|
if (y1 + dy) < 0.0:
|
||||||
|
@ -826,11 +761,10 @@ class WayPointMobility(WirelessModel):
|
||||||
node = netif.node
|
node = netif.node
|
||||||
if node.objid not in self.initial:
|
if node.objid not in self.initial:
|
||||||
continue
|
continue
|
||||||
(x, y, z) = self.initial[node.objid].coords
|
x, y, z = self.initial[node.objid].coords
|
||||||
self.setnodeposition(node, x, y, z)
|
self.setnodeposition(node, x, y, z)
|
||||||
moved.append(node)
|
moved.append(node)
|
||||||
moved_netifs.append(netif)
|
moved_netifs.append(netif)
|
||||||
# self.wlan.model.update(moved)
|
|
||||||
self.session.mobility.updatewlans(moved, moved_netifs)
|
self.session.mobility.updatewlans(moved, moved_netifs)
|
||||||
|
|
||||||
def addwaypoint(self, time, nodenum, x, y, z, speed):
|
def addwaypoint(self, time, nodenum, x, y, z, speed):
|
||||||
|
@ -845,7 +779,6 @@ class WayPointMobility(WirelessModel):
|
||||||
:param speed: speed
|
:param speed: speed
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# print "addwaypoint: %s %s %s,%s,%s %s" % (time, nodenum, x, y, z, speed)
|
|
||||||
wp = WayPoint(time, nodenum, coords=(x, y, z), speed=speed)
|
wp = WayPoint(time, nodenum, coords=(x, y, z), speed=speed)
|
||||||
heapq.heappush(self.queue, wp)
|
heapq.heappush(self.queue, wp)
|
||||||
|
|
||||||
|
@ -905,7 +838,6 @@ class WayPointMobility(WirelessModel):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# this would cause PyCoreNetIf.poshook() callback (range calculation)
|
# this would cause PyCoreNetIf.poshook() callback (range calculation)
|
||||||
# node.setposition(x, y, z)
|
|
||||||
node.position.set(x, y, z)
|
node.position.set(x, y, z)
|
||||||
node_data = node.data(message_type=0)
|
node_data = node.data(message_type=0)
|
||||||
self.session.broadcast_node(node_data)
|
self.session.broadcast_node(node_data)
|
||||||
|
@ -975,64 +907,58 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
BonnMotion.
|
BonnMotion.
|
||||||
"""
|
"""
|
||||||
name = "ns2script"
|
name = "ns2script"
|
||||||
|
options = [
|
||||||
config_matrix = [
|
Configuration(_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"),
|
||||||
("file", ConfigDataTypes.STRING.value, '',
|
Configuration(_id="refresh_ms", _type=ConfigDataTypes.UINT32, default="50", label="mobility script file"),
|
||||||
'', 'mobility script file'),
|
Configuration(_id="loop", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"], label="loop"),
|
||||||
("refresh_ms", ConfigDataTypes.UINT32.value, '50',
|
Configuration(_id="autostart", _type=ConfigDataTypes.STRING, label="auto-start seconds (0.0 for runtime)"),
|
||||||
'', 'refresh time (ms)'),
|
Configuration(_id="map", _type=ConfigDataTypes.STRING, label="node mapping (optional, e.g. 0:1,1:2,2:3)"),
|
||||||
("loop", ConfigDataTypes.BOOL.value, '1',
|
Configuration(_id="script_start", _type=ConfigDataTypes.STRING, label="script file to run upon start"),
|
||||||
'On,Off', 'loop'),
|
Configuration(_id="script_pause", _type=ConfigDataTypes.STRING, label="script file to run upon pause"),
|
||||||
("autostart", ConfigDataTypes.STRING.value, '',
|
Configuration(_id="script_stop", _type=ConfigDataTypes.STRING, label="script file to run upon stop")
|
||||||
'', 'auto-start seconds (0.0 for runtime)'),
|
|
||||||
("map", ConfigDataTypes.STRING.value, '',
|
|
||||||
'', 'node mapping (optional, e.g. 0:1,1:2,2:3)'),
|
|
||||||
("script_start", ConfigDataTypes.STRING.value, '',
|
|
||||||
'', 'script file to run upon start'),
|
|
||||||
("script_pause", ConfigDataTypes.STRING.value, '',
|
|
||||||
'', 'script file to run upon pause'),
|
|
||||||
("script_stop", ConfigDataTypes.STRING.value, '',
|
|
||||||
'', 'script file to run upon stop'),
|
|
||||||
]
|
]
|
||||||
config_groups = "ns-2 Mobility Script Parameters:1-%d" % len(config_matrix)
|
|
||||||
|
|
||||||
def __init__(self, session, object_id, values=None):
|
@classmethod
|
||||||
|
def config_groups(cls):
|
||||||
|
return [
|
||||||
|
ConfigGroup("ns-2 Mobility Script Parameters", 1, len(cls.configurations()))
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Creates a Ns2ScriptedMobility instance.
|
Creates a Ns2ScriptedMobility instance.
|
||||||
|
|
||||||
:param core.session.Session session: CORE session instance
|
:param core.session.Session session: CORE session instance
|
||||||
:param int object_id: object id
|
:param int object_id: object id
|
||||||
:param values: values
|
:param config: values
|
||||||
"""
|
"""
|
||||||
super(Ns2ScriptedMobility, self).__init__(session=session, object_id=object_id, values=values)
|
super(Ns2ScriptedMobility, self).__init__(session=session, object_id=object_id)
|
||||||
self._netifs = {}
|
self._netifs = {}
|
||||||
self._netifslock = threading.Lock()
|
self._netifslock = threading.Lock()
|
||||||
if values is None:
|
|
||||||
values = session.mobility.getconfig(object_id, self.name, self.getdefaultvalues())[1]
|
self.file = None
|
||||||
self.file = self.valueof("file", values)
|
self.refresh_ms = None
|
||||||
self.refresh_ms = int(self.valueof("refresh_ms", values))
|
self.loop = None
|
||||||
self.loop = self.valueof("loop", values).lower() == "on"
|
self.autostart = None
|
||||||
self.autostart = self.valueof("autostart", values)
|
self.nodemap = {}
|
||||||
self.parsemap(self.valueof("map", values))
|
self.script_start = None
|
||||||
self.script_start = self.valueof("script_start", values)
|
self.script_pause = None
|
||||||
self.script_pause = self.valueof("script_pause", values)
|
self.script_stop = None
|
||||||
self.script_stop = self.valueof("script_stop", values)
|
|
||||||
logger.info("ns-2 scripted mobility configured for WLAN %d using file: %s", object_id, self.file)
|
def update_config(self, config):
|
||||||
|
self.file = config["file"]
|
||||||
|
logger.info("ns-2 scripted mobility configured for WLAN %d using file: %s", self.object_id, self.file)
|
||||||
|
self.refresh_ms = int(config["refresh_ms"])
|
||||||
|
self.loop = config["loop"].lower() == "on"
|
||||||
|
self.autostart = config["autostart"]
|
||||||
|
self.parsemap(config["map"])
|
||||||
|
self.script_start = config["script_start"]
|
||||||
|
self.script_pause = config["script_pause"]
|
||||||
|
self.script_stop = config["script_stop"]
|
||||||
self.readscriptfile()
|
self.readscriptfile()
|
||||||
self.copywaypoints()
|
self.copywaypoints()
|
||||||
self.setendtime()
|
self.setendtime()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def configure_mob(cls, session, config_data):
|
|
||||||
"""
|
|
||||||
Handle configuration messages for setting up a model.
|
|
||||||
Pass the MobilityManager object as the manager object.
|
|
||||||
|
|
||||||
:param core.session.Session session: current session calling function
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
"""
|
|
||||||
return cls.configure(session.mobility, config_data)
|
|
||||||
|
|
||||||
def readscriptfile(self):
|
def readscriptfile(self):
|
||||||
"""
|
"""
|
||||||
Read in mobility script from a file. This adds waypoints to a
|
Read in mobility script from a file. This adds waypoints to a
|
||||||
|
@ -1043,9 +969,9 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
"""
|
"""
|
||||||
filename = self.findfile(self.file)
|
filename = self.findfile(self.file)
|
||||||
try:
|
try:
|
||||||
f = open(filename, 'r')
|
f = open(filename, "r")
|
||||||
except IOError:
|
except IOError:
|
||||||
logger.exception("ns-2 scripted mobility failed to load file '%s'", self.file)
|
logger.exception("ns-2 scripted mobility failed to load file: %s", self.file)
|
||||||
return
|
return
|
||||||
logger.info("reading ns-2 script file: %s" % filename)
|
logger.info("reading ns-2 script file: %s" % filename)
|
||||||
ln = 0
|
ln = 0
|
||||||
|
@ -1234,4 +1160,4 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
return
|
return
|
||||||
filename = self.findfile(filename)
|
filename = self.findfile(filename)
|
||||||
args = ["/bin/sh", filename, typestr]
|
args = ["/bin/sh", filename, typestr]
|
||||||
utils.check_cmd(args, cwd=self.session.sessiondir, env=self.session.get_environment())
|
utils.check_cmd(args, cwd=self.session.session_dir, env=self.session.get_environment())
|
||||||
|
|
|
@ -384,12 +384,13 @@ class WlanNode(LxBrNet):
|
||||||
Sets the mobility and wireless model.
|
Sets the mobility and wireless model.
|
||||||
|
|
||||||
:param core.mobility.WirelessModel.cls model: wireless model to set to
|
:param core.mobility.WirelessModel.cls model: wireless model to set to
|
||||||
:param config: model configuration
|
:param dict config: configuration for model being set
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logger.info("adding model: %s", model.name)
|
logger.info("adding model: %s", model.name)
|
||||||
if model.config_type == RegisterTlvs.WIRELESS.value:
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||||
self.model = model(session=self.session, object_id=self.objid, values=config)
|
self.model = model(session=self.session, object_id=self.objid)
|
||||||
|
self.model.update_config(config)
|
||||||
if self.model.position_callback:
|
if self.model.position_callback:
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
netif.poshook = self.model.position_callback
|
netif.poshook = self.model.position_callback
|
||||||
|
@ -398,33 +399,26 @@ class WlanNode(LxBrNet):
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
self.model.setlinkparams()
|
self.model.setlinkparams()
|
||||||
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, object_id=self.objid, values=config)
|
self.mobility = model(session=self.session, object_id=self.objid)
|
||||||
|
self.mobility.update_config(config)
|
||||||
|
|
||||||
def updatemodel(self, model_name, values):
|
def update_mobility(self, config):
|
||||||
"""
|
if not self.mobility:
|
||||||
Allow for model updates during runtime (similar to setmodel().)
|
raise ValueError("no mobility set to update for node(%s)", self.objid)
|
||||||
|
self.mobility.set_configs(config, node_id=self.objid)
|
||||||
|
|
||||||
:param model_name: model name to update
|
def updatemodel(self, config):
|
||||||
:param values: values to update model with
|
if not self.model:
|
||||||
:return: nothing
|
raise ValueError("no model set to update for node(%s)", self.objid)
|
||||||
"""
|
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
|
||||||
logger.info("updating model %s" % model_name)
|
self.model.set_configs(config, node_id=self.objid)
|
||||||
if self.model is None or self.model.name != model_name:
|
if self.model.position_callback:
|
||||||
return
|
for netif in self.netifs():
|
||||||
|
netif.poshook = self.model.position_callback
|
||||||
model = self.model
|
if netif.node is not None:
|
||||||
if model.config_type == RegisterTlvs.WIRELESS.value:
|
x, y, z = netif.node.position.get()
|
||||||
if not model.updateconfig(values):
|
netif.poshook(netif, x, y, z)
|
||||||
return
|
self.model.updateconfig()
|
||||||
|
|
||||||
if self.model.position_callback:
|
|
||||||
for netif in self.netifs():
|
|
||||||
netif.poshook = self.model.position_callback
|
|
||||||
if netif.node is not None:
|
|
||||||
(x, y, z) = netif.node.position.get()
|
|
||||||
netif.poshook(netif, x, y, z)
|
|
||||||
|
|
||||||
self.model.setlinkparams()
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -185,55 +185,55 @@ class OvsNet(PyCoreNet):
|
||||||
|
|
||||||
ebtables_queue.ebchange(self)
|
ebtables_queue.ebchange(self)
|
||||||
|
|
||||||
def linkconfig(self, interface, bw=None, delay=None, loss=None, duplicate=None,
|
def linkconfig(self, netif, bw=None, delay=None, loss=None, duplicate=None,
|
||||||
jitter=None, netif2=None, devname=None):
|
jitter=None, netif2=None, devname=None):
|
||||||
"""
|
"""
|
||||||
Configure link parameters by applying tc queuing disciplines on the
|
Configure link parameters by applying tc queuing disciplines on the
|
||||||
interface.
|
interface.
|
||||||
"""
|
"""
|
||||||
if not devname:
|
if not devname:
|
||||||
devname = interface.localname
|
devname = netif.localname
|
||||||
|
|
||||||
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
|
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
|
||||||
parent = ["root"]
|
parent = ["root"]
|
||||||
|
|
||||||
# attempt to set bandwidth and update as needed if value changed
|
# attempt to set bandwidth and update as needed if value changed
|
||||||
bandwidth_changed = interface.setparam("bw", bw)
|
bandwidth_changed = netif.setparam("bw", bw)
|
||||||
if bandwidth_changed:
|
if bandwidth_changed:
|
||||||
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
||||||
if bw > 0:
|
if bw > 0:
|
||||||
if self.up:
|
if self.up:
|
||||||
burst = max(2 * interface.mtu, bw / 1000)
|
burst = max(2 * netif.mtu, bw / 1000)
|
||||||
limit = 0xffff # max IP payload
|
limit = 0xffff # max IP payload
|
||||||
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
|
tbf = ["tbf", "rate", str(bw), "burst", str(burst), "limit", str(limit)]
|
||||||
logger.info("linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf])
|
logger.info("linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf])
|
||||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||||
interface.setparam("has_tbf", True)
|
netif.setparam("has_tbf", True)
|
||||||
elif interface.getparam("has_tbf") and bw <= 0:
|
elif netif.getparam("has_tbf") and bw <= 0:
|
||||||
tcd = [] + tc
|
tcd = [] + tc
|
||||||
tcd[2] = "delete"
|
tcd[2] = "delete"
|
||||||
|
|
||||||
if self.up:
|
if self.up:
|
||||||
utils.check_cmd(tcd + parent)
|
utils.check_cmd(tcd + parent)
|
||||||
|
|
||||||
interface.setparam("has_tbf", False)
|
netif.setparam("has_tbf", False)
|
||||||
# removing the parent removes the child
|
# removing the parent removes the child
|
||||||
interface.setparam("has_netem", False)
|
netif.setparam("has_netem", False)
|
||||||
|
|
||||||
if interface.getparam("has_tbf"):
|
if netif.getparam("has_tbf"):
|
||||||
parent = ["parent", "1:1"]
|
parent = ["parent", "1:1"]
|
||||||
|
|
||||||
netem = ["netem"]
|
netem = ["netem"]
|
||||||
delay_changed = interface.setparam("delay", delay)
|
delay_changed = netif.setparam("delay", delay)
|
||||||
|
|
||||||
if loss is not None:
|
if loss is not None:
|
||||||
loss = float(loss)
|
loss = float(loss)
|
||||||
loss_changed = interface.setparam("loss", loss)
|
loss_changed = netif.setparam("loss", loss)
|
||||||
|
|
||||||
if duplicate is not None:
|
if duplicate is not None:
|
||||||
duplicate = float(duplicate)
|
duplicate = float(duplicate)
|
||||||
duplicate_changed = interface.setparam("duplicate", duplicate)
|
duplicate_changed = netif.setparam("duplicate", duplicate)
|
||||||
jitter_changed = interface.setparam("jitter", jitter)
|
jitter_changed = netif.setparam("jitter", jitter)
|
||||||
|
|
||||||
# if nothing changed return
|
# if nothing changed return
|
||||||
if not any([bandwidth_changed, delay_changed, loss_changed, duplicate_changed, jitter_changed]):
|
if not any([bandwidth_changed, delay_changed, loss_changed, duplicate_changed, jitter_changed]):
|
||||||
|
@ -248,15 +248,15 @@ class OvsNet(PyCoreNet):
|
||||||
if jitter is not None:
|
if jitter is not None:
|
||||||
netem += ["%sus" % jitter, "25%"]
|
netem += ["%sus" % jitter, "25%"]
|
||||||
|
|
||||||
if loss is not None:
|
if loss is not None and loss > 0:
|
||||||
netem += ["loss", "%s%%" % min(loss, 100)]
|
netem += ["loss", "%s%%" % min(loss, 100)]
|
||||||
|
|
||||||
if duplicate is not None:
|
if duplicate is not None and duplicate > 0:
|
||||||
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
|
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
|
||||||
|
|
||||||
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
|
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
|
||||||
# possibly remove netem if it exists and parent queue wasn"t removed
|
# possibly remove netem if it exists and parent queue wasn"t removed
|
||||||
if not interface.getparam("has_netem"):
|
if not netif.getparam("has_netem"):
|
||||||
return
|
return
|
||||||
|
|
||||||
tc[2] = "delete"
|
tc[2] = "delete"
|
||||||
|
@ -264,12 +264,12 @@ class OvsNet(PyCoreNet):
|
||||||
if self.up:
|
if self.up:
|
||||||
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"])
|
utils.check_cmd(tc + parent + ["handle", "10:"])
|
||||||
interface.setparam("has_netem", False)
|
netif.setparam("has_netem", False)
|
||||||
elif len(netem) > 1:
|
elif len(netem) > 1:
|
||||||
if self.up:
|
if self.up:
|
||||||
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||||
interface.setparam("has_netem", True)
|
netif.setparam("has_netem", True)
|
||||||
|
|
||||||
def linknet(self, network):
|
def linknet(self, network):
|
||||||
"""
|
"""
|
||||||
|
@ -305,7 +305,7 @@ class OvsNet(PyCoreNet):
|
||||||
utils.check_cmd([constants.OVS_BIN, "add-port", network.bridge_name, interface.name])
|
utils.check_cmd([constants.OVS_BIN, "add-port", network.bridge_name, interface.name])
|
||||||
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
|
||||||
|
|
||||||
# TODO: is there a native method for this? see if this causes issues
|
# TODO: is there a native method for this? see if this causes issues
|
||||||
# i = network.newifindex()
|
# i = network.newifindex()
|
||||||
# network._netif[i] = interface
|
# network._netif[i] = interface
|
||||||
# with network._linked_lock:
|
# with network._linked_lock:
|
||||||
|
@ -351,12 +351,12 @@ class OvsCtrlNet(OvsNet):
|
||||||
|
|
||||||
def __init__(self, session, objid="ctrlnet", name=None, prefix=None, hostid=None,
|
def __init__(self, session, objid="ctrlnet", name=None, prefix=None, hostid=None,
|
||||||
start=True, assign_address=True, updown_script=None, serverintf=None):
|
start=True, assign_address=True, updown_script=None, serverintf=None):
|
||||||
OvsNet.__init__(self, session, objid=objid, name=name, start=start)
|
|
||||||
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
||||||
self.hostid = hostid
|
self.hostid = hostid
|
||||||
self.assign_address = assign_address
|
self.assign_address = assign_address
|
||||||
self.updown_script = updown_script
|
self.updown_script = updown_script
|
||||||
self.serverintf = serverintf
|
self.serverintf = serverintf
|
||||||
|
OvsNet.__init__(self, session, objid=objid, name=name, start=start)
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
if self.detectoldbridge():
|
if self.detectoldbridge():
|
||||||
|
@ -593,14 +593,14 @@ class OvsWlanNode(OvsNet):
|
||||||
interface.setposition(x, y, z)
|
interface.setposition(x, y, z)
|
||||||
# self.model.setlinkparams()
|
# self.model.setlinkparams()
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model, config=None):
|
||||||
"""
|
"""
|
||||||
Mobility and wireless model.
|
Mobility and wireless model.
|
||||||
"""
|
"""
|
||||||
logger.info("adding model %s", model.name)
|
logger.info("adding model %s", model.name)
|
||||||
|
|
||||||
if model.type == RegisterTlvs.WIRELESS.value:
|
if model.type == RegisterTlvs.WIRELESS.value:
|
||||||
self.model = model(session=self.session, object_id=self.objid, values=config)
|
self.model = model(session=self.session, object_id=self.objid, config=config)
|
||||||
if self.model.position_callback:
|
if self.model.position_callback:
|
||||||
for interface in self.netifs():
|
for interface in self.netifs():
|
||||||
interface.poshook = self.model.position_callback
|
interface.poshook = self.model.position_callback
|
||||||
|
@ -609,31 +609,20 @@ class OvsWlanNode(OvsNet):
|
||||||
interface.poshook(interface, x, y, z)
|
interface.poshook(interface, x, y, z)
|
||||||
self.model.setlinkparams()
|
self.model.setlinkparams()
|
||||||
elif model.type == RegisterTlvs.MOBILITY.value:
|
elif model.type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, object_id=self.objid, values=config)
|
self.mobility = model(session=self.session, object_id=self.objid, config=config)
|
||||||
|
|
||||||
def updatemodel(self, model_name, values):
|
def updatemodel(self, config):
|
||||||
"""
|
if not self.model:
|
||||||
Allow for model updates during runtime (similar to setmodel().)
|
raise ValueError("no model set to update for node(%s)", self.objid)
|
||||||
"""
|
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
|
||||||
logger.info("updating model %s", model_name)
|
self.model.set_configs(config, node_id=self.objid)
|
||||||
if self.model is None or self.model.name != model_name:
|
if self.model.position_callback:
|
||||||
logger.info(
|
for netif in self.netifs():
|
||||||
"failure to update model, model doesn't exist or invalid name: model(%s) - name(%s)",
|
netif.poshook = self.model.position_callback
|
||||||
self.model, model_name
|
if netif.node is not None:
|
||||||
)
|
x, y, z = netif.node.position.get()
|
||||||
return
|
netif.poshook(netif, x, y, z)
|
||||||
|
self.model.updateconfig()
|
||||||
model = self.model
|
|
||||||
if model.type == RegisterTlvs.WIRELESS.value:
|
|
||||||
if not model.updateconfig(values):
|
|
||||||
return
|
|
||||||
if self.model.position_callback:
|
|
||||||
for interface in self.netifs():
|
|
||||||
interface.poshook = self.model.position_callback
|
|
||||||
if interface.node is not None:
|
|
||||||
x, y, z = interface.node.position.get()
|
|
||||||
interface.poshook(interface, x, y, z)
|
|
||||||
self.model.setlinkparams()
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags):
|
||||||
all_links = OvsNet.all_link_data(self, flags)
|
all_links = OvsNet.all_link_data(self, flags)
|
||||||
|
|
|
@ -169,6 +169,7 @@ class TunTap(PyCoreNetIf):
|
||||||
:return: wait for device local response
|
:return: wait for device local response
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
|
logger.debug("waiting for device local: %s", self.localname)
|
||||||
|
|
||||||
def localdevexists():
|
def localdevexists():
|
||||||
args = [constants.IP_BIN, "link", "show", self.localname]
|
args = [constants.IP_BIN, "link", "show", self.localname]
|
||||||
|
@ -182,6 +183,7 @@ class TunTap(PyCoreNetIf):
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
|
logger.debug("waiting for device node: %s", self.name)
|
||||||
|
|
||||||
def nodedevexists():
|
def nodedevexists():
|
||||||
args = [constants.IP_BIN, "link", "show", self.name]
|
args = [constants.IP_BIN, "link", "show", self.name]
|
||||||
|
|
|
@ -440,7 +440,7 @@ class LxBrNet(PyCoreNet):
|
||||||
"burst", str(burst), "limit", str(limit)]
|
"burst", str(burst), "limit", str(limit)]
|
||||||
if bw > 0:
|
if bw > 0:
|
||||||
if self.up:
|
if self.up:
|
||||||
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],))
|
logger.debug("linkconfig: %s" % ([tc + parent + ["handle", "1:"] + tbf],))
|
||||||
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
|
||||||
netif.setparam("has_tbf", True)
|
netif.setparam("has_tbf", True)
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -475,9 +475,9 @@ class LxBrNet(PyCoreNet):
|
||||||
else:
|
else:
|
||||||
netem += ["%sus" % jitter, "25%"]
|
netem += ["%sus" % jitter, "25%"]
|
||||||
|
|
||||||
if loss is not None:
|
if loss is not None and loss > 0:
|
||||||
netem += ["loss", "%s%%" % min(loss, 100)]
|
netem += ["loss", "%s%%" % min(loss, 100)]
|
||||||
if duplicate is not None:
|
if duplicate is not None and duplicate > 0:
|
||||||
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
|
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
|
||||||
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
|
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
|
||||||
# possibly remove netem if it exists and parent queue wasn't removed
|
# possibly remove netem if it exists and parent queue wasn't removed
|
||||||
|
@ -485,12 +485,12 @@ class LxBrNet(PyCoreNet):
|
||||||
return
|
return
|
||||||
tc[2] = "delete"
|
tc[2] = "delete"
|
||||||
if self.up:
|
if self.up:
|
||||||
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
logger.debug("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"])
|
utils.check_cmd(tc + parent + ["handle", "10:"])
|
||||||
netif.setparam("has_netem", False)
|
netif.setparam("has_netem", False)
|
||||||
elif len(netem) > 1:
|
elif len(netem) > 1:
|
||||||
if self.up:
|
if self.up:
|
||||||
logger.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
logger.debug("linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],))
|
||||||
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
|
||||||
netif.setparam("has_netem", True)
|
netif.setparam("has_netem", True)
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,9 @@ class SimpleLxcNode(PyCoreNode):
|
||||||
if not self.up:
|
if not self.up:
|
||||||
return
|
return
|
||||||
|
|
||||||
# unmount all targets
|
# unmount all targets (NOTE: non-persistent mount namespaces are
|
||||||
while self._mounts:
|
# removed by the kernel when last referencing process is killed)
|
||||||
source, target = self._mounts.pop(-1)
|
self._mounts = []
|
||||||
self.umount(target)
|
|
||||||
|
|
||||||
# shutdown all interfaces
|
# shutdown all interfaces
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
|
@ -157,14 +156,6 @@ class SimpleLxcNode(PyCoreNode):
|
||||||
self.client.close()
|
self.client.close()
|
||||||
self.up = False
|
self.up = False
|
||||||
|
|
||||||
def boot(self):
|
|
||||||
"""
|
|
||||||
Boot logic.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def cmd(self, args, wait=True):
|
def cmd(self, args, wait=True):
|
||||||
"""
|
"""
|
||||||
Runs shell command on node, with option to not wait for a result.
|
Runs shell command on node, with option to not wait for a result.
|
||||||
|
@ -223,19 +214,6 @@ class SimpleLxcNode(PyCoreNode):
|
||||||
raise CoreCommandError(status, cmd, output)
|
raise CoreCommandError(status, cmd, output)
|
||||||
self._mounts.append((source, target))
|
self._mounts.append((source, target))
|
||||||
|
|
||||||
def umount(self, target):
|
|
||||||
"""
|
|
||||||
Unmount a target directory.
|
|
||||||
|
|
||||||
:param str target: target directory to unmount
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
logger.info("node(%s) unmounting: %s", self.name, target)
|
|
||||||
try:
|
|
||||||
self.check_cmd([constants.UMOUNT_BIN, "-n", "-l", target])
|
|
||||||
except CoreCommandError:
|
|
||||||
logger.exception("error during unmount")
|
|
||||||
|
|
||||||
def newifindex(self):
|
def newifindex(self):
|
||||||
"""
|
"""
|
||||||
Retrieve a new interface index.
|
Retrieve a new interface index.
|
||||||
|
@ -524,22 +502,6 @@ class LxcNode(SimpleLxcNode):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def boot(self):
|
|
||||||
"""
|
|
||||||
Boot the node.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
self.session.services.bootnodeservices(self)
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
"""
|
|
||||||
Validate the node.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
self.session.services.validatenodeservices(self)
|
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
"""
|
"""
|
||||||
Startup logic for the node.
|
Startup logic for the node.
|
||||||
|
|
|
@ -25,12 +25,6 @@ class PhysicalNode(PyCoreNode):
|
||||||
if start:
|
if start:
|
||||||
self.startup()
|
self.startup()
|
||||||
|
|
||||||
def boot(self):
|
|
||||||
self.session.services.bootnodeservices(self)
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
self.session.services.validatenodeservices(self)
|
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.makenodedir()
|
self.makenodedir()
|
||||||
|
|
|
@ -118,12 +118,7 @@ class Sdt(object):
|
||||||
:return: True if enabled, False otherwise
|
:return: True if enabled, False otherwise
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
if not hasattr(self.session.options, "enablesdt"):
|
return self.session.options.get_config("enablesdt") == "1"
|
||||||
return False
|
|
||||||
enabled = self.session.options.enablesdt
|
|
||||||
if enabled in ("1", "true", 1, True):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def seturl(self):
|
def seturl(self):
|
||||||
"""
|
"""
|
||||||
|
@ -132,11 +127,8 @@ class Sdt(object):
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
url = None
|
url = self.session.options.get_config("stdurl")
|
||||||
if hasattr(self.session.options, "sdturl"):
|
if not url:
|
||||||
if self.session.options.sdturl != "":
|
|
||||||
url = self.session.options.sdturl
|
|
||||||
if url is None or url == "":
|
|
||||||
url = self.DEFAULT_SDT_URL
|
url = self.DEFAULT_SDT_URL
|
||||||
self.url = urlparse(url)
|
self.url = urlparse(url)
|
||||||
self.address = (self.url.hostname, self.url.port)
|
self.address = (self.url.hostname, self.url.port)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,6 +15,7 @@ def load():
|
||||||
"""
|
"""
|
||||||
Loads all services from the modules that reside under core.services.
|
Loads all services from the modules that reside under core.services.
|
||||||
|
|
||||||
:return: nothing
|
:return: list of services that failed to load
|
||||||
|
:rtype: list[str]
|
||||||
"""
|
"""
|
||||||
ServiceManager.add_services(_PATH)
|
return ServiceManager.add_services(_PATH)
|
||||||
|
|
|
@ -9,23 +9,22 @@ class Bird(CoreService):
|
||||||
"""
|
"""
|
||||||
Bird router support
|
Bird router support
|
||||||
"""
|
"""
|
||||||
_name = "bird"
|
name = "bird"
|
||||||
_group = "BIRD"
|
executables = ("bird",)
|
||||||
_depends = ()
|
group = "BIRD"
|
||||||
_dirs = ("/etc/bird",)
|
dirs = ("/etc/bird",)
|
||||||
_configs = ("/etc/bird/bird.conf",)
|
configs = ("/etc/bird/bird.conf",)
|
||||||
_startindex = 35
|
startup = ("bird -c %s" % (configs[0]),)
|
||||||
_startup = ("bird -c %s" % (_configs[0]),)
|
shutdown = ("killall bird",)
|
||||||
_shutdown = ("killall bird",)
|
validate = ("pidof bird",)
|
||||||
_validate = ("pidof bird",)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the bird.conf file contents.
|
Return the bird.conf file contents.
|
||||||
"""
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls.configs[0]:
|
||||||
return cls.generateBirdConf(node, services)
|
return cls.generateBirdConf(node)
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ class Bird(CoreService):
|
||||||
Helper to return the first IPv4 address of a node as its router ID.
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
|
@ -44,7 +43,7 @@ class Bird(CoreService):
|
||||||
return "0.0.0.0"
|
return "0.0.0.0"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateBirdConf(cls, node, services):
|
def generateBirdConf(cls, node):
|
||||||
"""
|
"""
|
||||||
Returns configuration file text. Other services that depend on bird
|
Returns configuration file text. Other services that depend on bird
|
||||||
will have generatebirdifcconfig() and generatebirdconfig()
|
will have generatebirdifcconfig() and generatebirdconfig()
|
||||||
|
@ -73,11 +72,11 @@ protocol device {
|
||||||
scan time 10; # Scan interfaces every 10 seconds
|
scan time 10; # Scan interfaces every 10 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
""" % (cls._name, cls.routerid(node))
|
""" % (cls.name, cls.routerid(node))
|
||||||
|
|
||||||
# Generate protocol specific configurations
|
# Generate protocol specific configurations
|
||||||
for s in services:
|
for s in node.services:
|
||||||
if cls._name not in s._depends:
|
if cls.name not in s.dependencies:
|
||||||
continue
|
continue
|
||||||
cfg += s.generatebirdconfig(node)
|
cfg += s.generatebirdconfig(node)
|
||||||
|
|
||||||
|
@ -90,15 +89,15 @@ class BirdService(CoreService):
|
||||||
common to Bird's routing daemons.
|
common to Bird's routing daemons.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name = None
|
name = None
|
||||||
_group = "BIRD"
|
executables = ("bird",)
|
||||||
_depends = ("bird",)
|
group = "BIRD"
|
||||||
_dirs = ()
|
dependencies = ("bird",)
|
||||||
_configs = ()
|
dirs = ()
|
||||||
_startindex = 40
|
configs = ()
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ()
|
shutdown = ()
|
||||||
_meta = "The config file for this service can be found in the bird service."
|
meta = "The config file for this service can be found in the bird service."
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
|
@ -106,14 +105,15 @@ class BirdService(CoreService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdifcconfig(cls, node):
|
def generatebirdifcconfig(cls, node):
|
||||||
''' Use only bare interfaces descriptions in generated protocol
|
"""
|
||||||
|
Use only bare interfaces descriptions in generated protocol
|
||||||
configurations. This has the slight advantage of being the same
|
configurations. This has the slight advantage of being the same
|
||||||
everywhere.
|
everywhere.
|
||||||
'''
|
"""
|
||||||
cfg = ""
|
cfg = ""
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += ' interface "%s";\n' % ifc.name
|
cfg += ' interface "%s";\n' % ifc.name
|
||||||
|
|
||||||
|
@ -125,8 +125,8 @@ class BirdBgp(BirdService):
|
||||||
BGP BIRD Service (configuration generation)
|
BGP BIRD Service (configuration generation)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name = "BIRD_BGP"
|
name = "BIRD_BGP"
|
||||||
_custom_needed = True
|
custom_needed = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
|
@ -156,7 +156,7 @@ class BirdOspf(BirdService):
|
||||||
OSPF BIRD Service (configuration generation)
|
OSPF BIRD Service (configuration generation)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name = "BIRD_OSPFv2"
|
name = "BIRD_OSPFv2"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
|
@ -181,7 +181,7 @@ class BirdRadv(BirdService):
|
||||||
RADV BIRD Service (configuration generation)
|
RADV BIRD Service (configuration generation)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name = "BIRD_RADV"
|
name = "BIRD_RADV"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
|
@ -209,7 +209,7 @@ class BirdRip(BirdService):
|
||||||
RIP BIRD Service (configuration generation)
|
RIP BIRD Service (configuration generation)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name = "BIRD_RIP"
|
name = "BIRD_RIP"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
|
@ -231,8 +231,8 @@ class BirdStatic(BirdService):
|
||||||
Static Bird Service (configuration generation)
|
Static Bird Service (configuration generation)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name = "BIRD_static"
|
name = "BIRD_static"
|
||||||
_custom_needed = True
|
custom_needed = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatebirdconfig(cls, node):
|
def generatebirdconfig(cls, node):
|
||||||
|
|
|
@ -104,7 +104,7 @@ from core.service import ServiceManager
|
||||||
try:
|
try:
|
||||||
from docker import Client
|
from docker import Client
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.error("failure to import docker")
|
logger.warn("missing python docker bindings")
|
||||||
|
|
||||||
|
|
||||||
class DockerService(CoreService):
|
class DockerService(CoreService):
|
||||||
|
@ -112,19 +112,18 @@ class DockerService(CoreService):
|
||||||
This is a service which will allow running docker containers in a CORE
|
This is a service which will allow running docker containers in a CORE
|
||||||
node.
|
node.
|
||||||
"""
|
"""
|
||||||
_name = "Docker"
|
name = "Docker"
|
||||||
_group = "Docker"
|
executables = ("docker",)
|
||||||
_depends = ()
|
group = "Docker"
|
||||||
_dirs = ('/var/lib/docker/containers/', '/run/shm', '/run/resolvconf',)
|
dirs = ('/var/lib/docker/containers/', '/run/shm', '/run/resolvconf',)
|
||||||
_configs = ('docker.sh',)
|
configs = ('docker.sh',)
|
||||||
_startindex = 50
|
startup = ('sh docker.sh',)
|
||||||
_startup = ('sh docker.sh',)
|
shutdown = ('service docker stop',)
|
||||||
_shutdown = ('service docker stop',)
|
|
||||||
# Container image to start
|
# Container image to start
|
||||||
_image = ""
|
image = ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Returns a string having contents of a docker.sh script that
|
Returns a string having contents of a docker.sh script that
|
||||||
can be modified to start a specific docker image.
|
can be modified to start a specific docker image.
|
||||||
|
@ -139,7 +138,7 @@ class DockerService(CoreService):
|
||||||
# distros may just be docker
|
# distros may just be docker
|
||||||
cfg += 'service docker start\n'
|
cfg += 'service docker start\n'
|
||||||
cfg += "# you could add a command to start a image here eg:\n"
|
cfg += "# you could add a command to start a image here eg:\n"
|
||||||
if not cls._image:
|
if not cls.image:
|
||||||
cfg += "# docker run -d --net host --name coreDock <imagename>\n"
|
cfg += "# docker run -d --net host --name coreDock <imagename>\n"
|
||||||
else:
|
else:
|
||||||
cfg += """\
|
cfg += """\
|
||||||
|
@ -150,7 +149,7 @@ until [ $result -eq 0 ]; do
|
||||||
# this is to alleviate contention to docker's SQLite database
|
# this is to alleviate contention to docker's SQLite database
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
done
|
done
|
||||||
""" % (cls._image,)
|
""" % (cls.image,)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
37
daemon/core/services/emaneservices.py
Normal file
37
daemon/core/services/emaneservices.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.misc import nodeutils
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.xml import emanexml
|
||||||
|
|
||||||
|
|
||||||
|
class EmaneTransportService(CoreService):
|
||||||
|
name = "transportd"
|
||||||
|
executables = ("emanetransportd", "emanegentransportxml")
|
||||||
|
group = "EMANE"
|
||||||
|
dependencies = ()
|
||||||
|
dirs = ()
|
||||||
|
configs = ("emanetransport.sh",)
|
||||||
|
startup = ("sh %s" % configs[0],)
|
||||||
|
validate = ("pidof %s" % executables[0],)
|
||||||
|
validation_timer = 0.5
|
||||||
|
shutdown = ("killall %s" % executables[0],)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate_config(cls, node, filename):
|
||||||
|
if filename == cls.configs[0]:
|
||||||
|
transport_commands = []
|
||||||
|
for interface in node.netifs(sort=True):
|
||||||
|
network_node = node.session.get_object(interface.net.objid)
|
||||||
|
if nodeutils.is_node(network_node, NodeTypes.EMANE):
|
||||||
|
config = node.session.emane.get_configs(network_node.objid, network_node.model.name)
|
||||||
|
if config and emanexml.is_external(config):
|
||||||
|
nem_id = network_node.getnemid(interface)
|
||||||
|
command = "emanetransportd -r -l 0 -d ../transportdaemon%s.xml" % nem_id
|
||||||
|
transport_commands.append(command)
|
||||||
|
transport_commands = "\n".join(transport_commands)
|
||||||
|
return """
|
||||||
|
emanegentransportxml -o ../ ../platform%s.xml
|
||||||
|
%s
|
||||||
|
""" % (node.objid, transport_commands)
|
||||||
|
else:
|
||||||
|
raise ValueError
|
|
@ -13,17 +13,15 @@ class NrlService(CoreService):
|
||||||
Parent class for NRL services. Defines properties and methods
|
Parent class for NRL services. Defines properties and methods
|
||||||
common to NRL's routing daemons.
|
common to NRL's routing daemons.
|
||||||
"""""
|
"""""
|
||||||
_name = None
|
name = None
|
||||||
_group = "ProtoSvc"
|
group = "ProtoSvc"
|
||||||
_depends = ()
|
dirs = ()
|
||||||
_dirs = ()
|
configs = ()
|
||||||
_configs = ()
|
startup = ()
|
||||||
_startindex = 45
|
shutdown = ()
|
||||||
_startup = ()
|
|
||||||
_shutdown = ()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -34,7 +32,7 @@ class NrlService(CoreService):
|
||||||
interface's prefix length, so e.g. '/32' can turn into '/24'.
|
interface's prefix length, so e.g. '/32' can turn into '/24'.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
|
@ -46,15 +44,15 @@ class NrlService(CoreService):
|
||||||
|
|
||||||
|
|
||||||
class MgenSinkService(NrlService):
|
class MgenSinkService(NrlService):
|
||||||
_name = "MGEN_Sink"
|
name = "MGEN_Sink"
|
||||||
_configs = ("sink.mgen",)
|
executables = ("mgen",)
|
||||||
_startindex = 5
|
configs = ("sink.mgen",)
|
||||||
_startup = ("mgen input sink.mgen",)
|
startup = ("mgen input sink.mgen",)
|
||||||
_validate = ("pidof mgen",)
|
validate = ("pidof mgen",)
|
||||||
_shutdown = ("killall mgen",)
|
shutdown = ("killall mgen",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
cfg = "0.0 LISTEN UDP 5000\n"
|
cfg = "0.0 LISTEN UDP 5000\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
name = utils.sysctl_devname(ifc.name)
|
name = utils.sysctl_devname(ifc.name)
|
||||||
|
@ -62,8 +60,8 @@ class MgenSinkService(NrlService):
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def get_startup(cls, node):
|
||||||
cmd = cls._startup[0]
|
cmd = cls.startup[0]
|
||||||
cmd += " output /tmp/mgen_%s.log" % node.name
|
cmd += " output /tmp/mgen_%s.log" % node.name
|
||||||
return cmd,
|
return cmd,
|
||||||
|
|
||||||
|
@ -72,27 +70,27 @@ class NrlNhdp(NrlService):
|
||||||
"""
|
"""
|
||||||
NeighborHood Discovery Protocol for MANET networks.
|
NeighborHood Discovery Protocol for MANET networks.
|
||||||
"""
|
"""
|
||||||
_name = "NHDP"
|
name = "NHDP"
|
||||||
_startup = ("nrlnhdp",)
|
executables = ("nrlnhdp",)
|
||||||
_shutdown = ("killall nrlnhdp",)
|
startup = ("nrlnhdp",)
|
||||||
_validate = ("pidof nrlnhdp",)
|
shutdown = ("killall nrlnhdp",)
|
||||||
|
validate = ("pidof nrlnhdp",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def get_startup(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate the appropriate command-line based on node interfaces.
|
Generate the appropriate command-line based on node interfaces.
|
||||||
"""
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls.startup[0]
|
||||||
cmd += " -l /var/log/nrlnhdp.log"
|
cmd += " -l /var/log/nrlnhdp.log"
|
||||||
cmd += " -rpipe %s_nhdp" % node.name
|
cmd += " -rpipe %s_nhdp" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x._name, services)
|
servicenames = map(lambda x: x.name, node.services)
|
||||||
if "SMF" in servicenames:
|
if "SMF" in servicenames:
|
||||||
cmd += " -flooding ecds"
|
cmd += " -flooding ecds"
|
||||||
cmd += " -smfClient %s_smf" % node.name
|
cmd += " -smfClient %s_smf" % node.name
|
||||||
|
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), \
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
node.netifs())
|
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
interfacenames = map(lambda x: x.name, netifs)
|
interfacenames = map(lambda x: x.name, netifs)
|
||||||
cmd += " -i "
|
cmd += " -i "
|
||||||
|
@ -105,14 +103,15 @@ class NrlSmf(NrlService):
|
||||||
"""
|
"""
|
||||||
Simplified Multicast Forwarding for MANET networks.
|
Simplified Multicast Forwarding for MANET networks.
|
||||||
"""
|
"""
|
||||||
_name = "SMF"
|
name = "SMF"
|
||||||
_startup = ("sh startsmf.sh",)
|
executables = ("nrlsmf",)
|
||||||
_shutdown = ("killall nrlsmf",)
|
startup = ("sh startsmf.sh",)
|
||||||
_validate = ("pidof nrlsmf",)
|
shutdown = ("killall nrlsmf",)
|
||||||
_configs = ("startsmf.sh",)
|
validate = ("pidof nrlsmf",)
|
||||||
|
configs = ("startsmf.sh",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename, ):
|
||||||
"""
|
"""
|
||||||
Generate a startup script for SMF. Because nrlsmf does not
|
Generate a startup script for SMF. Because nrlsmf does not
|
||||||
daemonize, it can cause problems in some situations when launched
|
daemonize, it can cause problems in some situations when launched
|
||||||
|
@ -123,7 +122,7 @@ class NrlSmf(NrlService):
|
||||||
comments = ""
|
comments = ""
|
||||||
cmd = "nrlsmf instance %s_smf" % node.name
|
cmd = "nrlsmf instance %s_smf" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x._name, services)
|
servicenames = map(lambda x: x.name, node.services)
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
if len(netifs) == 0:
|
if len(netifs) == 0:
|
||||||
return ""
|
return ""
|
||||||
|
@ -156,17 +155,18 @@ class NrlOlsr(NrlService):
|
||||||
"""
|
"""
|
||||||
Optimized Link State Routing protocol for MANET networks.
|
Optimized Link State Routing protocol for MANET networks.
|
||||||
"""
|
"""
|
||||||
_name = "OLSR"
|
name = "OLSR"
|
||||||
_startup = ("nrlolsrd",)
|
executables = ("nrlolsrd",)
|
||||||
_shutdown = ("killall nrlolsrd",)
|
startup = ("nrlolsrd",)
|
||||||
_validate = ("pidof nrlolsrd",)
|
shutdown = ("killall nrlolsrd",)
|
||||||
|
validate = ("pidof nrlolsrd",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def get_startup(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate the appropriate command-line based on node interfaces.
|
Generate the appropriate command-line based on node interfaces.
|
||||||
"""
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls.startup[0]
|
||||||
# are multiple interfaces supported? No.
|
# are multiple interfaces supported? No.
|
||||||
netifs = list(node.netifs())
|
netifs = list(node.netifs())
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
|
@ -175,7 +175,7 @@ class NrlOlsr(NrlService):
|
||||||
cmd += " -l /var/log/nrlolsrd.log"
|
cmd += " -l /var/log/nrlolsrd.log"
|
||||||
cmd += " -rpipe %s_olsr" % node.name
|
cmd += " -rpipe %s_olsr" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x._name, services)
|
servicenames = map(lambda x: x.name, node.services)
|
||||||
if "SMF" in servicenames and not "NHDP" in servicenames:
|
if "SMF" in servicenames and not "NHDP" in servicenames:
|
||||||
cmd += " -flooding s-mpr"
|
cmd += " -flooding s-mpr"
|
||||||
cmd += " -smfClient %s_smf" % node.name
|
cmd += " -smfClient %s_smf" % node.name
|
||||||
|
@ -189,21 +189,22 @@ class NrlOlsrv2(NrlService):
|
||||||
"""
|
"""
|
||||||
Optimized Link State Routing protocol version 2 for MANET networks.
|
Optimized Link State Routing protocol version 2 for MANET networks.
|
||||||
"""
|
"""
|
||||||
_name = "OLSRv2"
|
name = "OLSRv2"
|
||||||
_startup = ("nrlolsrv2",)
|
executables = ("nrlolsrv2",)
|
||||||
_shutdown = ("killall nrlolsrv2",)
|
startup = ("nrlolsrv2",)
|
||||||
_validate = ("pidof nrlolsrv2",)
|
shutdown = ("killall nrlolsrv2",)
|
||||||
|
validate = ("pidof nrlolsrv2",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def get_startup(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate the appropriate command-line based on node interfaces.
|
Generate the appropriate command-line based on node interfaces.
|
||||||
"""
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls.startup[0]
|
||||||
cmd += " -l /var/log/nrlolsrv2.log"
|
cmd += " -l /var/log/nrlolsrv2.log"
|
||||||
cmd += " -rpipe %s_olsrv2" % node.name
|
cmd += " -rpipe %s_olsrv2" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x._name, services)
|
servicenames = map(lambda x: x.name, node.services)
|
||||||
if "SMF" in servicenames:
|
if "SMF" in servicenames:
|
||||||
cmd += " -flooding ecds"
|
cmd += " -flooding ecds"
|
||||||
cmd += " -smfClient %s_smf" % node.name
|
cmd += " -smfClient %s_smf" % node.name
|
||||||
|
@ -223,19 +224,20 @@ class OlsrOrg(NrlService):
|
||||||
"""
|
"""
|
||||||
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
||||||
"""
|
"""
|
||||||
_name = "OLSRORG"
|
name = "OLSRORG"
|
||||||
_configs = ("/etc/olsrd/olsrd.conf",)
|
executables = ("olsrd",)
|
||||||
_dirs = ("/etc/olsrd",)
|
configs = ("/etc/olsrd/olsrd.conf",)
|
||||||
_startup = ("olsrd",)
|
dirs = ("/etc/olsrd",)
|
||||||
_shutdown = ("killall olsrd",)
|
startup = ("olsrd",)
|
||||||
_validate = ("pidof olsrd",)
|
shutdown = ("killall olsrd",)
|
||||||
|
validate = ("pidof olsrd",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getstartup(cls, node, services):
|
def get_startup(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate the appropriate command-line based on node interfaces.
|
Generate the appropriate command-line based on node interfaces.
|
||||||
"""
|
"""
|
||||||
cmd = cls._startup[0]
|
cmd = cls.startup[0]
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
if len(netifs) > 0:
|
if len(netifs) > 0:
|
||||||
interfacenames = map(lambda x: x.name, netifs)
|
interfacenames = map(lambda x: x.name, netifs)
|
||||||
|
@ -245,7 +247,7 @@ class OlsrOrg(NrlService):
|
||||||
return cmd,
|
return cmd,
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a default olsrd config file to use the broadcast address of 255.255.255.255.
|
Generate a default olsrd config file to use the broadcast address of 255.255.255.255.
|
||||||
"""
|
"""
|
||||||
|
@ -572,27 +574,24 @@ class MgenActor(NrlService):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# a unique name is required, without spaces
|
# a unique name is required, without spaces
|
||||||
_name = "MgenActor"
|
name = "MgenActor"
|
||||||
|
executables = ("mgen",)
|
||||||
# you can create your own group here
|
# you can create your own group here
|
||||||
_group = "ProtoSvc"
|
group = "ProtoSvc"
|
||||||
# list of other services this service depends on
|
|
||||||
_depends = ()
|
|
||||||
# per-node directories
|
# per-node directories
|
||||||
_dirs = ()
|
dirs = ()
|
||||||
# generated files (without a full path this file goes in the node's dir,
|
# generated files (without a full path this file goes in the node's dir,
|
||||||
# e.g. /tmp/pycore.12345/n1.conf/)
|
# e.g. /tmp/pycore.12345/n1.conf/)
|
||||||
_configs = ('start_mgen_actor.sh',)
|
configs = ('start_mgen_actor.sh',)
|
||||||
# this controls the starting order vs other enabled services
|
|
||||||
_startindex = 50
|
|
||||||
# list of startup commands, also may be generated during startup
|
# list of startup commands, also may be generated during startup
|
||||||
_startup = ("sh start_mgen_actor.sh",)
|
startup = ("sh start_mgen_actor.sh",)
|
||||||
# list of validation commands
|
# list of validation commands
|
||||||
_validate = ("pidof mgen",)
|
validate = ("pidof mgen",)
|
||||||
# list of shutdown commands
|
# list of shutdown commands
|
||||||
_shutdown = ("killall mgen",)
|
shutdown = ("killall mgen",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a startup script for MgenActor. Because mgenActor does not
|
Generate a startup script for MgenActor. Because mgenActor does not
|
||||||
daemonize, it can cause problems in some situations when launched
|
daemonize, it can cause problems in some situations when launched
|
||||||
|
@ -603,7 +602,7 @@ class MgenActor(NrlService):
|
||||||
comments = ""
|
comments = ""
|
||||||
cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % node.name
|
cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % node.name
|
||||||
|
|
||||||
servicenames = map(lambda x: x._name, services)
|
servicenames = map(lambda x: x.name, node.services)
|
||||||
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
|
||||||
if len(netifs) == 0:
|
if len(netifs) == 0:
|
||||||
return ""
|
return ""
|
||||||
|
@ -616,15 +615,15 @@ class Arouted(NrlService):
|
||||||
"""
|
"""
|
||||||
Adaptive Routing
|
Adaptive Routing
|
||||||
"""
|
"""
|
||||||
_name = "arouted"
|
name = "arouted"
|
||||||
_configs = ("startarouted.sh",)
|
executables = ("arouted",)
|
||||||
_startindex = NrlService._startindex + 10
|
configs = ("startarouted.sh",)
|
||||||
_startup = ("sh startarouted.sh",)
|
startup = ("sh startarouted.sh",)
|
||||||
_shutdown = ("pkill arouted",)
|
shutdown = ("pkill arouted",)
|
||||||
_validate = ("pidof arouted",)
|
validate = ("pidof arouted",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the Quagga.conf or quaggaboot.sh file contents.
|
Return the Quagga.conf or quaggaboot.sh file contents.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -10,43 +10,41 @@ from core.service import CoreService
|
||||||
|
|
||||||
|
|
||||||
class Zebra(CoreService):
|
class Zebra(CoreService):
|
||||||
_name = "zebra"
|
name = "zebra"
|
||||||
_group = "Quagga"
|
group = "Quagga"
|
||||||
_dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
|
dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
|
||||||
_configs = (
|
configs = (
|
||||||
"/usr/local/etc/quagga/Quagga.conf",
|
"/usr/local/etc/quagga/Quagga.conf",
|
||||||
"quaggaboot.sh",
|
"quaggaboot.sh",
|
||||||
"/usr/local/etc/quagga/vtysh.conf"
|
"/usr/local/etc/quagga/vtysh.conf"
|
||||||
)
|
)
|
||||||
_startindex = 35
|
startup = ("sh quaggaboot.sh zebra",)
|
||||||
_startup = ("sh quaggaboot.sh zebra",)
|
shutdown = ("killall zebra",)
|
||||||
_shutdown = ("killall zebra",)
|
validate = ("pidof zebra",)
|
||||||
_validate = ("pidof zebra",)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the Quagga.conf or quaggaboot.sh file contents.
|
Return the Quagga.conf or quaggaboot.sh file contents.
|
||||||
"""
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls.configs[0]:
|
||||||
return cls.generateQuaggaConf(node, services)
|
return cls.generateQuaggaConf(node)
|
||||||
elif filename == cls._configs[1]:
|
elif filename == cls.configs[1]:
|
||||||
return cls.generateQuaggaBoot(node, services)
|
return cls.generateQuaggaBoot(node)
|
||||||
elif filename == cls._configs[2]:
|
elif filename == cls.configs[2]:
|
||||||
return cls.generateVtyshConf(node, services)
|
return cls.generateVtyshConf(node)
|
||||||
else:
|
else:
|
||||||
raise ValueError("file name (%s) is not a known configuration: %s",
|
raise ValueError("file name (%s) is not a known configuration: %s", filename, cls.configs)
|
||||||
filename, cls._configs)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVtyshConf(cls, node, services):
|
def generateVtyshConf(cls, node):
|
||||||
"""
|
"""
|
||||||
Returns configuration file text.
|
Returns configuration file text.
|
||||||
"""
|
"""
|
||||||
return "service integrated-vtysh-config\n"
|
return "service integrated-vtysh-config\n"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateQuaggaConf(cls, node, services):
|
def generateQuaggaConf(cls, node):
|
||||||
"""
|
"""
|
||||||
Returns configuration file text. Other services that depend on zebra
|
Returns configuration file text. Other services that depend on zebra
|
||||||
will have generatequaggaifcconfig() and generatequaggaconfig()
|
will have generatequaggaifcconfig() and generatequaggaconfig()
|
||||||
|
@ -66,13 +64,13 @@ class Zebra(CoreService):
|
||||||
cfgv6 = ""
|
cfgv6 = ""
|
||||||
want_ipv4 = False
|
want_ipv4 = False
|
||||||
want_ipv6 = False
|
want_ipv6 = False
|
||||||
for s in services:
|
for s in node.services:
|
||||||
if cls._name not in s._depends:
|
if cls.name not in s.dependencies:
|
||||||
continue
|
continue
|
||||||
ifccfg = s.generatequaggaifcconfig(node, ifc)
|
ifccfg = s.generatequaggaifcconfig(node, ifc)
|
||||||
if s._ipv4_routing:
|
if s.ipv4_routing:
|
||||||
want_ipv4 = True
|
want_ipv4 = True
|
||||||
if s._ipv6_routing:
|
if s.ipv6_routing:
|
||||||
want_ipv6 = True
|
want_ipv6 = True
|
||||||
cfgv6 += ifccfg
|
cfgv6 += ifccfg
|
||||||
else:
|
else:
|
||||||
|
@ -92,8 +90,8 @@ class Zebra(CoreService):
|
||||||
cfg += cfgv6
|
cfg += cfgv6
|
||||||
cfg += "!\n"
|
cfg += "!\n"
|
||||||
|
|
||||||
for s in services:
|
for s in node.services:
|
||||||
if cls._name not in s._depends:
|
if cls.name not in s.dependencies:
|
||||||
continue
|
continue
|
||||||
cfg += s.generatequaggaconfig(node)
|
cfg += s.generatequaggaconfig(node)
|
||||||
return cfg
|
return cfg
|
||||||
|
@ -111,16 +109,14 @@ class Zebra(CoreService):
|
||||||
raise ValueError("invalid address: %s", x)
|
raise ValueError("invalid address: %s", x)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateQuaggaBoot(cls, node, services):
|
def generateQuaggaBoot(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate a shell script used to boot the Quagga daemons.
|
Generate a shell script used to boot the Quagga daemons.
|
||||||
"""
|
"""
|
||||||
try:
|
quagga_bin_search = node.session.options.get_config("quagga_bin_search",
|
||||||
quagga_bin_search = node.session.config['quagga_bin_search']
|
default='"/usr/local/bin /usr/bin /usr/lib/quagga"')
|
||||||
quagga_sbin_search = node.session.config['quagga_sbin_search']
|
quagga_sbin_search = node.session.options.get_config('quagga_sbin_search',
|
||||||
except KeyError:
|
default='"/usr/local/sbin /usr/sbin /usr/lib/quagga"')
|
||||||
quagga_bin_search = '"/usr/local/bin /usr/bin /usr/lib/quagga"'
|
|
||||||
quagga_sbin_search = '"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
|
|
||||||
return """\
|
return """\
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# auto-generated by zebra service (quagga.py)
|
# auto-generated by zebra service (quagga.py)
|
||||||
|
@ -214,7 +210,7 @@ if [ "$1" != "zebra" ]; then
|
||||||
fi
|
fi
|
||||||
confcheck
|
confcheck
|
||||||
bootquagga
|
bootquagga
|
||||||
""" % (cls._configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR)
|
""" % (cls.configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR)
|
||||||
|
|
||||||
|
|
||||||
class QuaggaService(CoreService):
|
class QuaggaService(CoreService):
|
||||||
|
@ -222,18 +218,17 @@ class QuaggaService(CoreService):
|
||||||
Parent class for Quagga services. Defines properties and methods
|
Parent class for Quagga services. Defines properties and methods
|
||||||
common to Quagga's routing daemons.
|
common to Quagga's routing daemons.
|
||||||
"""
|
"""
|
||||||
_name = None
|
name = None
|
||||||
_group = "Quagga"
|
group = "Quagga"
|
||||||
_depends = ("zebra",)
|
dependencies = ("zebra",)
|
||||||
_dirs = ()
|
dirs = ()
|
||||||
_configs = ()
|
configs = ()
|
||||||
_startindex = 40
|
startup = ()
|
||||||
_startup = ()
|
shutdown = ()
|
||||||
_shutdown = ()
|
meta = "The config file for this service can be found in the Zebra service."
|
||||||
_meta = "The config file for this service can be found in the Zebra service."
|
|
||||||
|
|
||||||
_ipv4_routing = False
|
ipv4_routing = False
|
||||||
_ipv6_routing = False
|
ipv6_routing = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def routerid(node):
|
def routerid(node):
|
||||||
|
@ -241,7 +236,7 @@ class QuaggaService(CoreService):
|
||||||
Helper to return the first IPv4 address of a node as its router ID.
|
Helper to return the first IPv4 address of a node as its router ID.
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
for a in ifc.addrlist:
|
for a in ifc.addrlist:
|
||||||
if a.find(".") >= 0:
|
if a.find(".") >= 0:
|
||||||
|
@ -264,7 +259,7 @@ class QuaggaService(CoreService):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -282,11 +277,11 @@ class Ospfv2(QuaggaService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
"""
|
"""
|
||||||
_name = "OSPFv2"
|
name = "OSPFv2"
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ("killall ospfd",)
|
shutdown = ("killall ospfd",)
|
||||||
_validate = ("pidof ospfd",)
|
validate = ("pidof ospfd",)
|
||||||
_ipv4_routing = True
|
ipv4_routing = True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mtucheck(ifc):
|
def mtucheck(ifc):
|
||||||
|
@ -357,12 +352,12 @@ class Ospfv3(QuaggaService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
"""
|
"""
|
||||||
_name = "OSPFv3"
|
name = "OSPFv3"
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ("killall ospf6d",)
|
shutdown = ("killall ospf6d",)
|
||||||
_validate = ("pidof ospf6d",)
|
validate = ("pidof ospf6d",)
|
||||||
_ipv4_routing = True
|
ipv4_routing = True
|
||||||
_ipv6_routing = True
|
ipv6_routing = True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def minmtu(ifc):
|
def minmtu(ifc):
|
||||||
|
@ -438,8 +433,8 @@ class Ospfv3mdr(Ospfv3):
|
||||||
configuration file but has hooks for adding to the
|
configuration file but has hooks for adding to the
|
||||||
unified Quagga.conf file.
|
unified Quagga.conf file.
|
||||||
"""
|
"""
|
||||||
_name = "OSPFv3MDR"
|
name = "OSPFv3MDR"
|
||||||
_ipv4_routing = True
|
ipv4_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaifcconfig(cls, node, ifc):
|
def generatequaggaifcconfig(cls, node, ifc):
|
||||||
|
@ -466,13 +461,13 @@ class Bgp(QuaggaService):
|
||||||
Peers must be manually configured, with a full mesh for those
|
Peers must be manually configured, with a full mesh for those
|
||||||
having the same AS number.
|
having the same AS number.
|
||||||
"""
|
"""
|
||||||
_name = "BGP"
|
name = "BGP"
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ("killall bgpd",)
|
shutdown = ("killall bgpd",)
|
||||||
_validate = ("pidof bgpd",)
|
validate = ("pidof bgpd",)
|
||||||
_custom_needed = True
|
custom_needed = True
|
||||||
_ipv4_routing = True
|
ipv4_routing = True
|
||||||
_ipv6_routing = True
|
ipv6_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaconfig(cls, node):
|
def generatequaggaconfig(cls, node):
|
||||||
|
@ -491,11 +486,11 @@ class Rip(QuaggaService):
|
||||||
"""
|
"""
|
||||||
The RIP service provides IPv4 routing for wired networks.
|
The RIP service provides IPv4 routing for wired networks.
|
||||||
"""
|
"""
|
||||||
_name = "RIP"
|
name = "RIP"
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ("killall ripd",)
|
shutdown = ("killall ripd",)
|
||||||
_validate = ("pidof ripd",)
|
validate = ("pidof ripd",)
|
||||||
_ipv4_routing = True
|
ipv4_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaconfig(cls, node):
|
def generatequaggaconfig(cls, node):
|
||||||
|
@ -514,11 +509,11 @@ class Ripng(QuaggaService):
|
||||||
"""
|
"""
|
||||||
The RIP NG service provides IPv6 routing for wired networks.
|
The RIP NG service provides IPv6 routing for wired networks.
|
||||||
"""
|
"""
|
||||||
_name = "RIPNG"
|
name = "RIPNG"
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ("killall ripngd",)
|
shutdown = ("killall ripngd",)
|
||||||
_validate = ("pidof ripngd",)
|
validate = ("pidof ripngd",)
|
||||||
_ipv6_routing = True
|
ipv6_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaconfig(cls, node):
|
def generatequaggaconfig(cls, node):
|
||||||
|
@ -538,11 +533,11 @@ class Babel(QuaggaService):
|
||||||
The Babel service provides a loop-avoiding distance-vector routing
|
The Babel service provides a loop-avoiding distance-vector routing
|
||||||
protocol for IPv6 and IPv4 with fast convergence properties.
|
protocol for IPv6 and IPv4 with fast convergence properties.
|
||||||
"""
|
"""
|
||||||
_name = "Babel"
|
name = "Babel"
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ("killall babeld",)
|
shutdown = ("killall babeld",)
|
||||||
_validate = ("pidof babeld",)
|
validate = ("pidof babeld",)
|
||||||
_ipv6_routing = True
|
ipv6_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaconfig(cls, node):
|
def generatequaggaconfig(cls, node):
|
||||||
|
@ -567,11 +562,11 @@ class Xpimd(QuaggaService):
|
||||||
"""
|
"""
|
||||||
PIM multicast routing based on XORP.
|
PIM multicast routing based on XORP.
|
||||||
"""
|
"""
|
||||||
_name = 'Xpimd'
|
name = 'Xpimd'
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ('killall xpimd',)
|
shutdown = ('killall xpimd',)
|
||||||
_validate = ('pidof xpimd',)
|
validate = ('pidof xpimd',)
|
||||||
_ipv4_routing = True
|
ipv4_routing = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatequaggaconfig(cls, node):
|
def generatequaggaconfig(cls, node):
|
||||||
|
|
|
@ -11,42 +11,34 @@ class SdnService(CoreService):
|
||||||
"""
|
"""
|
||||||
Parent class for SDN services.
|
Parent class for SDN services.
|
||||||
"""
|
"""
|
||||||
_name = None
|
group = "SDN"
|
||||||
_group = "SDN"
|
|
||||||
_depends = ()
|
|
||||||
_dirs = ()
|
|
||||||
_configs = ()
|
|
||||||
_startindex = 50
|
|
||||||
_startup = ()
|
|
||||||
_shutdown = ()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class OvsService(SdnService):
|
class OvsService(SdnService):
|
||||||
_name = "OvsService"
|
name = "OvsService"
|
||||||
_group = "SDN"
|
executables = ("ovs-ofctl", "ovs-vsctl")
|
||||||
_depends = ()
|
group = "SDN"
|
||||||
_dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
|
dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
|
||||||
_configs = ('OvsService.sh',)
|
configs = ('OvsService.sh',)
|
||||||
_startindex = 50
|
startup = ('sh OvsService.sh',)
|
||||||
_startup = ('sh OvsService.sh',)
|
shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server')
|
||||||
_shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server')
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
# Check whether the node is running zebra
|
# Check whether the node is running zebra
|
||||||
has_zebra = 0
|
has_zebra = 0
|
||||||
for s in services:
|
for s in node.services:
|
||||||
if s._name == "zebra":
|
if s.name == "zebra":
|
||||||
has_zebra = 1
|
has_zebra = 1
|
||||||
|
|
||||||
# Check whether the node is running an SDN controller
|
# Check whether the node is running an SDN controller
|
||||||
has_sdn_ctrlr = 0
|
has_sdn_ctrlr = 0
|
||||||
for s in services:
|
for s in node.services:
|
||||||
if s._name == "ryuService":
|
if s.name == "ryuService":
|
||||||
has_sdn_ctrlr = 1
|
has_sdn_ctrlr = 1
|
||||||
|
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
|
@ -100,17 +92,16 @@ class OvsService(SdnService):
|
||||||
|
|
||||||
|
|
||||||
class RyuService(SdnService):
|
class RyuService(SdnService):
|
||||||
_name = "ryuService"
|
name = "ryuService"
|
||||||
_group = "SDN"
|
executables = ("ryu-manager",)
|
||||||
_depends = ()
|
group = "SDN"
|
||||||
_dirs = ()
|
dirs = ()
|
||||||
_configs = ('ryuService.sh',)
|
configs = ('ryuService.sh',)
|
||||||
_startindex = 50
|
startup = ('sh ryuService.sh',)
|
||||||
_startup = ('sh ryuService.sh',)
|
shutdown = ('killall ryu-manager',)
|
||||||
_shutdown = ('killall ryu-manager',)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return a string that will be written to filename, or sent to the
|
Return a string that will be written to filename, or sent to the
|
||||||
GUI for user customization.
|
GUI for user customization.
|
||||||
|
|
|
@ -9,17 +9,16 @@ from core.service import CoreService
|
||||||
|
|
||||||
|
|
||||||
class VPNClient(CoreService):
|
class VPNClient(CoreService):
|
||||||
_name = "VPNClient"
|
name = "VPNClient"
|
||||||
_group = "Security"
|
group = "Security"
|
||||||
_configs = ('vpnclient.sh',)
|
configs = ('vpnclient.sh',)
|
||||||
_startindex = 60
|
startup = ('sh vpnclient.sh',)
|
||||||
_startup = ('sh vpnclient.sh',)
|
shutdown = ("killall openvpn",)
|
||||||
_shutdown = ("killall openvpn",)
|
validate = ("pidof openvpn",)
|
||||||
_validate = ("pidof openvpn",)
|
custom_needed = True
|
||||||
_custom_needed = True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the client.conf and vpnclient.sh file contents to
|
Return the client.conf and vpnclient.sh file contents to
|
||||||
"""
|
"""
|
||||||
|
@ -36,17 +35,16 @@ class VPNClient(CoreService):
|
||||||
|
|
||||||
|
|
||||||
class VPNServer(CoreService):
|
class VPNServer(CoreService):
|
||||||
_name = "VPNServer"
|
name = "VPNServer"
|
||||||
_group = "Security"
|
group = "Security"
|
||||||
_configs = ('vpnserver.sh',)
|
configs = ('vpnserver.sh',)
|
||||||
_startindex = 50
|
startup = ('sh vpnserver.sh',)
|
||||||
_startup = ('sh vpnserver.sh',)
|
shutdown = ("killall openvpn",)
|
||||||
_shutdown = ("killall openvpn",)
|
validate = ("pidof openvpn",)
|
||||||
_validate = ("pidof openvpn",)
|
custom_needed = True
|
||||||
_custom_needed = True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the sample server.conf and vpnserver.sh file contents to
|
Return the sample server.conf and vpnserver.sh file contents to
|
||||||
GUI for user customization.
|
GUI for user customization.
|
||||||
|
@ -64,16 +62,15 @@ class VPNServer(CoreService):
|
||||||
|
|
||||||
|
|
||||||
class IPsec(CoreService):
|
class IPsec(CoreService):
|
||||||
_name = "IPsec"
|
name = "IPsec"
|
||||||
_group = "Security"
|
group = "Security"
|
||||||
_configs = ('ipsec.sh',)
|
configs = ('ipsec.sh',)
|
||||||
_startindex = 60
|
startup = ('sh ipsec.sh',)
|
||||||
_startup = ('sh ipsec.sh',)
|
shutdown = ("killall racoon",)
|
||||||
_shutdown = ("killall racoon",)
|
custom_needed = True
|
||||||
_custom_needed = True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the ipsec.conf and racoon.conf file contents to
|
Return the ipsec.conf and racoon.conf file contents to
|
||||||
GUI for user customization.
|
GUI for user customization.
|
||||||
|
@ -92,15 +89,14 @@ class IPsec(CoreService):
|
||||||
|
|
||||||
|
|
||||||
class Firewall(CoreService):
|
class Firewall(CoreService):
|
||||||
_name = "Firewall"
|
name = "Firewall"
|
||||||
_group = "Security"
|
group = "Security"
|
||||||
_configs = ('firewall.sh',)
|
configs = ('firewall.sh',)
|
||||||
_startindex = 20
|
startup = ('sh firewall.sh',)
|
||||||
_startup = ('sh firewall.sh',)
|
custom_needed = True
|
||||||
_custom_needed = True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the firewall rule examples to GUI for user customization.
|
Return the firewall rule examples to GUI for user customization.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
from inspect import isclass
|
|
||||||
from sys import maxint
|
|
||||||
|
|
||||||
from core.service import CoreService
|
|
||||||
|
|
||||||
|
|
||||||
class Startup(CoreService):
|
|
||||||
"""
|
|
||||||
A CORE service to start other services in order, serially
|
|
||||||
"""
|
|
||||||
_name = 'startup'
|
|
||||||
_group = 'Utility'
|
|
||||||
_depends = ()
|
|
||||||
_dirs = ()
|
|
||||||
_configs = ('startup.sh',)
|
|
||||||
_startindex = maxint
|
|
||||||
_startup = ('sh startup.sh',)
|
|
||||||
_shutdown = ()
|
|
||||||
_validate = ()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_startup_service(s):
|
|
||||||
return isinstance(s, Startup) or (isclass(s) and issubclass(s, Startup))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def generateconfig(cls, node, filename, services):
|
|
||||||
if filename != cls._configs[0]:
|
|
||||||
return ''
|
|
||||||
script = '#!/bin/sh\n' \
|
|
||||||
'# auto-generated by Startup (startup.py)\n\n' \
|
|
||||||
'exec > startup.log 2>&1\n\n'
|
|
||||||
for s in sorted(services, key=lambda x: x._startindex):
|
|
||||||
if cls.is_startup_service(s) or len(str(s._starttime)) > 0:
|
|
||||||
continue
|
|
||||||
start = '\n'.join(s.getstartup(node, services))
|
|
||||||
if start:
|
|
||||||
script += start + '\n'
|
|
||||||
return script
|
|
|
@ -8,35 +8,33 @@ UCARP_ETC = "/usr/local/etc/ucarp"
|
||||||
|
|
||||||
|
|
||||||
class Ucarp(CoreService):
|
class Ucarp(CoreService):
|
||||||
_name = "ucarp"
|
name = "ucarp"
|
||||||
_group = "Utility"
|
group = "Utility"
|
||||||
_depends = ( )
|
dirs = (UCARP_ETC,)
|
||||||
_dirs = (UCARP_ETC,)
|
configs = (
|
||||||
_configs = (
|
|
||||||
UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
|
UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
|
||||||
_startindex = 65
|
startup = ("sh ucarpboot.sh",)
|
||||||
_startup = ("sh ucarpboot.sh",)
|
shutdown = ("killall ucarp",)
|
||||||
_shutdown = ("killall ucarp",)
|
validate = ("pidof ucarp",)
|
||||||
_validate = ("pidof ucarp",)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return the default file contents
|
Return the default file contents
|
||||||
"""
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls.configs[0]:
|
||||||
return cls.generateUcarpConf(node, services)
|
return cls.generateUcarpConf(node)
|
||||||
elif filename == cls._configs[1]:
|
elif filename == cls.configs[1]:
|
||||||
return cls.generateVipUp(node, services)
|
return cls.generateVipUp(node)
|
||||||
elif filename == cls._configs[2]:
|
elif filename == cls.configs[2]:
|
||||||
return cls.generateVipDown(node, services)
|
return cls.generateVipDown(node)
|
||||||
elif filename == cls._configs[3]:
|
elif filename == cls.configs[3]:
|
||||||
return cls.generateUcarpBoot(node, services)
|
return cls.generateUcarpBoot(node)
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateUcarpConf(cls, node, services):
|
def generateUcarpConf(cls, node):
|
||||||
"""
|
"""
|
||||||
Returns configuration file text.
|
Returns configuration file text.
|
||||||
"""
|
"""
|
||||||
|
@ -105,7 +103,7 @@ ${UCARP_EXEC} -B ${UCARP_OPTS}
|
||||||
""" % (ucarp_bin, UCARP_ETC)
|
""" % (ucarp_bin, UCARP_ETC)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateUcarpBoot(cls, node, services):
|
def generateUcarpBoot(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate a shell script used to boot the Ucarp daemons.
|
Generate a shell script used to boot the Ucarp daemons.
|
||||||
"""
|
"""
|
||||||
|
@ -127,7 +125,7 @@ ${UCARP_CFGDIR}/default.sh
|
||||||
""" % UCARP_ETC
|
""" % UCARP_ETC
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVipUp(cls, node, services):
|
def generateVipUp(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate a shell script used to start the virtual ip
|
Generate a shell script used to start the virtual ip
|
||||||
"""
|
"""
|
||||||
|
@ -154,7 +152,7 @@ fi
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateVipDown(cls, node, services):
|
def generateVipDown(cls, node):
|
||||||
"""
|
"""
|
||||||
Generate a shell script used to stop the virtual ip
|
Generate a shell script used to stop the virtual ip
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -16,35 +16,32 @@ class UtilService(CoreService):
|
||||||
"""
|
"""
|
||||||
Parent class for utility services.
|
Parent class for utility services.
|
||||||
"""
|
"""
|
||||||
_name = None
|
name = None
|
||||||
_group = "Utility"
|
group = "Utility"
|
||||||
_depends = ()
|
dirs = ()
|
||||||
_dirs = ()
|
configs = ()
|
||||||
_configs = ()
|
startup = ()
|
||||||
_startindex = 80
|
shutdown = ()
|
||||||
_startup = ()
|
|
||||||
_shutdown = ()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class IPForwardService(UtilService):
|
class IPForwardService(UtilService):
|
||||||
_name = "IPForward"
|
name = "IPForward"
|
||||||
_configs = ("ipforward.sh",)
|
configs = ("ipforward.sh",)
|
||||||
_startindex = 5
|
startup = ("sh ipforward.sh",)
|
||||||
_startup = ("sh ipforward.sh",)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
if os.uname()[0] == "Linux":
|
if os.uname()[0] == "Linux":
|
||||||
return cls.generateconfiglinux(node, filename, services)
|
return cls.generateconfiglinux(node, filename)
|
||||||
else:
|
else:
|
||||||
raise Exception("unknown platform")
|
raise Exception("unknown platform")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfiglinux(cls, node, filename, services):
|
def generateconfiglinux(cls, node, filename):
|
||||||
cfg = """\
|
cfg = """\
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# auto-generated by IPForward service (utility.py)
|
# auto-generated by IPForward service (utility.py)
|
||||||
|
@ -67,12 +64,12 @@ class IPForwardService(UtilService):
|
||||||
|
|
||||||
|
|
||||||
class DefaultRouteService(UtilService):
|
class DefaultRouteService(UtilService):
|
||||||
_name = "DefaultRoute"
|
name = "DefaultRoute"
|
||||||
_configs = ("defaultroute.sh",)
|
configs = ("defaultroute.sh",)
|
||||||
_startup = ("sh defaultroute.sh",)
|
startup = ("sh defaultroute.sh",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
|
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
|
@ -101,19 +98,19 @@ class DefaultRouteService(UtilService):
|
||||||
|
|
||||||
|
|
||||||
class DefaultMulticastRouteService(UtilService):
|
class DefaultMulticastRouteService(UtilService):
|
||||||
_name = "DefaultMulticastRoute"
|
name = "DefaultMulticastRoute"
|
||||||
_configs = ("defaultmroute.sh",)
|
configs = ("defaultmroute.sh",)
|
||||||
_startup = ("sh defaultmroute.sh",)
|
startup = ("sh defaultmroute.sh",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by DefaultMulticastRoute service (utility.py)\n"
|
cfg += "# auto-generated by DefaultMulticastRoute service (utility.py)\n"
|
||||||
cfg += "# the first interface is chosen below; please change it "
|
cfg += "# the first interface is chosen below; please change it "
|
||||||
cfg += "as needed\n"
|
cfg += "as needed\n"
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
if os.uname()[0] == "Linux":
|
if os.uname()[0] == "Linux":
|
||||||
rtcmd = "ip route add 224.0.0.0/4 dev"
|
rtcmd = "ip route add 224.0.0.0/4 dev"
|
||||||
|
@ -126,13 +123,13 @@ class DefaultMulticastRouteService(UtilService):
|
||||||
|
|
||||||
|
|
||||||
class StaticRouteService(UtilService):
|
class StaticRouteService(UtilService):
|
||||||
_name = "StaticRoute"
|
name = "StaticRoute"
|
||||||
_configs = ("staticroute.sh",)
|
configs = ("staticroute.sh",)
|
||||||
_startup = ("sh staticroute.sh",)
|
startup = ("sh staticroute.sh",)
|
||||||
_custom_needed = True
|
custom_needed = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
cfg = "#!/bin/sh\n"
|
cfg = "#!/bin/sh\n"
|
||||||
cfg += "# auto-generated by StaticRoute service (utility.py)\n#\n"
|
cfg += "# auto-generated by StaticRoute service (utility.py)\n#\n"
|
||||||
cfg += "# NOTE: this service must be customized to be of any use\n"
|
cfg += "# NOTE: this service must be customized to be of any use\n"
|
||||||
|
@ -165,21 +162,21 @@ class StaticRouteService(UtilService):
|
||||||
|
|
||||||
|
|
||||||
class SshService(UtilService):
|
class SshService(UtilService):
|
||||||
_name = "SSH"
|
name = "SSH"
|
||||||
_configs = ("startsshd.sh", "/etc/ssh/sshd_config",)
|
configs = ("startsshd.sh", "/etc/ssh/sshd_config",)
|
||||||
_dirs = ("/etc/ssh", "/var/run/sshd",)
|
dirs = ("/etc/ssh", "/var/run/sshd",)
|
||||||
_startup = ("sh startsshd.sh",)
|
startup = ("sh startsshd.sh",)
|
||||||
_shutdown = ("killall sshd",)
|
shutdown = ("killall sshd",)
|
||||||
_validate = ()
|
validate = ()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Use a startup script for launching sshd in order to wait for host
|
Use a startup script for launching sshd in order to wait for host
|
||||||
key generation.
|
key generation.
|
||||||
"""
|
"""
|
||||||
sshcfgdir = cls._dirs[0]
|
sshcfgdir = cls.dirs[0]
|
||||||
sshstatedir = cls._dirs[1]
|
sshstatedir = cls.dirs[1]
|
||||||
sshlibdir = "/usr/lib/openssh"
|
sshlibdir = "/usr/lib/openssh"
|
||||||
if filename == "startsshd.sh":
|
if filename == "startsshd.sh":
|
||||||
return """\
|
return """\
|
||||||
|
@ -233,15 +230,15 @@ UseDNS no
|
||||||
|
|
||||||
|
|
||||||
class DhcpService(UtilService):
|
class DhcpService(UtilService):
|
||||||
_name = "DHCP"
|
name = "DHCP"
|
||||||
_configs = ("/etc/dhcp/dhcpd.conf",)
|
configs = ("/etc/dhcp/dhcpd.conf",)
|
||||||
_dirs = ("/etc/dhcp",)
|
dirs = ("/etc/dhcp",)
|
||||||
_startup = ("dhcpd",)
|
startup = ("dhcpd",)
|
||||||
_shutdown = ("killall dhcpd",)
|
shutdown = ("killall dhcpd",)
|
||||||
_validate = ("pidof dhcpd",)
|
validate = ("pidof dhcpd",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a dhcpd config file using the network address of
|
Generate a dhcpd config file using the network address of
|
||||||
each interface.
|
each interface.
|
||||||
|
@ -296,14 +293,14 @@ class DhcpClientService(UtilService):
|
||||||
"""
|
"""
|
||||||
Use a DHCP client for all interfaces for addressing.
|
Use a DHCP client for all interfaces for addressing.
|
||||||
"""
|
"""
|
||||||
_name = "DHCPClient"
|
name = "DHCPClient"
|
||||||
_configs = ("startdhcpclient.sh",)
|
configs = ("startdhcpclient.sh",)
|
||||||
_startup = ("sh startdhcpclient.sh",)
|
startup = ("sh startdhcpclient.sh",)
|
||||||
_shutdown = ("killall dhclient",)
|
shutdown = ("killall dhclient",)
|
||||||
_validate = ("pidof dhclient",)
|
validate = ("pidof dhclient",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a script to invoke dhclient on all interfaces.
|
Generate a script to invoke dhclient on all interfaces.
|
||||||
"""
|
"""
|
||||||
|
@ -314,7 +311,7 @@ class DhcpClientService(UtilService):
|
||||||
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
|
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
|
||||||
|
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % ifc.name
|
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % ifc.name
|
||||||
cfg += " /var/run/resolvconf/resolv.conf\n"
|
cfg += " /var/run/resolvconf/resolv.conf\n"
|
||||||
|
@ -327,15 +324,15 @@ class FtpService(UtilService):
|
||||||
"""
|
"""
|
||||||
Start a vsftpd server.
|
Start a vsftpd server.
|
||||||
"""
|
"""
|
||||||
_name = "FTP"
|
name = "FTP"
|
||||||
_configs = ("vsftpd.conf",)
|
configs = ("vsftpd.conf",)
|
||||||
_dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
|
dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
|
||||||
_startup = ("vsftpd ./vsftpd.conf",)
|
startup = ("vsftpd ./vsftpd.conf",)
|
||||||
_shutdown = ("killall vsftpd",)
|
shutdown = ("killall vsftpd",)
|
||||||
_validate = ("pidof vsftpd",)
|
validate = ("pidof vsftpd",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a vsftpd.conf configuration file.
|
Generate a vsftpd.conf configuration file.
|
||||||
"""
|
"""
|
||||||
|
@ -359,28 +356,28 @@ class HttpService(UtilService):
|
||||||
"""
|
"""
|
||||||
Start an apache server.
|
Start an apache server.
|
||||||
"""
|
"""
|
||||||
_name = "HTTP"
|
name = "HTTP"
|
||||||
_configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
|
configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
|
||||||
"/var/www/index.html",)
|
"/var/www/index.html",)
|
||||||
_dirs = ("/etc/apache2", "/var/run/apache2", "/var/log/apache2",
|
dirs = ("/etc/apache2", "/var/run/apache2", "/var/log/apache2",
|
||||||
"/run/lock", "/var/lock/apache2", "/var/www",)
|
"/run/lock", "/var/lock/apache2", "/var/www",)
|
||||||
_startup = ("chown www-data /var/lock/apache2", "apache2ctl start",)
|
startup = ("chown www-data /var/lock/apache2", "apache2ctl start",)
|
||||||
_shutdown = ("apache2ctl stop",)
|
shutdown = ("apache2ctl stop",)
|
||||||
_validate = ("pidof apache2",)
|
validate = ("pidof apache2",)
|
||||||
|
|
||||||
APACHEVER22, APACHEVER24 = (22, 24)
|
APACHEVER22, APACHEVER24 = (22, 24)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate an apache2.conf configuration file.
|
Generate an apache2.conf configuration file.
|
||||||
"""
|
"""
|
||||||
if filename == cls._configs[0]:
|
if filename == cls.configs[0]:
|
||||||
return cls.generateapache2conf(node, filename, services)
|
return cls.generateapache2conf(node, filename)
|
||||||
elif filename == cls._configs[1]:
|
elif filename == cls.configs[1]:
|
||||||
return cls.generateenvvars(node, filename, services)
|
return cls.generateenvvars(node, filename)
|
||||||
elif filename == cls._configs[2]:
|
elif filename == cls.configs[2]:
|
||||||
return cls.generatehtml(node, filename, services)
|
return cls.generatehtml(node, filename)
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -400,7 +397,7 @@ class HttpService(UtilService):
|
||||||
return cls.APACHEVER22
|
return cls.APACHEVER22
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateapache2conf(cls, node, filename, services):
|
def generateapache2conf(cls, node, filename):
|
||||||
lockstr = {cls.APACHEVER22:
|
lockstr = {cls.APACHEVER22:
|
||||||
'LockFile ${APACHE_LOCK_DIR}/accept.lock\n',
|
'LockFile ${APACHE_LOCK_DIR}/accept.lock\n',
|
||||||
cls.APACHEVER24:
|
cls.APACHEVER24:
|
||||||
|
@ -538,7 +535,7 @@ TraceEnable Off
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateenvvars(cls, node, filename, services):
|
def generateenvvars(cls, node, filename):
|
||||||
return """\
|
return """\
|
||||||
# this file is used by apache2ctl - generated by utility.py:HttpService
|
# this file is used by apache2ctl - generated by utility.py:HttpService
|
||||||
# these settings come from a default Ubuntu apache2 installation
|
# these settings come from a default Ubuntu apache2 installation
|
||||||
|
@ -553,7 +550,7 @@ export LANG
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatehtml(cls, node, filename, services):
|
def generatehtml(cls, node, filename):
|
||||||
body = """\
|
body = """\
|
||||||
<!-- generated by utility.py:HttpService -->
|
<!-- generated by utility.py:HttpService -->
|
||||||
<h1>%s web server</h1>
|
<h1>%s web server</h1>
|
||||||
|
@ -561,7 +558,7 @@ export LANG
|
||||||
<p>The web server software is running but no content has been added, yet.</p>
|
<p>The web server software is running but no content has been added, yet.</p>
|
||||||
""" % node.name
|
""" % node.name
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
|
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
|
||||||
return "<html><body>%s</body></html>" % body
|
return "<html><body>%s</body></html>" % body
|
||||||
|
@ -571,17 +568,16 @@ class PcapService(UtilService):
|
||||||
"""
|
"""
|
||||||
Pcap service for logging packets.
|
Pcap service for logging packets.
|
||||||
"""
|
"""
|
||||||
_name = "pcap"
|
name = "pcap"
|
||||||
_configs = ("pcap.sh",)
|
configs = ("pcap.sh",)
|
||||||
_dirs = ()
|
dirs = ()
|
||||||
_startindex = 1
|
startup = ("sh pcap.sh start",)
|
||||||
_startup = ("sh pcap.sh start",)
|
shutdown = ("sh pcap.sh stop",)
|
||||||
_shutdown = ("sh pcap.sh stop",)
|
validate = ("pidof tcpdump",)
|
||||||
_validate = ("pidof tcpdump",)
|
meta = "logs network traffic to pcap packet capture files"
|
||||||
_meta = "logs network traffic to pcap packet capture files"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a startpcap.sh traffic logging script.
|
Generate a startpcap.sh traffic logging script.
|
||||||
"""
|
"""
|
||||||
|
@ -595,7 +591,7 @@ if [ "x$1" = "xstart" ]; then
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
cfg += '# '
|
cfg += '# '
|
||||||
redir = "< /dev/null"
|
redir = "< /dev/null"
|
||||||
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \
|
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \
|
||||||
|
@ -611,22 +607,22 @@ fi;
|
||||||
|
|
||||||
|
|
||||||
class RadvdService(UtilService):
|
class RadvdService(UtilService):
|
||||||
_name = "radvd"
|
name = "radvd"
|
||||||
_configs = ("/etc/radvd/radvd.conf",)
|
configs = ("/etc/radvd/radvd.conf",)
|
||||||
_dirs = ("/etc/radvd",)
|
dirs = ("/etc/radvd",)
|
||||||
_startup = ("radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",)
|
startup = ("radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",)
|
||||||
_shutdown = ("pkill radvd",)
|
shutdown = ("pkill radvd",)
|
||||||
_validate = ("pidof radvd",)
|
validate = ("pidof radvd",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Generate a RADVD router advertisement daemon config file
|
Generate a RADVD router advertisement daemon config file
|
||||||
using the network address of each interface.
|
using the network address of each interface.
|
||||||
"""
|
"""
|
||||||
cfg = "# auto-generated by RADVD service (utility.py)\n"
|
cfg = "# auto-generated by RADVD service (utility.py)\n"
|
||||||
for ifc in node.netifs():
|
for ifc in node.netifs():
|
||||||
if hasattr(ifc, 'control') and ifc.control == True:
|
if hasattr(ifc, 'control') and ifc.control is True:
|
||||||
continue
|
continue
|
||||||
prefixes = map(cls.subnetentry, ifc.addrlist)
|
prefixes = map(cls.subnetentry, ifc.addrlist)
|
||||||
if len(prefixes) < 1:
|
if len(prefixes) < 1:
|
||||||
|
@ -671,14 +667,14 @@ class AtdService(UtilService):
|
||||||
"""
|
"""
|
||||||
Atd service for scheduling at jobs
|
Atd service for scheduling at jobs
|
||||||
"""
|
"""
|
||||||
_name = "atd"
|
name = "atd"
|
||||||
_configs = ("startatd.sh",)
|
configs = ("startatd.sh",)
|
||||||
_dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
|
dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
|
||||||
_startup = ("sh startatd.sh",)
|
startup = ("sh startatd.sh",)
|
||||||
_shutdown = ("pkill atd",)
|
shutdown = ("pkill atd",)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
return """
|
return """
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo 00001 > /var/spool/cron/atjobs/.SEQ
|
echo 00001 > /var/spool/cron/atjobs/.SEQ
|
||||||
|
@ -692,6 +688,5 @@ class UserDefinedService(UtilService):
|
||||||
"""
|
"""
|
||||||
Dummy service allowing customization of anything.
|
Dummy service allowing customization of anything.
|
||||||
"""
|
"""
|
||||||
_name = "UserDefined"
|
name = "UserDefined"
|
||||||
_startindex = 50
|
meta = "Customize this service to do anything upon startup."
|
||||||
_meta = "Customize this service to do anything upon startup."
|
|
||||||
|
|
|
@ -11,18 +11,17 @@ class XorpRtrmgr(CoreService):
|
||||||
XORP router manager service builds a config.boot file based on other
|
XORP router manager service builds a config.boot file based on other
|
||||||
enabled XORP services, and launches necessary daemons upon startup.
|
enabled XORP services, and launches necessary daemons upon startup.
|
||||||
"""
|
"""
|
||||||
_name = "xorp_rtrmgr"
|
name = "xorp_rtrmgr"
|
||||||
_group = "XORP"
|
executables = ("xorp_rtrmgr",)
|
||||||
_depends = ()
|
group = "XORP"
|
||||||
_dirs = ("/etc/xorp",)
|
dirs = ("/etc/xorp",)
|
||||||
_configs = ("/etc/xorp/config.boot",)
|
configs = ("/etc/xorp/config.boot",)
|
||||||
_startindex = 35
|
startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (configs[0], name, name),)
|
||||||
_startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (_configs[0], _name, _name),)
|
shutdown = ("killall xorp_rtrmgr",)
|
||||||
_shutdown = ("killall xorp_rtrmgr",)
|
validate = ("pidof xorp_rtrmgr",)
|
||||||
_validate = ("pidof xorp_rtrmgr",)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Returns config.boot configuration file text. Other services that
|
Returns config.boot configuration file text. Other services that
|
||||||
depend on this will have generatexorpconfig() hooks that are
|
depend on this will have generatexorpconfig() hooks that are
|
||||||
|
@ -38,12 +37,12 @@ class XorpRtrmgr(CoreService):
|
||||||
cfg += " }\n"
|
cfg += " }\n"
|
||||||
cfg += "}\n\n"
|
cfg += "}\n\n"
|
||||||
|
|
||||||
for s in services:
|
for s in node.services:
|
||||||
try:
|
try:
|
||||||
s._depends.index(cls._name)
|
s.dependencies.index(cls.name)
|
||||||
cfg += s.generatexorpconfig(node)
|
cfg += s.generatexorpconfig(node)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.exception("error getting value from service: %s", cls._name)
|
logger.exception("error getting value from service: %s", cls.name)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -74,15 +73,15 @@ class XorpService(CoreService):
|
||||||
Parent class for XORP services. Defines properties and methods
|
Parent class for XORP services. Defines properties and methods
|
||||||
common to XORP's routing daemons.
|
common to XORP's routing daemons.
|
||||||
"""
|
"""
|
||||||
_name = None
|
name = None
|
||||||
_group = "XORP"
|
executables = ("xorp_rtrmgr",)
|
||||||
_depends = ("xorp_rtrmgr",)
|
group = "XORP"
|
||||||
_dirs = ()
|
dependencies = ("xorp_rtrmgr",)
|
||||||
_configs = ()
|
dirs = ()
|
||||||
_startindex = 40
|
configs = ()
|
||||||
_startup = ()
|
startup = ()
|
||||||
_shutdown = ()
|
shutdown = ()
|
||||||
_meta = "The config file for this service can be found in the xorp_rtrmgr service."
|
meta = "The config file for this service can be found in the xorp_rtrmgr service."
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fea(forwarding):
|
def fea(forwarding):
|
||||||
|
@ -151,7 +150,7 @@ class XorpService(CoreService):
|
||||||
return "0.0.0.0"
|
return "0.0.0.0"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -165,7 +164,7 @@ class XorpOspfv2(XorpService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified XORP configuration file.
|
unified XORP configuration file.
|
||||||
"""
|
"""
|
||||||
_name = "XORP_OSPFv2"
|
name = "XORP_OSPFv2"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
@ -200,7 +199,7 @@ class XorpOspfv3(XorpService):
|
||||||
not build its own configuration file but has hooks for adding to the
|
not build its own configuration file but has hooks for adding to the
|
||||||
unified XORP configuration file.
|
unified XORP configuration file.
|
||||||
"""
|
"""
|
||||||
_name = "XORP_OSPFv3"
|
name = "XORP_OSPFv3"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
@ -227,8 +226,8 @@ class XorpBgp(XorpService):
|
||||||
"""
|
"""
|
||||||
IPv4 inter-domain routing. AS numbers and peers must be customized.
|
IPv4 inter-domain routing. AS numbers and peers must be customized.
|
||||||
"""
|
"""
|
||||||
_name = "XORP_BGP"
|
name = "XORP_BGP"
|
||||||
_custom_needed = True
|
custom_needed = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
@ -257,7 +256,7 @@ class XorpRip(XorpService):
|
||||||
RIP IPv4 unicast routing.
|
RIP IPv4 unicast routing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name = "XORP_RIP"
|
name = "XORP_RIP"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
@ -289,7 +288,7 @@ class XorpRipng(XorpService):
|
||||||
"""
|
"""
|
||||||
RIP NG IPv6 unicast routing.
|
RIP NG IPv6 unicast routing.
|
||||||
"""
|
"""
|
||||||
_name = "XORP_RIPNG"
|
name = "XORP_RIPNG"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
@ -324,7 +323,7 @@ class XorpPimSm4(XorpService):
|
||||||
"""
|
"""
|
||||||
PIM Sparse Mode IPv4 multicast routing.
|
PIM Sparse Mode IPv4 multicast routing.
|
||||||
"""
|
"""
|
||||||
_name = "XORP_PIMSM4"
|
name = "XORP_PIMSM4"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
@ -383,7 +382,7 @@ class XorpPimSm6(XorpService):
|
||||||
"""
|
"""
|
||||||
PIM Sparse Mode IPv6 multicast routing.
|
PIM Sparse Mode IPv6 multicast routing.
|
||||||
"""
|
"""
|
||||||
_name = "XORP_PIMSM6"
|
name = "XORP_PIMSM6"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
@ -442,7 +441,7 @@ class XorpOlsr(XorpService):
|
||||||
"""
|
"""
|
||||||
OLSR IPv4 unicast MANET routing.
|
OLSR IPv4 unicast MANET routing.
|
||||||
"""
|
"""
|
||||||
_name = "XORP_OLSR"
|
name = "XORP_OLSR"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generatexorpconfig(cls, node):
|
def generatexorpconfig(cls, node):
|
||||||
|
|
|
@ -4,13 +4,13 @@ that manages a CORE session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pprint
|
|
||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from multiprocessing.pool import ThreadPool
|
||||||
|
|
||||||
import pwd
|
import pwd
|
||||||
|
|
||||||
|
@ -18,18 +18,15 @@ from core import constants
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.broker import CoreBroker
|
from core.broker import CoreBroker
|
||||||
from core.conf import Configurable
|
|
||||||
from core.conf import ConfigurableManager
|
from core.conf import ConfigurableManager
|
||||||
from core.data import ConfigData
|
from core.conf import ConfigurableOptions
|
||||||
|
from core.conf import Configuration
|
||||||
from core.data import EventData
|
from core.data import EventData
|
||||||
from core.data import ExceptionData
|
from core.data import ExceptionData
|
||||||
from core.data import FileData
|
|
||||||
from core.emane.emanemanager import EmaneManager
|
from core.emane.emanemanager import EmaneManager
|
||||||
from core.enumerations import ConfigDataTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
from core.enumerations import ConfigFlags
|
|
||||||
from core.enumerations import EventTypes
|
from core.enumerations import EventTypes
|
||||||
from core.enumerations import ExceptionLevels
|
from core.enumerations import ExceptionLevels
|
||||||
from core.enumerations import MessageFlags
|
|
||||||
from core.enumerations import NodeTypes
|
from core.enumerations import NodeTypes
|
||||||
from core.enumerations import RegisterTlvs
|
from core.enumerations import RegisterTlvs
|
||||||
from core.location import CoreLocation
|
from core.location import CoreLocation
|
||||||
|
@ -37,13 +34,11 @@ from core.misc import nodeutils
|
||||||
from core.misc import utils
|
from core.misc import utils
|
||||||
from core.misc.event import EventLoop
|
from core.misc.event import EventLoop
|
||||||
from core.misc.ipaddress import MacAddress
|
from core.misc.ipaddress import MacAddress
|
||||||
from core.mobility import BasicRangeModel
|
|
||||||
from core.mobility import MobilityManager
|
from core.mobility import MobilityManager
|
||||||
from core.mobility import Ns2ScriptedMobility
|
|
||||||
from core.netns import nodes
|
from core.netns import nodes
|
||||||
from core.sdt import Sdt
|
from core.sdt import Sdt
|
||||||
from core.service import CoreServices
|
from core.service import CoreServices
|
||||||
from core.xml.xmlsession import save_session_xml
|
from core.xml import corexml
|
||||||
|
|
||||||
|
|
||||||
class Session(object):
|
class Session(object):
|
||||||
|
@ -61,11 +56,6 @@ class Session(object):
|
||||||
"""
|
"""
|
||||||
self.session_id = session_id
|
self.session_id = session_id
|
||||||
|
|
||||||
# dict of configuration items from /etc/core/core.conf config file
|
|
||||||
if not config:
|
|
||||||
config = {}
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
# define and create session directory when desired
|
# define and create session directory when desired
|
||||||
self.session_dir = os.path.join(tempfile.gettempdir(), "pycore.%s" % self.session_id)
|
self.session_dir = os.path.join(tempfile.gettempdir(), "pycore.%s" % self.session_id)
|
||||||
if mkdir:
|
if mkdir:
|
||||||
|
@ -81,10 +71,6 @@ class Session(object):
|
||||||
self.objects = {}
|
self.objects = {}
|
||||||
self._objects_lock = threading.Lock()
|
self._objects_lock = threading.Lock()
|
||||||
|
|
||||||
# dict of configurable objects
|
|
||||||
self.config_objects = {}
|
|
||||||
self._config_objects_lock = threading.Lock()
|
|
||||||
|
|
||||||
# TODO: should the default state be definition?
|
# TODO: should the default state be definition?
|
||||||
self.state = EventTypes.NONE.value
|
self.state = EventTypes.NONE.value
|
||||||
self._state_time = time.time()
|
self._state_time = time.time()
|
||||||
|
@ -106,60 +92,36 @@ class Session(object):
|
||||||
self.config_handlers = []
|
self.config_handlers = []
|
||||||
self.shutdown_handlers = []
|
self.shutdown_handlers = []
|
||||||
|
|
||||||
# setup broker
|
# session options/metadata
|
||||||
self.broker = CoreBroker(session=self)
|
self.options = SessionConfig()
|
||||||
self.add_config_object(CoreBroker.name, CoreBroker.config_type, self.broker.configure)
|
if not config:
|
||||||
|
config = {}
|
||||||
# setup location
|
for key, value in config.iteritems():
|
||||||
self.location = CoreLocation()
|
self.options.set_config(key, value)
|
||||||
self.add_config_object(CoreLocation.name, CoreLocation.config_type, self.location.configure)
|
|
||||||
|
|
||||||
# setup mobiliy
|
|
||||||
self.mobility = MobilityManager(session=self)
|
|
||||||
self.add_config_object(MobilityManager.name, MobilityManager.config_type, self.mobility.configure)
|
|
||||||
self.add_config_object(BasicRangeModel.name, BasicRangeModel.config_type, BasicRangeModel.configure_mob)
|
|
||||||
self.add_config_object(Ns2ScriptedMobility.name, Ns2ScriptedMobility.config_type,
|
|
||||||
Ns2ScriptedMobility.configure_mob)
|
|
||||||
|
|
||||||
# setup services
|
|
||||||
self.services = CoreServices(session=self)
|
|
||||||
self.add_config_object(CoreServices.name, CoreServices.config_type, self.services.configure)
|
|
||||||
|
|
||||||
# setup emane
|
|
||||||
self.emane = EmaneManager(session=self)
|
|
||||||
self.add_config_object(EmaneManager.name, EmaneManager.config_type, self.emane.configure)
|
|
||||||
|
|
||||||
# setup sdt
|
|
||||||
self.sdt = Sdt(session=self)
|
|
||||||
|
|
||||||
# future parameters set by the GUI may go here
|
|
||||||
self.options = SessionConfig(session=self)
|
|
||||||
self.add_config_object(SessionConfig.name, SessionConfig.config_type, self.options.configure)
|
|
||||||
self.metadata = SessionMetaData()
|
self.metadata = SessionMetaData()
|
||||||
self.add_config_object(SessionMetaData.name, SessionMetaData.config_type, self.metadata.configure)
|
|
||||||
|
# initialize session feature helpers
|
||||||
|
self.broker = CoreBroker(session=self)
|
||||||
|
self.location = CoreLocation()
|
||||||
|
self.mobility = MobilityManager(session=self)
|
||||||
|
self.services = CoreServices(session=self)
|
||||||
|
self.emane = EmaneManager(session=self)
|
||||||
|
self.sdt = Sdt(session=self)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""
|
"""
|
||||||
Shutdown all emulation objects and remove the session directory.
|
Shutdown all emulation objects and remove the session directory.
|
||||||
"""
|
"""
|
||||||
|
# shutdown/cleanup feature helpers
|
||||||
# shutdown emane
|
|
||||||
self.emane.shutdown()
|
self.emane.shutdown()
|
||||||
|
|
||||||
# shutdown broker
|
|
||||||
self.broker.shutdown()
|
self.broker.shutdown()
|
||||||
|
|
||||||
# shutdown NRL's SDT3D
|
|
||||||
self.sdt.shutdown()
|
self.sdt.shutdown()
|
||||||
|
|
||||||
# delete all current objects
|
# delete all current objects
|
||||||
self.delete_objects()
|
self.delete_objects()
|
||||||
|
|
||||||
preserve = False
|
|
||||||
if hasattr(self.options, "preservedir") and self.options.preservedir == "1":
|
|
||||||
preserve = True
|
|
||||||
|
|
||||||
# remove this sessions working directory
|
# remove this sessions working directory
|
||||||
|
preserve = self.options.get_config("preservedir") == "1"
|
||||||
if not preserve:
|
if not preserve:
|
||||||
shutil.rmtree(self.session_dir, ignore_errors=True)
|
shutil.rmtree(self.session_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
@ -379,12 +341,7 @@ class Session(object):
|
||||||
except:
|
except:
|
||||||
message = "exception occured when running %s state hook: %s" % (coreapi.state_name(state), hook)
|
message = "exception occured when running %s state hook: %s" % (coreapi.state_name(state), hook)
|
||||||
logger.exception(message)
|
logger.exception(message)
|
||||||
self.exception(
|
self.exception(ExceptionLevels.ERROR, "Session.run_state_hooks", None, message)
|
||||||
ExceptionLevels.ERROR,
|
|
||||||
"Session.run_state_hooks",
|
|
||||||
None,
|
|
||||||
message
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_state_hook(self, state, hook):
|
def add_state_hook(self, state, hook):
|
||||||
"""
|
"""
|
||||||
|
@ -421,10 +378,10 @@ class Session(object):
|
||||||
"""
|
"""
|
||||||
if state == EventTypes.RUNTIME_STATE.value:
|
if state == EventTypes.RUNTIME_STATE.value:
|
||||||
self.emane.poststartup()
|
self.emane.poststartup()
|
||||||
xml_file_version = self.get_config_item("xmlfilever")
|
xml_file_version = self.options.get_config("xmlfilever")
|
||||||
if xml_file_version in ('1.0',):
|
if xml_file_version in ("1.0",):
|
||||||
xml_file_name = os.path.join(self.session_dir, "session-deployed.xml")
|
xml_file_name = os.path.join(self.session_dir, "session-deployed.xml")
|
||||||
save_session_xml(self, xml_file_name, xml_file_version)
|
corexml.CoreXmlWriter(self).write(xml_file_name)
|
||||||
|
|
||||||
def get_environment(self, state=True):
|
def get_environment(self, state=True):
|
||||||
"""
|
"""
|
||||||
|
@ -597,64 +554,6 @@ class Session(object):
|
||||||
except IOError:
|
except IOError:
|
||||||
logger.exception("error writing nodes file")
|
logger.exception("error writing nodes file")
|
||||||
|
|
||||||
def add_config_object(self, name, object_type, callback):
|
|
||||||
"""
|
|
||||||
Objects can register configuration objects that are included in
|
|
||||||
the Register Message and may be configured via the Configure
|
|
||||||
Message. The callback is invoked when receiving a Configure Message.
|
|
||||||
|
|
||||||
:param str name: name of configuration object to add
|
|
||||||
:param int object_type: register tlv type
|
|
||||||
:param func callback: callback function for object
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
register_tlv = RegisterTlvs(object_type)
|
|
||||||
logger.debug("adding config object callback: %s - %s", name, register_tlv)
|
|
||||||
with self._config_objects_lock:
|
|
||||||
self.config_objects[name] = (object_type, callback)
|
|
||||||
|
|
||||||
def config_object(self, config_data):
|
|
||||||
"""
|
|
||||||
Invoke the callback for an object upon receipt of configuration data for that object.
|
|
||||||
A no-op if the object doesn't exist.
|
|
||||||
|
|
||||||
:param core.data.ConfigData config_data: configuration data to execute against
|
|
||||||
:return: responses to the configuration data
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
name = config_data.object
|
|
||||||
logger.info("session(%s) setting config(%s)", self.session_id, name)
|
|
||||||
for key, value in config_data.__dict__.iteritems():
|
|
||||||
logger.debug("%s = %s", key, value)
|
|
||||||
|
|
||||||
replies = []
|
|
||||||
|
|
||||||
if name == "all":
|
|
||||||
with self._config_objects_lock:
|
|
||||||
for name in self.config_objects:
|
|
||||||
config_type, callback = self.config_objects[name]
|
|
||||||
reply = callback(self, config_data)
|
|
||||||
|
|
||||||
if reply:
|
|
||||||
replies.append(reply)
|
|
||||||
|
|
||||||
return replies
|
|
||||||
|
|
||||||
if name in self.config_objects:
|
|
||||||
with self._config_objects_lock:
|
|
||||||
config_type, callback = self.config_objects[name]
|
|
||||||
|
|
||||||
reply = callback(self, config_data)
|
|
||||||
|
|
||||||
if reply:
|
|
||||||
replies.append(reply)
|
|
||||||
|
|
||||||
return replies
|
|
||||||
else:
|
|
||||||
logger.info("session object doesn't own model '%s', ignoring", name)
|
|
||||||
|
|
||||||
return replies
|
|
||||||
|
|
||||||
def dump_session(self):
|
def dump_session(self):
|
||||||
"""
|
"""
|
||||||
Log information about the session in its current state.
|
Log information about the session in its current state.
|
||||||
|
@ -685,46 +584,6 @@ class Session(object):
|
||||||
|
|
||||||
self.broadcast_exception(exception_data)
|
self.broadcast_exception(exception_data)
|
||||||
|
|
||||||
def get_config_item(self, name):
|
|
||||||
"""
|
|
||||||
Return an entry from the configuration dictionary that comes from
|
|
||||||
command-line arguments and/or the core.conf config file.
|
|
||||||
|
|
||||||
:param str name: name of configuration to retrieve
|
|
||||||
:return: config value
|
|
||||||
"""
|
|
||||||
return self.config.get(name)
|
|
||||||
|
|
||||||
def get_config_item_bool(self, name, default=None):
|
|
||||||
"""
|
|
||||||
Return a boolean entry from the configuration dictionary, may
|
|
||||||
return None if undefined.
|
|
||||||
|
|
||||||
:param str name: configuration item name
|
|
||||||
:param default: default value to return if not found
|
|
||||||
:return: boolean value of the configuration item
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
item = self.get_config_item(name)
|
|
||||||
if item is None:
|
|
||||||
return default
|
|
||||||
return bool(item.lower() == "true")
|
|
||||||
|
|
||||||
def get_config_item_int(self, name, default=None):
|
|
||||||
"""
|
|
||||||
Return an integer entry from the configuration dictionary, may
|
|
||||||
return None if undefined.
|
|
||||||
|
|
||||||
:param str name: configuration item name
|
|
||||||
:param default: default value to return if not found
|
|
||||||
:return: integer value of the configuration item
|
|
||||||
:rtype: int
|
|
||||||
"""
|
|
||||||
item = self.get_config_item(name)
|
|
||||||
if item is None:
|
|
||||||
return default
|
|
||||||
return int(item)
|
|
||||||
|
|
||||||
def instantiate(self):
|
def instantiate(self):
|
||||||
"""
|
"""
|
||||||
We have entered the instantiation state, invoke startup methods
|
We have entered the instantiation state, invoke startup methods
|
||||||
|
@ -742,21 +601,13 @@ class Session(object):
|
||||||
if self.emane.startup() == self.emane.NOT_READY:
|
if self.emane.startup() == self.emane.NOT_READY:
|
||||||
return
|
return
|
||||||
|
|
||||||
# startup broker
|
# start feature helpers
|
||||||
self.broker.startup()
|
self.broker.startup()
|
||||||
|
|
||||||
# startup mobility
|
|
||||||
self.mobility.startup()
|
self.mobility.startup()
|
||||||
|
|
||||||
# boot the services on each node
|
# boot the services on each node
|
||||||
self.boot_nodes()
|
self.boot_nodes()
|
||||||
|
|
||||||
# allow time for processes to start
|
|
||||||
time.sleep(0.125)
|
|
||||||
|
|
||||||
# validate nodes
|
|
||||||
self.validate_nodes()
|
|
||||||
|
|
||||||
# set broker local instantiation to complete
|
# set broker local instantiation to complete
|
||||||
self.broker.local_instantiation_complete()
|
self.broker.local_instantiation_complete()
|
||||||
|
|
||||||
|
@ -822,7 +673,7 @@ class Session(object):
|
||||||
for obj in self.objects.itervalues():
|
for obj in self.objects.itervalues():
|
||||||
# TODO: determine if checking for CoreNode alone is ok
|
# TODO: determine if checking for CoreNode alone is ok
|
||||||
if isinstance(obj, nodes.PyCoreNode):
|
if isinstance(obj, nodes.PyCoreNode):
|
||||||
self.services.stopnodeservices(obj)
|
self.services.stop_services(obj)
|
||||||
|
|
||||||
# shutdown emane
|
# shutdown emane
|
||||||
self.emane.shutdown()
|
self.emane.shutdown()
|
||||||
|
@ -866,34 +717,27 @@ class Session(object):
|
||||||
request flag.
|
request flag.
|
||||||
"""
|
"""
|
||||||
with self._objects_lock:
|
with self._objects_lock:
|
||||||
|
pool = ThreadPool()
|
||||||
|
results = []
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
for obj in self.objects.itervalues():
|
for obj in self.objects.itervalues():
|
||||||
# TODO: PyCoreNode is not the type to check
|
# TODO: PyCoreNode is not the type to check
|
||||||
if isinstance(obj, nodes.PyCoreNode) and not nodeutils.is_node(obj, NodeTypes.RJ45):
|
if isinstance(obj, nodes.PyCoreNode) and not nodeutils.is_node(obj, NodeTypes.RJ45):
|
||||||
# add a control interface if configured
|
# add a control interface if configured
|
||||||
logger.info("booting node: %s", obj.name)
|
logger.info("booting node: %s", obj.name)
|
||||||
self.add_remove_control_interface(node=obj, remove=False)
|
self.add_remove_control_interface(node=obj, remove=False)
|
||||||
obj.boot()
|
result = pool.apply_async(self.services.boot_services, (obj,))
|
||||||
|
results.append(result)
|
||||||
|
|
||||||
|
pool.close()
|
||||||
|
pool.join()
|
||||||
|
for result in results:
|
||||||
|
result.get()
|
||||||
|
logger.info("BOOT RUN TIME: %s", time.time() - start)
|
||||||
|
|
||||||
self.update_control_interface_hosts()
|
self.update_control_interface_hosts()
|
||||||
|
|
||||||
def validate_nodes(self):
|
|
||||||
"""
|
|
||||||
Validate all nodes that are known by the session.
|
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
with self._objects_lock:
|
|
||||||
for obj in self.objects.itervalues():
|
|
||||||
# TODO: issues with checking PyCoreNode alone, validate is not a method
|
|
||||||
# such as vnoded process, bridges, etc.
|
|
||||||
if not isinstance(obj, nodes.PyCoreNode):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if nodeutils.is_node(obj, NodeTypes.RJ45):
|
|
||||||
continue
|
|
||||||
|
|
||||||
obj.validate()
|
|
||||||
|
|
||||||
def get_control_net_prefixes(self):
|
def get_control_net_prefixes(self):
|
||||||
"""
|
"""
|
||||||
Retrieve control net prefixes.
|
Retrieve control net prefixes.
|
||||||
|
@ -901,11 +745,11 @@ class Session(object):
|
||||||
:return: control net prefix list
|
:return: control net prefix list
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
p = getattr(self.options, "controlnet", self.config.get("controlnet"))
|
p = self.options.get_config("controlnet")
|
||||||
p0 = getattr(self.options, "controlnet0", self.config.get("controlnet0"))
|
p0 = self.options.get_config("controlnet0")
|
||||||
p1 = getattr(self.options, "controlnet1", self.config.get("controlnet1"))
|
p1 = self.options.get_config("controlnet1")
|
||||||
p2 = getattr(self.options, "controlnet2", self.config.get("controlnet2"))
|
p2 = self.options.get_config("controlnet2")
|
||||||
p3 = getattr(self.options, "controlnet3", self.config.get("controlnet3"))
|
p3 = self.options.get_config("controlnet3")
|
||||||
|
|
||||||
if not p0 and p:
|
if not p0 and p:
|
||||||
p0 = p
|
p0 = p
|
||||||
|
@ -919,12 +763,12 @@ class Session(object):
|
||||||
:return: list of control net server interfaces
|
:return: list of control net server interfaces
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
d0 = self.config.get("controlnetif0")
|
d0 = self.options.get_config("controlnetif0")
|
||||||
if d0:
|
if d0:
|
||||||
logger.error("controlnet0 cannot be assigned with a host interface")
|
logger.error("controlnet0 cannot be assigned with a host interface")
|
||||||
d1 = self.config.get("controlnetif1")
|
d1 = self.options.get_config("controlnetif1")
|
||||||
d2 = self.config.get("controlnetif2")
|
d2 = self.options.get_config("controlnetif2")
|
||||||
d3 = self.config.get("controlnetif3")
|
d3 = self.options.get_config("controlnetif3")
|
||||||
return [None, d1, d2, d3]
|
return [None, d1, d2, d3]
|
||||||
|
|
||||||
def get_control_net_index(self, dev):
|
def get_control_net_index(self, dev):
|
||||||
|
@ -995,15 +839,10 @@ class Session(object):
|
||||||
updown_script = None
|
updown_script = None
|
||||||
|
|
||||||
if net_index == 0:
|
if net_index == 0:
|
||||||
updown_script = self.config.get("controlnet_updown_script")
|
updown_script = self.options.get_config("controlnet_updown_script")
|
||||||
if not updown_script:
|
if not updown_script:
|
||||||
logger.warning("controlnet updown script not configured")
|
logger.warning("controlnet updown script not configured")
|
||||||
|
|
||||||
# check if session option set, overwrite if so
|
|
||||||
options_updown_script = getattr(self.options, "controlnet_updown_script", None)
|
|
||||||
if options_updown_script:
|
|
||||||
updown_script = options_updown_script
|
|
||||||
|
|
||||||
prefixes = prefix_spec.split()
|
prefixes = prefix_spec.split()
|
||||||
if len(prefixes) > 1:
|
if len(prefixes) > 1:
|
||||||
# a list of per-host prefixes is provided
|
# a list of per-host prefixes is provided
|
||||||
|
@ -1112,7 +951,7 @@ class Session(object):
|
||||||
:param bool remove: flag to check if it should be removed
|
:param bool remove: flag to check if it should be removed
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if not self.get_config_item_bool("update_etc_hosts", False):
|
if not self.options.get_config_bool("update_etc_hosts", default=False):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1194,165 +1033,51 @@ class Session(object):
|
||||||
node = self.get_object(node_id)
|
node = self.get_object(node_id)
|
||||||
node.cmd(data, wait=False)
|
node.cmd(data, wait=False)
|
||||||
|
|
||||||
def send_objects(self):
|
|
||||||
"""
|
|
||||||
Return API messages that describe the current session.
|
|
||||||
"""
|
|
||||||
# find all nodes and links
|
|
||||||
nodes_data = []
|
|
||||||
links_data = []
|
|
||||||
with self._objects_lock:
|
|
||||||
for obj in self.objects.itervalues():
|
|
||||||
node_data = obj.data(message_type=MessageFlags.ADD.value)
|
|
||||||
if node_data:
|
|
||||||
nodes_data.append(node_data)
|
|
||||||
|
|
||||||
node_links = obj.all_link_data(flags=MessageFlags.ADD.value)
|
class SessionConfig(ConfigurableManager, ConfigurableOptions):
|
||||||
for link_data in node_links:
|
|
||||||
links_data.append(link_data)
|
|
||||||
|
|
||||||
# send all nodes first, so that they will exist for any links
|
|
||||||
logger.info("sending nodes:")
|
|
||||||
for node_data in nodes_data:
|
|
||||||
logger.info(pprint.pformat(dict(node_data._asdict())))
|
|
||||||
self.broadcast_node(node_data)
|
|
||||||
|
|
||||||
logger.info("sending links:")
|
|
||||||
for link_data in links_data:
|
|
||||||
logger.info(pprint.pformat(dict(link_data._asdict())))
|
|
||||||
self.broadcast_link(link_data)
|
|
||||||
|
|
||||||
# send model info
|
|
||||||
configs = self.mobility.getallconfigs()
|
|
||||||
configs += self.emane.getallconfigs()
|
|
||||||
logger.info("sending model configs:")
|
|
||||||
for node_number, cls, values in configs:
|
|
||||||
logger.info("config: node(%s) class(%s) values(%s)", node_number, cls, values)
|
|
||||||
config_data = cls.config_data(
|
|
||||||
flags=0,
|
|
||||||
node_id=node_number,
|
|
||||||
type_flags=ConfigFlags.UPDATE.value,
|
|
||||||
values=values
|
|
||||||
)
|
|
||||||
logger.info(pprint.pformat(dict(config_data._asdict())))
|
|
||||||
self.broadcast_config(config_data)
|
|
||||||
|
|
||||||
# service customizations
|
|
||||||
service_configs = self.services.getallconfigs()
|
|
||||||
for node_number, service in service_configs:
|
|
||||||
opaque = "service:%s" % service._name
|
|
||||||
config_data = ConfigData(
|
|
||||||
node=node_number,
|
|
||||||
opaque=opaque
|
|
||||||
)
|
|
||||||
config_response = self.services.configure_request(config_data)
|
|
||||||
self.broadcast_config(config_response)
|
|
||||||
|
|
||||||
for file_name, config_data in self.services.getallfiles(service):
|
|
||||||
file_data = FileData(
|
|
||||||
message_type=MessageFlags.ADD.value,
|
|
||||||
node=node_number,
|
|
||||||
name=str(file_name),
|
|
||||||
type=opaque,
|
|
||||||
data=str(config_data)
|
|
||||||
)
|
|
||||||
self.broadcast_file(file_data)
|
|
||||||
|
|
||||||
# TODO: send location info
|
|
||||||
|
|
||||||
# send hook scripts
|
|
||||||
for state in sorted(self._hooks.keys()):
|
|
||||||
for file_name, config_data in self._hooks[state]:
|
|
||||||
file_data = FileData(
|
|
||||||
message_type=MessageFlags.ADD.value,
|
|
||||||
name=str(file_name),
|
|
||||||
type="hook:%s" % state,
|
|
||||||
data=str(config_data)
|
|
||||||
)
|
|
||||||
self.broadcast_file(file_data)
|
|
||||||
|
|
||||||
config_data = ConfigData()
|
|
||||||
|
|
||||||
# retrieve session configuration data
|
|
||||||
options_config = self.options.configure_request(config_data, type_flags=ConfigFlags.UPDATE.value)
|
|
||||||
self.broadcast_config(options_config)
|
|
||||||
|
|
||||||
# retrieve session metadata
|
|
||||||
metadata_config = self.metadata.configure_request(config_data, type_flags=ConfigFlags.UPDATE.value)
|
|
||||||
self.broadcast_config(metadata_config)
|
|
||||||
|
|
||||||
logger.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data))
|
|
||||||
|
|
||||||
|
|
||||||
class SessionConfig(ConfigurableManager, Configurable):
|
|
||||||
"""
|
"""
|
||||||
Session configuration object.
|
Session configuration object.
|
||||||
"""
|
"""
|
||||||
name = "session"
|
name = "session"
|
||||||
config_type = RegisterTlvs.UTILITY.value
|
options = [
|
||||||
config_matrix = [
|
Configuration(_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"),
|
||||||
("controlnet", ConfigDataTypes.STRING.value, "", "", "Control network"),
|
Configuration(_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"),
|
||||||
("controlnet_updown_script", ConfigDataTypes.STRING.value, "", "", "Control network script"),
|
Configuration(_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"),
|
||||||
("enablerj45", ConfigDataTypes.BOOL.value, "1", "On,Off", "Enable RJ45s"),
|
Configuration(_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"),
|
||||||
("preservedir", ConfigDataTypes.BOOL.value, "0", "On,Off", "Preserve session dir"),
|
Configuration(_id="controlnet3", _type=ConfigDataTypes.STRING, label="Control Network 3"),
|
||||||
("enablesdt", ConfigDataTypes.BOOL.value, "0", "On,Off", "Enable SDT3D output"),
|
Configuration(_id="controlnet_updown_script", _type=ConfigDataTypes.STRING, label="Control Network Script"),
|
||||||
("sdturl", ConfigDataTypes.STRING.value, Sdt.DEFAULT_SDT_URL, "", "SDT3D URL"),
|
Configuration(_id="enablerj45", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"],
|
||||||
|
label="Enable RJ45s"),
|
||||||
|
Configuration(_id="preservedir", _type=ConfigDataTypes.BOOL, default="0", options=["On", "Off"],
|
||||||
|
label="Preserve session dir"),
|
||||||
|
Configuration(_id="enablesdt", _type=ConfigDataTypes.BOOL, default="0", options=["On", "Off"],
|
||||||
|
label="Enable SDT3D output"),
|
||||||
|
Configuration(_id="sdturl", _type=ConfigDataTypes.STRING, default=Sdt.DEFAULT_SDT_URL, label="SDT3D URL")
|
||||||
]
|
]
|
||||||
config_groups = "Options:1-%d" % len(config_matrix)
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
|
||||||
def __init__(self, session):
|
def __init__(self):
|
||||||
"""
|
super(SessionConfig, self).__init__()
|
||||||
Creates a SessionConfig instance.
|
self.set_configs(self.default_values())
|
||||||
|
|
||||||
:param core.session.Session session: session this manager is tied to
|
def get_config(self, _id, node_id=ConfigurableManager._default_node,
|
||||||
:return: nothing
|
config_type=ConfigurableManager._default_type, default=None):
|
||||||
"""
|
value = super(SessionConfig, self).get_config(_id, node_id, config_type, default)
|
||||||
ConfigurableManager.__init__(self)
|
if value == "":
|
||||||
self.session = session
|
value = default
|
||||||
self.reset()
|
return value
|
||||||
|
|
||||||
def reset(self):
|
def get_config_bool(self, name, default=None):
|
||||||
"""
|
value = self.get_config(name)
|
||||||
Reset the session configuration.
|
if value is None:
|
||||||
|
return default
|
||||||
|
return value.lower() == "true"
|
||||||
|
|
||||||
:return: nothing
|
def get_config_int(self, name, default=None):
|
||||||
"""
|
value = self.get_config(name, default=default)
|
||||||
defaults = self.getdefaultvalues()
|
if value is not None:
|
||||||
for key in self.getnames():
|
value = int(value)
|
||||||
# value may come from config file
|
return value
|
||||||
value = self.session.get_config_item(key)
|
|
||||||
if value is None:
|
|
||||||
value = self.valueof(key, defaults)
|
|
||||||
value = self.offontobool(value)
|
|
||||||
setattr(self, key, value)
|
|
||||||
|
|
||||||
def configure_values(self, config_data):
|
|
||||||
"""
|
|
||||||
Handle configuration values.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
return self.configure_values_keyvalues(config_data, self, self.getnames())
|
|
||||||
|
|
||||||
def configure_request(self, config_data, type_flags=ConfigFlags.NONE.value):
|
|
||||||
"""
|
|
||||||
Handle a configuration request.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:param type_flags:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
node_id = config_data.node
|
|
||||||
values = []
|
|
||||||
|
|
||||||
for key in self.getnames():
|
|
||||||
value = getattr(self, key)
|
|
||||||
if value is None:
|
|
||||||
value = ""
|
|
||||||
values.append("%s" % value)
|
|
||||||
|
|
||||||
return self.config_data(0, node_id, type_flags, values)
|
|
||||||
|
|
||||||
|
|
||||||
class SessionMetaData(ConfigurableManager):
|
class SessionMetaData(ConfigurableManager):
|
||||||
|
@ -1363,92 +1088,3 @@ class SessionMetaData(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
name = "metadata"
|
name = "metadata"
|
||||||
config_type = RegisterTlvs.UTILITY.value
|
config_type = RegisterTlvs.UTILITY.value
|
||||||
|
|
||||||
def configure_values(self, config_data):
|
|
||||||
"""
|
|
||||||
Handle configuration values.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
values = config_data.data_values
|
|
||||||
if values is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
key_values = values.split('|')
|
|
||||||
for key_value in key_values:
|
|
||||||
try:
|
|
||||||
key, value = key_value.split('=', 1)
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError("invalid key in metdata: %s", key_value)
|
|
||||||
|
|
||||||
self.add_item(key, value)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def configure_request(self, config_data, type_flags=ConfigFlags.NONE.value):
|
|
||||||
"""
|
|
||||||
Handle a configuration request.
|
|
||||||
|
|
||||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
|
||||||
:param int type_flags: configuration request flag value
|
|
||||||
:return: configuration data
|
|
||||||
:rtype: ConfigData
|
|
||||||
"""
|
|
||||||
node_number = config_data.node
|
|
||||||
values_str = "|".join(map(lambda item: "%s=%s" % item, self.items()))
|
|
||||||
return self.config_data(0, node_number, type_flags, values_str)
|
|
||||||
|
|
||||||
def config_data(self, flags, node_id, type_flags, values_str):
|
|
||||||
"""
|
|
||||||
Retrieve configuration data object, leveraging provided data.
|
|
||||||
|
|
||||||
:param flags: configuration data flags
|
|
||||||
:param int node_id: node id
|
|
||||||
:param type_flags: type flags
|
|
||||||
:param values_str: values string
|
|
||||||
:return: configuration data
|
|
||||||
:rtype: ConfigData
|
|
||||||
"""
|
|
||||||
data_types = tuple(map(lambda (k, v): ConfigDataTypes.STRING.value, self.items()))
|
|
||||||
|
|
||||||
return ConfigData(
|
|
||||||
message_type=flags,
|
|
||||||
node=node_id,
|
|
||||||
object=self.name,
|
|
||||||
type=type_flags,
|
|
||||||
data_types=data_types,
|
|
||||||
data_values=values_str
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_item(self, key, value):
|
|
||||||
"""
|
|
||||||
Add configuration key/value pair.
|
|
||||||
|
|
||||||
:param key: configuration key
|
|
||||||
:param value: configuration value
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
self.configs[key] = value
|
|
||||||
|
|
||||||
def get_item(self, key):
|
|
||||||
"""
|
|
||||||
Retrieve configuration value.
|
|
||||||
|
|
||||||
:param key: key for configuration value to retrieve
|
|
||||||
:return: configuration value
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.configs[key]
|
|
||||||
except KeyError:
|
|
||||||
logger.exception("error retrieving item from configs: %s", key)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
"""
|
|
||||||
Retrieve configuration items.
|
|
||||||
|
|
||||||
:return: configuration items iterator
|
|
||||||
"""
|
|
||||||
return self.configs.iteritems()
|
|
||||||
|
|
919
daemon/core/xml/corexml.py
Normal file
919
daemon/core/xml/corexml.py
Normal file
|
@ -0,0 +1,919 @@
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
from core import coreobj
|
||||||
|
from core import logger
|
||||||
|
from core.emulator.emudata import InterfaceData
|
||||||
|
from core.emulator.emudata import LinkOptions
|
||||||
|
from core.emulator.emudata import NodeOptions
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.misc import ipaddress
|
||||||
|
from core.misc import nodeutils
|
||||||
|
from core.misc.ipaddress import MacAddress
|
||||||
|
from core.netns import nodes
|
||||||
|
|
||||||
|
|
||||||
|
def write_xml_file(xml_element, file_path, doctype=None):
|
||||||
|
xml_data = etree.tostring(xml_element, xml_declaration=True, pretty_print=True, encoding="UTF-8", doctype=doctype)
|
||||||
|
with open(file_path, "w") as xml_file:
|
||||||
|
xml_file.write(xml_data)
|
||||||
|
|
||||||
|
|
||||||
|
def get_type(element, name, _type):
|
||||||
|
value = element.get(name)
|
||||||
|
if value is not None:
|
||||||
|
value = _type(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def get_float(element, name):
|
||||||
|
return get_type(element, name, float)
|
||||||
|
|
||||||
|
|
||||||
|
def get_int(element, name):
|
||||||
|
return get_type(element, name, int)
|
||||||
|
|
||||||
|
|
||||||
|
def add_attribute(element, name, value):
|
||||||
|
if value is not None:
|
||||||
|
element.set(name, str(value))
|
||||||
|
|
||||||
|
|
||||||
|
def create_interface_data(interface_element):
|
||||||
|
interface_id = int(interface_element.get("id"))
|
||||||
|
name = interface_element.get("name")
|
||||||
|
mac = interface_element.get("mac")
|
||||||
|
if mac:
|
||||||
|
mac = MacAddress.from_string(mac)
|
||||||
|
ip4 = interface_element.get("ip4")
|
||||||
|
ip4_mask = get_int(interface_element, "ip4_mask")
|
||||||
|
ip6 = interface_element.get("ip6")
|
||||||
|
ip6_mask = get_int(interface_element, "ip6_mask")
|
||||||
|
return InterfaceData(interface_id, name, mac, ip4, ip4_mask, ip6, ip6_mask)
|
||||||
|
|
||||||
|
|
||||||
|
def create_emane_config(node_id, emane_config, config):
|
||||||
|
emane_configuration = etree.Element("emane_configuration")
|
||||||
|
add_attribute(emane_configuration, "node", node_id)
|
||||||
|
add_attribute(emane_configuration, "model", "emane")
|
||||||
|
|
||||||
|
emulator_element = etree.SubElement(emane_configuration, "emulator")
|
||||||
|
for emulator_config in emane_config.emulator_config:
|
||||||
|
value = config[emulator_config.id]
|
||||||
|
add_configuration(emulator_element, emulator_config.id, value)
|
||||||
|
|
||||||
|
nem_element = etree.SubElement(emane_configuration, "nem")
|
||||||
|
for nem_config in emane_config.nem_config:
|
||||||
|
value = config[nem_config.id]
|
||||||
|
add_configuration(nem_element, nem_config.id, value)
|
||||||
|
|
||||||
|
return emane_configuration
|
||||||
|
|
||||||
|
|
||||||
|
def create_emane_model_config(node_id, model, config):
|
||||||
|
emane_element = etree.Element("emane_configuration")
|
||||||
|
add_attribute(emane_element, "node", node_id)
|
||||||
|
add_attribute(emane_element, "model", model.name)
|
||||||
|
|
||||||
|
mac_element = etree.SubElement(emane_element, "mac")
|
||||||
|
for mac_config in model.mac_config:
|
||||||
|
value = config[mac_config.id]
|
||||||
|
add_configuration(mac_element, mac_config.id, value)
|
||||||
|
|
||||||
|
phy_element = etree.SubElement(emane_element, "phy")
|
||||||
|
for phy_config in model.phy_config:
|
||||||
|
value = config[phy_config.id]
|
||||||
|
add_configuration(phy_element, phy_config.id, value)
|
||||||
|
|
||||||
|
external_element = etree.SubElement(emane_element, "external")
|
||||||
|
for external_config in model.external_config:
|
||||||
|
value = config[external_config.id]
|
||||||
|
add_configuration(external_element, external_config.id, value)
|
||||||
|
|
||||||
|
return emane_element
|
||||||
|
|
||||||
|
|
||||||
|
def add_configuration(parent, name, value):
|
||||||
|
config_element = etree.SubElement(parent, "configuration")
|
||||||
|
add_attribute(config_element, "name", name)
|
||||||
|
add_attribute(config_element, "value", value)
|
||||||
|
|
||||||
|
|
||||||
|
def get_endpoints(node):
|
||||||
|
endpoints = []
|
||||||
|
for interface in node.netifs(sort=True):
|
||||||
|
endpoint = get_endpoint(node, interface)
|
||||||
|
endpoints.append(endpoint)
|
||||||
|
return endpoints
|
||||||
|
|
||||||
|
|
||||||
|
def get_endpoint(node, interface):
|
||||||
|
l2devport = None
|
||||||
|
othernet = getattr(interface, "othernet", None)
|
||||||
|
|
||||||
|
# reference interface of node that is part of this network
|
||||||
|
if interface.net.objid == node.objid and interface.node:
|
||||||
|
params = interface.getparams()
|
||||||
|
if nodeutils.is_node(interface.net, (NodeTypes.HUB, NodeTypes.SWITCH)):
|
||||||
|
l2devport = "%s/e%s" % (interface.net.name, interface.netindex)
|
||||||
|
endpoint_id = "%s/%s" % (interface.node.name, interface.name)
|
||||||
|
endpoint = Endpoint(
|
||||||
|
node,
|
||||||
|
interface,
|
||||||
|
"interface",
|
||||||
|
endpoint_id,
|
||||||
|
l2devport,
|
||||||
|
params
|
||||||
|
)
|
||||||
|
# references another node connected to this network
|
||||||
|
elif othernet and othernet.objid == node.objid:
|
||||||
|
interface.swapparams("_params_up")
|
||||||
|
params = interface.getparams()
|
||||||
|
interface.swapparams("_params_up")
|
||||||
|
l2devport = "%s/e%s" % (othernet.name, interface.netindex)
|
||||||
|
endpoint_id = "%s/%s/%s" % (node.name, interface.node.name, interface.netindex)
|
||||||
|
endpoint = Endpoint(
|
||||||
|
interface.net,
|
||||||
|
interface,
|
||||||
|
"interface",
|
||||||
|
endpoint_id,
|
||||||
|
l2devport,
|
||||||
|
params
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
endpoint = Endpoint(
|
||||||
|
node,
|
||||||
|
interface,
|
||||||
|
)
|
||||||
|
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
|
||||||
|
def get_downstream_l2_devices(node):
|
||||||
|
all_endpoints = []
|
||||||
|
l2_devices = [node]
|
||||||
|
current_endpoint = get_endpoints(node)
|
||||||
|
all_endpoints.extend(current_endpoint)
|
||||||
|
for endpoint in current_endpoint:
|
||||||
|
if endpoint.type and endpoint.network.objid != node.objid:
|
||||||
|
new_l2_devices, new_endpoints = get_downstream_l2_devices(endpoint.network)
|
||||||
|
l2_devices.extend(new_l2_devices)
|
||||||
|
all_endpoints.extend(new_endpoints)
|
||||||
|
return l2_devices, all_endpoints
|
||||||
|
|
||||||
|
|
||||||
|
def create_link_element(link_data):
|
||||||
|
link_element = etree.Element("link")
|
||||||
|
add_attribute(link_element, "node_one", link_data.node1_id)
|
||||||
|
add_attribute(link_element, "node_two", link_data.node2_id)
|
||||||
|
|
||||||
|
# check for interface one
|
||||||
|
interface_one = etree.Element("interface_one")
|
||||||
|
add_attribute(interface_one, "id", link_data.interface1_id)
|
||||||
|
add_attribute(interface_one, "name", link_data.interface1_name)
|
||||||
|
add_attribute(interface_one, "mac", link_data.interface1_mac)
|
||||||
|
add_attribute(interface_one, "ip4", link_data.interface1_ip4)
|
||||||
|
add_attribute(interface_one, "ip4_mask", link_data.interface1_ip4_mask)
|
||||||
|
add_attribute(interface_one, "ip6", link_data.interface1_ip6)
|
||||||
|
add_attribute(interface_one, "ip6_mask", link_data.interface1_ip6_mask)
|
||||||
|
if interface_one.items():
|
||||||
|
link_element.append(interface_one)
|
||||||
|
|
||||||
|
# check for interface two
|
||||||
|
interface_two = etree.Element("interface_two")
|
||||||
|
add_attribute(interface_two, "id", link_data.interface2_id)
|
||||||
|
add_attribute(interface_two, "name", link_data.interface2_name)
|
||||||
|
add_attribute(interface_two, "mac", link_data.interface2_mac)
|
||||||
|
add_attribute(interface_two, "ip4", link_data.interface2_ip4)
|
||||||
|
add_attribute(interface_two, "ip4_mask", link_data.interface2_ip4_mask)
|
||||||
|
add_attribute(interface_two, "ip6", link_data.interface2_ip6)
|
||||||
|
add_attribute(interface_two, "ip6_mask", link_data.interface2_ip6_mask)
|
||||||
|
if interface_two.items():
|
||||||
|
link_element.append(interface_two)
|
||||||
|
|
||||||
|
# check for options
|
||||||
|
options = etree.Element("options")
|
||||||
|
add_attribute(options, "delay", link_data.delay)
|
||||||
|
add_attribute(options, "bandwidth", link_data.bandwidth)
|
||||||
|
add_attribute(options, "per", link_data.per)
|
||||||
|
add_attribute(options, "dup", link_data.dup)
|
||||||
|
add_attribute(options, "jitter", link_data.jitter)
|
||||||
|
add_attribute(options, "mer", link_data.mer)
|
||||||
|
add_attribute(options, "burst", link_data.burst)
|
||||||
|
add_attribute(options, "mburst", link_data.mburst)
|
||||||
|
add_attribute(options, "type", link_data.link_type)
|
||||||
|
add_attribute(options, "gui_attributes", link_data.gui_attributes)
|
||||||
|
add_attribute(options, "unidirectional", link_data.unidirectional)
|
||||||
|
add_attribute(options, "emulation_id", link_data.emulation_id)
|
||||||
|
add_attribute(options, "network_id", link_data.network_id)
|
||||||
|
add_attribute(options, "key", link_data.key)
|
||||||
|
add_attribute(options, "opaque", link_data.opaque)
|
||||||
|
add_attribute(options, "session", link_data.session)
|
||||||
|
if options.items():
|
||||||
|
link_element.append(options)
|
||||||
|
|
||||||
|
return link_element
|
||||||
|
|
||||||
|
|
||||||
|
class Endpoint(object):
|
||||||
|
def __init__(self, network, interface, _type=None, _id=None, l2devport=None, params=None):
|
||||||
|
self.network = network
|
||||||
|
self.interface = interface
|
||||||
|
self.type = _type
|
||||||
|
self.id = _id
|
||||||
|
self.l2devport = l2devport
|
||||||
|
self.params = params
|
||||||
|
|
||||||
|
|
||||||
|
class NodeElement(object):
|
||||||
|
def __init__(self, session, node, element_name):
|
||||||
|
self.session = session
|
||||||
|
self.node = node
|
||||||
|
self.element = etree.Element(element_name)
|
||||||
|
add_attribute(self.element, "id", node.objid)
|
||||||
|
add_attribute(self.element, "name", node.name)
|
||||||
|
add_attribute(self.element, "icon", node.icon)
|
||||||
|
add_attribute(self.element, "canvas", node.canvas)
|
||||||
|
self.add_position()
|
||||||
|
|
||||||
|
def add_position(self):
|
||||||
|
x = self.node.position.x
|
||||||
|
y = self.node.position.y
|
||||||
|
z = self.node.position.z
|
||||||
|
lat, lon, alt = None, None, None
|
||||||
|
if x is not None and y is not None:
|
||||||
|
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||||
|
position = etree.SubElement(self.element, "position")
|
||||||
|
add_attribute(position, "x", x)
|
||||||
|
add_attribute(position, "y", y)
|
||||||
|
add_attribute(position, "z", z)
|
||||||
|
add_attribute(position, "lat", lat)
|
||||||
|
add_attribute(position, "lon", lon)
|
||||||
|
add_attribute(position, "alt", alt)
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceElement(object):
|
||||||
|
def __init__(self, session, node, interface):
|
||||||
|
self.session = session
|
||||||
|
self.node = node
|
||||||
|
self.interface = interface
|
||||||
|
self.element = etree.Element("interface")
|
||||||
|
add_attribute(self.element, "id", interface.netindex)
|
||||||
|
add_attribute(self.element, "name", interface.name)
|
||||||
|
mac = etree.SubElement(self.element, "mac")
|
||||||
|
mac.text = str(interface.hwaddr)
|
||||||
|
self.add_mtu()
|
||||||
|
self.addresses = etree.SubElement(self.element, "addresses")
|
||||||
|
self.add_addresses()
|
||||||
|
self.add_model()
|
||||||
|
|
||||||
|
def add_mtu(self):
|
||||||
|
# check to add mtu
|
||||||
|
if self.interface.mtu and self.interface.mtu != 1500:
|
||||||
|
add_attribute(self.element, "mtu", self.interface.mtu)
|
||||||
|
|
||||||
|
def add_model(self):
|
||||||
|
# check for emane specific interface configuration
|
||||||
|
net_model = None
|
||||||
|
if self.interface.net and hasattr(self.interface.net, "model"):
|
||||||
|
net_model = self.interface.net.model
|
||||||
|
|
||||||
|
if net_model and net_model.name.startswith("emane_"):
|
||||||
|
config = self.session.emane.getifcconfig(self.node.objid, self.interface, net_model.name)
|
||||||
|
if config:
|
||||||
|
emane_element = create_emane_model_config(net_model, config)
|
||||||
|
self.element.append(emane_element)
|
||||||
|
|
||||||
|
def add_addresses(self):
|
||||||
|
for address in self.interface.addrlist:
|
||||||
|
ip, mask = address.split("/")
|
||||||
|
if ipaddress.is_ipv4_address(ip):
|
||||||
|
address_type = "IPv4"
|
||||||
|
else:
|
||||||
|
address_type = "IPv6"
|
||||||
|
address_element = etree.SubElement(self.addresses, "address")
|
||||||
|
add_attribute(address_element, "type", address_type)
|
||||||
|
address_element.text = str(address)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceElement(object):
|
||||||
|
def __init__(self, service):
|
||||||
|
self.service = service
|
||||||
|
self.element = etree.Element("service")
|
||||||
|
add_attribute(self.element, "name", service.name)
|
||||||
|
self.add_directories()
|
||||||
|
self.add_startup()
|
||||||
|
self.add_validate()
|
||||||
|
self.add_shutdown()
|
||||||
|
self.add_files()
|
||||||
|
|
||||||
|
def add_directories(self):
|
||||||
|
# get custom directories
|
||||||
|
directories = etree.Element("directories")
|
||||||
|
for directory in self.service.dirs:
|
||||||
|
directory_element = etree.SubElement(directories, "directory")
|
||||||
|
directory_element.text = directory
|
||||||
|
|
||||||
|
if directories.getchildren():
|
||||||
|
self.element.append(directories)
|
||||||
|
|
||||||
|
def add_files(self):
|
||||||
|
# get custom files
|
||||||
|
file_elements = etree.Element("files")
|
||||||
|
for file_name, data in self.service.config_data.iteritems():
|
||||||
|
file_element = etree.SubElement(file_elements, "file")
|
||||||
|
add_attribute(file_element, "name", file_name)
|
||||||
|
file_element.text = data
|
||||||
|
|
||||||
|
if file_elements.getchildren():
|
||||||
|
self.element.append(file_elements)
|
||||||
|
|
||||||
|
def add_startup(self):
|
||||||
|
# get custom startup
|
||||||
|
startup_elements = etree.Element("startups")
|
||||||
|
for startup in self.service.startup:
|
||||||
|
startup_element = etree.SubElement(startup_elements, "startup")
|
||||||
|
startup_element.text = startup
|
||||||
|
|
||||||
|
if startup_elements.getchildren():
|
||||||
|
self.element.append(startup_elements)
|
||||||
|
|
||||||
|
def add_validate(self):
|
||||||
|
# get custom validate
|
||||||
|
validate_elements = etree.Element("validates")
|
||||||
|
for validate in self.service.validate:
|
||||||
|
validate_element = etree.SubElement(validate_elements, "validate")
|
||||||
|
validate_element.text = validate
|
||||||
|
|
||||||
|
if validate_elements.getchildren():
|
||||||
|
self.element.append(validate_elements)
|
||||||
|
|
||||||
|
def add_shutdown(self):
|
||||||
|
# get custom shutdown
|
||||||
|
shutdown_elements = etree.Element("shutdowns")
|
||||||
|
for shutdown in self.service.shutdown:
|
||||||
|
shutdown_element = etree.SubElement(shutdown_elements, "shutdown")
|
||||||
|
shutdown_element.text = shutdown
|
||||||
|
|
||||||
|
if shutdown_elements.getchildren():
|
||||||
|
self.element.append(shutdown_elements)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceElement(NodeElement):
|
||||||
|
def __init__(self, session, node):
|
||||||
|
super(DeviceElement, self).__init__(session, node, "device")
|
||||||
|
add_attribute(self.element, "type", node.type)
|
||||||
|
# self.add_interfaces()
|
||||||
|
self.add_services()
|
||||||
|
|
||||||
|
def add_services(self):
|
||||||
|
service_elements = etree.Element("services")
|
||||||
|
for service in self.node.services:
|
||||||
|
etree.SubElement(service_elements, "service", name=service.name)
|
||||||
|
|
||||||
|
if service_elements.getchildren():
|
||||||
|
self.element.append(service_elements)
|
||||||
|
|
||||||
|
def add_interfaces(self):
|
||||||
|
interfaces = etree.Element("interfaces")
|
||||||
|
for interface in self.node.netifs(sort=True):
|
||||||
|
interface_element = InterfaceElement(self.session, self.node, interface)
|
||||||
|
interfaces.append(interface_element.element)
|
||||||
|
|
||||||
|
if interfaces.getchildren():
|
||||||
|
self.element.append(interfaces)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkElement(NodeElement):
|
||||||
|
def __init__(self, session, node):
|
||||||
|
super(NetworkElement, self).__init__(session, node, "network")
|
||||||
|
model = getattr(self.node, "model", None)
|
||||||
|
if model:
|
||||||
|
add_attribute(self.element, "model", model.name)
|
||||||
|
mobility = getattr(self.node, "mobility", None)
|
||||||
|
if mobility:
|
||||||
|
add_attribute(self.element, "mobility", mobility.name)
|
||||||
|
grekey = getattr(self.node, "grekey", None)
|
||||||
|
if grekey and grekey is not None:
|
||||||
|
add_attribute(self.element, "grekey", grekey)
|
||||||
|
self.add_type()
|
||||||
|
# self.endpoints = get_endpoints(self.node)
|
||||||
|
# self.l2_devices = self.get_l2_devices()
|
||||||
|
# self.add_configs()
|
||||||
|
|
||||||
|
def add_type(self):
|
||||||
|
if self.node.apitype:
|
||||||
|
node_type = NodeTypes(self.node.apitype).name
|
||||||
|
else:
|
||||||
|
node_type = self.node.__class__.__name__
|
||||||
|
add_attribute(self.element, "type", node_type)
|
||||||
|
|
||||||
|
def get_l2_devices(self):
|
||||||
|
l2_devices = []
|
||||||
|
found_l2_devices = []
|
||||||
|
found_endpoints = []
|
||||||
|
if nodeutils.is_node(self.node, (NodeTypes.SWITCH, NodeTypes.HUB)):
|
||||||
|
for endpoint in self.endpoints:
|
||||||
|
if endpoint.type and endpoint.network.objid != self.node.objid:
|
||||||
|
downstream_l2_devices, downstream_endpoints = get_downstream_l2_devices(endpoint.network)
|
||||||
|
found_l2_devices.extend(downstream_l2_devices)
|
||||||
|
found_endpoints.extend(downstream_endpoints)
|
||||||
|
|
||||||
|
for l2_device in found_l2_devices:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.endpoints.extend(found_endpoints)
|
||||||
|
return l2_devices
|
||||||
|
|
||||||
|
def add_peer_to_peer_config(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_switch_hub_tunnel_config(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_configs(self):
|
||||||
|
if nodeutils.is_node(self.node, NodeTypes.PEER_TO_PEER):
|
||||||
|
self.add_peer_to_peer_config()
|
||||||
|
elif nodeutils.is_node(self.node, (NodeTypes.SWITCH, NodeTypes.HUB, NodeTypes.TUNNEL)):
|
||||||
|
self.add_switch_hub_tunnel_config()
|
||||||
|
|
||||||
|
|
||||||
|
class CoreXmlWriter(object):
|
||||||
|
def __init__(self, session):
|
||||||
|
self.session = session
|
||||||
|
self.scenario = None
|
||||||
|
self.networks = None
|
||||||
|
self.devices = None
|
||||||
|
|
||||||
|
def write(self, file_name):
|
||||||
|
# generate xml content
|
||||||
|
self.scenario = etree.Element("scenario", name=file_name)
|
||||||
|
links = self.write_nodes()
|
||||||
|
self.write_links(links)
|
||||||
|
self.write_mobility_configs()
|
||||||
|
self.write_emane_configs()
|
||||||
|
self.write_service_configs()
|
||||||
|
self.write_session_origin()
|
||||||
|
self.write_session_hooks()
|
||||||
|
self.write_session_options()
|
||||||
|
self.write_session_metadata()
|
||||||
|
self.write_default_services()
|
||||||
|
|
||||||
|
# write out generated xml
|
||||||
|
xml_tree = etree.ElementTree(self.scenario)
|
||||||
|
xml_tree.write(file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8")
|
||||||
|
|
||||||
|
def write_session_origin(self):
|
||||||
|
# origin: geolocation of cartesian coordinate 0,0,0
|
||||||
|
lat, lon, alt = self.session.location.refgeo
|
||||||
|
origin = etree.Element("session_origin")
|
||||||
|
add_attribute(origin, "lat", lat)
|
||||||
|
add_attribute(origin, "lon", lon)
|
||||||
|
add_attribute(origin, "alt", alt)
|
||||||
|
has_origin = len(origin.items()) > 0
|
||||||
|
|
||||||
|
if has_origin:
|
||||||
|
self.scenario.append(origin)
|
||||||
|
refscale = self.session.location.refscale
|
||||||
|
if refscale != 1.0:
|
||||||
|
add_attribute(origin, "scale", refscale)
|
||||||
|
if self.session.location.refxyz != (0.0, 0.0, 0.0):
|
||||||
|
x, y, z = self.session.location.refxyz
|
||||||
|
add_attribute(origin, "x", x)
|
||||||
|
add_attribute(origin, "y", y)
|
||||||
|
add_attribute(origin, "z", z)
|
||||||
|
|
||||||
|
def write_session_hooks(self):
|
||||||
|
# hook scripts
|
||||||
|
hooks = etree.Element("session_hooks")
|
||||||
|
for state in sorted(self.session._hooks.keys()):
|
||||||
|
for file_name, data in self.session._hooks[state]:
|
||||||
|
hook = etree.SubElement(hooks, "hook")
|
||||||
|
add_attribute(hook, "name", file_name)
|
||||||
|
add_attribute(hook, "state", state)
|
||||||
|
hook.text = data
|
||||||
|
|
||||||
|
if hooks.getchildren():
|
||||||
|
self.scenario.append(hooks)
|
||||||
|
|
||||||
|
def write_session_options(self):
|
||||||
|
option_elements = etree.Element("session_options")
|
||||||
|
options_config = self.session.options.get_configs()
|
||||||
|
if not options_config:
|
||||||
|
return
|
||||||
|
|
||||||
|
for _id, default_value in self.session.options.default_values().iteritems():
|
||||||
|
# TODO: should we just save the current config regardless, since it may change?
|
||||||
|
value = options_config[_id]
|
||||||
|
if value != default_value:
|
||||||
|
add_configuration(option_elements, _id, value)
|
||||||
|
|
||||||
|
if option_elements.getchildren():
|
||||||
|
self.scenario.append(option_elements)
|
||||||
|
|
||||||
|
def write_session_metadata(self):
|
||||||
|
# metadata
|
||||||
|
metadata_elements = etree.Element("session_metadata")
|
||||||
|
config = self.session.metadata.get_configs()
|
||||||
|
if not config:
|
||||||
|
return
|
||||||
|
|
||||||
|
for _id, value in config.iteritems():
|
||||||
|
add_configuration(metadata_elements, _id, value)
|
||||||
|
|
||||||
|
if metadata_elements.getchildren():
|
||||||
|
self.scenario.append(metadata_elements)
|
||||||
|
|
||||||
|
def write_emane_configs(self):
|
||||||
|
emane_configurations = etree.Element("emane_configurations")
|
||||||
|
for node_id in self.session.emane.nodes():
|
||||||
|
all_configs = self.session.emane.get_all_configs(node_id)
|
||||||
|
if not all_configs:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for model_name, config in all_configs.iteritems():
|
||||||
|
logger.info("writing emane config node(%s) model(%s)", node_id, model_name)
|
||||||
|
if model_name == -1:
|
||||||
|
emane_configuration = create_emane_config(node_id, self.session.emane.emane_config, config)
|
||||||
|
else:
|
||||||
|
model = self.session.emane.models[model_name]
|
||||||
|
emane_configuration = create_emane_model_config(node_id, model, config)
|
||||||
|
emane_configurations.append(emane_configuration)
|
||||||
|
|
||||||
|
if emane_configurations.getchildren():
|
||||||
|
self.scenario.append(emane_configurations)
|
||||||
|
|
||||||
|
def write_mobility_configs(self):
|
||||||
|
mobility_configurations = etree.Element("mobility_configurations")
|
||||||
|
for node_id in self.session.mobility.nodes():
|
||||||
|
all_configs = self.session.mobility.get_all_configs(node_id)
|
||||||
|
if not all_configs:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for model_name, config in all_configs.iteritems():
|
||||||
|
logger.info("writing mobility config node(%s) model(%s)", node_id, model_name)
|
||||||
|
mobility_configuration = etree.SubElement(mobility_configurations, "mobility_configuration")
|
||||||
|
add_attribute(mobility_configuration, "node", node_id)
|
||||||
|
add_attribute(mobility_configuration, "model", model_name)
|
||||||
|
for name, value in config.iteritems():
|
||||||
|
add_configuration(mobility_configuration, name, value)
|
||||||
|
|
||||||
|
if mobility_configurations.getchildren():
|
||||||
|
self.scenario.append(mobility_configurations)
|
||||||
|
|
||||||
|
def write_service_configs(self):
|
||||||
|
service_configurations = etree.Element("service_configurations")
|
||||||
|
service_configs = self.session.services.all_configs()
|
||||||
|
for node_id, service in service_configs:
|
||||||
|
service_element = ServiceElement(service)
|
||||||
|
add_attribute(service_element.element, "node", node_id)
|
||||||
|
service_configurations.append(service_element.element)
|
||||||
|
|
||||||
|
if service_configurations.getchildren():
|
||||||
|
self.scenario.append(service_configurations)
|
||||||
|
|
||||||
|
def write_default_services(self):
|
||||||
|
node_types = etree.Element("default_services")
|
||||||
|
for node_type, services in self.session.services.default_services.iteritems():
|
||||||
|
node_type = etree.SubElement(node_types, "node", type=node_type)
|
||||||
|
for service in services:
|
||||||
|
etree.SubElement(node_type, "service", name=service)
|
||||||
|
|
||||||
|
if node_types.getchildren():
|
||||||
|
self.scenario.append(node_types)
|
||||||
|
|
||||||
|
def write_nodes(self):
|
||||||
|
self.networks = etree.SubElement(self.scenario, "networks")
|
||||||
|
self.devices = etree.SubElement(self.scenario, "devices")
|
||||||
|
|
||||||
|
links = []
|
||||||
|
for node in self.session.objects.itervalues():
|
||||||
|
logger.info("writer adding node(%s)", node.name)
|
||||||
|
|
||||||
|
# network node
|
||||||
|
if isinstance(node, coreobj.PyCoreNet) and not nodeutils.is_node(node, NodeTypes.CONTROL_NET):
|
||||||
|
self.write_network(node)
|
||||||
|
# device node
|
||||||
|
elif isinstance(node, nodes.PyCoreNode):
|
||||||
|
self.write_device(node)
|
||||||
|
|
||||||
|
# add known links
|
||||||
|
links.extend(node.all_link_data(0))
|
||||||
|
|
||||||
|
return links
|
||||||
|
|
||||||
|
def write_network(self, node):
|
||||||
|
# ignore p2p and other nodes that are not part of the api
|
||||||
|
if not node.apitype:
|
||||||
|
return
|
||||||
|
|
||||||
|
# ignore nodes tied to a different network
|
||||||
|
if nodeutils.is_node(node, (NodeTypes.SWITCH, NodeTypes.HUB)):
|
||||||
|
for netif in node.netifs(sort=True):
|
||||||
|
othernet = getattr(netif, "othernet", None)
|
||||||
|
if othernet and othernet.objid != node.objid:
|
||||||
|
logger.info("writer ignoring node(%s) othernet(%s)", node.name, othernet.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
network = NetworkElement(self.session, node)
|
||||||
|
self.networks.append(network.element)
|
||||||
|
|
||||||
|
def write_links(self, links):
|
||||||
|
link_elements = etree.Element("links")
|
||||||
|
# add link data
|
||||||
|
for link_data in links:
|
||||||
|
# skip basic range links
|
||||||
|
if link_data.interface1_id is None and link_data.interface2_id is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
link_element = create_link_element(link_data)
|
||||||
|
link_elements.append(link_element)
|
||||||
|
|
||||||
|
if link_elements.getchildren():
|
||||||
|
self.scenario.append(link_elements)
|
||||||
|
|
||||||
|
def write_device(self, node):
|
||||||
|
device = DeviceElement(self.session, node)
|
||||||
|
self.devices.append(device.element)
|
||||||
|
|
||||||
|
|
||||||
|
class CoreXmlReader(object):
|
||||||
|
def __init__(self, session):
|
||||||
|
self.session = session
|
||||||
|
self.scenario = None
|
||||||
|
|
||||||
|
def read(self, file_name):
|
||||||
|
xml_tree = etree.parse(file_name)
|
||||||
|
self.scenario = xml_tree.getroot()
|
||||||
|
|
||||||
|
# read xml session content
|
||||||
|
self.read_default_services()
|
||||||
|
self.read_session_metadata()
|
||||||
|
self.read_session_options()
|
||||||
|
self.read_session_hooks()
|
||||||
|
self.read_session_origin()
|
||||||
|
self.read_service_configs()
|
||||||
|
self.read_mobility_configs()
|
||||||
|
self.read_emane_configs()
|
||||||
|
self.read_nodes()
|
||||||
|
self.read_links()
|
||||||
|
|
||||||
|
def read_default_services(self):
|
||||||
|
default_services = self.scenario.find("default_services")
|
||||||
|
if default_services is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for node in default_services.iterchildren():
|
||||||
|
node_type = node.get("type")
|
||||||
|
services = []
|
||||||
|
for service in node.iterchildren():
|
||||||
|
services.append(service.get("name"))
|
||||||
|
logger.info("reading default services for nodes(%s): %s", node_type, services)
|
||||||
|
self.session.services.default_services[node_type] = services
|
||||||
|
|
||||||
|
def read_session_metadata(self):
|
||||||
|
session_metadata = self.scenario.find("session_metadata")
|
||||||
|
if session_metadata is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
configs = {}
|
||||||
|
for data in session_metadata.iterchildren():
|
||||||
|
name = data.get("name")
|
||||||
|
value = data.get("value")
|
||||||
|
configs[name] = value
|
||||||
|
logger.info("reading session metadata: %s", configs)
|
||||||
|
self.session.metadata.set_configs(configs)
|
||||||
|
|
||||||
|
def read_session_options(self):
|
||||||
|
session_options = self.scenario.find("session_options")
|
||||||
|
if session_options is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
configs = {}
|
||||||
|
for config in session_options.iterchildren():
|
||||||
|
name = config.get("name")
|
||||||
|
value = config.get("value")
|
||||||
|
configs[name] = value
|
||||||
|
logger.info("reading session options: %s", configs)
|
||||||
|
self.session.options.set_configs(configs)
|
||||||
|
|
||||||
|
def read_session_hooks(self):
|
||||||
|
session_hooks = self.scenario.find("session_hooks")
|
||||||
|
if session_hooks is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for hook in session_hooks.iterchildren():
|
||||||
|
name = hook.get("name")
|
||||||
|
state = hook.get("state")
|
||||||
|
data = hook.text
|
||||||
|
self.session.add_state_hook()
|
||||||
|
hook_type = "hook:%s" % state
|
||||||
|
logger.info("reading hook: state(%s) name(%s)", state, name)
|
||||||
|
self.session.set_hook(hook_type, file_name=name, source_name=None, data=data)
|
||||||
|
|
||||||
|
def read_session_origin(self):
|
||||||
|
session_origin = self.scenario.find("session_origin")
|
||||||
|
if session_origin is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
lat = get_float(session_origin, "lat")
|
||||||
|
lon = get_float(session_origin, "lon")
|
||||||
|
alt = get_float(session_origin, "alt")
|
||||||
|
if all([lat, lon, alt]):
|
||||||
|
logger.info("reading session reference geo: %s, %s, %s", lat, lon, alt)
|
||||||
|
self.session.location.setrefgeo(lat, lon, alt)
|
||||||
|
|
||||||
|
scale = get_float(session_origin, "scale")
|
||||||
|
if scale:
|
||||||
|
logger.info("reading session reference scale: %s", scale)
|
||||||
|
self.session.location.refscale = scale
|
||||||
|
|
||||||
|
x = get_float(session_origin, "x")
|
||||||
|
y = get_float(session_origin, "y")
|
||||||
|
z = get_float(session_origin, "z")
|
||||||
|
if all([x, y]):
|
||||||
|
logger.info("reading session reference xyz: %s, %s, %s", x, y, z)
|
||||||
|
self.session.location.refxyz = (x, y, z)
|
||||||
|
|
||||||
|
def read_service_configs(self):
|
||||||
|
service_configurations = self.scenario.find("service_configurations")
|
||||||
|
if service_configurations is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for service_configuration in service_configurations.iterchildren():
|
||||||
|
node_id = get_int(service_configuration, "node")
|
||||||
|
service_name = service_configuration.get("name")
|
||||||
|
logger.info("reading custom service(%s) for node(%s)", service_name, node_id)
|
||||||
|
self.session.services.set_service(node_id, service_name)
|
||||||
|
service = self.session.services.get_service(node_id, service_name)
|
||||||
|
|
||||||
|
directory_elements = service_configuration.find("directories")
|
||||||
|
if directory_elements is not None:
|
||||||
|
service.directories = tuple(x.text for x in directory_elements.iterchildren())
|
||||||
|
|
||||||
|
startup_elements = service_configuration.find("startups")
|
||||||
|
if startup_elements is not None:
|
||||||
|
service.startup = tuple(x.text for x in startup_elements.iterchildren())
|
||||||
|
|
||||||
|
validate_elements = service_configuration.find("validates")
|
||||||
|
if validate_elements is not None:
|
||||||
|
service.validate = tuple(x.text for x in validate_elements.iterchildren())
|
||||||
|
|
||||||
|
shutdown_elements = service_configuration.find("shutdowns")
|
||||||
|
if shutdown_elements is not None:
|
||||||
|
service.shutdown = tuple(x.text for x in shutdown_elements.iterchildren())
|
||||||
|
|
||||||
|
file_elements = service_configuration.find("files")
|
||||||
|
if file_elements is not None:
|
||||||
|
for file_element in file_elements.iterchildren():
|
||||||
|
name = file_element.get("name")
|
||||||
|
data = file_element.text
|
||||||
|
service.config_data[name] = data
|
||||||
|
|
||||||
|
def read_emane_configs(self):
|
||||||
|
emane_configurations = self.scenario.find("emane_configurations")
|
||||||
|
if emane_configurations is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for emane_configuration in emane_configurations.iterchildren():
|
||||||
|
node_id = get_int(emane_configuration, "node")
|
||||||
|
model_name = emane_configuration.get("model")
|
||||||
|
configs = {}
|
||||||
|
|
||||||
|
mac_configuration = emane_configuration.find("mac")
|
||||||
|
for config in mac_configuration.iterchildren():
|
||||||
|
name = config.get("name")
|
||||||
|
value = config.get("value")
|
||||||
|
configs[name] = value
|
||||||
|
|
||||||
|
phy_configuration = emane_configuration.find("phy")
|
||||||
|
for config in phy_configuration.iterchildren():
|
||||||
|
name = config.get("name")
|
||||||
|
value = config.get("value")
|
||||||
|
configs[name] = value
|
||||||
|
|
||||||
|
external_configuration = emane_configuration.find("external")
|
||||||
|
for config in external_configuration.iterchildren():
|
||||||
|
name = config.get("name")
|
||||||
|
value = config.get("value")
|
||||||
|
configs[name] = value
|
||||||
|
|
||||||
|
logger.info("reading emane configuration node(%s) model(%s)", node_id, model_name)
|
||||||
|
self.session.emane.set_model_config(node_id, model_name, configs)
|
||||||
|
|
||||||
|
def read_mobility_configs(self):
|
||||||
|
mobility_configurations = self.scenario.find("mobility_configurations")
|
||||||
|
if mobility_configurations is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for mobility_configuration in mobility_configurations.iterchildren():
|
||||||
|
node_id = get_int(mobility_configuration, "node")
|
||||||
|
model_name = mobility_configuration.get("model")
|
||||||
|
configs = {}
|
||||||
|
|
||||||
|
for config in mobility_configuration.iterchildren():
|
||||||
|
name = config.get("name")
|
||||||
|
value = config.get("value")
|
||||||
|
configs[name] = value
|
||||||
|
|
||||||
|
logger.info("reading mobility configuration node(%s) model(%s)", node_id, model_name)
|
||||||
|
self.session.mobility.set_model_config(node_id, model_name, configs)
|
||||||
|
|
||||||
|
def read_nodes(self):
|
||||||
|
device_elements = self.scenario.find("devices")
|
||||||
|
if device_elements is not None:
|
||||||
|
for device_element in device_elements.iterchildren():
|
||||||
|
self.read_device(device_element)
|
||||||
|
|
||||||
|
network_elements = self.scenario.find("networks")
|
||||||
|
if network_elements is not None:
|
||||||
|
for network_element in network_elements.iterchildren():
|
||||||
|
self.read_network(network_element)
|
||||||
|
|
||||||
|
def read_device(self, device_element):
|
||||||
|
node_id = get_int(device_element, "id")
|
||||||
|
name = device_element.get("name")
|
||||||
|
model = device_element.get("type")
|
||||||
|
node_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()]
|
||||||
|
|
||||||
|
position_element = device_element.find("position")
|
||||||
|
if position_element is not None:
|
||||||
|
x = get_float(position_element, "x")
|
||||||
|
y = get_float(position_element, "y")
|
||||||
|
if all([x, y]):
|
||||||
|
node_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)
|
||||||
|
|
||||||
|
logger.info("reading node id(%s) model(%s) name(%s)", node_id, model, name)
|
||||||
|
self.session.add_node(_id=node_id, node_options=node_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)
|
||||||
|
|
||||||
|
position_element = network_element.find("position")
|
||||||
|
if position_element is not None:
|
||||||
|
x = get_float(position_element, "x")
|
||||||
|
y = get_float(position_element, "y")
|
||||||
|
if all([x, y]):
|
||||||
|
node_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)
|
||||||
|
|
||||||
|
logger.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)
|
||||||
|
|
||||||
|
def read_links(self):
|
||||||
|
link_elements = self.scenario.find("links")
|
||||||
|
if link_elements is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for link_element in link_elements.iterchildren():
|
||||||
|
node_one = get_int(link_element, "node_one")
|
||||||
|
node_two = get_int(link_element, "node_two")
|
||||||
|
|
||||||
|
interface_one_element = link_element.find("interface_one")
|
||||||
|
interface_one = None
|
||||||
|
if interface_one_element is not None:
|
||||||
|
interface_one = create_interface_data(interface_one_element)
|
||||||
|
|
||||||
|
interface_two_element = link_element.find("interface_two")
|
||||||
|
interface_two = None
|
||||||
|
if interface_two_element is not None:
|
||||||
|
interface_two = create_interface_data(interface_two_element)
|
||||||
|
|
||||||
|
options_element = link_element.find("options")
|
||||||
|
link_options = LinkOptions()
|
||||||
|
if options_element is not None:
|
||||||
|
link_options.bandwidth = get_float(options_element, "bandwidth")
|
||||||
|
link_options.burst = get_float(options_element, "burst")
|
||||||
|
link_options.delay = get_float(options_element, "delay")
|
||||||
|
link_options.dup = get_float(options_element, "dup")
|
||||||
|
link_options.mer = get_float(options_element, "mer")
|
||||||
|
link_options.mburst = get_float(options_element, "mburst")
|
||||||
|
link_options.jitter = get_float(options_element, "jitter")
|
||||||
|
link_options.key = get_float(options_element, "key")
|
||||||
|
link_options.per = get_float(options_element, "per")
|
||||||
|
link_options.unidirectional = get_int(options_element, "unidirectional")
|
||||||
|
link_options.session = options_element.get("session")
|
||||||
|
link_options.emulation_id = get_int(options_element, "emulation_id")
|
||||||
|
link_options.network_id = get_int(options_element, "network_id")
|
||||||
|
link_options.opaque = options_element.get("opaque")
|
||||||
|
link_options.gui_attributes = options_element.get("gui_attributes")
|
||||||
|
|
||||||
|
logger.info("reading link node_one(%s) node_two(%s)", node_one, node_two)
|
||||||
|
self.session.add_link(node_one, node_two, interface_one, interface_two, link_options)
|
441
daemon/core/xml/emanexml.py
Normal file
441
daemon/core/xml/emanexml.py
Normal file
|
@ -0,0 +1,441 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
from core import logger
|
||||||
|
from core.misc import utils
|
||||||
|
from core.misc.ipaddress import MacAddress
|
||||||
|
from core.xml import corexml
|
||||||
|
|
||||||
|
_hwaddr_prefix = "02:02"
|
||||||
|
|
||||||
|
|
||||||
|
def is_external(config):
|
||||||
|
"""
|
||||||
|
Checks if the configuration is for an external transport.
|
||||||
|
|
||||||
|
:param dict config: configuration to check
|
||||||
|
:return: True if external, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return config.get("external") == "1"
|
||||||
|
|
||||||
|
|
||||||
|
def _value_to_params(value):
|
||||||
|
"""
|
||||||
|
Helper to convert a parameter to a parameter tuple.
|
||||||
|
|
||||||
|
:param str value: value string to convert to tuple
|
||||||
|
:return: parameter tuple, None otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
values = utils.make_tuple_fromstr(value, str)
|
||||||
|
|
||||||
|
if not hasattr(values, "__iter__"):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(values) < 2:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
except SyntaxError:
|
||||||
|
logger.exception("error in value string to param list")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def create_file(xml_element, doc_name, file_path):
|
||||||
|
"""
|
||||||
|
Create xml file.
|
||||||
|
|
||||||
|
:param lxml.etree.Element xml_element: root element to write to file
|
||||||
|
:param str doc_name: name to use in the emane doctype
|
||||||
|
:param str file_path: file path to write xml file to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
doctype = '<!DOCTYPE %(doc_name)s SYSTEM "file:///usr/share/emane/dtd/%(doc_name)s.dtd">' % {"doc_name": doc_name}
|
||||||
|
corexml.write_xml_file(xml_element, file_path, doctype=doctype)
|
||||||
|
|
||||||
|
|
||||||
|
def add_param(xml_element, name, value):
|
||||||
|
"""
|
||||||
|
Add emane configuration parameter to xml element.
|
||||||
|
|
||||||
|
:param lxml.etree.Element xml_element: element to append parameter to
|
||||||
|
:param str name: name of parameter
|
||||||
|
:param str value: value for parameter
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
etree.SubElement(xml_element, "param", name=name, value=value)
|
||||||
|
|
||||||
|
|
||||||
|
def add_configurations(xml_element, configurations, config, config_ignore):
|
||||||
|
"""
|
||||||
|
Add emane model configurations to xml element.
|
||||||
|
|
||||||
|
:param lxml.etree.Element xml_element: xml element to add emane configurations to
|
||||||
|
:param list[core.config.Configuration] configurations: configurations to add to xml
|
||||||
|
:param dict config: configuration values
|
||||||
|
:param set config_ignore: configuration options to ignore
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
for configuration in configurations:
|
||||||
|
# ignore custom configurations
|
||||||
|
name = configuration.id
|
||||||
|
if name in config_ignore:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check if value is a multi param
|
||||||
|
value = str(config[name])
|
||||||
|
params = _value_to_params(value)
|
||||||
|
if params:
|
||||||
|
params_element = etree.SubElement(xml_element, "paramlist", name=name)
|
||||||
|
for param in params:
|
||||||
|
etree.SubElement(params_element, "item", value=param)
|
||||||
|
else:
|
||||||
|
add_param(xml_element, name, value)
|
||||||
|
|
||||||
|
|
||||||
|
def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_xmls):
|
||||||
|
"""
|
||||||
|
Create platform xml for a specific node.
|
||||||
|
|
||||||
|
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations
|
||||||
|
:param core.netns.nodes.CtrlNet control_net: control net node for this emane network
|
||||||
|
:param core.emane.nodes.EmaneNode node: node to write platform xml for
|
||||||
|
:param int nem_id: nem id to use for interfaces for this node
|
||||||
|
:param dict platform_xmls: stores platform xml elements to append nem entries to
|
||||||
|
:return: the next nem id that can be used for creating platform xml files
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
logger.debug("building emane platform xml for node(%s): %s", node, node.name)
|
||||||
|
nem_entries = {}
|
||||||
|
|
||||||
|
if node.model is None:
|
||||||
|
logger.warn("warning: EmaneNode %s has no associated model", node.name)
|
||||||
|
return nem_entries
|
||||||
|
|
||||||
|
for netif in node.netifs():
|
||||||
|
# build nem xml
|
||||||
|
nem_definition = nem_file_name(node.model, netif)
|
||||||
|
nem_element = etree.Element("nem", id=str(nem_id), name=netif.localname, definition=nem_definition)
|
||||||
|
|
||||||
|
# check if this is an external transport, get default config if an interface specific one does not exist
|
||||||
|
config = emane_manager.getifcconfig(node.model.object_id, netif, node.model.name)
|
||||||
|
|
||||||
|
if is_external(config):
|
||||||
|
nem_element.set("transport", "external")
|
||||||
|
platform_endpoint = "platformendpoint"
|
||||||
|
add_param(nem_element, platform_endpoint, config[platform_endpoint])
|
||||||
|
transport_endpoint = "transportendpoint"
|
||||||
|
add_param(nem_element, transport_endpoint, config[transport_endpoint])
|
||||||
|
else:
|
||||||
|
# build transport xml
|
||||||
|
transport_type = netif.transport_type
|
||||||
|
if not transport_type:
|
||||||
|
logger.info("warning: %s interface type unsupported!", netif.name)
|
||||||
|
transport_type = "raw"
|
||||||
|
transport_file = transport_file_name(node.objid, transport_type)
|
||||||
|
transport_element = etree.SubElement(nem_element, "transport", definition=transport_file)
|
||||||
|
|
||||||
|
# add transport parameter
|
||||||
|
add_param(transport_element, "device", netif.name)
|
||||||
|
|
||||||
|
# add nem entry
|
||||||
|
nem_entries[netif] = nem_element
|
||||||
|
|
||||||
|
# merging code
|
||||||
|
key = netif.node.objid
|
||||||
|
if netif.transport_type == "raw":
|
||||||
|
key = "host"
|
||||||
|
otadev = control_net.brname
|
||||||
|
eventdev = control_net.brname
|
||||||
|
else:
|
||||||
|
otadev = None
|
||||||
|
eventdev = None
|
||||||
|
|
||||||
|
platform_element = platform_xmls.get(key)
|
||||||
|
if not platform_element:
|
||||||
|
platform_element = etree.Element("platform")
|
||||||
|
|
||||||
|
if otadev:
|
||||||
|
emane_manager.set_config("otamanagerdevice", otadev)
|
||||||
|
|
||||||
|
if eventdev:
|
||||||
|
emane_manager.set_config("eventservicedevice", eventdev)
|
||||||
|
|
||||||
|
# append all platform options (except starting id) to doc
|
||||||
|
for configuration in emane_manager.emane_config.emulator_config:
|
||||||
|
name = configuration.id
|
||||||
|
if name == "platform_id_start":
|
||||||
|
continue
|
||||||
|
|
||||||
|
value = emane_manager.get_config(name)
|
||||||
|
add_param(platform_element, name, value)
|
||||||
|
|
||||||
|
# add platform xml
|
||||||
|
platform_xmls[key] = platform_element
|
||||||
|
|
||||||
|
platform_element.append(nem_element)
|
||||||
|
|
||||||
|
node.setnemid(netif, nem_id)
|
||||||
|
macstr = _hwaddr_prefix + ":00:00:"
|
||||||
|
macstr += "%02X:%02X" % ((nem_id >> 8) & 0xFF, nem_id & 0xFF)
|
||||||
|
netif.sethwaddr(MacAddress.from_string(macstr))
|
||||||
|
|
||||||
|
# increment nem id
|
||||||
|
nem_id += 1
|
||||||
|
|
||||||
|
for key in sorted(platform_xmls.keys()):
|
||||||
|
if key == "host":
|
||||||
|
file_name = "platform.xml"
|
||||||
|
else:
|
||||||
|
file_name = "platform%d.xml" % key
|
||||||
|
|
||||||
|
platform_element = platform_xmls[key]
|
||||||
|
|
||||||
|
doc_name = "platform"
|
||||||
|
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||||
|
create_file(platform_element, doc_name, file_path)
|
||||||
|
|
||||||
|
return nem_id
|
||||||
|
|
||||||
|
|
||||||
|
def build_xml_files(emane_manager, node):
|
||||||
|
"""
|
||||||
|
Generate emane xml files required for node.
|
||||||
|
|
||||||
|
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations
|
||||||
|
:param core.emane.nodes.EmaneNode node: node to write platform xml for
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logger.debug("building all emane xml for node(%s): %s", node, node.name)
|
||||||
|
if node.model is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# get model configurations
|
||||||
|
config = emane_manager.get_configs(node.model.object_id, node.model.name)
|
||||||
|
if not config:
|
||||||
|
return
|
||||||
|
|
||||||
|
# build XML for overall network (EmaneNode) configs
|
||||||
|
node.model.build_xml_files(config)
|
||||||
|
|
||||||
|
# build XML for specific interface (NEM) configs
|
||||||
|
need_virtual = False
|
||||||
|
need_raw = False
|
||||||
|
vtype = "virtual"
|
||||||
|
rtype = "raw"
|
||||||
|
|
||||||
|
for netif in node.netifs():
|
||||||
|
# check for interface specific emane configuration and write xml files, if needed
|
||||||
|
config = emane_manager.getifcconfig(node.model.object_id, netif, node.model.name)
|
||||||
|
if config:
|
||||||
|
node.model.build_xml_files(config, netif)
|
||||||
|
|
||||||
|
# check transport type needed for interface
|
||||||
|
if "virtual" in netif.transport_type:
|
||||||
|
need_virtual = True
|
||||||
|
vtype = netif.transport_type
|
||||||
|
else:
|
||||||
|
need_raw = True
|
||||||
|
rtype = netif.transport_type
|
||||||
|
|
||||||
|
if need_virtual:
|
||||||
|
build_transport_xml(emane_manager, node, vtype)
|
||||||
|
|
||||||
|
if need_raw:
|
||||||
|
build_transport_xml(emane_manager, node, rtype)
|
||||||
|
|
||||||
|
|
||||||
|
def build_transport_xml(emane_manager, node, transport_type):
|
||||||
|
"""
|
||||||
|
Build transport xml file for node and transport type.
|
||||||
|
|
||||||
|
:param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations
|
||||||
|
:param core.emane.nodes.EmaneNode node: node to write platform xml for
|
||||||
|
:param str transport_type: transport type to build xml for
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
transport_element = etree.Element(
|
||||||
|
"transport",
|
||||||
|
name="%s Transport" % transport_type.capitalize(),
|
||||||
|
library="trans%s" % transport_type.lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
# add bitrate
|
||||||
|
add_param(transport_element, "bitrate", "0")
|
||||||
|
|
||||||
|
# get emane model cnfiguration
|
||||||
|
config = emane_manager.get_configs(node.objid, node.model.name)
|
||||||
|
flowcontrol = config.get("flowcontrolenable", "0") == "1"
|
||||||
|
|
||||||
|
if "virtual" in transport_type.lower():
|
||||||
|
device_path = "/dev/net/tun_flowctl"
|
||||||
|
if not os.path.exists(device_path):
|
||||||
|
device_path = "/dev/net/tun"
|
||||||
|
add_param(transport_element, "devicepath", device_path)
|
||||||
|
|
||||||
|
if flowcontrol:
|
||||||
|
add_param(transport_element, "flowcontrolenable", "on")
|
||||||
|
|
||||||
|
doc_name = "transport"
|
||||||
|
file_name = transport_file_name(node.objid, transport_type)
|
||||||
|
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||||
|
create_file(transport_element, doc_name, file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def create_phy_xml(emane_model, config, file_path):
|
||||||
|
"""
|
||||||
|
Create the phy xml document.
|
||||||
|
|
||||||
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
||||||
|
:param dict config: all current configuration values
|
||||||
|
:param str file_path: path to write file to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
phy_element = etree.Element("phy", name="%s PHY" % emane_model.name)
|
||||||
|
if emane_model.phy_library:
|
||||||
|
phy_element.set("library", emane_model.phy_library)
|
||||||
|
|
||||||
|
add_configurations(phy_element, emane_model.phy_config, config, emane_model.config_ignore)
|
||||||
|
create_file(phy_element, "phy", file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def create_mac_xml(emane_model, config, file_path):
|
||||||
|
"""
|
||||||
|
Create the mac xml document.
|
||||||
|
|
||||||
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
||||||
|
:param dict config: all current configuration values
|
||||||
|
:param str file_path: path to write file to
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
if not emane_model.mac_library:
|
||||||
|
raise ValueError("must define emane model library")
|
||||||
|
|
||||||
|
mac_element = etree.Element("mac", name="%s MAC" % emane_model.name, library=emane_model.mac_library)
|
||||||
|
add_configurations(mac_element, emane_model.mac_config, config, emane_model.config_ignore)
|
||||||
|
create_file(mac_element, "mac", file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def create_nem_xml(emane_model, config, nem_file, transport_definition, mac_definition, phy_definition):
|
||||||
|
"""
|
||||||
|
Create the nem xml document.
|
||||||
|
|
||||||
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
||||||
|
:param dict config: all current configuration values
|
||||||
|
:param str nem_file: nem file path to write
|
||||||
|
:param str transport_definition: transport file definition path
|
||||||
|
:param str mac_definition: mac file definition path
|
||||||
|
:param str phy_definition: phy file definition path
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
nem_element = etree.Element("nem", name="%s NEM" % emane_model.name)
|
||||||
|
if is_external(config):
|
||||||
|
nem_element.set("type", "unstructured")
|
||||||
|
else:
|
||||||
|
etree.SubElement(nem_element, "transport", definition=transport_definition)
|
||||||
|
etree.SubElement(nem_element, "mac", definition=mac_definition)
|
||||||
|
etree.SubElement(nem_element, "phy", definition=phy_definition)
|
||||||
|
create_file(nem_element, "nem", nem_file)
|
||||||
|
|
||||||
|
|
||||||
|
def create_event_service_xml(group, port, device, file_directory):
|
||||||
|
"""
|
||||||
|
Create a emane event service xml file.
|
||||||
|
|
||||||
|
:param str group: event group
|
||||||
|
:param str port: event port
|
||||||
|
:param str device: event device
|
||||||
|
:param str file_directory: directory to create file in
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
event_element = etree.Element("emaneeventmsgsvc")
|
||||||
|
for name, value in (("group", group), ("port", port), ("device", device), ("mcloop", "1"), ("ttl", "32")):
|
||||||
|
sub_element = etree.SubElement(event_element, name)
|
||||||
|
sub_element.text = value
|
||||||
|
file_name = "libemaneeventservice.xml"
|
||||||
|
file_path = os.path.join(file_directory, file_name)
|
||||||
|
create_file(event_element, "emaneeventmsgsvc", file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def transport_file_name(node_id, transport_type):
|
||||||
|
"""
|
||||||
|
Create name for a transport xml file.
|
||||||
|
|
||||||
|
:param int node_id: node id to generate transport file name for
|
||||||
|
:param str transport_type: transport type to generate transport file
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return "n%strans%s.xml" % (node_id, transport_type)
|
||||||
|
|
||||||
|
|
||||||
|
def _basename(emane_model, interface=None):
|
||||||
|
"""
|
||||||
|
Create name that is leveraged for configuration file creation.
|
||||||
|
|
||||||
|
:param interface: interface for this model
|
||||||
|
:return: basename used for file creation
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
name = "n%s" % emane_model.object_id
|
||||||
|
|
||||||
|
if interface:
|
||||||
|
node_id = interface.node.objid
|
||||||
|
if emane_model.session.emane.getifcconfig(node_id, interface, emane_model.name):
|
||||||
|
name = interface.localname.replace(".", "_")
|
||||||
|
|
||||||
|
return "%s%s" % (name, emane_model.name)
|
||||||
|
|
||||||
|
|
||||||
|
def nem_file_name(emane_model, interface=None):
|
||||||
|
"""
|
||||||
|
Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml"
|
||||||
|
|
||||||
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
||||||
|
:param interface: interface for this model
|
||||||
|
:return: nem xml filename
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
basename = _basename(emane_model, interface)
|
||||||
|
append = ""
|
||||||
|
if interface and interface.transport_type == "raw":
|
||||||
|
append = "_raw"
|
||||||
|
return "%snem%s.xml" % (basename, append)
|
||||||
|
|
||||||
|
|
||||||
|
def shim_file_name(emane_model, interface=None):
|
||||||
|
"""
|
||||||
|
Return the string name for the SHIM XML file, e.g. "commeffectshim.xml"
|
||||||
|
|
||||||
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
||||||
|
:param interface: interface for this model
|
||||||
|
:return: shim xml filename
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return "%sshim.xml" % _basename(emane_model, interface)
|
||||||
|
|
||||||
|
|
||||||
|
def mac_file_name(emane_model, interface=None):
|
||||||
|
"""
|
||||||
|
Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml"
|
||||||
|
|
||||||
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
||||||
|
:param interface: interface for this model
|
||||||
|
:return: mac xml filename
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return "%smac.xml" % _basename(emane_model, interface)
|
||||||
|
|
||||||
|
|
||||||
|
def phy_file_name(emane_model, interface=None):
|
||||||
|
"""
|
||||||
|
Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml"
|
||||||
|
|
||||||
|
:param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for
|
||||||
|
:param interface: interface for this model
|
||||||
|
:return: phy xml filename
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return "%sphy.xml" % _basename(emane_model, interface)
|
|
@ -1,9 +1,10 @@
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
|
from core.conf import ConfigShim
|
||||||
from core.enumerations import NodeTypes
|
from core.enumerations import NodeTypes
|
||||||
from core.misc import nodeutils
|
from core.misc import nodeutils
|
||||||
from core.service import ServiceManager
|
from core.service import ServiceManager, ServiceShim
|
||||||
from core.xml import xmlutils
|
from core.xml import xmlutils
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,7 +72,12 @@ class CoreDocumentParser0(object):
|
||||||
"""
|
"""
|
||||||
Helper to return tuple of attributes common to nodes and nets.
|
Helper to return tuple of attributes common to nodes and nets.
|
||||||
"""
|
"""
|
||||||
node_id = int(obj.getAttribute("id"))
|
node_id = obj.getAttribute("id")
|
||||||
|
try:
|
||||||
|
node_id = int(node_id)
|
||||||
|
except:
|
||||||
|
logger.debug("parsing node without integer id: %s", node_id)
|
||||||
|
|
||||||
name = str(obj.getAttribute("name"))
|
name = str(obj.getAttribute("name"))
|
||||||
node_type = str(obj.getAttribute("type"))
|
node_type = str(obj.getAttribute("type"))
|
||||||
return node_id, name, node_type
|
return node_id, name, node_type
|
||||||
|
@ -204,7 +210,8 @@ class CoreDocumentParser0(object):
|
||||||
|
|
||||||
# TODO: assign other config managers here
|
# TODO: assign other config managers here
|
||||||
if mgr:
|
if mgr:
|
||||||
mgr.setconfig_keyvalues(nodenum, name, kvs)
|
for k, v in kvs:
|
||||||
|
mgr.set_config(k, v, node_id=nodenum, config_type=name)
|
||||||
|
|
||||||
def parsenetem(self, model, obj, kvs):
|
def parsenetem(self, model, obj, kvs):
|
||||||
"""
|
"""
|
||||||
|
@ -217,7 +224,6 @@ class CoreDocumentParser0(object):
|
||||||
# nodes and interfaces do not exist yet, at this point of the parsing,
|
# nodes and interfaces do not exist yet, at this point of the parsing,
|
||||||
# save (key, value) pairs for later
|
# save (key, value) pairs for later
|
||||||
try:
|
try:
|
||||||
# kvs = map(lambda(k, v): (int(v)), kvs)
|
|
||||||
kvs = map(self.numericvalue, kvs)
|
kvs = map(self.numericvalue, kvs)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.warn("error parsing link parameters for '%s' on '%s'", ifname, peer)
|
logger.warn("error parsing link parameters for '%s' on '%s'", ifname, peer)
|
||||||
|
@ -273,7 +279,7 @@ class CoreDocumentParser0(object):
|
||||||
services = []
|
services = []
|
||||||
for service in node.getElementsByTagName("Service"):
|
for service in node.getElementsByTagName("Service"):
|
||||||
services.append(str(service.getAttribute("name")))
|
services.append(str(service.getAttribute("name")))
|
||||||
self.session.services.defaultservices[type] = services
|
self.session.services.default_services[type] = services
|
||||||
logger.info("default services for type %s set to %s" % (type, services))
|
logger.info("default services for type %s set to %s" % (type, services))
|
||||||
|
|
||||||
def parseservices(self):
|
def parseservices(self):
|
||||||
|
@ -310,7 +316,10 @@ class CoreDocumentParser0(object):
|
||||||
# associate nodes with services
|
# associate nodes with services
|
||||||
for objid in sorted(svclists.keys()):
|
for objid in sorted(svclists.keys()):
|
||||||
n = self.session.get_object(objid)
|
n = self.session.get_object(objid)
|
||||||
self.session.services.addservicestonode(node=n, nodetype=n.type, services_str=svclists[objid])
|
services = svclists[objid]
|
||||||
|
if services:
|
||||||
|
services = services.split("|")
|
||||||
|
self.session.services.add_services(node=n, node_type=n.type, services=services)
|
||||||
|
|
||||||
def parseservice(self, service, n):
|
def parseservice(self, service, n):
|
||||||
"""
|
"""
|
||||||
|
@ -361,15 +370,20 @@ class CoreDocumentParser0(object):
|
||||||
filename = file.getAttribute("name")
|
filename = file.getAttribute("name")
|
||||||
files.append(filename)
|
files.append(filename)
|
||||||
data = xmlutils.get_text_child(file)
|
data = xmlutils.get_text_child(file)
|
||||||
typestr = "service:%s:%s" % (name, filename)
|
self.session.services.set_service_file(node_id=n.objid, service_name=name, file_name=filename, data=data)
|
||||||
self.session.services.setservicefile(nodenum=n.objid, type=typestr,
|
|
||||||
filename=filename,
|
|
||||||
srcname=None, data=data)
|
|
||||||
if len(files):
|
if len(files):
|
||||||
values.append("files=%s" % files)
|
values.append("files=%s" % files)
|
||||||
if not bool(service.getAttribute("custom")):
|
if not bool(service.getAttribute("custom")):
|
||||||
return True
|
return True
|
||||||
self.session.services.setcustomservice(n.objid, svc, values)
|
self.session.services.set_service(n.objid, svc)
|
||||||
|
# set custom values for custom service
|
||||||
|
svc = self.session.services.get_service(n.objid, None)
|
||||||
|
if not svc:
|
||||||
|
raise ValueError("custom service(%s) for node(%s) does not exist", svc.name, n.objid)
|
||||||
|
values = ConfigShim.str_to_dict("|".join(values))
|
||||||
|
for name, value in values.iteritems():
|
||||||
|
ServiceShim.setvalue(svc, name, value)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def parsehooks(self, hooks):
|
def parsehooks(self, hooks):
|
||||||
|
@ -392,7 +406,7 @@ class CoreDocumentParser0(object):
|
||||||
v = str(param.getAttribute("value"))
|
v = str(param.getAttribute("value"))
|
||||||
if v == '':
|
if v == '':
|
||||||
v = xmlutils.get_text_child(param) # allow attribute/text for newlines
|
v = xmlutils.get_text_child(param) # allow attribute/text for newlines
|
||||||
setattr(self.session.options, k, v)
|
self.session.options.set_config(k, v)
|
||||||
hooks = xmlutils.get_one_element(self.meta, "Hooks")
|
hooks = xmlutils.get_one_element(self.meta, "Hooks")
|
||||||
if hooks:
|
if hooks:
|
||||||
self.parsehooks(hooks)
|
self.parsehooks(hooks)
|
||||||
|
@ -403,4 +417,4 @@ class CoreDocumentParser0(object):
|
||||||
v = str(param.getAttribute("value"))
|
v = str(param.getAttribute("value"))
|
||||||
if v == '':
|
if v == '':
|
||||||
v = xmlutils.get_text_child(param)
|
v = xmlutils.get_text_child(param)
|
||||||
self.session.metadata.add_item(k, v)
|
self.session.metadata.set_config(k, v)
|
||||||
|
|
|
@ -4,10 +4,11 @@ from xml.dom.minidom import parse
|
||||||
|
|
||||||
from core import constants
|
from core import constants
|
||||||
from core import logger
|
from core import logger
|
||||||
|
from core.conf import ConfigShim
|
||||||
from core.enumerations import NodeTypes
|
from core.enumerations import NodeTypes
|
||||||
from core.misc import nodeutils
|
from core.misc import nodeutils
|
||||||
from core.misc.ipaddress import MacAddress
|
from core.misc.ipaddress import MacAddress
|
||||||
from core.service import ServiceManager
|
from core.service import ServiceManager, ServiceShim
|
||||||
from core.xml import xmlutils
|
from core.xml import xmlutils
|
||||||
|
|
||||||
|
|
||||||
|
@ -209,9 +210,11 @@ class CoreDocumentParser1(object):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
logger.info("setting wireless link params: node(%s) model(%s) mobility_model(%s)",
|
logger.info("setting wireless link params: node(%s) model(%s) mobility_model(%s)",
|
||||||
nodenum, model_name, mobility_model_name)
|
nodenum, model_name, mobility_model_name)
|
||||||
mgr.setconfig_keyvalues(nodenum, model_name, link_params.items())
|
mgr.set_model_config(node_id=nodenum, model_name=model_name, config=link_params)
|
||||||
|
|
||||||
if mobility_model_name and mobility_params:
|
if mobility_model_name and mobility_params:
|
||||||
mgr.setconfig_keyvalues(nodenum, mobility_model_name, mobility_params.items())
|
self.session.mobility.set_model_config(node_id=nodenum, model_name=mobility_model_name,
|
||||||
|
config=mobility_params)
|
||||||
|
|
||||||
def link_layer2_devices(self, device1, ifname1, device2, ifname2):
|
def link_layer2_devices(self, device1, ifname1, device2, ifname2):
|
||||||
"""
|
"""
|
||||||
|
@ -615,6 +618,7 @@ class CoreDocumentParser1(object):
|
||||||
values.append('cmddown=%s' % shutdown)
|
values.append('cmddown=%s' % shutdown)
|
||||||
if validate:
|
if validate:
|
||||||
values.append('cmdval=%s' % validate)
|
values.append('cmdval=%s' % validate)
|
||||||
|
|
||||||
filenames = []
|
filenames = []
|
||||||
files = []
|
files = []
|
||||||
for f in xmlutils.iter_children_with_name(service, 'file'):
|
for f in xmlutils.iter_children_with_name(service, 'file'):
|
||||||
|
@ -629,19 +633,25 @@ class CoreDocumentParser1(object):
|
||||||
data = None
|
data = None
|
||||||
typestr = 'service:%s:%s' % (name, filename)
|
typestr = 'service:%s:%s' % (name, filename)
|
||||||
files.append((typestr, filename, data))
|
files.append((typestr, filename, data))
|
||||||
|
|
||||||
if filenames:
|
if filenames:
|
||||||
values.append('files=%s' % filenames)
|
values.append('files=%s' % filenames)
|
||||||
|
|
||||||
custom = service.getAttribute('custom')
|
custom = service.getAttribute('custom')
|
||||||
if custom and custom.lower() == 'true':
|
if custom and custom.lower() == 'true':
|
||||||
self.session.services.setcustomservice(node.objid, session_service, values)
|
self.session.services.set_service(node.objid, session_service.name)
|
||||||
|
values = ConfigShim.str_to_dict("|".join(values))
|
||||||
|
for key, value in values.iteritems():
|
||||||
|
ServiceShim.setvalue(session_service, key, value)
|
||||||
|
|
||||||
# NOTE: if a custom service is used, setservicefile() must be
|
# NOTE: if a custom service is used, setservicefile() must be
|
||||||
# called after the custom service exists
|
# called after the custom service exists
|
||||||
for typestr, filename, data in files:
|
for typestr, filename, data in files:
|
||||||
self.session.services.setservicefile(
|
svcname = typestr.split(":")[1]
|
||||||
nodenum=node.objid,
|
self.session.services.set_service_file(
|
||||||
type=typestr,
|
node_id=node.objid,
|
||||||
filename=filename,
|
service_name=svcname,
|
||||||
srcname=None,
|
file_name=filename,
|
||||||
data=data
|
data=data
|
||||||
)
|
)
|
||||||
return str(name)
|
return str(name)
|
||||||
|
@ -670,10 +680,13 @@ class CoreDocumentParser1(object):
|
||||||
services_str = None # default services will be added
|
services_str = None # default services will be added
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
self.session.services.addservicestonode(
|
if services_str:
|
||||||
|
services_str = services_str.split("|")
|
||||||
|
|
||||||
|
self.session.services.add_services(
|
||||||
node=node,
|
node=node,
|
||||||
nodetype=node_type,
|
node_type=node_type,
|
||||||
services_str=services_str
|
services=services_str
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_object_presentation(self, obj, element, node_type):
|
def set_object_presentation(self, obj, element, node_type):
|
||||||
|
@ -812,7 +825,7 @@ class CoreDocumentParser1(object):
|
||||||
params = self.parse_parameter_children(options)
|
params = self.parse_parameter_children(options)
|
||||||
for name, value in params.iteritems():
|
for name, value in params.iteritems():
|
||||||
if name and value:
|
if name and value:
|
||||||
setattr(self.session.options, str(name), str(value))
|
self.session.options.set_config(str(name), str(value))
|
||||||
|
|
||||||
def parse_session_hooks(self, session_config):
|
def parse_session_hooks(self, session_config):
|
||||||
"""
|
"""
|
||||||
|
@ -837,7 +850,7 @@ class CoreDocumentParser1(object):
|
||||||
params = self.parse_parameter_children(metadata)
|
params = self.parse_parameter_children(metadata)
|
||||||
for name, value in params.iteritems():
|
for name, value in params.iteritems():
|
||||||
if name and value:
|
if name and value:
|
||||||
self.session.metadata.add_item(str(name), str(value))
|
self.session.metadata.set_config(str(name), str(value))
|
||||||
|
|
||||||
def parse_session_config(self):
|
def parse_session_config(self):
|
||||||
session_config = xmlutils.get_first_child_by_tag_name(self.scenario, 'CORE:sessionconfig')
|
session_config = xmlutils.get_first_child_by_tag_name(self.scenario, 'CORE:sessionconfig')
|
||||||
|
@ -872,5 +885,5 @@ class CoreDocumentParser1(object):
|
||||||
self.default_services[device_type] = services
|
self.default_services[device_type] = services
|
||||||
# store default services for the session
|
# store default services for the session
|
||||||
for t, s in self.default_services.iteritems():
|
for t, s in self.default_services.iteritems():
|
||||||
self.session.services.defaultservices[t] = s
|
self.session.services.default_services[t] = s
|
||||||
logger.info('default services for node type \'%s\' set to: %s' % (t, s))
|
logger.info('default services for node type \'%s\' set to: %s' % (t, s))
|
||||||
|
|
|
@ -7,6 +7,7 @@ import os.path
|
||||||
|
|
||||||
from core.enumerations import NodeTypes
|
from core.enumerations import NodeTypes
|
||||||
from core.misc import nodeutils
|
from core.misc import nodeutils
|
||||||
|
from core.xml import corexml
|
||||||
from core.xml.xmlparser import core_document_parser
|
from core.xml.xmlparser import core_document_parser
|
||||||
from core.xml.xmlwriter import core_document_writer
|
from core.xml.xmlwriter import core_document_writer
|
||||||
|
|
||||||
|
@ -32,5 +33,4 @@ def save_session_xml(session, filename, version):
|
||||||
"""
|
"""
|
||||||
Export a session to the EmulationScript XML format.
|
Export a session to the EmulationScript XML format.
|
||||||
"""
|
"""
|
||||||
doc = core_document_writer(session, version)
|
corexml.CoreXmlWriter(session).write(filename)
|
||||||
doc.writexml(filename)
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import pwd
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.coreobj import PyCoreNet
|
from core.coreobj import PyCoreNet
|
||||||
from core.coreobj import PyCoreNode
|
from core.coreobj import PyCoreNode
|
||||||
from core.enumerations import RegisterTlvs
|
from core.enumerations import RegisterTlvs, EventTypes
|
||||||
from core.xml import xmlutils
|
from core.xml import xmlutils
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ class CoreDocumentWriter0(Document):
|
||||||
self.populatefromsession()
|
self.populatefromsession()
|
||||||
|
|
||||||
def populatefromsession(self):
|
def populatefromsession(self):
|
||||||
self.session.emane.setup() # not during runtime?
|
if self.session.state != EventTypes.RUNTIME_STATE.value:
|
||||||
|
self.session.emane.setup() # not during runtime?
|
||||||
self.addorigin()
|
self.addorigin()
|
||||||
self.adddefaultservices()
|
self.adddefaultservices()
|
||||||
self.addnets()
|
self.addnets()
|
||||||
|
@ -83,8 +84,8 @@ class CoreDocumentWriter0(Document):
|
||||||
for netif in net.netifs(sort=True):
|
for netif in net.netifs(sort=True):
|
||||||
self.addnetem(n, netif)
|
self.addnetem(n, netif)
|
||||||
# wireless/mobility models
|
# wireless/mobility models
|
||||||
modelconfigs = net.session.mobility.getmodels(net)
|
modelconfigs = net.session.mobility.get_models(net)
|
||||||
modelconfigs += net.session.emane.getmodels(net)
|
modelconfigs += net.session.emane.get_models(net)
|
||||||
self.addmodels(n, modelconfigs)
|
self.addmodels(n, modelconfigs)
|
||||||
self.addposition(net)
|
self.addposition(net)
|
||||||
|
|
||||||
|
@ -136,14 +137,14 @@ class CoreDocumentWriter0(Document):
|
||||||
for m, conf in configs:
|
for m, conf in configs:
|
||||||
model = self.createElement("model")
|
model = self.createElement("model")
|
||||||
n.appendChild(model)
|
n.appendChild(model)
|
||||||
model.setAttribute("name", m._name)
|
model.setAttribute("name", m.name)
|
||||||
type = "wireless"
|
type = "wireless"
|
||||||
if m._type == RegisterTlvs.MOBILITY.value:
|
if m.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
type = "mobility"
|
type = "mobility"
|
||||||
model.setAttribute("type", type)
|
model.setAttribute("type", type)
|
||||||
for i, k in enumerate(m.getnames()):
|
|
||||||
|
for k, value in conf.iteritems():
|
||||||
key = self.createElement(k)
|
key = self.createElement(k)
|
||||||
value = conf[i]
|
|
||||||
if value is None:
|
if value is None:
|
||||||
value = ""
|
value = ""
|
||||||
key.appendChild(self.createTextNode("%s" % value))
|
key.appendChild(self.createTextNode("%s" % value))
|
||||||
|
@ -193,9 +194,8 @@ class CoreDocumentWriter0(Document):
|
||||||
# could use ifc.params, transport_type
|
# could use ifc.params, transport_type
|
||||||
self.addaddresses(i, ifc)
|
self.addaddresses(i, ifc)
|
||||||
# per-interface models
|
# per-interface models
|
||||||
if netmodel and netmodel._name[:6] == "emane_":
|
if netmodel and netmodel.name[:6] == "emane_":
|
||||||
cfg = self.session.emane.getifcconfig(node.objid, netmodel._name,
|
cfg = self.session.emane.getifcconfig(node.objid, ifc, netmodel.name)
|
||||||
None, ifc)
|
|
||||||
if cfg:
|
if cfg:
|
||||||
self.addmodels(i, ((netmodel, cfg),))
|
self.addmodels(i, ((netmodel, cfg),))
|
||||||
|
|
||||||
|
@ -276,15 +276,15 @@ class CoreDocumentWriter0(Document):
|
||||||
"""
|
"""
|
||||||
Add default services and node types to the ServicePlan.
|
Add default services and node types to the ServicePlan.
|
||||||
"""
|
"""
|
||||||
for type in self.session.services.defaultservices:
|
for type in self.session.services.default_services:
|
||||||
defaults = self.session.services.getdefaultservices(type)
|
defaults = self.session.services.get_default_services(type)
|
||||||
spn = self.createElement("Node")
|
spn = self.createElement("Node")
|
||||||
spn.setAttribute("type", type)
|
spn.setAttribute("type", type)
|
||||||
self.sp.appendChild(spn)
|
self.sp.appendChild(spn)
|
||||||
for svc in defaults:
|
for svc in defaults:
|
||||||
s = self.createElement("Service")
|
s = self.createElement("Service")
|
||||||
spn.appendChild(s)
|
spn.appendChild(s)
|
||||||
s.setAttribute("name", str(svc._name))
|
s.setAttribute("name", str(svc.name))
|
||||||
|
|
||||||
def addservices(self, node):
|
def addservices(self, node):
|
||||||
"""
|
"""
|
||||||
|
@ -292,7 +292,7 @@ class CoreDocumentWriter0(Document):
|
||||||
"""
|
"""
|
||||||
if len(node.services) == 0:
|
if len(node.services) == 0:
|
||||||
return
|
return
|
||||||
defaults = self.session.services.getdefaultservices(node.type)
|
defaults = self.session.services.get_default_services(node.type)
|
||||||
if node.services == defaults:
|
if node.services == defaults:
|
||||||
return
|
return
|
||||||
spn = self.createElement("Node")
|
spn = self.createElement("Node")
|
||||||
|
@ -302,24 +302,21 @@ class CoreDocumentWriter0(Document):
|
||||||
for svc in node.services:
|
for svc in node.services:
|
||||||
s = self.createElement("Service")
|
s = self.createElement("Service")
|
||||||
spn.appendChild(s)
|
spn.appendChild(s)
|
||||||
s.setAttribute("name", str(svc._name))
|
s.setAttribute("name", str(svc.name))
|
||||||
s.setAttribute("startup_idx", str(svc._startindex))
|
|
||||||
if svc._starttime != "":
|
|
||||||
s.setAttribute("start_time", str(svc._starttime))
|
|
||||||
# only record service names if not a customized service
|
# only record service names if not a customized service
|
||||||
if not svc._custom:
|
if not svc.custom:
|
||||||
continue
|
continue
|
||||||
s.setAttribute("custom", str(svc._custom))
|
s.setAttribute("custom", str(svc.custom))
|
||||||
xmlutils.add_elements_from_list(self, s, svc._dirs, "Directory", "name")
|
xmlutils.add_elements_from_list(self, s, svc.dirs, "Directory", "name")
|
||||||
|
|
||||||
for fn in svc._configs:
|
for fn in svc.configs:
|
||||||
if len(fn) == 0:
|
if len(fn) == 0:
|
||||||
continue
|
continue
|
||||||
f = self.createElement("File")
|
f = self.createElement("File")
|
||||||
f.setAttribute("name", fn)
|
f.setAttribute("name", fn)
|
||||||
# all file names are added to determine when a file has been deleted
|
# all file names are added to determine when a file has been deleted
|
||||||
s.appendChild(f)
|
s.appendChild(f)
|
||||||
data = self.session.services.getservicefiledata(svc, fn)
|
data = svc.config_data.get(fn)
|
||||||
if data is None:
|
if data is None:
|
||||||
# this includes only customized file contents and skips
|
# this includes only customized file contents and skips
|
||||||
# the auto-generated files
|
# the auto-generated files
|
||||||
|
@ -327,9 +324,9 @@ class CoreDocumentWriter0(Document):
|
||||||
txt = self.createTextNode(data)
|
txt = self.createTextNode(data)
|
||||||
f.appendChild(txt)
|
f.appendChild(txt)
|
||||||
|
|
||||||
xmlutils.add_text_elements_from_list(self, s, svc._startup, "Command", (("type", "start"),))
|
xmlutils.add_text_elements_from_list(self, s, svc.startup, "Command", (("type", "start"),))
|
||||||
xmlutils.add_text_elements_from_list(self, s, svc._shutdown, "Command", (("type", "stop"),))
|
xmlutils.add_text_elements_from_list(self, s, svc.shutdown, "Command", (("type", "stop"),))
|
||||||
xmlutils.add_text_elements_from_list(self, s, svc._validate, "Command", (("type", "validate"),))
|
xmlutils.add_text_elements_from_list(self, s, svc.validate, "Command", (("type", "validate"),))
|
||||||
|
|
||||||
def addaddresses(self, i, netif):
|
def addaddresses(self, i, netif):
|
||||||
"""
|
"""
|
||||||
|
@ -370,18 +367,20 @@ class CoreDocumentWriter0(Document):
|
||||||
"""
|
"""
|
||||||
# options
|
# options
|
||||||
options = self.createElement("SessionOptions")
|
options = self.createElement("SessionOptions")
|
||||||
defaults = self.session.options.getdefaultvalues()
|
defaults = self.session.options.default_values()
|
||||||
for i, (k, v) in enumerate(self.session.options.getkeyvaluelist()):
|
for name, current_value in self.session.options.get_configs().iteritems():
|
||||||
if str(v) != str(defaults[i]):
|
default_value = defaults[name]
|
||||||
xmlutils.add_text_param_to_parent(self, options, k, v)
|
if current_value != default_value:
|
||||||
# addparamtoparent(self, options, k, v)
|
xmlutils.add_text_param_to_parent(self, options, name, current_value)
|
||||||
|
|
||||||
if options.hasChildNodes():
|
if options.hasChildNodes():
|
||||||
self.meta.appendChild(options)
|
self.meta.appendChild(options)
|
||||||
|
|
||||||
# hook scripts
|
# hook scripts
|
||||||
self.addhooks()
|
self.addhooks()
|
||||||
|
|
||||||
# meta
|
# meta
|
||||||
meta = self.createElement("MetaData")
|
meta = self.createElement("MetaData")
|
||||||
self.meta.appendChild(meta)
|
self.meta.appendChild(meta)
|
||||||
for k, v in self.session.metadata.items():
|
for name, current_value in self.session.metadata.get_configs().iteritems():
|
||||||
xmlutils.add_text_param_to_parent(self, meta, k, v)
|
xmlutils.add_text_param_to_parent(self, meta, name, current_value)
|
||||||
# addparamtoparent(self, meta, k, v)
|
|
||||||
|
|
|
@ -217,13 +217,7 @@ class ScenarioPlan(XmlElement):
|
||||||
self.last_network_id = 0
|
self.last_network_id = 0
|
||||||
self.addNetworks()
|
self.addNetworks()
|
||||||
self.addDevices()
|
self.addDevices()
|
||||||
|
|
||||||
# XXX Do we need these?
|
|
||||||
# self.session.emane.setup() # not during runtime?
|
|
||||||
# self.addorigin()
|
|
||||||
|
|
||||||
self.addDefaultServices()
|
self.addDefaultServices()
|
||||||
|
|
||||||
self.addSessionConfiguration()
|
self.addSessionConfiguration()
|
||||||
|
|
||||||
def addNetworks(self):
|
def addNetworks(self):
|
||||||
|
@ -275,15 +269,15 @@ class ScenarioPlan(XmlElement):
|
||||||
Add default services and node types to the ServicePlan.
|
Add default services and node types to the ServicePlan.
|
||||||
"""
|
"""
|
||||||
defaultservices = self.createElement("CORE:defaultservices")
|
defaultservices = self.createElement("CORE:defaultservices")
|
||||||
for type in self.coreSession.services.defaultservices:
|
for type in self.coreSession.services.default_services:
|
||||||
defaults = self.coreSession.services.getdefaultservices(type)
|
defaults = self.coreSession.services.get_default_services(type)
|
||||||
spn = self.createElement("device")
|
spn = self.createElement("device")
|
||||||
spn.setAttribute("type", type)
|
spn.setAttribute("type", type)
|
||||||
defaultservices.appendChild(spn)
|
defaultservices.appendChild(spn)
|
||||||
for svc in defaults:
|
for svc in defaults:
|
||||||
s = self.createElement("service")
|
s = self.createElement("service")
|
||||||
spn.appendChild(s)
|
spn.appendChild(s)
|
||||||
s.setAttribute("name", str(svc._name))
|
s.setAttribute("name", str(svc.name))
|
||||||
if defaultservices.hasChildNodes():
|
if defaultservices.hasChildNodes():
|
||||||
self.appendChild(defaultservices)
|
self.appendChild(defaultservices)
|
||||||
|
|
||||||
|
@ -318,10 +312,12 @@ class ScenarioPlan(XmlElement):
|
||||||
|
|
||||||
# options
|
# options
|
||||||
options = self.createElement("options")
|
options = self.createElement("options")
|
||||||
defaults = self.coreSession.options.getdefaultvalues()
|
options_config = self.coreSession.options.get_configs()
|
||||||
for i, (k, v) in enumerate(self.coreSession.options.getkeyvaluelist()):
|
for _id, default_value in self.coreSession.options.default_values().iteritems():
|
||||||
if str(v) != str(defaults[i]):
|
value = options_config[_id]
|
||||||
XmlElement.add_parameter(self.document, options, k, v)
|
if value != default_value:
|
||||||
|
XmlElement.add_parameter(self.document, options, _id, value)
|
||||||
|
|
||||||
if options.hasChildNodes():
|
if options.hasChildNodes():
|
||||||
config.appendChild(options)
|
config.appendChild(options)
|
||||||
|
|
||||||
|
@ -340,7 +336,7 @@ class ScenarioPlan(XmlElement):
|
||||||
|
|
||||||
# metadata
|
# metadata
|
||||||
meta = self.createElement("metadata")
|
meta = self.createElement("metadata")
|
||||||
for k, v in self.coreSession.metadata.items():
|
for k, v in self.coreSession.metadata.get_configs().iteritems():
|
||||||
XmlElement.add_parameter(self.document, meta, k, v)
|
XmlElement.add_parameter(self.document, meta, k, v)
|
||||||
if meta.hasChildNodes():
|
if meta.hasChildNodes():
|
||||||
config.appendChild(meta)
|
config.appendChild(meta)
|
||||||
|
@ -479,9 +475,10 @@ class NetworkElement(NamedXmlElement):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if nodeutils.is_node(network_object, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
if nodeutils.is_node(network_object, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
||||||
modelconfigs = network_object.session.mobility.getmodels(network_object)
|
modelconfigs = network_object.session.mobility.get_models(network_object)
|
||||||
modelconfigs += network_object.session.emane.getmodels(network_object)
|
modelconfigs += network_object.session.emane.get_models(network_object)
|
||||||
chan = None
|
chan = None
|
||||||
|
|
||||||
for model, conf in modelconfigs:
|
for model, conf in modelconfigs:
|
||||||
# Handle mobility parameters below
|
# Handle mobility parameters below
|
||||||
if model.config_type == RegisterTlvs.MOBILITY.value:
|
if model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
|
@ -496,10 +493,9 @@ class NetworkElement(NamedXmlElement):
|
||||||
channel_domain="CORE")
|
channel_domain="CORE")
|
||||||
|
|
||||||
# Add wireless model parameters
|
# Add wireless model parameters
|
||||||
for i, key in enumerate(model.getnames()):
|
for key, value in conf.iteritems():
|
||||||
value = conf[i]
|
|
||||||
if value is not None:
|
if value is not None:
|
||||||
chan.addParameter(key, model.valueof(key, conf))
|
chan.addParameter(key, value)
|
||||||
|
|
||||||
for model, conf in modelconfigs:
|
for model, conf in modelconfigs:
|
||||||
if model.config_type == RegisterTlvs.MOBILITY.value:
|
if model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
|
@ -509,8 +505,8 @@ class NetworkElement(NamedXmlElement):
|
||||||
type_element = self.createElement("type")
|
type_element = self.createElement("type")
|
||||||
type_element.appendChild(self.createTextNode(model.name))
|
type_element.appendChild(self.createTextNode(model.name))
|
||||||
mobility.appendChild(type_element)
|
mobility.appendChild(type_element)
|
||||||
for i, key in enumerate(model.getnames()):
|
|
||||||
value = conf[i]
|
for key, value in conf.iteritems():
|
||||||
if value is not None:
|
if value is not None:
|
||||||
mobility.addParameter(key, value)
|
mobility.addParameter(key, value)
|
||||||
|
|
||||||
|
@ -658,8 +654,7 @@ class DeviceElement(NamedXmlElement):
|
||||||
# per-interface models
|
# per-interface models
|
||||||
# XXX Remove???
|
# XXX Remove???
|
||||||
if netmodel and netmodel.name[:6] == "emane_":
|
if netmodel and netmodel.name[:6] == "emane_":
|
||||||
cfg = self.coreSession.emane.getifcconfig(device_object.objid, netmodel.name,
|
cfg = self.coreSession.emane.getifcconfig(device_object.objid, interface_object, netmodel.name)
|
||||||
None, interface_object)
|
|
||||||
if cfg:
|
if cfg:
|
||||||
interface_element.addModels(((netmodel, cfg),))
|
interface_element.addModels(((netmodel, cfg),))
|
||||||
|
|
||||||
|
@ -675,7 +670,7 @@ class DeviceElement(NamedXmlElement):
|
||||||
if len(device_object.services) == 0:
|
if len(device_object.services) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
defaults = self.coreSession.services.getdefaultservices(device_object.type)
|
defaults = self.coreSession.services.get_default_services(device_object.type)
|
||||||
if device_object.services == defaults:
|
if device_object.services == defaults:
|
||||||
return
|
return
|
||||||
spn = self.createElement("CORE:services")
|
spn = self.createElement("CORE:services")
|
||||||
|
@ -685,24 +680,21 @@ class DeviceElement(NamedXmlElement):
|
||||||
for svc in device_object.services:
|
for svc in device_object.services:
|
||||||
s = self.createElement("service")
|
s = self.createElement("service")
|
||||||
spn.appendChild(s)
|
spn.appendChild(s)
|
||||||
s.setAttribute("name", str(svc._name))
|
s.setAttribute("name", str(svc.name))
|
||||||
s.setAttribute("startup_idx", str(svc._startindex))
|
|
||||||
if svc._starttime != "":
|
|
||||||
s.setAttribute("start_time", str(svc._starttime))
|
|
||||||
# only record service names if not a customized service
|
# only record service names if not a customized service
|
||||||
if not svc._custom:
|
if not svc.custom:
|
||||||
continue
|
continue
|
||||||
s.setAttribute("custom", str(svc._custom))
|
s.setAttribute("custom", str(svc.custom))
|
||||||
xmlutils.add_elements_from_list(self, s, svc._dirs, "directory", "name")
|
xmlutils.add_elements_from_list(self, s, svc.dirs, "directory", "name")
|
||||||
|
|
||||||
for fn in svc._configs:
|
for fn in svc.configs:
|
||||||
if len(fn) == 0:
|
if len(fn) == 0:
|
||||||
continue
|
continue
|
||||||
f = self.createElement("file")
|
f = self.createElement("file")
|
||||||
f.setAttribute("name", fn)
|
f.setAttribute("name", fn)
|
||||||
# all file names are added to determine when a file has been deleted
|
# all file names are added to determine when a file has been deleted
|
||||||
s.appendChild(f)
|
s.appendChild(f)
|
||||||
data = self.coreSession.services.getservicefiledata(svc, fn)
|
data = svc.config_data.get(fn)
|
||||||
if data is None:
|
if data is None:
|
||||||
# this includes only customized file contents and skips
|
# this includes only customized file contents and skips
|
||||||
# the auto-generated files
|
# the auto-generated files
|
||||||
|
@ -710,12 +702,9 @@ class DeviceElement(NamedXmlElement):
|
||||||
txt = self.createTextNode("\n" + data)
|
txt = self.createTextNode("\n" + data)
|
||||||
f.appendChild(txt)
|
f.appendChild(txt)
|
||||||
|
|
||||||
xmlutils.add_text_elements_from_list(self, s, svc._startup, "command",
|
xmlutils.add_text_elements_from_list(self, s, svc.startup, "command", (("type", "start"),))
|
||||||
(("type", "start"),))
|
xmlutils.add_text_elements_from_list(self, s, svc.shutdown, "command", (("type", "stop"),))
|
||||||
xmlutils.add_text_elements_from_list(self, s, svc._shutdown, "command",
|
xmlutils.add_text_elements_from_list(self, s, svc.validate, "command", (("type", "validate"),))
|
||||||
(("type", "stop"),))
|
|
||||||
xmlutils.add_text_elements_from_list(self, s, svc._validate, "command",
|
|
||||||
(("type", "validate"),))
|
|
||||||
|
|
||||||
|
|
||||||
class ChannelElement(NamedXmlElement):
|
class ChannelElement(NamedXmlElement):
|
||||||
|
|
|
@ -23,12 +23,12 @@ quagga_sbin_search = "/usr/local/sbin /usr/sbin /usr/lib/quagga"
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# uncomment and edit to establish a distributed control backchannel
|
# uncomment and edit to establish a distributed control backchannel
|
||||||
#controlnet = core1:172.16.1.0/24 core:172.16.2.0/24 core3:172.16.3.0/24 core4 :172.16.4.0/24 core5:172.16.5.0/24
|
#controlnet = core1:172.16.1.0/24 core2:172.16.2.0/24 core3:172.16.3.0/24 core4:172.16.4.0/24 core5:172.16.5.0/24
|
||||||
|
|
||||||
# uncomment and edit to establish distributed auxiliary control channels.
|
# uncomment and edit to establish distributed auxiliary control channels.
|
||||||
#controlnet1 = core1:172.17.1.0/24 core:172.17.2.0/24 core3:172.17.3.0/24 core4 :172.17.4.0/24 core5:172.17.5.0/24
|
#controlnet1 = core1:172.17.1.0/24 core2:172.17.2.0/24 core3:172.17.3.0/24 core4:172.17.4.0/24 core5:172.17.5.0/24
|
||||||
#controlnet2 = core1:172.18.1.0/24 core:172.18.2.0/24 core3:172.18.3.0/24 core4 :172.18.4.0/24 core5:172.18.5.0/24
|
#controlnet2 = core1:172.18.1.0/24 core2:172.18.2.0/24 core3:172.18.3.0/24 core4:172.18.4.0/24 core5:172.18.5.0/24
|
||||||
#controlnet3 = core1:172.19.1.0/24 core:172.19.2.0/24 core3:172.19.3.0/24 core4 :172.19.4.0/24 core5:172.19.5.0/24
|
#controlnet3 = core1:172.19.1.0/24 core2:172.19.2.0/24 core3:172.19.3.0/24 core4:172.19.4.0/24 core5:172.19.5.0/24
|
||||||
|
|
||||||
# uncomment and edit to assign host interfaces to auxilary control channels
|
# uncomment and edit to assign host interfaces to auxilary control channels
|
||||||
# for use in connecting with other servers in a distributed environments.
|
# for use in connecting with other servers in a distributed environments.
|
||||||
|
|
|
@ -27,7 +27,7 @@ def example(options):
|
||||||
|
|
||||||
# create wlan network node
|
# create wlan network node
|
||||||
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
session.set_wireless_model(wlan, BasicRangeModel)
|
session.mobility.set_model(wlan, BasicRangeModel)
|
||||||
|
|
||||||
# create nodes
|
# create nodes
|
||||||
wireless_nodes = []
|
wireless_nodes = []
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
|
|
||||||
|
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emane import emanemodel
|
from core.emane import emanemodel
|
||||||
|
|
||||||
|
|
||||||
## Custom EMANE Model
|
## Custom EMANE Model
|
||||||
class ExampleModel(emanemodel.EmaneModel):
|
class ExampleModel(emanemodel.EmaneModel):
|
||||||
### MAC Definition
|
### MAC Definition
|
||||||
|
@ -47,10 +46,3 @@ class ExampleModel(emanemodel.EmaneModel):
|
||||||
# Allows you to ignore options within phy/mac, used typically if you needed to add a custom option for display
|
# Allows you to ignore options within phy/mac, used typically if you needed to add a custom option for display
|
||||||
# within the gui.
|
# within the gui.
|
||||||
config_ignore = set()
|
config_ignore = set()
|
||||||
# Allows you to override how options are displayed with the GUI, using the GUI format of
|
|
||||||
# "name:1-2|othername:3-4". This will be parsed into tabs, split by "|" and account for items based on the indexed
|
|
||||||
# numbers after ":" for including values in each tab.
|
|
||||||
config_groups_override = None
|
|
||||||
# Allows you to override the default config matrix list. This value by default is the mac_config + phy_config, in
|
|
||||||
# that order.
|
|
||||||
config_matrix_override = None
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ Sample user-defined service.
|
||||||
|
|
||||||
from core.misc.ipaddress import Ipv4Prefix
|
from core.misc.ipaddress import Ipv4Prefix
|
||||||
from core.service import CoreService
|
from core.service import CoreService
|
||||||
from core.service import ServiceManager
|
|
||||||
|
|
||||||
|
|
||||||
class MyService(CoreService):
|
class MyService(CoreService):
|
||||||
|
@ -12,25 +11,25 @@ class MyService(CoreService):
|
||||||
This is a sample user-defined service.
|
This is a sample user-defined service.
|
||||||
"""
|
"""
|
||||||
# a unique name is required, without spaces
|
# a unique name is required, without spaces
|
||||||
_name = "MyService"
|
name = "MyService"
|
||||||
# you can create your own group here
|
# you can create your own group here
|
||||||
_group = "Utility"
|
group = "Utility"
|
||||||
|
# list executables that this service requires
|
||||||
|
executables = ()
|
||||||
# list of other services this service depends on
|
# list of other services this service depends on
|
||||||
_depends = ()
|
dependencies = ()
|
||||||
# per-node directories
|
# per-node directories
|
||||||
_dirs = ()
|
dirs = ()
|
||||||
# generated files (without a full path this file goes in the node's dir,
|
# generated files (without a full path this file goes in the node's dir,
|
||||||
# e.g. /tmp/pycore.12345/n1.conf/)
|
# e.g. /tmp/pycore.12345/n1.conf/)
|
||||||
_configs = ('myservice.sh',)
|
configs = ("myservice.sh",)
|
||||||
# this controls the starting order vs other enabled services
|
|
||||||
_startindex = 50
|
|
||||||
# list of startup commands, also may be generated during startup
|
# list of startup commands, also may be generated during startup
|
||||||
_startup = ('sh myservice.sh',)
|
startup = ("sh myservice.sh",)
|
||||||
# list of shutdown commands
|
# list of shutdown commands
|
||||||
_shutdown = ()
|
shutdown = ()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generateconfig(cls, node, filename, services):
|
def generate_config(cls, node, filename):
|
||||||
"""
|
"""
|
||||||
Return a string that will be written to filename, or sent to the
|
Return a string that will be written to filename, or sent to the
|
||||||
GUI for user customization.
|
GUI for user customization.
|
||||||
|
@ -57,9 +56,3 @@ class MyService(CoreService):
|
||||||
else:
|
else:
|
||||||
net = Ipv4Prefix(x)
|
net = Ipv4Prefix(x)
|
||||||
return 'echo " network %s"' % net
|
return 'echo " network %s"' % net
|
||||||
|
|
||||||
|
|
||||||
# this is needed to load desired services when being integrated into core, otherwise this is not needed
|
|
||||||
def load_services():
|
|
||||||
# this line is required to add the above class to the list of available services
|
|
||||||
ServiceManager.add(MyService)
|
|
||||||
|
|
|
@ -159,8 +159,8 @@ def main():
|
||||||
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
n.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
n.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
|
||||||
if options.services is not None:
|
if options.services is not None:
|
||||||
session.services.addservicestonode(n, "", options.services)
|
session.services.add_services(n, "", options.services.split("|"))
|
||||||
n.boot()
|
session.services.boot_services(n)
|
||||||
nodelist.append(n)
|
nodelist.append(n)
|
||||||
if i % 25 == 0:
|
if i % 25 == 0:
|
||||||
print "\n%s nodes created " % i,
|
print "\n%s nodes created " % i,
|
||||||
|
|
|
@ -88,7 +88,7 @@ ip forwarding
|
||||||
|
|
||||||
def boot(self):
|
def boot(self):
|
||||||
self.config()
|
self.config()
|
||||||
self.session.services.bootnodeservices(self)
|
self.session.services.boot_services(self)
|
||||||
|
|
||||||
def bootscript(self):
|
def bootscript(self):
|
||||||
return """\
|
return """\
|
||||||
|
|
|
@ -420,8 +420,8 @@ class Experiment(object):
|
||||||
tmp = self.session.add_object(cls=nodes.CoreNode, objid=i, name="n%d" % i)
|
tmp = self.session.add_object(cls=nodes.CoreNode, objid=i, name="n%d" % i)
|
||||||
tmp.newnetif(self.net, [addr])
|
tmp.newnetif(self.net, [addr])
|
||||||
self.nodes.append(tmp)
|
self.nodes.append(tmp)
|
||||||
self.session.services.addservicestonode(tmp, "router", "IPForward")
|
self.session.services.add_services(tmp, "router", "IPForward")
|
||||||
self.session.services.bootnodeservices(tmp)
|
self.session.services.boot_services(tmp)
|
||||||
self.staticroutes(i, prefix, numnodes)
|
self.staticroutes(i, prefix, numnodes)
|
||||||
|
|
||||||
# link each node in a chain, with the previous node
|
# link each node in a chain, with the previous node
|
||||||
|
@ -429,8 +429,7 @@ class Experiment(object):
|
||||||
self.net.link(prev.netif(0), tmp.netif(0))
|
self.net.link(prev.netif(0), tmp.netif(0))
|
||||||
prev = tmp
|
prev = tmp
|
||||||
|
|
||||||
def createemanesession(self, numnodes, verbose=False, cls=None,
|
def createemanesession(self, numnodes, verbose=False, cls=None, values=None):
|
||||||
values=None):
|
|
||||||
""" Build a topology consisting of the given number of LxcNodes
|
""" Build a topology consisting of the given number of LxcNodes
|
||||||
connected to an EMANE WLAN.
|
connected to an EMANE WLAN.
|
||||||
"""
|
"""
|
||||||
|
@ -440,7 +439,6 @@ class Experiment(object):
|
||||||
self.session.master = True
|
self.session.master = True
|
||||||
self.session.location.setrefgeo(47.57917, -122.13232, 2.00000)
|
self.session.location.setrefgeo(47.57917, -122.13232, 2.00000)
|
||||||
self.session.location.refscale = 150.0
|
self.session.location.refscale = 150.0
|
||||||
self.session.config["emane_models"] = "RfPipe, Ieee80211abg, Bypass"
|
|
||||||
self.session.emane.loadmodels()
|
self.session.emane.loadmodels()
|
||||||
self.net = self.session.add_object(cls=EmaneNode, objid=numnodes + 1, name="wlan1")
|
self.net = self.session.add_object(cls=EmaneNode, objid=numnodes + 1, name="wlan1")
|
||||||
self.net.verbose = verbose
|
self.net.verbose = verbose
|
||||||
|
@ -453,7 +451,7 @@ class Experiment(object):
|
||||||
tmp.setposition(50, 50, None)
|
tmp.setposition(50, 50, None)
|
||||||
tmp.newnetif(self.net, [addr])
|
tmp.newnetif(self.net, [addr])
|
||||||
self.nodes.append(tmp)
|
self.nodes.append(tmp)
|
||||||
self.session.services.addservicestonode(tmp, "router", "IPForward")
|
self.session.services.add_services(tmp, "router", "IPForward")
|
||||||
|
|
||||||
if values is None:
|
if values is None:
|
||||||
values = cls.getdefaultvalues()
|
values = cls.getdefaultvalues()
|
||||||
|
@ -465,7 +463,7 @@ class Experiment(object):
|
||||||
|
|
||||||
for i in xrange(1, numnodes + 1):
|
for i in xrange(1, numnodes + 1):
|
||||||
tmp = self.nodes[i - 1]
|
tmp = self.nodes[i - 1]
|
||||||
self.session.services.bootnodeservices(tmp)
|
self.session.services.boot_services(tmp)
|
||||||
self.staticroutes(i, prefix, numnodes)
|
self.staticroutes(i, prefix, numnodes)
|
||||||
|
|
||||||
def setnodes(self):
|
def setnodes(self):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
enum34==1.1.6
|
enum34==1.1.6
|
||||||
|
lxml==3.5.0
|
||||||
mock==1.3.0
|
mock==1.3.0
|
||||||
pycco==0.5.1
|
pycco==0.5.1
|
||||||
pytest==3.0.7
|
pytest==3.0.7
|
||||||
|
|
0
daemon/scripts/core-cleanup
Executable file → Normal file
0
daemon/scripts/core-cleanup
Executable file → Normal file
|
@ -16,7 +16,6 @@ from core import logger
|
||||||
from core.corehandlers import CoreHandler
|
from core.corehandlers import CoreHandler
|
||||||
from core.coreserver import CoreServer
|
from core.coreserver import CoreServer
|
||||||
from core.misc.utils import close_onexec
|
from core.misc.utils import close_onexec
|
||||||
from core.service import ServiceManager
|
|
||||||
|
|
||||||
|
|
||||||
def banner():
|
def banner():
|
||||||
|
@ -116,14 +115,6 @@ def main():
|
||||||
for a in args:
|
for a in args:
|
||||||
logger.error("ignoring command line argument: %s", a)
|
logger.error("ignoring command line argument: %s", a)
|
||||||
|
|
||||||
# attempt load custom services
|
|
||||||
service_paths = cfg.get("custom_services_dir")
|
|
||||||
logger.debug("custom service paths: %s", service_paths)
|
|
||||||
if service_paths:
|
|
||||||
for service_path in service_paths.split(','):
|
|
||||||
service_path = service_path.strip()
|
|
||||||
ServiceManager.add_services(service_path)
|
|
||||||
|
|
||||||
banner()
|
banner()
|
||||||
|
|
||||||
# check if ovs flag was provided
|
# check if ovs flag was provided
|
||||||
|
|
0
daemon/scripts/core-manage
Executable file → Normal file
0
daemon/scripts/core-manage
Executable file → Normal file
0
daemon/scripts/coresendmsg
Executable file → Normal file
0
daemon/scripts/coresendmsg
Executable file → Normal file
|
@ -39,10 +39,11 @@ data_files.extend(recursive_files(_EXAMPLES_DIR, "examples"))
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="core",
|
name="core",
|
||||||
version="5.1",
|
version="5.2",
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"enum34",
|
"enum34",
|
||||||
|
"lxml"
|
||||||
],
|
],
|
||||||
tests_require=[
|
tests_require=[
|
||||||
"pytest",
|
"pytest",
|
||||||
|
|
73
daemon/src/Makefile.am
Normal file
73
daemon/src/Makefile.am
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# CORE
|
||||||
|
# (c)2010-2013 the Boeing Company.
|
||||||
|
# See the LICENSE file included in this distribution.
|
||||||
|
#
|
||||||
|
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||||
|
#
|
||||||
|
# Makefile for building netns.
|
||||||
|
#
|
||||||
|
|
||||||
|
AM_CFLAGS = -Wall -fno-strict-aliasing -O3 -g @libev_CFLAGS@
|
||||||
|
SETUPPY = setup.py
|
||||||
|
SETUPPYFLAGS = -v
|
||||||
|
# -DDEBUG
|
||||||
|
|
||||||
|
SRC_COMMON = vnode_msg.c vnode_cmd.c vnode_chnl.c vnode_io.c \
|
||||||
|
vnode_msg.h vnode_cmd.h vnode_chnl.h vnode_io.h \
|
||||||
|
vnode_tlv.h myerr.h netns.h
|
||||||
|
SRC_VNODED = vnoded_main.c vnode_server.c netns.c \
|
||||||
|
vnode_server.h
|
||||||
|
SRC_VCMD = vcmd_main.c vnode_client.c \
|
||||||
|
vnode_client.h
|
||||||
|
SRC_NETNS = netns_main.c netns.c netns.h
|
||||||
|
|
||||||
|
sbin_PROGRAMS = vnoded vcmd netns
|
||||||
|
vnoded_LDADD = @libev_LIBS@
|
||||||
|
vnoded_SOURCES = ${SRC_COMMON} ${SRC_VNODED}
|
||||||
|
vcmd_LDADD = @libev_LIBS@
|
||||||
|
vcmd_SOURCES = ${SRC_COMMON} ${SRC_VCMD}
|
||||||
|
netns_SOURCES = ${SRC_NETNS}
|
||||||
|
|
||||||
|
# this triggers automake to run setup.py for building the Python libraries
|
||||||
|
# actual library names are netns.so and vcmd.so
|
||||||
|
# SOURCES line prevents 'make dist' from looking for a 'libnetns.c' file
|
||||||
|
noinst_LIBRARIES = libnetns.a
|
||||||
|
libnetns_a_SOURCES = netnsmodule.c vcmdmodule.c
|
||||||
|
libnetns.a:
|
||||||
|
SBINDIR=@SBINDIR@ LDFLAGS="$(LDFLAGS) @libev_LIBS@" CFLAGS="$(CFLAGS) @libev_CFLAGS@" $(PYTHON) setup.py build
|
||||||
|
|
||||||
|
# Python libraries install
|
||||||
|
install-exec-local:
|
||||||
|
$(MKDIR_P) ${DESTDIR}/${pythondir}
|
||||||
|
$(MKDIR_P) ${DESTDIR}/${pyexecdir}
|
||||||
|
SBINDIR=${DESTDIR}/@SBINDIR@ PYTHONPATH=${DESTDIR}/${pythondir}:${DESTDIR}/${pyexecdir} $(PYTHON) $(SETUPPY) $(SETUPPYFLAGS) install \
|
||||||
|
--prefix=${DESTDIR}/${pyprefix} \
|
||||||
|
--install-purelib=${DESTDIR}/${pythondir} \
|
||||||
|
--install-platlib=${DESTDIR}/${pyexecdir} \
|
||||||
|
--no-compile
|
||||||
|
|
||||||
|
# Python libraries uninstall
|
||||||
|
uninstall-hook:
|
||||||
|
rm -f ${pyexecdir}/core_python_netns-1.0-py${PYTHON_VERSION}.egg-info
|
||||||
|
rm -f ${pyexecdir}/netns.so
|
||||||
|
rm -f ${pyexecdir}/vcmd.so
|
||||||
|
|
||||||
|
# Python libraries cleanup
|
||||||
|
clean-local: clean-local-check
|
||||||
|
.PHONY: clean-local-check
|
||||||
|
clean-local-check:
|
||||||
|
-rm -rf build
|
||||||
|
|
||||||
|
rpmbuild.sh:
|
||||||
|
echo SBINDIR=@SBINDIR@ LDFLAGS="$(LDFLAGS)" CFLAGS="$(CFLAGS) @libev_CFLAGS@" $(PYTHON) setup.py build > rpmbuild.sh
|
||||||
|
chmod a+x rpmbuild.sh
|
||||||
|
|
||||||
|
rpm: rpmbuild.sh
|
||||||
|
$(PYTHON) setup.py bdist_rpm --build-script=rpmbuild.sh --requires="libev" --build-requires="libev-devel"
|
||||||
|
|
||||||
|
# extra cruft to remove
|
||||||
|
DISTCLEANFILES = Makefile.in rpmbuild.sh MANIFEST
|
||||||
|
|
||||||
|
# include source files for Python libraries with distribution tarball
|
||||||
|
EXTRA_DIST = setup.py MANIFEST.in
|
||||||
|
|
|
@ -7,7 +7,6 @@ import os
|
||||||
import pytest
|
import pytest
|
||||||
from mock.mock import MagicMock
|
from mock.mock import MagicMock
|
||||||
|
|
||||||
from core import services
|
|
||||||
from core.api.coreapi import CoreConfMessage
|
from core.api.coreapi import CoreConfMessage
|
||||||
from core.api.coreapi import CoreEventMessage
|
from core.api.coreapi import CoreEventMessage
|
||||||
from core.api.coreapi import CoreExecMessage
|
from core.api.coreapi import CoreExecMessage
|
||||||
|
@ -29,6 +28,7 @@ from core.enumerations import NodeTlvs
|
||||||
from core.enumerations import NodeTypes
|
from core.enumerations import NodeTypes
|
||||||
from core.misc import ipaddress
|
from core.misc import ipaddress
|
||||||
from core.misc.ipaddress import MacAddress
|
from core.misc.ipaddress import MacAddress
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward"
|
EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward"
|
||||||
|
|
||||||
|
@ -199,6 +199,7 @@ class CoreServerTest(object):
|
||||||
self.request_handler.handle_message(message)
|
self.request_handler.handle_message(message)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
self.server.coreemu.shutdown()
|
||||||
self.server.shutdown()
|
self.server.shutdown()
|
||||||
self.server.server_close()
|
self.server.server_close()
|
||||||
|
|
||||||
|
@ -214,9 +215,18 @@ def session():
|
||||||
# return created session
|
# return created session
|
||||||
yield session_fixture
|
yield session_fixture
|
||||||
|
|
||||||
|
# clear session configurations
|
||||||
|
session_fixture.location.reset()
|
||||||
|
session_fixture.services.reset()
|
||||||
|
session_fixture.mobility.config_reset()
|
||||||
|
session_fixture.emane.config_reset()
|
||||||
|
|
||||||
# shutdown coreemu
|
# shutdown coreemu
|
||||||
coreemu.shutdown()
|
coreemu.shutdown()
|
||||||
|
|
||||||
|
# clear services, since they will be reloaded
|
||||||
|
ServiceManager.services.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def ip_prefixes():
|
def ip_prefixes():
|
||||||
|
@ -225,9 +235,6 @@ def ip_prefixes():
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def cored():
|
def cored():
|
||||||
# load default services
|
|
||||||
services.load()
|
|
||||||
|
|
||||||
# create and return server
|
# create and return server
|
||||||
server = CoreServerTest()
|
server = CoreServerTest()
|
||||||
yield server
|
yield server
|
||||||
|
@ -235,6 +242,11 @@ def cored():
|
||||||
# cleanup
|
# cleanup
|
||||||
server.shutdown()
|
server.shutdown()
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
# cleanup services
|
||||||
|
ServiceManager.services.clear()
|
||||||
|
|
||||||
|
|
||||||
def ping(from_node, to_node, ip_prefixes, count=3):
|
def ping(from_node, to_node, ip_prefixes, count=3):
|
||||||
address = ip_prefixes.ip4_address(to_node)
|
address = ip_prefixes.ip4_address(to_node)
|
||||||
|
|
|
@ -6,22 +6,25 @@ from core.service import CoreService
|
||||||
|
|
||||||
|
|
||||||
class MyService(CoreService):
|
class MyService(CoreService):
|
||||||
_name = "MyService"
|
name = "MyService"
|
||||||
_group = "Utility"
|
group = "Utility"
|
||||||
_depends = ()
|
configs = ("myservice.sh",)
|
||||||
_dirs = ()
|
startup = ("sh myservice.sh",)
|
||||||
_configs = ('myservice.sh',)
|
shutdown = ("sh myservice.sh",)
|
||||||
_startindex = 50
|
|
||||||
_startup = ('sh myservice.sh',)
|
@classmethod
|
||||||
_shutdown = ()
|
def generate_config(cls, node, filename):
|
||||||
|
return "# test file"
|
||||||
|
|
||||||
|
|
||||||
class MyService2(CoreService):
|
class MyService2(MyService):
|
||||||
_name = "MyService2"
|
name = "MyService2"
|
||||||
_group = "Utility"
|
group = "Utility"
|
||||||
_depends = ()
|
configs = ("myservice2.sh",)
|
||||||
_dirs = ()
|
startup = ("sh myservice2.sh",)
|
||||||
_configs = ('myservice.sh',)
|
shutdown = startup
|
||||||
_startindex = 50
|
validate = startup
|
||||||
_startup = ('sh myservice.sh',)
|
|
||||||
_shutdown = ()
|
@classmethod
|
||||||
|
def generate_config(cls, node, filename):
|
||||||
|
return "exit 1"
|
||||||
|
|
183
daemon/tests/test_conf.py
Normal file
183
daemon/tests/test_conf.py
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.conf import ConfigurableManager
|
||||||
|
from core.conf import ConfigurableOptions
|
||||||
|
from core.conf import Configuration
|
||||||
|
from core.conf import ModelManager
|
||||||
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
|
from core.enumerations import ConfigDataTypes
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.mobility import BasicRangeModel
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfigurableOptions(ConfigurableOptions):
|
||||||
|
name_one = "value1"
|
||||||
|
name_two = "value2"
|
||||||
|
options = [
|
||||||
|
Configuration(
|
||||||
|
_id=name_one,
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label=name_one
|
||||||
|
),
|
||||||
|
Configuration(
|
||||||
|
_id=name_two,
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label=name_two
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestConf:
|
||||||
|
def test_configurable_options_default(self):
|
||||||
|
# given
|
||||||
|
configurable_options = TestConfigurableOptions()
|
||||||
|
|
||||||
|
# when
|
||||||
|
default_values = TestConfigurableOptions.default_values()
|
||||||
|
instance_default_values = configurable_options.default_values()
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert len(default_values) == 2
|
||||||
|
assert TestConfigurableOptions.name_one in default_values
|
||||||
|
assert TestConfigurableOptions.name_two in default_values
|
||||||
|
assert len(instance_default_values) == 2
|
||||||
|
assert TestConfigurableOptions.name_one in instance_default_values
|
||||||
|
assert TestConfigurableOptions.name_two in instance_default_values
|
||||||
|
|
||||||
|
def test_nodes(self):
|
||||||
|
# given
|
||||||
|
config_manager = ConfigurableManager()
|
||||||
|
test_config = {1: 2}
|
||||||
|
node_id = 1
|
||||||
|
config_manager.set_configs(test_config)
|
||||||
|
config_manager.set_configs(test_config, node_id=node_id)
|
||||||
|
|
||||||
|
# when
|
||||||
|
nodes = config_manager.nodes()
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert len(nodes) == 1
|
||||||
|
assert node_id in nodes
|
||||||
|
|
||||||
|
def test_config_reset_all(self):
|
||||||
|
# given
|
||||||
|
config_manager = ConfigurableManager()
|
||||||
|
test_config = {1: 2}
|
||||||
|
node_id = 1
|
||||||
|
config_manager.set_configs(test_config)
|
||||||
|
config_manager.set_configs(test_config, node_id=node_id)
|
||||||
|
|
||||||
|
# when
|
||||||
|
config_manager.config_reset()
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert not config_manager.node_configurations
|
||||||
|
|
||||||
|
def test_config_reset_node(self):
|
||||||
|
# given
|
||||||
|
config_manager = ConfigurableManager()
|
||||||
|
test_config = {1: 2}
|
||||||
|
node_id = 1
|
||||||
|
config_manager.set_configs(test_config)
|
||||||
|
config_manager.set_configs(test_config, node_id=node_id)
|
||||||
|
|
||||||
|
# when
|
||||||
|
config_manager.config_reset(node_id)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert not config_manager.get_configs(node_id=node_id)
|
||||||
|
assert config_manager.get_configs()
|
||||||
|
|
||||||
|
def test_configs_setget(self):
|
||||||
|
# given
|
||||||
|
config_manager = ConfigurableManager()
|
||||||
|
test_config = {1: 2}
|
||||||
|
node_id = 1
|
||||||
|
config_manager.set_configs(test_config)
|
||||||
|
config_manager.set_configs(test_config, node_id=node_id)
|
||||||
|
|
||||||
|
# when
|
||||||
|
default_config = config_manager.get_configs()
|
||||||
|
node_config = config_manager.get_configs(node_id)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert default_config
|
||||||
|
assert node_config
|
||||||
|
|
||||||
|
def test_config_setget(self):
|
||||||
|
# given
|
||||||
|
config_manager = ConfigurableManager()
|
||||||
|
name = "test"
|
||||||
|
value = "1"
|
||||||
|
node_id = 1
|
||||||
|
config_manager.set_config(name, value)
|
||||||
|
config_manager.set_config(name, value, node_id=node_id)
|
||||||
|
|
||||||
|
# when
|
||||||
|
defaults_value = config_manager.get_config(name)
|
||||||
|
node_value = config_manager.get_config(name, node_id=node_id)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert defaults_value == value
|
||||||
|
assert node_value == value
|
||||||
|
|
||||||
|
def test_model_setget_config(self):
|
||||||
|
# given
|
||||||
|
manager = ModelManager()
|
||||||
|
manager.models[BasicRangeModel.name] = BasicRangeModel
|
||||||
|
|
||||||
|
# when
|
||||||
|
manager.set_model_config(1, BasicRangeModel.name)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert manager.get_model_config(1, BasicRangeModel.name)
|
||||||
|
|
||||||
|
def test_model_set_config_error(self):
|
||||||
|
# given
|
||||||
|
manager = ModelManager()
|
||||||
|
manager.models[BasicRangeModel.name] = BasicRangeModel
|
||||||
|
bad_name = "bad-model"
|
||||||
|
|
||||||
|
# when/then
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
manager.set_model_config(1, bad_name)
|
||||||
|
|
||||||
|
def test_model_get_config_error(self):
|
||||||
|
# given
|
||||||
|
manager = ModelManager()
|
||||||
|
manager.models[BasicRangeModel.name] = BasicRangeModel
|
||||||
|
bad_name = "bad-model"
|
||||||
|
|
||||||
|
# when/then
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
manager.get_model_config(1, bad_name)
|
||||||
|
|
||||||
|
def test_model_set(self, session):
|
||||||
|
# given
|
||||||
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
|
|
||||||
|
# when
|
||||||
|
session.mobility.set_model(wlan_node, BasicRangeModel)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert session.mobility.get_model_config(wlan_node.objid, BasicRangeModel.name)
|
||||||
|
|
||||||
|
def test_model_set_error(self, session):
|
||||||
|
# given
|
||||||
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
|
|
||||||
|
# when / then
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
session.mobility.set_model(wlan_node, EmaneIeee80211abgModel)
|
||||||
|
|
||||||
|
def test_get_models(self, session):
|
||||||
|
# given
|
||||||
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
|
session.mobility.set_model(wlan_node, BasicRangeModel)
|
||||||
|
|
||||||
|
# when
|
||||||
|
models = session.mobility.get_models(wlan_node)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert models
|
||||||
|
assert len(models) == 1
|
|
@ -5,22 +5,19 @@ Unit tests for testing basic CORE networks.
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import threading
|
import threading
|
||||||
from xml.etree import ElementTree
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from mock import MagicMock
|
from mock import MagicMock
|
||||||
|
|
||||||
from core.data import ConfigData
|
|
||||||
from core.emulator.emudata import NodeOptions
|
from core.emulator.emudata import NodeOptions
|
||||||
from core.enumerations import MessageFlags, NodeTypes
|
from core.enumerations import MessageFlags
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
from core.mobility import BasicRangeModel
|
from core.mobility import BasicRangeModel
|
||||||
|
from core.mobility import Ns2ScriptedMobility
|
||||||
from core.netns.vnodeclient import VnodeClient
|
from core.netns.vnodeclient import VnodeClient
|
||||||
from core.service import ServiceManager
|
|
||||||
|
|
||||||
_PATH = os.path.abspath(os.path.dirname(__file__))
|
_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||||
_SERVICES_PATH = os.path.join(_PATH, "myservices")
|
|
||||||
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
|
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
|
||||||
_XML_VERSIONS = ["0.0", "1.0"]
|
|
||||||
_WIRED = [
|
_WIRED = [
|
||||||
NodeTypes.PEER_TO_PEER,
|
NodeTypes.PEER_TO_PEER,
|
||||||
NodeTypes.HUB,
|
NodeTypes.HUB,
|
||||||
|
@ -52,16 +49,6 @@ def ping(from_node, to_node, ip_prefixes):
|
||||||
|
|
||||||
|
|
||||||
class TestCore:
|
class TestCore:
|
||||||
def test_import_service(self):
|
|
||||||
"""
|
|
||||||
Test importing a custom service.
|
|
||||||
|
|
||||||
:param conftest.Core core: core fixture to test with
|
|
||||||
"""
|
|
||||||
ServiceManager.add_services(_SERVICES_PATH)
|
|
||||||
assert ServiceManager.get("MyService")
|
|
||||||
assert ServiceManager.get("MyService2")
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("net_type", _WIRED)
|
@pytest.mark.parametrize("net_type", _WIRED)
|
||||||
def test_wired_ping(self, session, net_type, ip_prefixes):
|
def test_wired_ping(self, session, net_type, ip_prefixes):
|
||||||
"""
|
"""
|
||||||
|
@ -91,61 +78,6 @@ class TestCore:
|
||||||
status = ping(node_one, node_two, ip_prefixes)
|
status = ping(node_one, node_two, ip_prefixes)
|
||||||
assert not status
|
assert not status
|
||||||
|
|
||||||
@pytest.mark.parametrize("version", _XML_VERSIONS)
|
|
||||||
def test_xml(self, session, tmpdir, version, ip_prefixes):
|
|
||||||
"""
|
|
||||||
Test xml client methods.
|
|
||||||
|
|
||||||
:param session: session for test
|
|
||||||
:param tmpdir: tmpdir to create data in
|
|
||||||
:param str version: xml version to write and parse
|
|
||||||
:param ip_prefixes: generates ip addresses for nodes
|
|
||||||
"""
|
|
||||||
|
|
||||||
# create ptp
|
|
||||||
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
|
|
||||||
|
|
||||||
# create nodes
|
|
||||||
node_one = session.add_node()
|
|
||||||
node_two = session.add_node()
|
|
||||||
|
|
||||||
# link nodes to ptp net
|
|
||||||
for node in [node_one, node_two]:
|
|
||||||
interface = ip_prefixes.create_interface(node)
|
|
||||||
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
|
|
||||||
|
|
||||||
# instantiate session
|
|
||||||
session.instantiate()
|
|
||||||
|
|
||||||
# get ids for nodes
|
|
||||||
n1_id = node_one.objid
|
|
||||||
n2_id = node_two.objid
|
|
||||||
|
|
||||||
# save xml
|
|
||||||
xml_file = tmpdir.join("session.xml")
|
|
||||||
file_path = xml_file.strpath
|
|
||||||
session.save_xml(file_path, version)
|
|
||||||
|
|
||||||
# verify xml file was created and can be parsed
|
|
||||||
assert xml_file.isfile()
|
|
||||||
assert ElementTree.parse(file_path)
|
|
||||||
|
|
||||||
# stop current session, clearing data
|
|
||||||
session.shutdown()
|
|
||||||
|
|
||||||
# verify nodes have been removed from session
|
|
||||||
with pytest.raises(KeyError):
|
|
||||||
assert not session.get_object(n1_id)
|
|
||||||
with pytest.raises(KeyError):
|
|
||||||
assert not session.get_object(n2_id)
|
|
||||||
|
|
||||||
# load saved xml
|
|
||||||
session.open_xml(file_path, start=True)
|
|
||||||
|
|
||||||
# verify nodes have been recreated
|
|
||||||
assert session.get_object(n1_id)
|
|
||||||
assert session.get_object(n2_id)
|
|
||||||
|
|
||||||
def test_vnode_client(self, session, ip_prefixes):
|
def test_vnode_client(self, session, ip_prefixes):
|
||||||
"""
|
"""
|
||||||
Test vnode client methods.
|
Test vnode client methods.
|
||||||
|
@ -256,7 +188,7 @@ class TestCore:
|
||||||
|
|
||||||
# create wlan
|
# create wlan
|
||||||
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
session.set_wireless_model(wlan_node, BasicRangeModel)
|
session.mobility.set_model(wlan_node, BasicRangeModel)
|
||||||
|
|
||||||
# create nodes
|
# create nodes
|
||||||
node_options = NodeOptions()
|
node_options = NodeOptions()
|
||||||
|
@ -289,7 +221,7 @@ class TestCore:
|
||||||
|
|
||||||
# create wlan
|
# create wlan
|
||||||
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
session.set_wireless_model(wlan_node, BasicRangeModel)
|
session.mobility.set_model(wlan_node, BasicRangeModel)
|
||||||
|
|
||||||
# create nodes
|
# create nodes
|
||||||
node_options = NodeOptions()
|
node_options = NodeOptions()
|
||||||
|
@ -306,15 +238,17 @@ class TestCore:
|
||||||
session.wireless_link_all(wlan_node, [node_one, node_two])
|
session.wireless_link_all(wlan_node, [node_one, node_two])
|
||||||
|
|
||||||
# configure mobility script for session
|
# configure mobility script for session
|
||||||
config = ConfigData(
|
config = {
|
||||||
node=wlan_node.objid,
|
"file": _MOBILITY_FILE,
|
||||||
object="ns2script",
|
"refresh_ms": "50",
|
||||||
type=0,
|
"loop": "1",
|
||||||
data_types=(10, 3, 11, 10, 10, 10, 10, 10, 0),
|
"autostart": "0.0",
|
||||||
data_values="file=%s|refresh_ms=50|loop=1|autostart=0.0|"
|
"map": "",
|
||||||
"map=|script_start=|script_pause=|script_stop=" % _MOBILITY_FILE
|
"script_start": "",
|
||||||
)
|
"script_pause": "",
|
||||||
session.config_object(config)
|
"script_stop": "",
|
||||||
|
}
|
||||||
|
session.mobility.set_model(wlan_node, Ns2ScriptedMobility, config)
|
||||||
|
|
||||||
# add handler for receiving node updates
|
# add handler for receiving node updates
|
||||||
event = threading.Event()
|
event = threading.Event()
|
||||||
|
|
|
@ -101,7 +101,7 @@ def run_cmd(node, exec_cmd):
|
||||||
|
|
||||||
|
|
||||||
class TestGui:
|
class TestGui:
|
||||||
def test_broker(self, session, cored):
|
def test_broker(self, cored):
|
||||||
"""
|
"""
|
||||||
Test session broker creation.
|
Test session broker creation.
|
||||||
|
|
||||||
|
@ -119,6 +119,7 @@ class TestGui:
|
||||||
daemon = "localhost"
|
daemon = "localhost"
|
||||||
|
|
||||||
# add server
|
# add server
|
||||||
|
session = cored.server.coreemu.create_session()
|
||||||
session.broker.addserver(daemon, "127.0.0.1", CORE_API_PORT)
|
session.broker.addserver(daemon, "127.0.0.1", CORE_API_PORT)
|
||||||
|
|
||||||
# setup server
|
# setup server
|
||||||
|
|
|
@ -39,7 +39,6 @@ class TestNodes:
|
||||||
assert node.alive()
|
assert node.alive()
|
||||||
assert node.up
|
assert node.up
|
||||||
assert node.check_cmd(["ip", "addr", "show", "lo"])
|
assert node.check_cmd(["ip", "addr", "show", "lo"])
|
||||||
node.validate()
|
|
||||||
|
|
||||||
def test_node_update(self, session):
|
def test_node_update(self, session):
|
||||||
# given
|
# given
|
||||||
|
|
291
daemon/tests/test_services.py
Normal file
291
daemon/tests/test_services.py
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.service import CoreService
|
||||||
|
from core.service import ServiceManager
|
||||||
|
|
||||||
|
_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
_SERVICES_PATH = os.path.join(_PATH, "myservices")
|
||||||
|
|
||||||
|
SERVICE_ONE = "MyService"
|
||||||
|
SERVICE_TWO = "MyService2"
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceA(CoreService):
|
||||||
|
name = "A"
|
||||||
|
dependencies = ("B",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceB(CoreService):
|
||||||
|
name = "B"
|
||||||
|
dependencies = ("C",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceC(CoreService):
|
||||||
|
name = "C"
|
||||||
|
dependencies = ()
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceD(CoreService):
|
||||||
|
name = "D"
|
||||||
|
dependencies = ("A",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceE(CoreService):
|
||||||
|
name = "E"
|
||||||
|
dependencies = ("Z",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceF(CoreService):
|
||||||
|
name = "F"
|
||||||
|
dependencies = ()
|
||||||
|
|
||||||
|
|
||||||
|
class TestServices:
|
||||||
|
def test_service_all_files(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
file_name = "myservice.sh"
|
||||||
|
node = session.add_node()
|
||||||
|
|
||||||
|
# when
|
||||||
|
session.services.set_service_file(node.objid, SERVICE_ONE, file_name, "# test")
|
||||||
|
|
||||||
|
# then
|
||||||
|
service = session.services.get_service(node.objid, SERVICE_ONE)
|
||||||
|
all_files = session.services.all_files(service)
|
||||||
|
assert service
|
||||||
|
assert all_files and len(all_files) == 1
|
||||||
|
|
||||||
|
def test_service_all_configs(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
node = session.add_node()
|
||||||
|
|
||||||
|
# when
|
||||||
|
session.services.set_service(node.objid, SERVICE_ONE)
|
||||||
|
session.services.set_service(node.objid, SERVICE_TWO)
|
||||||
|
|
||||||
|
# then
|
||||||
|
all_configs = session.services.all_configs()
|
||||||
|
assert all_configs
|
||||||
|
assert len(all_configs) == 2
|
||||||
|
|
||||||
|
def test_service_add_services(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
node = session.add_node()
|
||||||
|
total_service = len(node.services)
|
||||||
|
|
||||||
|
# when
|
||||||
|
session.services.add_services(node, node.type, [SERVICE_ONE, SERVICE_TWO])
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert node.services
|
||||||
|
assert len(node.services) == total_service + 2
|
||||||
|
|
||||||
|
def test_service_file(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_ONE)
|
||||||
|
node = session.add_node()
|
||||||
|
file_name = my_service.configs[0]
|
||||||
|
file_path = node.hostfilename(file_name)
|
||||||
|
|
||||||
|
# when
|
||||||
|
session.services.create_service_files(node, my_service)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert os.path.exists(file_path)
|
||||||
|
|
||||||
|
def test_service_validate(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_ONE)
|
||||||
|
node = session.add_node()
|
||||||
|
session.services.create_service_files(node, my_service)
|
||||||
|
|
||||||
|
# when
|
||||||
|
status = session.services.validate_service(node, my_service)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert not status
|
||||||
|
|
||||||
|
def test_service_validate_error(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_TWO)
|
||||||
|
node = session.add_node()
|
||||||
|
session.services.create_service_files(node, my_service)
|
||||||
|
|
||||||
|
# when
|
||||||
|
status = session.services.validate_service(node, my_service)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert status
|
||||||
|
|
||||||
|
def test_service_startup(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_ONE)
|
||||||
|
node = session.add_node()
|
||||||
|
session.services.create_service_files(node, my_service)
|
||||||
|
|
||||||
|
# when
|
||||||
|
status = session.services.startup_service(node, my_service, wait=True)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert not status
|
||||||
|
|
||||||
|
def test_service_startup_error(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_TWO)
|
||||||
|
node = session.add_node()
|
||||||
|
session.services.create_service_files(node, my_service)
|
||||||
|
|
||||||
|
# when
|
||||||
|
status = session.services.startup_service(node, my_service, wait=True)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert status
|
||||||
|
|
||||||
|
def test_service_stop(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_ONE)
|
||||||
|
node = session.add_node()
|
||||||
|
session.services.create_service_files(node, my_service)
|
||||||
|
|
||||||
|
# when
|
||||||
|
status = session.services.stop_service(node, my_service)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert not status
|
||||||
|
|
||||||
|
def test_service_stop_error(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_TWO)
|
||||||
|
node = session.add_node()
|
||||||
|
session.services.create_service_files(node, my_service)
|
||||||
|
|
||||||
|
# when
|
||||||
|
status = session.services.stop_service(node, my_service)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert status
|
||||||
|
|
||||||
|
def test_service_custom_startup(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_ONE)
|
||||||
|
node = session.add_node()
|
||||||
|
|
||||||
|
# when
|
||||||
|
session.services.set_service(node.objid, my_service.name)
|
||||||
|
custom_my_service = session.services.get_service(node.objid, my_service.name)
|
||||||
|
custom_my_service.startup = ("sh custom.sh",)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert my_service.startup != custom_my_service.startup
|
||||||
|
|
||||||
|
def test_service_set_file(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_ONE)
|
||||||
|
node_one = session.add_node()
|
||||||
|
node_two = session.add_node()
|
||||||
|
file_name = my_service.configs[0]
|
||||||
|
file_data_one = "# custom file one"
|
||||||
|
file_data_two = "# custom file two"
|
||||||
|
session.services.set_service_file(node_one.objid, my_service.name, file_name, file_data_one)
|
||||||
|
session.services.set_service_file(node_two.objid, my_service.name, file_name, file_data_two)
|
||||||
|
|
||||||
|
# when
|
||||||
|
custom_service_one = session.services.get_service(node_one.objid, my_service.name)
|
||||||
|
session.services.create_service_files(node_one, custom_service_one)
|
||||||
|
custom_service_two = session.services.get_service(node_two.objid, 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.
|
||||||
|
"""
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
assert ServiceManager.get(SERVICE_ONE)
|
||||||
|
assert ServiceManager.get(SERVICE_TWO)
|
||||||
|
|
||||||
|
def test_service_setget(self, session):
|
||||||
|
# given
|
||||||
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
|
my_service = ServiceManager.get(SERVICE_ONE)
|
||||||
|
node = session.add_node()
|
||||||
|
|
||||||
|
# when
|
||||||
|
no_service = session.services.get_service(node.objid, SERVICE_ONE)
|
||||||
|
default_service = session.services.get_service(node.objid, SERVICE_ONE, default_service=True)
|
||||||
|
session.services.set_service(node.objid, SERVICE_ONE)
|
||||||
|
custom_service = session.services.get_service(node.objid, SERVICE_ONE, default_service=True)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert no_service is None
|
||||||
|
assert default_service == my_service
|
||||||
|
assert custom_service and custom_service != my_service
|
||||||
|
|
||||||
|
def test_services_dependencies(self, session):
|
||||||
|
# given
|
||||||
|
services = [
|
||||||
|
ServiceA,
|
||||||
|
ServiceB,
|
||||||
|
ServiceC,
|
||||||
|
ServiceD,
|
||||||
|
ServiceF,
|
||||||
|
]
|
||||||
|
|
||||||
|
# when
|
||||||
|
startups = session.services.create_boot_paths(services)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert len(startups) == 2
|
||||||
|
|
||||||
|
def test_services_dependencies_not_present(self, session):
|
||||||
|
# given
|
||||||
|
services = [
|
||||||
|
ServiceA,
|
||||||
|
ServiceB,
|
||||||
|
ServiceC,
|
||||||
|
ServiceE
|
||||||
|
]
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
session.services.create_boot_paths(services)
|
||||||
|
|
||||||
|
def test_services_dependencies_cycle(self, session):
|
||||||
|
# given
|
||||||
|
service_c = ServiceC()
|
||||||
|
service_c.dependencies = ("D",)
|
||||||
|
services = [
|
||||||
|
ServiceA,
|
||||||
|
ServiceB,
|
||||||
|
service_c,
|
||||||
|
ServiceD,
|
||||||
|
ServiceF
|
||||||
|
]
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
session.services.create_boot_paths(services)
|
269
daemon/tests/test_xml.py
Normal file
269
daemon/tests/test_xml.py
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
|
from core.emulator.emudata import NodeOptions
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.mobility import BasicRangeModel
|
||||||
|
from core.services.utility import SshService
|
||||||
|
|
||||||
|
_XML_VERSIONS = [
|
||||||
|
"0.0",
|
||||||
|
"1.0"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestXml:
|
||||||
|
@pytest.mark.parametrize("version", _XML_VERSIONS)
|
||||||
|
def test_xml_ptp(self, session, tmpdir, version, ip_prefixes):
|
||||||
|
"""
|
||||||
|
Test xml client methods for a ptp neetwork.
|
||||||
|
|
||||||
|
:param session: session for test
|
||||||
|
:param tmpdir: tmpdir to create data in
|
||||||
|
:param str version: xml version to write and parse
|
||||||
|
:param ip_prefixes: generates ip addresses for nodes
|
||||||
|
"""
|
||||||
|
# create ptp
|
||||||
|
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
|
||||||
|
|
||||||
|
# create nodes
|
||||||
|
node_one = session.add_node()
|
||||||
|
node_two = session.add_node()
|
||||||
|
|
||||||
|
# link nodes to ptp net
|
||||||
|
for node in [node_one, node_two]:
|
||||||
|
interface = ip_prefixes.create_interface(node)
|
||||||
|
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
|
||||||
|
|
||||||
|
# instantiate session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# get ids for nodes
|
||||||
|
n1_id = node_one.objid
|
||||||
|
n2_id = node_two.objid
|
||||||
|
|
||||||
|
# save xml
|
||||||
|
xml_file = tmpdir.join("session.xml")
|
||||||
|
file_path = xml_file.strpath
|
||||||
|
session.save_xml(file_path, version)
|
||||||
|
|
||||||
|
# verify xml file was created and can be parsed
|
||||||
|
assert xml_file.isfile()
|
||||||
|
assert ElementTree.parse(file_path)
|
||||||
|
|
||||||
|
# stop current session, clearing data
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
# verify nodes have been removed from session
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n1_id)
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n2_id)
|
||||||
|
|
||||||
|
# load saved xml
|
||||||
|
session.open_xml(file_path, start=True)
|
||||||
|
|
||||||
|
# verify nodes have been recreated
|
||||||
|
assert session.get_object(n1_id)
|
||||||
|
assert session.get_object(n2_id)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", _XML_VERSIONS)
|
||||||
|
def test_xml_ptp_services(self, session, tmpdir, version, ip_prefixes):
|
||||||
|
"""
|
||||||
|
Test xml client methods for a ptp neetwork.
|
||||||
|
|
||||||
|
:param session: session for test
|
||||||
|
:param tmpdir: tmpdir to create data in
|
||||||
|
:param str version: xml version to write and parse
|
||||||
|
:param ip_prefixes: generates ip addresses for nodes
|
||||||
|
"""
|
||||||
|
# create ptp
|
||||||
|
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)
|
||||||
|
node_two = session.add_node()
|
||||||
|
|
||||||
|
# link nodes to ptp net
|
||||||
|
for node in [node_one, node_two]:
|
||||||
|
interface = ip_prefixes.create_interface(node)
|
||||||
|
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
|
||||||
|
|
||||||
|
# set custom values for node service
|
||||||
|
session.services.set_service(node_one.objid, SshService.name)
|
||||||
|
service_file = SshService.configs[0]
|
||||||
|
file_data = "# test"
|
||||||
|
session.services.set_service_file(node_one.objid, SshService.name, service_file, file_data)
|
||||||
|
|
||||||
|
# instantiate session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# get ids for nodes
|
||||||
|
n1_id = node_one.objid
|
||||||
|
n2_id = node_two.objid
|
||||||
|
|
||||||
|
# save xml
|
||||||
|
xml_file = tmpdir.join("session.xml")
|
||||||
|
file_path = xml_file.strpath
|
||||||
|
session.save_xml(file_path, version)
|
||||||
|
|
||||||
|
# verify xml file was created and can be parsed
|
||||||
|
assert xml_file.isfile()
|
||||||
|
assert ElementTree.parse(file_path)
|
||||||
|
|
||||||
|
# stop current session, clearing data
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
# verify nodes have been removed from session
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n1_id)
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n2_id)
|
||||||
|
|
||||||
|
# load saved xml
|
||||||
|
session.open_xml(file_path, start=True)
|
||||||
|
|
||||||
|
# retrieve custom service
|
||||||
|
service = session.services.get_service(node_one.objid, SshService.name)
|
||||||
|
|
||||||
|
# verify nodes have been recreated
|
||||||
|
assert session.get_object(n1_id)
|
||||||
|
assert session.get_object(n2_id)
|
||||||
|
assert service.config_data.get(service_file) == file_data
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", _XML_VERSIONS)
|
||||||
|
def test_xml_mobility(self, session, tmpdir, version, ip_prefixes):
|
||||||
|
"""
|
||||||
|
Test xml client methods for mobility.
|
||||||
|
|
||||||
|
:param session: session for test
|
||||||
|
:param tmpdir: tmpdir to create data in
|
||||||
|
:param str version: xml version to write and parse
|
||||||
|
:param ip_prefixes: generates ip addresses for nodes
|
||||||
|
"""
|
||||||
|
# create wlan
|
||||||
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# link nodes
|
||||||
|
for node in [node_one, node_two]:
|
||||||
|
interface = ip_prefixes.create_interface(node)
|
||||||
|
session.add_link(node.objid, wlan_node.objid, interface_one=interface)
|
||||||
|
|
||||||
|
# link nodes in wlan
|
||||||
|
session.wireless_link_all(wlan_node, [node_one, node_two])
|
||||||
|
|
||||||
|
# instantiate session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# get ids for nodes
|
||||||
|
wlan_id = wlan_node.objid
|
||||||
|
n1_id = node_one.objid
|
||||||
|
n2_id = node_two.objid
|
||||||
|
|
||||||
|
# save xml
|
||||||
|
xml_file = tmpdir.join("session.xml")
|
||||||
|
file_path = xml_file.strpath
|
||||||
|
session.save_xml(file_path, version)
|
||||||
|
|
||||||
|
# verify xml file was created and can be parsed
|
||||||
|
assert xml_file.isfile()
|
||||||
|
assert ElementTree.parse(file_path)
|
||||||
|
|
||||||
|
# stop current session, clearing data
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
# verify nodes have been removed from session
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n1_id)
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n2_id)
|
||||||
|
|
||||||
|
# load saved xml
|
||||||
|
session.open_xml(file_path, start=True)
|
||||||
|
|
||||||
|
# retrieve configuration we set originally
|
||||||
|
value = str(session.mobility.get_config("test", wlan_id, BasicRangeModel.name))
|
||||||
|
|
||||||
|
# verify nodes and configuration were restored
|
||||||
|
assert session.get_object(n1_id)
|
||||||
|
assert session.get_object(n2_id)
|
||||||
|
assert session.get_object(wlan_id)
|
||||||
|
assert value == "1"
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", ["1.0"])
|
||||||
|
def test_xml_emane(self, session, tmpdir, version, ip_prefixes):
|
||||||
|
"""
|
||||||
|
Test xml client methods for emane.
|
||||||
|
|
||||||
|
:param session: session for test
|
||||||
|
:param tmpdir: tmpdir to create data in
|
||||||
|
:param str version: xml version to write and parse
|
||||||
|
:param ip_prefixes: generates ip addresses for nodes
|
||||||
|
"""
|
||||||
|
# create emane node for networking the core nodes
|
||||||
|
emane_network = session.create_emane_network(
|
||||||
|
EmaneIeee80211abgModel,
|
||||||
|
geo_reference=(47.57917, -122.13232, 2.00000),
|
||||||
|
config={"test": "1"}
|
||||||
|
)
|
||||||
|
emane_network.setposition(x=80, y=50)
|
||||||
|
|
||||||
|
# create nodes
|
||||||
|
node_options = NodeOptions()
|
||||||
|
node_options.set_position(150, 150)
|
||||||
|
node_one = session.create_wireless_node(node_options=node_options)
|
||||||
|
node_options.set_position(300, 150)
|
||||||
|
node_two = session.create_wireless_node(node_options=node_options)
|
||||||
|
|
||||||
|
for i, node in enumerate([node_one, node_two]):
|
||||||
|
node.setposition(x=150 * (i + 1), y=150)
|
||||||
|
interface = ip_prefixes.create_interface(node)
|
||||||
|
session.add_link(node.objid, emane_network.objid, interface_one=interface)
|
||||||
|
|
||||||
|
# instantiate session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# get ids for nodes
|
||||||
|
emane_id = emane_network.objid
|
||||||
|
n1_id = node_one.objid
|
||||||
|
n2_id = node_two.objid
|
||||||
|
|
||||||
|
# save xml
|
||||||
|
xml_file = tmpdir.join("session.xml")
|
||||||
|
file_path = xml_file.strpath
|
||||||
|
session.save_xml(file_path, version)
|
||||||
|
|
||||||
|
# verify xml file was created and can be parsed
|
||||||
|
assert xml_file.isfile()
|
||||||
|
assert ElementTree.parse(file_path)
|
||||||
|
|
||||||
|
# stop current session, clearing data
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
# verify nodes have been removed from session
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n1_id)
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n2_id)
|
||||||
|
|
||||||
|
# load saved xml
|
||||||
|
session.open_xml(file_path, start=True)
|
||||||
|
|
||||||
|
# retrieve configuration we set originally
|
||||||
|
value = str(session.emane.get_config("test", emane_id, EmaneIeee80211abgModel.name))
|
||||||
|
|
||||||
|
# verify nodes and configuration were restored
|
||||||
|
assert session.get_object(n1_id)
|
||||||
|
assert session.get_object(n2_id)
|
||||||
|
assert session.get_object(emane_id)
|
||||||
|
assert value == "1"
|
|
@ -24,6 +24,6 @@ remove the core-daemon.log file
|
||||||
.BR vnoded(1)
|
.BR vnoded(1)
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report bugs to
|
Report bugs to
|
||||||
.BI core-dev@pf.itd.nrl.navy.mil.
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,5 +48,5 @@ enable debug logging; default = False
|
||||||
.BR vnoded(1)
|
.BR vnoded(1)
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report bugs to
|
Report bugs to
|
||||||
.BI core-dev@pf.itd.nrl.navy.mil.
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
||||||
|
|
|
@ -40,5 +40,5 @@ With no parameters, starts the GUI in edit mode with a blank canvas.
|
||||||
.BR vnoded(1)
|
.BR vnoded(1)
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report bugs to
|
Report bugs to
|
||||||
.BI core-dev@pf.itd.nrl.navy.mil.
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
||||||
|
|
28
doc/man/core-xen-cleanup.1
Normal file
28
doc/man/core-xen-cleanup.1
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
|
||||||
|
.TH CORE-XEN-CLEANUP "1" "2014-08-06" "CORE-XEN-CLEANUP" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
core-xen-cleanup \- clean-up script for CORE Xen domUs
|
||||||
|
.SH DESCRIPTION
|
||||||
|
usage: core\-xen\-cleanup [\-d]
|
||||||
|
.IP
|
||||||
|
Clean up all CORE Xen domUs, bridges, interfaces, and session
|
||||||
|
directories. Options:
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR
|
||||||
|
show this help message and exit
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR
|
||||||
|
also kill the Python daemon
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR core-gui(1),
|
||||||
|
.BR core-daemon(1),
|
||||||
|
.BR coresendmsg(1),
|
||||||
|
.BR core-cleanup(1),
|
||||||
|
.BR vcmd(1),
|
||||||
|
.BR vnoded(1)
|
||||||
|
.SH BUGS
|
||||||
|
Warning! This script will remove logical volumes that match the name "/dev/vg*/c*-n*-" on all volume groups. Use with care.
|
||||||
|
Report bugs to
|
||||||
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
||||||
|
|
|
@ -82,4 +82,4 @@ coresendmsg \-H
|
||||||
.BR vnoded(1)
|
.BR vnoded(1)
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report bugs to
|
Report bugs to
|
||||||
.BI core-dev@pf.itd.nrl.navy.mil.
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
|
@ -26,5 +26,5 @@ wait for command to complete (useful for interactive commands)
|
||||||
.BR vnoded(1)
|
.BR vnoded(1)
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report bugs to
|
Report bugs to
|
||||||
.BI core-dev@pf.itd.nrl.navy.mil.
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
||||||
|
|
|
@ -38,5 +38,5 @@ control channel name (e.g. '/tmp/pycore.45647/n3')
|
||||||
.BR vnoded(1),
|
.BR vnoded(1),
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report bugs to
|
Report bugs to
|
||||||
.BI core-dev@pf.itd.nrl.navy.mil.
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
||||||
|
|
|
@ -40,5 +40,5 @@ establish the specified <control channel> for receiving control commands
|
||||||
.BR vcmd(1),
|
.BR vcmd(1),
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report bugs to
|
Report bugs to
|
||||||
.BI core-dev@pf.itd.nrl.navy.mil.
|
.BI core-dev@nrl.navy.mil.
|
||||||
|
|
||||||
|
|
15
gui/exec.tcl
15
gui/exec.tcl
|
@ -772,14 +772,17 @@ proc manageCPUwindow {xpos ypos start} {
|
||||||
}
|
}
|
||||||
|
|
||||||
proc getMyIP { } {
|
proc getMyIP { } {
|
||||||
if { [catch {set theServer [socket -server none -myaddr \
|
variable myIP
|
||||||
[info hostname] 0]} ] } {
|
if { ![info exists myIP] } {
|
||||||
return "127.0.0.1"
|
if { [catch {set theServer [socket -server none \
|
||||||
|
-myaddr [info hostname] 0]} ] } {
|
||||||
|
set myIP "127.0.0.1"
|
||||||
|
} else {
|
||||||
|
set myIP [lindex [fconfigure $theServer -sockname] 0]
|
||||||
|
close $theServer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set myIP [lindex [fconfigure $theServer -sockname] 0]
|
|
||||||
close $theServer
|
|
||||||
return $myIP
|
return $myIP
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# display all values stored in cpu usage history for each server
|
# display all values stored in cpu usage history for each server
|
||||||
|
|
0
gui/icons/normal/OVS.gif
Normal file → Executable file
0
gui/icons/normal/OVS.gif
Normal file → Executable file
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B |
0
gui/icons/svg/OVS.svg
Normal file → Executable file
0
gui/icons/svg/OVS.svg
Normal file → Executable file
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
0
gui/icons/tiny/OVS.gif
Normal file → Executable file
0
gui/icons/tiny/OVS.gif
Normal file → Executable file
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B |
|
@ -43,46 +43,46 @@
|
||||||
# global variables
|
# global variables
|
||||||
# FUNCTION
|
# FUNCTION
|
||||||
# GUI-related global varibles
|
# GUI-related global varibles
|
||||||
#
|
#
|
||||||
# * newlink -- helps when creating a new link. If there is no
|
# * newlink -- helps when creating a new link. If there is no
|
||||||
# link currently created, this value is set to an empty string.
|
# link currently created, this value is set to an empty string.
|
||||||
# * selectbox -- the value of the box representing all the selected items
|
# * selectbox -- the value of the box representing all the selected items
|
||||||
# * selected -- containes the list of node_id's of all selected nodes.
|
# * selected -- containes the list of node_id's of all selected nodes.
|
||||||
# * newCanvas --
|
# * newCanvas --
|
||||||
#
|
#
|
||||||
# * animatephase -- starting dashoffset. With this value the effect of
|
# * animatephase -- starting dashoffset. With this value the effect of
|
||||||
# rotating line around selected itme is achived.
|
# rotating line around selected itme is achived.
|
||||||
# * undolevel -- control variable for undo.
|
# * undolevel -- control variable for undo.
|
||||||
# * redolevel -- control variable for redo.
|
# * redolevel -- control variable for redo.
|
||||||
# * undolog -- control variable for saving all the past configurations.
|
# * undolog -- control variable for saving all the past configurations.
|
||||||
# * changed -- control variable for indicating that there something changed
|
# * changed -- control variable for indicating that there something changed
|
||||||
# in active configuration.
|
# in active configuration.
|
||||||
# * badentry -- control variable indicating that there has been a bad entry
|
# * badentry -- control variable indicating that there has been a bad entry
|
||||||
# in the text box.
|
# in the text box.
|
||||||
# * cursorstate -- control variable for animating cursor.
|
# * cursorstate -- control variable for animating cursor.
|
||||||
# * clock_seconds -- control variable for animating cursor.
|
# * clock_seconds -- control variable for animating cursor.
|
||||||
# * oper_mode -- control variable reresenting operating mode, possible
|
# * oper_mode -- control variable reresenting operating mode, possible
|
||||||
# values are edit and exec.
|
# values are edit and exec.
|
||||||
# * grid -- control variable representing grid distance. All new
|
# * grid -- control variable representing grid distance. All new
|
||||||
# elements on the
|
# elements on the
|
||||||
# canvas are snaped to grid. Default value is 24.
|
# canvas are snaped to grid. Default value is 24.
|
||||||
# * sizex -- X size of the canvas.
|
# * sizex -- X size of the canvas.
|
||||||
# * sizey -- Y size of the canvas.
|
# * sizey -- Y size of the canvas.
|
||||||
# * curcanvas -- the value of the current canvas.
|
# * curcanvas -- the value of the current canvas.
|
||||||
# * autorearrange_enabled -- control variable indicating is
|
# * autorearrange_enabled -- control variable indicating is
|
||||||
# autorearrange enabled.
|
# autorearrange enabled.
|
||||||
#
|
#
|
||||||
# * defLinkColor -- defines the default link color, default link color is set
|
# * defLinkColor -- defines the default link color, default link color is set
|
||||||
# to red.
|
# to red.
|
||||||
# * defLinkWidth -- defines the width of the link, default is 2.
|
# * defLinkWidth -- defines the width of the link, default is 2.
|
||||||
# * defEthBandwidth -- defines the ethernet bandwidth, default is set to
|
# * defEthBandwidth -- defines the ethernet bandwidth, default is set to
|
||||||
# 100000000.
|
# 100000000.
|
||||||
# * defSerBandwidth -- defines the serail link bandwidth, default is 2048000.
|
# * defSerBandwidth -- defines the serail link bandwidth, default is 2048000.
|
||||||
# * defSerDelay -- defines the serail link delay, default is 2500.
|
# * defSerDelay -- defines the serail link delay, default is 2500.
|
||||||
# * showIfNames -- control variable for showing interface names, default is 1
|
# * showIfNames -- control variable for showing interface names, default is 1
|
||||||
# * showIfIPaddrs -- control variable for showing interface IPv4 addresses,
|
# * showIfIPaddrs -- control variable for showing interface IPv4 addresses,
|
||||||
# default is 1 (addresses are visible).
|
# default is 1 (addresses are visible).
|
||||||
# * showIfIPv6addrs -- control variable for showing interface IPv4
|
# * showIfIPv6addrs -- control variable for showing interface IPv4
|
||||||
# addresses, default is 1 (addresses are visible).
|
# addresses, default is 1 (addresses are visible).
|
||||||
# * showNodeLabels -- control variable for showing node labels, default is 1.
|
# * showNodeLabels -- control variable for showing node labels, default is 1.
|
||||||
# * showLinkLabels -- control variable for showing link labels, default is 1.
|
# * showLinkLabels -- control variable for showing link labels, default is 1.
|
||||||
|
@ -118,19 +118,19 @@ set resizemode false
|
||||||
set thruplotResize false
|
set thruplotResize false
|
||||||
|
|
||||||
# dictionary that maps cursor style to resize mode
|
# dictionary that maps cursor style to resize mode
|
||||||
set cursorToResizemode [dict create top_left_corner lu]
|
set cursorToResizemode [dict create top_left_corner lu]
|
||||||
dict set cursorToResizemode bottom_left_corner ld
|
dict set cursorToResizemode bottom_left_corner ld
|
||||||
dict set cursorToResizemode left_side l
|
dict set cursorToResizemode left_side l
|
||||||
dict set cursorToResizemode top_right_corner ru
|
dict set cursorToResizemode top_right_corner ru
|
||||||
dict set cursorToResizemode bottom_right_corner rd
|
dict set cursorToResizemode bottom_right_corner rd
|
||||||
dict set cursorToResizemode right_side r
|
dict set cursorToResizemode right_side r
|
||||||
dict set cursorToResizemode top_side u
|
dict set cursorToResizemode top_side u
|
||||||
dict set cursorToResizemode bottom_side d
|
dict set cursorToResizemode bottom_side d
|
||||||
|
|
||||||
# dictionary that maps thruplot to color
|
# dictionary that maps thruplot to color
|
||||||
set thruPlotColor [dict create default blue]
|
set thruPlotColor [dict create default blue]
|
||||||
set thruPlotDragStart false
|
set thruPlotDragStart false
|
||||||
set thruPlotCur null
|
set thruPlotCur null
|
||||||
|
|
||||||
set curPlotLineColor blue
|
set curPlotLineColor blue
|
||||||
set curPlotFillColor "#7f9eee"
|
set curPlotFillColor "#7f9eee"
|
||||||
|
@ -143,7 +143,7 @@ set defThruPlotMaxKBPS 10
|
||||||
#
|
#
|
||||||
# Initialize a few variables to default values
|
# Initialize a few variables to default values
|
||||||
#
|
#
|
||||||
set defLinkColor Red
|
set defLinkColor Red
|
||||||
set defFillColor Gray
|
set defFillColor Gray
|
||||||
set defLinkWidth 2
|
set defLinkWidth 2
|
||||||
set defEthBandwidth 0
|
set defEthBandwidth 0
|
||||||
|
@ -224,7 +224,7 @@ bind . <Control-n> "fileNewDialogBox"
|
||||||
bind . <Control-o> "fileOpenDialogBox"
|
bind . <Control-o> "fileOpenDialogBox"
|
||||||
|
|
||||||
.menubar.file add command -label "Reload" -underline 0 \
|
.menubar.file add command -label "Reload" -underline 0 \
|
||||||
-command { openFile $currentFile }
|
-command { openFile $currentFile }
|
||||||
|
|
||||||
.menubar.file add command -label Save -underline 0 \
|
.menubar.file add command -label Save -underline 0 \
|
||||||
-accelerator "Ctrl+S" -command { fileSaveDialogBox "" }
|
-accelerator "Ctrl+S" -command { fileSaveDialogBox "" }
|
||||||
|
@ -245,7 +245,7 @@ bind . <Control-s> "fileSaveDialogBox {}"
|
||||||
|
|
||||||
.menubar.file add separator
|
.menubar.file add separator
|
||||||
.menubar.file add command -label "Open current file in editor..." \
|
.menubar.file add command -label "Open current file in editor..." \
|
||||||
-underline 21 -command {
|
-underline 21 -command {
|
||||||
global currentFile
|
global currentFile
|
||||||
set ed [get_text_editor false]
|
set ed [get_text_editor false]
|
||||||
set t [get_term_prog false]
|
set t [get_term_prog false]
|
||||||
|
@ -428,10 +428,10 @@ menu .menubar.tools.experimental
|
||||||
toplevel .ns2im-dialog
|
toplevel .ns2im-dialog
|
||||||
wm transient .ns2im-dialog .
|
wm transient .ns2im-dialog .
|
||||||
wm title .ns2im-dialog "ns2imunes converter"
|
wm title .ns2im-dialog "ns2imunes converter"
|
||||||
|
|
||||||
set f1 [frame .ns2im-dialog.entry1]
|
set f1 [frame .ns2im-dialog.entry1]
|
||||||
set f2 [frame .ns2im-dialog.buttons]
|
set f2 [frame .ns2im-dialog.buttons]
|
||||||
|
|
||||||
label $f1.l -text "ns2 file:"
|
label $f1.l -text "ns2 file:"
|
||||||
entry $f1.e -width 25 -textvariable ns2srcfile
|
entry $f1.e -width 25 -textvariable ns2srcfile
|
||||||
button $f1.b -text "Browse" -width 8 \
|
button $f1.b -text "Browse" -width 8 \
|
||||||
|
@ -440,13 +440,13 @@ menu .menubar.tools.experimental
|
||||||
-initialfile $ns2srcfile]
|
-initialfile $ns2srcfile]
|
||||||
$f1.e delete 0 end
|
$f1.e delete 0 end
|
||||||
$f1.e insert 0 "$srcfile"
|
$f1.e insert 0 "$srcfile"
|
||||||
}
|
}
|
||||||
button $f2.b1 -text "OK" -command {
|
button $f2.b1 -text "OK" -command {
|
||||||
ns2im $srcfile
|
ns2im $srcfile
|
||||||
destroy .ns2im-dialog
|
destroy .ns2im-dialog
|
||||||
}
|
}
|
||||||
button $f2.b2 -text "Cancel" -command { destroy .ns2im-dialog}
|
button $f2.b2 -text "Cancel" -command { destroy .ns2im-dialog}
|
||||||
|
|
||||||
pack $f1.b $f1.e -side right
|
pack $f1.b $f1.e -side right
|
||||||
pack $f1.l -side right -fill x -expand true
|
pack $f1.l -side right -fill x -expand true
|
||||||
pack $f2.b1 -side left -expand true -anchor e
|
pack $f2.b1 -side left -expand true -anchor e
|
||||||
|
@ -594,10 +594,10 @@ menu .menubar.session -tearoff 1
|
||||||
# Help
|
# Help
|
||||||
#
|
#
|
||||||
menu .menubar.help -tearoff 0
|
menu .menubar.help -tearoff 0
|
||||||
.menubar.help add command -label "Online manual (www)" -command \
|
.menubar.help add command -label "CORE GitHub (www)" -command \
|
||||||
"_launchBrowser https://downloads.pf.itd.nrl.navy.mil/docs/core/core-html/"
|
"_launchBrowser https://github.com/coreemu/core"
|
||||||
.menubar.help add command -label "CORE website (www)" -command \
|
.menubar.help add command -label "CORE Documentation (www)" -command \
|
||||||
"_launchBrowser https://www.nrl.navy.mil/itd/ncs/products/core"
|
"_launchBrowser https://coreemu.github.io/core/"
|
||||||
.menubar.help add command -label "Mailing list (www)" -command \
|
.menubar.help add command -label "Mailing list (www)" -command \
|
||||||
"_launchBrowser https://publists.nrl.navy.mil/mailman/listinfo/core-users"
|
"_launchBrowser https://publists.nrl.navy.mil/mailman/listinfo/core-users"
|
||||||
.menubar.help add command -label "About" -command popupAbout
|
.menubar.help add command -label "About" -command popupAbout
|
||||||
|
@ -686,7 +686,7 @@ pack .bottom -side bottom -fill x
|
||||||
label .bottom.textbox -relief sunken -bd 1 -anchor w -width 999
|
label .bottom.textbox -relief sunken -bd 1 -anchor w -width 999
|
||||||
label .bottom.zoom -relief sunken -bd 1 -anchor w -width 10
|
label .bottom.zoom -relief sunken -bd 1 -anchor w -width 10
|
||||||
label .bottom.cpu_load -relief sunken -bd 1 -anchor w -width 9
|
label .bottom.cpu_load -relief sunken -bd 1 -anchor w -width 9
|
||||||
label .bottom.mbuf -relief sunken -bd 1 -anchor w -width 9
|
label .bottom.mbuf -relief sunken -bd 1 -anchor w -width 9
|
||||||
label .bottom.indicators -relief sunken -bd 1 -anchor w -width 5
|
label .bottom.indicators -relief sunken -bd 1 -anchor w -width 5
|
||||||
pack .bottom.indicators .bottom.mbuf .bottom.cpu_load \
|
pack .bottom.indicators .bottom.mbuf .bottom.cpu_load \
|
||||||
.bottom.zoom .bottom.textbox -side right -padx 0 -fill both
|
.bottom.zoom .bottom.textbox -side right -padx 0 -fill both
|
||||||
|
@ -808,7 +808,7 @@ set ::tk::dialog::file::showHiddenBtn 1
|
||||||
#
|
#
|
||||||
switchCanvas first
|
switchCanvas first
|
||||||
|
|
||||||
focus -force .
|
focus -force .
|
||||||
|
|
||||||
#
|
#
|
||||||
# Fire up the animation loop - used basically for selectbox
|
# Fire up the animation loop - used basically for selectbox
|
||||||
|
|
|
@ -29,7 +29,7 @@ vcmd = Extension(
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="core-netns",
|
name="core-netns",
|
||||||
version="5.1",
|
version="5.2",
|
||||||
description="Extension modules to support virtual nodes using Linux network namespaces",
|
description="Extension modules to support virtual nodes using Linux network namespaces",
|
||||||
scripts=["vcmd", "vnoded", "netns"],
|
scripts=["vcmd", "vnoded", "netns"],
|
||||||
ext_modules=[
|
ext_modules=[
|
||||||
|
|
|
@ -488,7 +488,7 @@ class Ns3Session(Session):
|
||||||
Start a tracing thread using the ASCII output from the ns3
|
Start a tracing thread using the ASCII output from the ns3
|
||||||
mobility helper.
|
mobility helper.
|
||||||
"""
|
"""
|
||||||
net.mobility = WayPointMobility(session=self, object_id=net.objid, values=None)
|
net.mobility = WayPointMobility(session=self, object_id=net.objid, config=None)
|
||||||
net.mobility.setendtime()
|
net.mobility.setendtime()
|
||||||
net.mobility.refresh_ms = 300
|
net.mobility.refresh_ms = 300
|
||||||
net.mobility.empty_queue_stop = False
|
net.mobility.empty_queue_stop = False
|
||||||
|
|
0
ns3/examples/ns3lte.py
Executable file → Normal file
0
ns3/examples/ns3lte.py
Executable file → Normal file
0
ns3/examples/ns3wifi.py
Executable file → Normal file
0
ns3/examples/ns3wifi.py
Executable file → Normal file
8
ns3/examples/ns3wifirandomwalk.py
Executable file → Normal file
8
ns3/examples/ns3wifirandomwalk.py
Executable file → Normal file
|
@ -16,13 +16,13 @@ import sys
|
||||||
|
|
||||||
import ns.core
|
import ns.core
|
||||||
import ns.network
|
import ns.network
|
||||||
|
from corens3.obj import Ns3Session
|
||||||
|
from corens3.obj import Ns3WifiNet
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.misc import ipaddress
|
from core.misc import ipaddress
|
||||||
from core.misc import nodemaps
|
from core.misc import nodemaps
|
||||||
from core.misc import nodeutils
|
from core.misc import nodeutils
|
||||||
from corens3.obj import Ns3Session
|
|
||||||
from corens3.obj import Ns3WifiNet
|
|
||||||
|
|
||||||
|
|
||||||
def add_to_server(session):
|
def add_to_server(session):
|
||||||
|
@ -60,8 +60,8 @@ def wifisession(opt):
|
||||||
node = session.addnode(name="n%d" % i)
|
node = session.addnode(name="n%d" % i)
|
||||||
node.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
node.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
session.services.addservicestonode(node, "router", services_str)
|
session.services.add_services(node, "router", services_str.split("|"))
|
||||||
session.services.bootnodeservices(node)
|
session.services.boot_services(node)
|
||||||
session.setuprandomwalkmobility(bounds=(1000.0, 750.0, 0))
|
session.setuprandomwalkmobility(bounds=(1000.0, 750.0, 0))
|
||||||
|
|
||||||
# PHY tracing
|
# PHY tracing
|
||||||
|
|
0
ns3/examples/ns3wimax.py
Executable file → Normal file
0
ns3/examples/ns3wimax.py
Executable file → Normal file
|
@ -6,7 +6,7 @@ _EXAMPLES_DIR = "share/corens3/examples"
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="core-ns3",
|
name="core-ns3",
|
||||||
version="5.1",
|
version="5.2",
|
||||||
packages=[
|
packages=[
|
||||||
"corens3",
|
"corens3",
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# branch name for the sonar analysis
|
# branch name for the sonar analysis
|
||||||
sonar.branch.name=5.1_shell_cleanup
|
sonar.branch.name=rel/5.2
|
||||||
sonar.branch.target=master
|
sonar.branch.target=master
|
||||||
|
|
||||||
# required metadata
|
# required metadata
|
||||||
sonar.projectKey=CORE
|
sonar.projectKey=CORE
|
||||||
sonar.projectName=CORE
|
sonar.projectName=CORE
|
||||||
sonar.projectVersion=5.1
|
sonar.projectVersion=5.2
|
||||||
|
|
||||||
# define modules
|
# define modules
|
||||||
sonar.modules=daemon,ns3,netns
|
sonar.modules=daemon,ns3,netns
|
||||||
|
|
Loading…
Add table
Reference in a new issue