initial commit with things working for the most part

This commit is contained in:
Blake J. Harnden 2018-06-06 14:51:45 -07:00
parent c1b6747a26
commit 2ede43e3ae
21 changed files with 1018 additions and 1397 deletions

View file

@ -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

View file

@ -2,384 +2,24 @@
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):
""" @classmethod
A generic class for managing Configurables. This class can register def str_to_dict(cls, key_values):
with a session to receive Config Messages for setting some parameters key_values = key_values.split("|")
for itself or for the Configurables that it manages. values = OrderedDict()
""" for key_value in key_values:
# name corresponds to configuration object field key, value = key_value.split("=", 1)
name = "" values[key] = value
return values
# 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 config_data(cls, flags, node_id, type_flags, configurable_options, config):
"""
Retrieve default values from configuration matrix.
:return: tuple of default values
:rtype: tuple
"""
return tuple(map(lambda x: x[2], cls.config_matrix))
@classmethod
def getnames(cls):
"""
Retrieve name values from configuration matrix.
:return: tuple of name values
:rtype: tuple
"""
return tuple(map(lambda x: x[0], cls.config_matrix))
@classmethod
def configure(cls, manager, config_data):
"""
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
@ -388,106 +28,143 @@ class Configurable(object):
:param flags: message flags :param flags: message flags
:param int node_id: node id :param int node_id: node id
:param type_flags: type flags :param 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
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=configurable_options.config_groups()
) )
@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 def __init__(self, _id, _type, label, default="", options=None):
:rtype: str self.id = _id
""" self.type = _type
if value == "1" or value == "true" or value == "on": self.default = default
return "on" if not options:
else: options = []
return "off" self.options = options
if not label:
label = _id
self.label = label
@staticmethod def __str__(self):
def offontobool(value): return "%s(id=%s, type=%s, default=%s, options=%s)" % (
""" self.__class__.__name__, self.id, self.type, self.default, self.options)
Convenience helper for converting an on/off string to a integer.
:param str value: on/off string
:return: on/off integer value class ConfigurableOptions(object):
:rtype: int # unique name to receive configuration changes
""" name = None
if type(value) == str: bitmap = None
if value.lower() == "on":
return 1
elif value.lower() == "off":
return 0
return value
@classmethod @classmethod
def valueof(cls, name, values): def configurations(cls):
""" """
Helper to return a value by the name defined in confmatrix. Returns configuration options supported by this class.
Checks if it is boolean
:param str name: name to get value of :return: list of configuration options
:param values: values to get value from :rtype: list[Configuration]
:return: value for name
""" """
i = cls.getnames().index(name) return []
if cls.config_matrix[i][1] == ConfigDataTypes.BOOL.value and values[i] != "":
return cls.booltooffon(values[i])
else:
return values[i]
@staticmethod @classmethod
def haskeyvalues(values): def config_groups(cls):
""" """
Helper to check for list of key=value pairs versus a plain old String formatted to specify configuration groupings, using list index positions.
list of values. Returns True if all elements are "key=value".
:param values: items to check for key/value pairs Example:
:return: True if all values are key/value pairs, False otherwise "Group1:start-stop|Group2:start-stop"
:rtype: bool
:return: config groups
:rtype: str
""" """
if len(values) == 0: return None
return False
for v in values:
if "=" not in v:
return False
return True
def getkeyvaluelist(self): @classmethod
def default_values(cls):
""" """
Helper to return a list of (key, value) tuples. Keys come from Retrieves default values for configurations.
configuration matrix and values are instance attributes.
:return: tuples of key value pairs :return: mapping of configuration options that can also be iterated in order of definition
:rtype: list :rtype: OrderedDict
""" """
key_values = [] return OrderedDict([(config.id, config.default) for config in cls.configurations()])
for name in self.getnames():
if hasattr(self, name):
value = getattr(self, name)
key_values.append((name, value))
return key_values class NewConfigurableManager(object):
_default_node = -1
_default_type = "default"
def __init__(self):
self._configuration_maps = {}
def nodes(self):
return [node_id for node_id in self._configuration_maps.iterkeys() if node_id != self._default_node]
def config_reset(self, node_id=None):
if not node_id:
self._configuration_maps.clear()
elif node_id in self._configuration_maps:
self._configuration_maps.pop(node_id)
def set_config(self, _id, value, node_id=_default_node, config_type=_default_type):
logger.debug("setting config for node(%s) type(%s): %s=%s", node_id, config_type, _id, value)
node_type_map = self.get_configs(node_id, config_type)
node_type_map[_id] = value
def set_configs(self, config, node_id=_default_node, config_type=_default_type):
logger.debug("setting config for node(%s) type(%s): %s", node_id, config_type, config)
node_type_map = self.get_configs(node_id, config_type)
node_type_map.clear()
node_type_map.update(config)
def get_config(self, _id, node_id=_default_node, config_type=_default_type):
logger.debug("getting config for node(%s) type(%s): %s", node_id, config_type, _id)
node_type_map = self.get_configs(node_id, config_type)
return node_type_map.get(_id)
def get_configs(self, node_id=_default_node, config_type=_default_type):
logger.debug("getting configs for node(%s) type(%s)", node_id, config_type)
node_map = self._configuration_maps.setdefault(node_id, {})
return node_map.setdefault(config_type, {})
def get_config_types(self, node_id=_default_node):
return self._configuration_maps.get(node_id, {})

View file

@ -10,15 +10,17 @@ 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.conf import ConfigShim
from core.data import ConfigData from core.data import ConfigData
from core.data import EventData from core.data import EventData
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 ConfigTlvs from core.enumerations import ConfigTlvs, ConfigFlags, ConfigDataTypes
from core.enumerations import EventTlvs from core.enumerations import EventTlvs
from core.enumerations import EventTypes from core.enumerations import EventTypes
from core.enumerations import ExceptionTlvs from core.enumerations import ExceptionTlvs
@ -35,6 +37,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.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.service import ServiceManager
class CoreHandler(SocketServer.BaseRequestHandler): class CoreHandler(SocketServer.BaseRequestHandler):
@ -407,12 +411,19 @@ 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_name in self.session.mobility.mobility_models():
# type must be in coreapi.reg_tlvs model_class = self.session.mobility.get_model_class(model_name)
tlv_data += coreapi.CoreRegisterTlv.pack(config_type, name) tlv_data += coreapi.CoreRegisterTlv.pack(model_class.config_type, model_class.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_name in self.session.emane.emane_models():
model_class = self.session.emane.get_model_class(model_name)
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)
@ -941,15 +952,399 @@ 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 [BasicRangeModel.name, Ns2ScriptedMobility.name]:
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.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.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)))
values = "|".join(repeat('0', len(ServiceManager.services)))
names = map(lambda x: x._name, ServiceManager.services)
captions = "|".join(names)
possible_values = ""
for s in ServiceManager.services:
if s._custom_needed:
possible_values += '1'
possible_values += '|'
groups = self.session.services.buildgroups(ServiceManager.services)
# 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
servicesstring = opaque.split(':')
services, unknown = self.session.services.servicesfromopaque(opaque, node.objid)
for u in unknown:
logger.warn("Request for unknown service '%s'" % u)
if not services:
return replies
if len(servicesstring) == 3:
# a file request: e.g. "service:zebra:quagga.conf"
file_data = self.session.services.getservicefile(services, node, servicesstring[2])
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
svc = services[0]
# send back:
# dirs, configs, startindex, startup, shutdown, metadata, config
type_flag = ConfigFlags.UPDATE.value
data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(svc.keys)))
values = svc.tovaluelist(node, services)
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.defaultservices[key] = values
logger.debug("default services for type %s set to %s", key, values)
elif node_id:
# store service customized config in self.customservices[]
services, unknown = self.session.services.servicesfromopaque(opaque, node_id)
for u in unknown:
logger.warn("Request for unknown service '%s'" % u)
if services:
svc = services[0]
values = ConfigShim.str_to_dict(values)
self.session.services.setcustomservice(node_id, svc, values)
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)
if object_name == "all":
typeflags = ConfigFlags.UPDATE.value
else:
typeflags = ConfigFlags.NONE.value
model_class = self.session.mobility.get_model_class(object_name)
if not model_class:
logger.warn("model class does not exist: %s", object_name)
return []
config = self.session.mobility.get_configs(node_id, object_name)
if not config:
config = model_class.default_values()
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
replies.append(config_response)
elif message_type == ConfigFlags.RESET:
if object_name == "all":
self.session.mobility.config_reset(node_id)
else:
# 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 []
model_class = self.session.mobility.get_model_class(object_name)
if not model_class:
logger.warn("model class does not exist: %s", object_name)
return []
if values_str:
config = ConfigShim.str_to_dict(values_str)
else:
config = model_class.default_values()
self.session.mobility.set_configs(config, node_id, object_name)
return replies
def handle_config_emane(self, message_type, config_data):
replies = []
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", object_name, node_id)
if message_type == ConfigFlags.REQUEST:
logger.info("replying to configure request for %s model", object_name)
if object_name == "all":
typeflags = ConfigFlags.UPDATE.value
else:
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 config_type == ConfigFlags.RESET.value:
if object_name == "all":
self.session.emane.config_reset(node_id)
else:
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)
if object_name == "all":
typeflags = ConfigFlags.UPDATE.value
else:
typeflags = ConfigFlags.NONE.value
model_class = self.session.emane.get_model_class(object_name)
if not model_class:
logger.warn("model class does not exist: %s", object_name)
return []
config = self.session.emane.get_configs(node_id, object_name)
if not config:
config = model_class.default_values()
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
replies.append(config_response)
elif message_type == ConfigFlags.RESET:
if object_name == "all":
self.session.emane.config_reset(node_id)
else:
# 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 []
model_class = self.session.emane.get_model_class(object_name)
if not model_class:
logger.warn("model class does not exist: %s", object_name)
return []
if values_str:
config = ConfigShim.str_to_dict(values_str)
else:
config = model_class.default_values()
self.session.emane.set_configs(config, node_id, object_name)
return replies
def handle_file_message(self, message): def handle_file_message(self, message):
""" """
File Message handler File Message handler

View file

@ -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:

View file

@ -1,7 +1,7 @@
""" """
EMANE Bypass model for CORE EMANE Bypass model for CORE
""" """
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 +15,20 @@ 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 "Bypass Parameters:1-1"

View file

@ -35,8 +35,13 @@ 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) @classmethod
config_matrix_override = config_shim def configurations(cls):
return cls.config_shim
@classmethod
def config_groups(cls):
return "CommEffect SHIM Parameters:1-%d" % len(cls.configurations())
def build_xml_files(self, emane_manager, interface): def build_xml_files(self, emane_manager, interface):
""" """
@ -49,8 +54,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
: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) default_values = self.default_values()
if values is None: config = emane_manager.getifcconfig(self.object_id, self.name, default_values, interface)
if not config:
return return
# retrieve xml names # retrieve xml names
@ -67,23 +73,22 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
nem_element.appendChild(shim_xml) nem_element.appendChild(shim_xml)
emane_manager.xmlwrite(nem_document, nem_name) emane_manager.xmlwrite(nem_document, nem_name)
names = self.getnames()
shim_names = list(names)
shim_names.remove("filterfile")
shim_document = emane_manager.xmldoc("shim") shim_document = emane_manager.xmldoc("shim")
shim_element = shim_document.getElementsByTagName("shim").pop() shim_element = shim_document.getElementsByTagName("shim").pop()
shim_element.setAttribute("name", "%s SHIM" % self.name) shim_element.setAttribute("name", "%s SHIM" % self.name)
shim_element.setAttribute("library", self.shim_library) 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
if name == "filterfile":
continue
value = config[name]
param = emane_manager.xmlparam(shim_document, name, value) param = emane_manager.xmlparam(shim_document, name, value)
shim_element.appendChild(param) shim_element.appendChild(param)
# 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)) shim_element.appendChild(emane_manager.xmlparam(shim_document, "filterfile", ff))
emane_manager.xmlwrite(shim_document, shim_name) emane_manager.xmlwrite(shim_document, shim_name)

View file

@ -10,7 +10,9 @@ 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.conf import ConfigShim
from core.conf import Configuration
from core.conf import NewConfigurableManager
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
@ -50,7 +52,7 @@ EMANE_MODELS = [
] ]
class EmaneManager(ConfigurableManager): class EmaneManager(NewConfigurableManager):
""" """
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
@ -70,7 +72,7 @@ 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()
@ -84,16 +86,22 @@ class EmaneManager(ConfigurableManager):
# model for global EMANE configuration options # model for global EMANE configuration options
self.emane_config = EmaneGlobalModel(session, None) self.emane_config = EmaneGlobalModel(session, None)
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._modelclsmap = {}
self.emane_config.name: self.emane_config
}
self.service = None self.service = None
self.emane_check() self.emane_check()
def emane_models(self):
return self._modelclsmap.keys()
def get_model_class(self, model_name):
return self._modelclsmap[model_name]
def emane_check(self): def emane_check(self):
""" """
Check if emane is installed and load models. Check if emane is installed and load models.
@ -138,9 +146,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)
@ -170,7 +177,6 @@ class EmaneManager(ConfigurableManager):
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._modelclsmap[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,26 +202,31 @@ class EmaneManager(ConfigurableManager):
nodes.add(netif.node) nodes.add(netif.node)
return nodes return nodes
def getmodels(self, n): def getmodels(self, node):
""" """
Used with XML export; see ConfigurableManager.getmodels() Used with XML export.
""" """
r = ConfigurableManager.getmodels(self, n) configs = self.get_config_types(node.objid)
# EMANE global params are stored with first EMANE node (if non-default models = []
# values are configured) for model_name, config in configs.iteritems():
sorted_ids = sorted(self.configs.keys()) model_class = self._modelclsmap[model_name]
if None in self.configs and len(sorted_ids) > 1 and n.objid == sorted_ids[1]: models.append((model_class, config))
v = self.configs[None] logger.debug("emane models: %s", models)
for model in v: return models
cls = self._modelclsmap[model[0]]
vals = model[1]
r.append((cls, vals))
return r
def getifcconfig(self, nodenum, conftype, defaultvalues, ifc): def getifcconfig(self, node_id, config_type, default_values, ifc):
"""
Retrieve interface configuration or node configuration if not provided.
:param int node_id: node id
:param str config_type: configuration type
:param dict default_values: default configuration values
:param ifc: node interface
:return:
"""
# use the network-wide config values or interface(NEM)-specific values? # use the network-wide config values or interface(NEM)-specific values?
if ifc is None: if ifc is None:
return self.getconfig(nodenum, conftype, defaultvalues)[1] return self.get_configs(node_id, config_type) or default_values
else: else:
# don"t use default values when interface config is the same as net # 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 # note here that using ifc.node.objid as key allows for only one type
@ -229,16 +240,19 @@ class EmaneManager(ConfigurableManager):
if ifc.netindex is not None: if ifc.netindex is not None:
key += ifc.netindex key += ifc.netindex
values = self.getconfig(key, conftype, None)[1] # try retrieve interface specific configuration
if not values: config = self.get_configs(key, config_type)
values = self.getconfig(ifc.node.objid, conftype, None)[1]
if not values and ifc.transport_type == "raw": # otherwise retrieve the interfaces node configuration
if not config:
config = self.get_configs(ifc.node.objid, config_type)
if not config and ifc.transport_type == "raw":
# with EMANE 0.9.2+, we need an extra NEM XML from # with EMANE 0.9.2+, we need an extra NEM XML from
# model.buildnemxmlfiles(), so defaults are returned here # model.buildnemxmlfiles(), so defaults are returned here
values = self.getconfig(nodenum, conftype, defaultvalues)[1] config = self.get_configs(node_id, config_type) or default_values
return values return config
def setup(self): def setup(self):
""" """
@ -264,9 +278,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 +287,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,10 +300,11 @@ 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.setnodemodels()
@ -359,7 +372,7 @@ 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.get_config_item_int("emane_platform_port", 8100)
self.transformport = self.session.get_config_item_int("emane_transform_port", 8200) self.transformport = self.session.get_config_item_int("emane_transform_port", 8200)
@ -416,20 +429,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,9 +457,10 @@ 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) msg = ConfigShim.config_data(0, None, typeflags, self.emane_config, self.get_configs())
# TODO: this needs to be converted into a sendable TLV message
server.sock.send(msg) server.sock.send(msg)
# increment nemid for next server by number of interfaces # increment nemid for next server by number of interfaces
with self._ifccountslock: with self._ifccountslock:
@ -489,8 +499,9 @@ 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) if not prefix:
prefix = session.config.get("controlnet")
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):
@ -557,24 +568,17 @@ class EmaneManager(ConfigurableManager):
for key in self._emane_nodes: for key in self._emane_nodes:
self.setnodemodel(key) self.setnodemodel(key)
def setnodemodel(self, key): def setnodemodel(self, node_id):
logger.debug("setting emane node model: %s", key) logger.debug("setting emane models for node: %s", node_id)
emane_node = self._emane_nodes[key] node_config_types = self.get_config_types(node_id)
if key not in self.configs: if not node_config_types:
logger.debug("no emane node model configuration, leaving") logger.debug("no emane node model configuration, leaving: %s", node_id)
return False return False
for t, v in self.configs[key]: emane_node = self._emane_nodes[node_id]
logger.debug("configuration: key(%s) value(%s)", t, v) for model_class, config in self.getmodels(emane_node):
if t is None: logger.debug("setting emane model(%s) config(%s)", model_class, config)
continue emane_node.setmodel(model_class, config)
if t == self.emane_config.name:
continue
# only use the first valid EmaneModel
# convert model name to class (e.g. emane_rfpipe -> EmaneRfPipe)
cls = self._modelclsmap[t]
emane_node.setmodel(cls, v)
return True return True
# no model has been configured for this EmaneNode # no model has been configured for this EmaneNode
@ -588,8 +592,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,7 +611,7 @@ 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): def newplatformxmldoc(self, otadev=None, eventdev=None):
""" """
Start a new platform XML file. Use global EMANE config values Start a new platform XML file. Use global EMANE config values
as keys. Override OTA manager and event service devices if as keys. Override OTA manager and event service devices if
@ -615,21 +619,20 @@ class EmaneManager(ConfigurableManager):
""" """
doc = self.xmldoc("platform") doc = self.xmldoc("platform")
plat = doc.getElementsByTagName("platform").pop() 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: if otadev:
i = platform_names.index("otamanagerdevice") self.set_config("otamanagerdevice", otadev)
platform_values[i] = otadev
if eventdev: if eventdev:
i = platform_names.index("eventservicedevice") self.set_config("eventservicedevice", eventdev)
platform_values[i] = eventdev
# append all platform options (except starting id) to doc # append all platform options (except starting id) to doc
for name in platform_names: for configuration in self.emane_config.emulator_config:
value = self.emane_config.valueof(name, platform_values) name = configuration.id
if name == "platform_id_start":
continue
value = self.get_config(name)
param = self.xmlparam(doc, name, value) param = self.xmlparam(doc, name, value)
plat.appendChild(param) plat.appendChild(param)
@ -639,8 +642,7 @@ class EmaneManager(ConfigurableManager):
""" """
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))
platformxmls = {} platformxmls = {}
# assume self._objslock is already held here # assume self._objslock is already held here
@ -660,7 +662,7 @@ class EmaneManager(ConfigurableManager):
eventdev = None eventdev = None
if key not in platformxmls: if key not in platformxmls:
platformxmls[key] = self.newplatformxmldoc(values, otadev, eventdev) platformxmls[key] = self.newplatformxmldoc(otadev, eventdev)
doc = platformxmls[key] doc = platformxmls[key]
plat = doc.getElementsByTagName("platform").pop() plat = doc.getElementsByTagName("platform").pop()
@ -713,13 +715,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,12 +729,12 @@ 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") doc = self.xmldoc("emaneeventmsgsvc")
es = doc.getElementsByTagName("emaneeventmsgsvc").pop() es = doc.getElementsByTagName("emaneeventmsgsvc").pop()
kvs = (("group", group), ("port", port), ("device", dev), ("mcloop", "1"), ("ttl", "32")) kvs = (("group", group), ("port", port), ("device", dev), ("mcloop", "1"), ("ttl", "32"))
@ -761,13 +761,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 +798,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)
@ -855,23 +853,6 @@ 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.
@ -900,11 +881,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 +999,7 @@ class EmaneGlobalModel(EmaneModel):
""" """
Global EMANE configuration options. Global EMANE configuration options.
""" """
_DEFAULT_DEV = "ctrl0" _DEFAULT_DEV = "ctrl0"
name = "emane" name = "emane"
@ -1033,19 +1014,26 @@ 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):
return "Platform Attributes:1-%d|NEM Parameters:%d-%d" % (
len(cls.emulator_config), len(cls.emulator_config) + 1, len(cls.configurations()))
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, emane_manager, interface):
""" """

View file

@ -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):
@ -36,14 +37,13 @@ def _get_possible(config_type, config_regex):
:rtype: str :rtype: str
""" """
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

View file

@ -34,51 +34,12 @@ def value_to_params(doc, name, value):
return xmlutils.add_param_list_to_parent(doc, parent=None, name=name, values=values) 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):
""" """
EMANE models inherit from this parent class, which takes care of EMANE models inherit from this parent class, which takes care of
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
@ -97,10 +58,20 @@ class EmaneModel(WirelessModel):
config_ignore = set() config_ignore = set()
config_groups_override = None config_groups_override = None
config_matrix_override = None configurations_override = None
@classmethod
def configurations(cls):
return cls.mac_config + cls.phy_config
@classmethod
def config_groups(cls):
mac_len = len(cls.mac_config)
config_len = len(cls.configurations())
return "MAC Parameters:1-%d|PHY Parameters:%d-%d" % (mac_len, mac_len + 1, config_len)
def __init__(self, session, object_id=None): def __init__(self, session, object_id=None):
WirelessModel.__init__(self, session, object_id) super(EmaneModel, self).__init__(session, object_id)
def build_xml_files(self, emane_manager, interface): def build_xml_files(self, emane_manager, interface):
""" """
@ -111,8 +82,8 @@ class EmaneModel(WirelessModel):
:return: nothing :return: nothing
""" """
# retrieve configuration values # retrieve configuration values
values = emane_manager.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), interface) config = emane_manager.getifcconfig(self.object_id, self.name, self.default_values(), interface)
if values is None: if not config:
return return
# create document and write to disk # create document and write to disk
@ -122,12 +93,12 @@ class EmaneModel(WirelessModel):
# create mac document and write to disk # create mac document and write to disk
mac_name = self.mac_name(interface) mac_name = self.mac_name(interface)
mac_document = self.create_mac_doc(emane_manager, values) mac_document = self.create_mac_doc(emane_manager, config)
emane_manager.xmlwrite(mac_document, mac_name) emane_manager.xmlwrite(mac_document, mac_name)
# create phy document and write to disk # create phy document and write to disk
phy_name = self.phy_name(interface) phy_name = self.phy_name(interface)
phy_document = self.create_phy_doc(emane_manager, values) phy_document = self.create_phy_doc(emane_manager, config)
emane_manager.xmlwrite(phy_document, phy_name) emane_manager.xmlwrite(phy_document, phy_name)
def create_nem_doc(self, emane_manager, interface): def create_nem_doc(self, emane_manager, interface):
@ -157,18 +128,15 @@ class EmaneModel(WirelessModel):
return nem_document return nem_document
def create_mac_doc(self, emane_manager, values): def create_mac_doc(self, emane_manager, config):
""" """
Create the mac xml document. Create the mac xml document.
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager :param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
:param tuple values: all current configuration values, mac + phy :param dict config: all current configuration values, mac + phy
:return: nem document :return: nem document
:rtype: xml.dom.minidom.Document :rtype: xml.dom.minidom.Document
""" """
names = list(self.getnames())
mac_names = names[:len(self.mac_config)]
mac_document = emane_manager.xmldoc("mac") mac_document = emane_manager.xmldoc("mac")
mac_element = mac_document.getElementsByTagName("mac").pop() mac_element = mac_document.getElementsByTagName("mac").pop()
mac_element.setAttribute("name", "%s MAC" % self.name) mac_element.setAttribute("name", "%s MAC" % self.name)
@ -177,13 +145,14 @@ class EmaneModel(WirelessModel):
raise ValueError("must define emane model library") raise ValueError("must define emane model library")
mac_element.setAttribute("library", self.mac_library) mac_element.setAttribute("library", self.mac_library)
for name in mac_names: for mac_configuration in self.mac_config:
# ignore custom configurations # ignore custom configurations
name = mac_configuration.id
if name in self.config_ignore: if name in self.config_ignore:
continue continue
# check if value is a multi param # check if value is a multi param
value = self.valueof(name, values) value = config[name]
param = value_to_params(mac_document, name, value) param = value_to_params(mac_document, name, value)
if not param: if not param:
param = emane_manager.xmlparam(mac_document, name, value) param = emane_manager.xmlparam(mac_document, name, value)
@ -192,18 +161,15 @@ class EmaneModel(WirelessModel):
return mac_document return mac_document
def create_phy_doc(self, emane_manager, values): def create_phy_doc(self, emane_manager, config):
""" """
Create the phy xml document. Create the phy xml document.
:param core.emane.emanemanager.EmaneManager emane_manager: core emane manager :param core.emane.emanemanager.EmaneManager emane_manager: core emane manager
:param tuple values: all current configuration values, mac + phy :param dict config: all current configuration values, mac + phy
:return: nem document :return: nem document
:rtype: xml.dom.minidom.Document :rtype: xml.dom.minidom.Document
""" """
names = list(self.getnames())
phy_names = names[len(self.mac_config):]
phy_document = emane_manager.xmldoc("phy") phy_document = emane_manager.xmldoc("phy")
phy_element = phy_document.getElementsByTagName("phy").pop() phy_element = phy_document.getElementsByTagName("phy").pop()
phy_element.setAttribute("name", "%s PHY" % self.name) phy_element.setAttribute("name", "%s PHY" % self.name)
@ -212,13 +178,14 @@ class EmaneModel(WirelessModel):
phy_element.setAttribute("library", self.phy_library) phy_element.setAttribute("library", self.phy_library)
# append all phy options # append all phy options
for name in phy_names: for phy_configuration in self.phy_config:
# ignore custom configurations # ignore custom configurations
name = phy_configuration.id
if name in self.config_ignore: if name in self.config_ignore:
continue continue
# check if value is a multi param # check if value is a multi param
value = self.valueof(name, values) value = config[name]
param = value_to_params(phy_document, name, value) param = value_to_params(phy_document, name, value)
if not param: if not param:
param = emane_manager.xmlparam(phy_document, name, value) param = emane_manager.xmlparam(phy_document, name, value)
@ -227,16 +194,6 @@ class EmaneModel(WirelessModel):
return phy_document 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): 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.
@ -317,7 +274,7 @@ class EmaneModel(WirelessModel):
if interface: if interface:
node_id = interface.node.objid node_id = interface.node.objid
if emane_manager.getifcconfig(node_id, self.name, None, interface) is not None: if emane_manager.getifcconfig(node_id, self.name, {}, interface):
name = interface.localname.replace(".", "_") name = interface.localname.replace(".", "_")
return "%s%s" % (name, self.name) return "%s%s" % (name, self.name)

View file

@ -174,14 +174,9 @@ class EmaneNode(EmaneNet):
trans.setAttribute("library", "trans%s" % transport_type.lower()) trans.setAttribute("library", "trans%s" % transport_type.lower())
trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0")) trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0"))
flowcontrol = False config = emane.get_configs(self.objid, self.model.name)
names = self.model.getnames() logger.debug("transport xml config: %s", config)
values = emane.getconfig(self.objid, self.model.name, self.model.getdefaultvalues())[1] flowcontrol = config.get("flowcontrolenable", "0") == "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 "virtual" in transport_type.lower():
if os.path.exists("/dev/net/tun_flowctl"): if os.path.exists("/dev/net/tun_flowctl"):
@ -193,11 +188,11 @@ class EmaneNode(EmaneNet):
emane.xmlwrite(transdoc, self.transportxmlname(transport_type.lower())) emane.xmlwrite(transdoc, self.transportxmlname(transport_type.lower()))
def transportxmlname(self, type): def transportxmlname(self, _type):
""" """
Return the string name for the Transport XML file, e.g. 'n3transvirtual.xml' Return the string name for the Transport XML file, e.g. 'n3transvirtual.xml'
""" """
return "n%strans%s.xml" % (self.objid, type) return "n%strans%s.xml" % (self.objid, _type)
def installnetifs(self, do_netns=True): def installnetifs(self, do_netns=True):
""" """

View file

@ -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,7 +30,12 @@ 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}
@ -41,10 +47,10 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
:return: nothing :return: nothing
""" """
# get configured schedule # get configured schedule
values = emane_manager.getconfig(self.object_id, self.name, self.getdefaultvalues())[1] config = emane_manager.get_configs()
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 event_device = emane_manager.event_device

View file

@ -378,7 +378,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, interface_one_id=None, interface_two_id=None, link_options=LinkOptions()): 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.
@ -480,7 +481,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

View file

@ -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

View file

@ -9,10 +9,12 @@ import threading
import time import time
from core import logger from core import logger
from core.conf import Configurable from core.conf import ConfigurableOptions
from core.conf import ConfigurableManager from core.conf import Configuration
from core.conf import NewConfigurableManager
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 +26,7 @@ from core.misc import utils
from core.misc.ipaddress import IpAddress from core.misc.ipaddress import IpAddress
class MobilityManager(ConfigurableManager): class MobilityManager(NewConfigurableManager):
""" """
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,20 +40,42 @@ 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 # configurations for basic range, indexed by WLAN node number, are stored in configurations
# stored in self.configs
# mapping from model names to their classes # mapping from model names to their classes
self._modelclsmap = { self._modelclsmap = {
BasicRangeModel.name: BasicRangeModel, BasicRangeModel.name: BasicRangeModel,
Ns2ScriptedMobility.name: Ns2ScriptedMobility 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 = {}
self.session.broker.handlers.add(self.physnodehandlelink) self.session.broker.handlers.add(self.physnodehandlelink)
def mobility_models(self):
return self._modelclsmap.keys()
def get_model_class(self, model_name):
return self._modelclsmap[model_name]
def getmodels(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
"""
configs = self.get_config_types(node.objid)
models = []
for model_name, config in configs.iteritems():
model_class = self._modelclsmap[model_name]
models.append((model_class, config))
return models
def startup(self, node_ids=None): def startup(self, node_ids=None):
""" """
Session is transitioning from instantiation to runtime state. Session is transitioning from instantiation to runtime state.
@ -61,7 +85,7 @@ 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)
@ -69,22 +93,22 @@ class MobilityManager(ConfigurableManager):
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: node_configs = self.get_config_types(node_id)
logger.warn("missing mobility configuration for node %d." % node_id) if not node_configs:
logger.warn("missing mobility configuration for node: %s", node_id)
continue continue
v = self.configs[node_id] for model_name in node_configs.iterkeys():
for model in v:
try: try:
logger.info("setting mobility model to node: %s", model) clazz = self._modelclsmap[model_name]
cls = self._modelclsmap[model[0]] model_config = self.get_config(node_id, model_name)
node.setmodel(cls, model[1]) logger.info("setting mobility model(%s) to node: %s", model_name, model_config)
node.setmodel(clazz, model_config)
except KeyError: except KeyError:
logger.warn("skipping mobility configuration for unknown model '%s'" % model[0]) logger.warn("skipping mobility configuration for unknown model: %s", model_name)
continue continue
if self.session.master: if self.session.master:
@ -99,26 +123,32 @@ class MobilityManager(ConfigurableManager):
:return: nothing :return: nothing
""" """
self.clearconfig(nodenum=None) self.config_reset()
def setconfig(self, node_id, config_type, values): def set_configs(self, config, node_id=None, config_type=None):
""" """
Normal setconfig() with check for run-time updates for WLANs. Adds a check for run-time updates for WLANs after providing normal set configs.
:param dict config: configuration value
:param int node_id: node id :param int node_id: node id
:param config_type: configuration type :param str config_type: configuration type
:param values: configuration value
:return: nothing :return: nothing
""" """
super(MobilityManager, self).setconfig(node_id, config_type, values) if not node_id or not config_type:
raise ValueError("mobility manager invalid node id or config type: %s - %s", node_id, config_type)
super(MobilityManager, self).set_configs(config, node_id, config_type)
if self.session is None: if self.session is None:
return return
if self.session.state == EventTypes.RUNTIME_STATE.value: if self.session.state == EventTypes.RUNTIME_STATE.value:
try: try:
node = self.session.get_object(node_id) node = self.session.get_object(node_id)
node.updatemodel(config_type, values) # TODO: this need to be updated
node.updatemodel(config_type, config)
except KeyError: except KeyError:
logger.exception("Skipping mobility configuration for unknown node %d.", node_id) logger.exception("skipping mobility configuration for unknown node %s", node_id)
def handleevent(self, event_data): def handleevent(self, event_data):
""" """
@ -206,13 +236,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,12 +252,12 @@ 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):
@ -247,8 +277,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 +320,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.
@ -308,9 +337,8 @@ class WirelessModel(Configurable):
:param int object_id: object id :param int object_id: object id
:param values: values :param values: 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,12 +361,12 @@ class WirelessModel(Configurable):
""" """
raise NotImplementedError raise NotImplementedError
def updateconfig(self, values): def updateconfig(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: value to update
:return: False :return: False
:rtype: bool :rtype: bool
""" """
@ -353,23 +381,20 @@ class BasicRangeModel(WirelessModel):
""" """
name = "basic_range" name = "basic_range"
# configuration parameters are @classmethod
# ( 'name', 'type', 'default', 'possible-value-list', 'caption') def configurations(cls):
config_matrix = [ return [
("range", ConfigDataTypes.UINT32.value, '275', Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"),
'', 'wireless range (pixels)'), Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT32, default="54000", label="bandwidth (bps)"),
("bandwidth", ConfigDataTypes.UINT32.value, '54000', Configuration(_id="jitter", _type=ConfigDataTypes.FLOAT, default="0.0", label="transmission jitter (usec)"),
'', 'bandwidth (bps)'), Configuration(_id="delay", _type=ConfigDataTypes.FLOAT, default="5000.0",
("jitter", ConfigDataTypes.FLOAT.value, '0.0', label="transmission delay (usec)"),
'', 'transmission jitter (usec)'), Configuration(_id="error", _type=ConfigDataTypes.FLOAT, default="0.0", label="error rate (%)")
("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 "Basic Range Parameters:1-%d" % len(cls.configurations())
def __init__(self, session, object_id, values=None): def __init__(self, session, object_id, values=None):
""" """
@ -377,57 +402,49 @@ class BasicRangeModel(WirelessModel):
: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 values: 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: if not values:
values = session.mobility.getconfig(object_id, self.name, self.getdefaultvalues())[1] values = self.default_values()
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 # TODO: can this be handled in a better spot
self.session.mobility.set_configs(values, node_id=object_id, config_type=self.name)
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): self.values_from_config(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 +452,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 +477,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 +502,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:
@ -557,18 +572,17 @@ 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 updateconfig(self, config):
""" """
Configuration has changed during runtime. Configuration has changed during runtime.
MobilityManager.setconfig() -> WlanNode.updatemodel() -> MobilityManager.setconfig() -> WlanNode.updatemodel() ->
WirelessModel.updateconfig() WirelessModel.updateconfig()
:param values: values to update configuration :param dict config: values to update configuration
:return: was update successful :return: was update successful
:rtype: bool :rtype: bool
""" """
self.valuestolinkparams(values) self.values_from_config(config)
self.range = float(self.valueof("range", values))
return True return True
def create_link_data(self, interface1, interface2, message_type): def create_link_data(self, interface1, interface2, message_type):
@ -581,7 +595,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,
@ -678,6 +691,7 @@ class WayPointMobility(WirelessModel):
:return: :return:
""" """
super(WayPointMobility, self).__init__(session=session, object_id=object_id, values=values) super(WayPointMobility, self).__init__(session=session, object_id=object_id, values=values)
self.state = self.STATE_STOPPED self.state = self.STATE_STOPPED
self.queue = [] self.queue = []
self.queue_copy = [] self.queue_copy = []
@ -705,7 +719,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 +754,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 +818,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,7 +837,7 @@ 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)
@ -845,7 +856,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)
@ -976,25 +986,22 @@ class Ns2ScriptedMobility(WayPointMobility):
""" """
name = "ns2script" name = "ns2script"
config_matrix = [ @classmethod
("file", ConfigDataTypes.STRING.value, '', def configurations(cls):
'', 'mobility script file'), return [
("refresh_ms", ConfigDataTypes.UINT32.value, '50', Configuration(_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"),
'', 'refresh time (ms)'), Configuration(_id="refresh_ms", _type=ConfigDataTypes.UINT32, default="50", label="mobility script file"),
("loop", ConfigDataTypes.BOOL.value, '1', Configuration(_id="loop", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"], label="loop"),
'On,Off', 'loop'), Configuration(_id="autostart", _type=ConfigDataTypes.STRING, label="auto-start seconds (0.0 for runtime)"),
("autostart", ConfigDataTypes.STRING.value, '', Configuration(_id="map", _type=ConfigDataTypes.STRING, label="node mapping (optional, e.g. 0:1,1:2,2:3)"),
'', 'auto-start seconds (0.0 for runtime)'), Configuration(_id="script_start", _type=ConfigDataTypes.STRING, label="script file to run upon start"),
("map", ConfigDataTypes.STRING.value, '', Configuration(_id="script_pause", _type=ConfigDataTypes.STRING, label="script file to run upon pause"),
'', 'node mapping (optional, e.g. 0:1,1:2,2:3)'), Configuration(_id="script_stop", _type=ConfigDataTypes.STRING, label="script file to run upon stop")
("script_start", ConfigDataTypes.STRING.value, '', ]
'', 'script file to run upon start'),
("script_pause", ConfigDataTypes.STRING.value, '', @classmethod
'', 'script file to run upon pause'), def config_groups(cls):
("script_stop", ConfigDataTypes.STRING.value, '', return "ns-2 Mobility Script Parameters:1-%d" % len(cls.configurations())
'', '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): def __init__(self, session, object_id, values=None):
""" """
@ -1007,32 +1014,24 @@ class Ns2ScriptedMobility(WayPointMobility):
super(Ns2ScriptedMobility, self).__init__(session=session, object_id=object_id, values=values) super(Ns2ScriptedMobility, self).__init__(session=session, object_id=object_id, values=values)
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] if not values:
self.file = self.valueof("file", values) values = self.default_values()
self.refresh_ms = int(self.valueof("refresh_ms", values)) self.session.mobility.set_configs(values, node_id=object_id, config_type=self.name)
self.loop = self.valueof("loop", values).lower() == "on"
self.autostart = self.valueof("autostart", values) self.file = values["file"]
self.parsemap(self.valueof("map", values)) self.refresh_ms = int(values["refresh_ms"])
self.script_start = self.valueof("script_start", values) self.loop = values["loop"].lower() == "on"
self.script_pause = self.valueof("script_pause", values) self.autostart = values["autostart"]
self.script_stop = self.valueof("script_stop", values) self.parsemap(values["map"])
self.script_start = values["script_start"]
self.script_pause = values["script_pause"]
self.script_stop = values["script_stop"]
logger.info("ns-2 scripted mobility configured for WLAN %d using file: %s", object_id, self.file) logger.info("ns-2 scripted mobility configured for WLAN %d using file: %s", object_id, self.file)
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
@ -1234,4 +1233,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())

View file

@ -398,12 +398,12 @@ class WlanNode(LxBrNet):
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, values=config)
def updatemodel(self, model_name, values): def updatemodel(self, model_name, config):
""" """
Allow for model updates during runtime (similar to setmodel().) Allow for model updates during runtime (similar to setmodel().)
:param model_name: model name to update :param str model_name: model name to update
:param values: values to update model with :param dict config: values to update model with
:return: nothing :return: nothing
""" """
logger.info("updating model %s" % model_name) logger.info("updating model %s" % model_name)
@ -412,14 +412,14 @@ class WlanNode(LxBrNet):
model = self.model model = self.model
if model.config_type == RegisterTlvs.WIRELESS.value: if model.config_type == RegisterTlvs.WIRELESS.value:
if not model.updateconfig(values): if not model.updateconfig(config):
return return
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
if netif.node is not None: if netif.node is not None:
(x, y, z) = netif.node.position.get() x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z) netif.poshook(netif, x, y, z)
self.model.setlinkparams() self.model.setlinkparams()

View file

@ -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)

View file

@ -7,17 +7,11 @@ a list of available services to the GUI and for configuring individual
services. services.
""" """
import time import time
from itertools import repeat
from core import CoreCommandError from core import CoreCommandError
from core import logger from core import logger
from core.conf import Configurable
from core.conf import ConfigurableManager
from core.data import ConfigData
from core.data import EventData from core.data import EventData
from core.data import FileData from core.data import FileData
from core.enumerations import ConfigDataTypes
from core.enumerations import ConfigFlags
from core.enumerations import EventTypes from core.enumerations import EventTypes
from core.enumerations import MessageFlags from core.enumerations import MessageFlags
from core.enumerations import RegisterTlvs from core.enumerations import RegisterTlvs
@ -78,7 +72,7 @@ class ServiceManager(object):
cls.add(service) cls.add(service)
class CoreServices(ConfigurableManager): class CoreServices(object):
""" """
Class for interacting with a list of available startup services for Class for interacting with a list of available startup services for
nodes. Mostly used to convert a CoreService into a Config API nodes. Mostly used to convert a CoreService into a Config API
@ -88,7 +82,6 @@ class CoreServices(ConfigurableManager):
""" """
name = "services" name = "services"
config_type = RegisterTlvs.UTILITY.value config_type = RegisterTlvs.UTILITY.value
_invalid_custom_names = ("core", "api", "emane", "misc", "netns", "phys", "services") _invalid_custom_names = ("core", "api", "emane", "misc", "netns", "phys", "services")
def __init__(self, session): def __init__(self, session):
@ -98,7 +91,7 @@ class CoreServices(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) # ConfigurableManager.__init__(self)
self.session = session self.session = session
# dict of default services tuples, key is node type # dict of default services tuples, key is node type
self.defaultservices = {} self.defaultservices = {}
@ -154,34 +147,31 @@ class CoreServices(ConfigurableManager):
return s return s
return service return service
def setcustomservice(self, object_id, service, values): def setcustomservice(self, object_id, service, config):
""" """
Store service customizations in an instantiated service object Store service customizations in an instantiated service object
using a list of values that came from a config message. using a list of values that came from a config message.
:param int object_id: object id to set custom service for :param int object_id: object id to set custom service for
:param class service: service to set :param class service: service to set
:param list values: values to :param dict config: values to
:return: :return:
""" """
logger.debug("setting custom service(%s) for node(%s): %s", object_id, service, config)
if service._custom: if service._custom:
s = service s = service
else: else:
# instantiate the class, for storing config customization # instantiate the class, for storing config customization
s = service() s = service()
# values are new key=value format; not all keys need to be present
# a missing key means go with the default # set custom service configuration
if Configurable.haskeyvalues(values): for name, value in config.iteritems():
for v in values: s.setvalue(name, value)
key, value = v.split('=', 1)
s.setvalue(key, value)
# old-style config, list of values
else:
s.fromvaluelist(values)
# assume custom service already in dict # assume custom service already in dict
if service._custom: if service._custom:
return return
# add the custom service to dict # add the custom service to dict
if object_id in self.customservices: if object_id in self.customservices:
self.customservices[object_id] += (s,) self.customservices[object_id] += (s,)
@ -436,130 +426,6 @@ class CoreServices(ConfigurableManager):
status = "-1" status = "-1"
return status return status
def configure_request(self, config_data):
"""
Receive configuration message for configuring services.
With a request flag set, a list of services has been requested.
When the opaque field is present, a specific service is being
configured or requested.
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
:return: response messages
:rtype: ConfigData
"""
node_id = config_data.node
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)))
values = "|".join(repeat('0', len(ServiceManager.services)))
names = map(lambda x: x._name, ServiceManager.services)
captions = "|".join(names)
possible_values = ""
for s in ServiceManager.services:
if s._custom_needed:
possible_values += '1'
possible_values += '|'
groups = self.buildgroups(ServiceManager.services)
# send back the properties for this service
else:
if node_id is None:
return None
node = self.session.get_object(node_id)
if node is None:
logger.warn("Request to configure service for unknown node %s", node_id)
return None
servicesstring = opaque.split(':')
services, unknown = self.servicesfromopaque(opaque, node.objid)
for u in unknown:
logger.warn("Request for unknown service '%s'" % u)
if len(services) < 1:
return None
if len(servicesstring) == 3:
# a file request: e.g. "service:zebra:quagga.conf"
file_data = self.getservicefile(services, node, servicesstring[2])
self.session.broadcast_file(file_data)
# short circuit this request early to avoid returning response below
return None
# the first service in the list is the one being configured
svc = services[0]
# send back:
# dirs, configs, startindex, startup, shutdown, metadata, config
type_flag = ConfigFlags.UPDATE.value
data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(svc.keys)))
values = svc.tovaluelist(node, services)
captions = None
possible_values = None
groups = None
return ConfigData(
message_type=0,
node=node_id,
object=self.name,
type=type_flag,
data_types=data_types,
data_values=values,
captions=captions,
possible_values=possible_values,
groups=groups,
session=session_id,
opaque=opaque
)
def configure_values(self, config_data):
"""
Receive configuration message for configuring services.
With a request flag set, a list of services has been requested.
When the opaque field is present, a specific service is being
configured or requested.
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
:return: None
"""
data_types = config_data.data_types
values = config_data.data_values
node_id = config_data.node
opaque = config_data.opaque
error_message = "services config message that I don't know how to handle"
if values is None:
logger.error(error_message)
return None
else:
values = values.split('|')
if opaque is None:
# 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.defaultservices[key] = values
logger.debug("default services for type %s set to %s", key, values)
else:
# store service customized config in self.customservices[]
if node_id is None:
return None
services, unknown = self.servicesfromopaque(opaque, node_id)
for u in unknown:
logger.warn("Request for unknown service '%s'" % u)
if len(services) < 1:
return None
svc = services[0]
self.setcustomservice(node_id, svc, values)
return None
def servicesfromopaque(self, opaque, object_id): def servicesfromopaque(self, opaque, object_id):
""" """
Build a list of services from an opaque data string. Build a list of services from an opaque data string.

View file

@ -11,6 +11,7 @@ import subprocess
import tempfile import tempfile
import threading import threading
import time import time
from itertools import repeat
import pwd import pwd
@ -18,8 +19,10 @@ 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 ConfigShim
from core.conf import ConfigurableManager from core.conf import ConfigurableOptions
from core.conf import Configuration
from core.conf import NewConfigurableManager
from core.data import ConfigData from core.data import ConfigData
from core.data import EventData from core.data import EventData
from core.data import ExceptionData from core.data import ExceptionData
@ -37,11 +40,10 @@ 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 CoreService
from core.service import CoreServices from core.service import CoreServices
from core.xml.xmlsession import save_session_xml from core.xml.xmlsession import save_session_xml
@ -81,10 +83,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 +104,30 @@ class Session(object):
self.config_handlers = [] self.config_handlers = []
self.shutdown_handlers = [] self.shutdown_handlers = []
# setup broker # initialize feature helpers
self.broker = CoreBroker(session=self) self.broker = CoreBroker(session=self)
self.add_config_object(CoreBroker.name, CoreBroker.config_type, self.broker.configure)
# setup location
self.location = CoreLocation() self.location = CoreLocation()
self.add_config_object(CoreLocation.name, CoreLocation.config_type, self.location.configure)
# setup mobiliy
self.mobility = MobilityManager(session=self) 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.services = CoreServices(session=self)
self.add_config_object(CoreServices.name, CoreServices.config_type, self.services.configure)
# setup emane
self.emane = EmaneManager(session=self) self.emane = EmaneManager(session=self)
self.add_config_object(EmaneManager.name, EmaneManager.config_type, self.emane.configure) self.options = SessionConfig()
# 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) 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 +347,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):
""" """
@ -422,7 +385,7 @@ 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.get_config_item("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) save_session_xml(self, xml_file_name, xml_file_version)
@ -597,64 +560,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.
@ -742,10 +647,8 @@ 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
@ -901,11 +804,25 @@ 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")) if not p:
p1 = getattr(self.options, "controlnet1", self.config.get("controlnet1")) p = self.config.get("controlnet")
p2 = getattr(self.options, "controlnet2", self.config.get("controlnet2"))
p3 = getattr(self.options, "controlnet3", self.config.get("controlnet3")) p0 = self.options.get_config("controlnet0")
if not p0:
p0 = self.config.get("controlnet0")
p1 = self.options.get_config("controlnet1")
if not p1:
p1 = self.config.get("controlnet1")
p2 = self.options.get_config("controlnet2")
if not p2:
p2 = self.config.get("controlnet2")
p3 = self.options.get_config("controlnet3")
if not p3:
p3 = self.config.get("controlnet3")
if not p0 and p: if not p0 and p:
p0 = p p0 = p
@ -1000,7 +917,7 @@ class Session(object):
logger.warning("controlnet updown script not configured") logger.warning("controlnet updown script not configured")
# check if session option set, overwrite if so # check if session option set, overwrite if so
options_updown_script = getattr(self.options, "controlnet_updown_script", None) options_updown_script = self.options.get_config("controlnet_updown_script")
if options_updown_script: if options_updown_script:
updown_script = options_updown_script updown_script = options_updown_script
@ -1194,6 +1111,7 @@ 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)
# TODO: move to core handlers
def send_objects(self): def send_objects(self):
""" """
Return API messages that describe the current session. Return API messages that describe the current session.
@ -1222,36 +1140,48 @@ class Session(object):
logger.info(pprint.pformat(dict(link_data._asdict()))) logger.info(pprint.pformat(dict(link_data._asdict())))
self.broadcast_link(link_data) self.broadcast_link(link_data)
# send model info # send mobility model info
configs = self.mobility.getallconfigs() for node_id in self.mobility.nodes():
configs += self.emane.getallconfigs() node = self.get_object(node_id)
logger.info("sending model configs:") for model_class, config in self.mobility.getmodels(node):
for node_number, cls, values in configs: logger.info("mobility config: node(%s) class(%s) values(%s)", node_id, model_class, config)
logger.info("config: node(%s) class(%s) values(%s)", node_number, cls, values) config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config)
config_data = cls.config_data( self.broadcast_config(config_data)
flags=0,
node_id=node_number, # send emane model info
type_flags=ConfigFlags.UPDATE.value, for node_id in self.emane.nodes():
values=values if node_id not in self.objects:
) continue
logger.info(pprint.pformat(dict(config_data._asdict())))
self.broadcast_config(config_data) node = self.get_object(node_id)
for model_class, config in self.emane.getmodels(node):
logger.info("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.broadcast_config(config_data)
# service customizations # service customizations
service_configs = self.services.getallconfigs() service_configs = self.services.getallconfigs()
for node_number, service in service_configs: for node_id, service in service_configs:
opaque = "service:%s" % service._name opaque = "service:%s" % service._name
data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(CoreService.keys)))
node = self.get_object(node_id)
values = CoreService.tovaluelist(node, node.services)
config_data = ConfigData( config_data = ConfigData(
node=node_number, message_type=0,
node=node_id,
object=self.services.name,
type=ConfigFlags.UPDATE.value,
data_types=data_types,
data_values=values,
session=self.session_id,
opaque=opaque opaque=opaque
) )
config_response = self.services.configure_request(config_data) self.broadcast_config(config_data)
self.broadcast_config(config_response)
for file_name, config_data in self.services.getallfiles(service): for file_name, config_data in self.services.getallfiles(service):
file_data = FileData( file_data = FileData(
message_type=MessageFlags.ADD.value, message_type=MessageFlags.ADD.value,
node=node_number, node=node_id,
name=str(file_name), name=str(file_name),
type=opaque, type=opaque,
data=str(config_data) data=str(config_data)
@ -1271,91 +1201,62 @@ class Session(object):
) )
self.broadcast_file(file_data) self.broadcast_file(file_data)
config_data = ConfigData() # send session configuration
session_config = self.options.get_configs()
config_data = ConfigShim.config_data(0, None, ConfigFlags.UPDATE.value, self.options, session_config)
self.broadcast_config(config_data)
# retrieve session configuration data # send session metadata
options_config = self.options.configure_request(config_data, type_flags=ConfigFlags.UPDATE.value) data_values = "|".join(["%s=%s" % item for item in self.metadata.get_configs().iteritems()])
self.broadcast_config(options_config) data_types = tuple(ConfigDataTypes.STRING.value for _ in self.metadata.get_configs())
config_data = ConfigData(
# retrieve session metadata message_type=0,
metadata_config = self.metadata.configure_request(config_data, type_flags=ConfigFlags.UPDATE.value) object=self.metadata.name,
self.broadcast_config(metadata_config) type=ConfigFlags.NONE.value,
data_types=data_types,
data_values=data_values
)
self.broadcast_config(config_data)
logger.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) logger.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data))
class SessionConfig(ConfigurableManager, Configurable): class SessionConfig(NewConfigurableManager, ConfigurableOptions):
""" """
Session configuration object. Session configuration object.
""" """
name = "session" name = "session"
config_type = RegisterTlvs.UTILITY.value config_type = RegisterTlvs.UTILITY.value
config_matrix = [
("controlnet", ConfigDataTypes.STRING.value, "", "", "Control network"),
("controlnet_updown_script", ConfigDataTypes.STRING.value, "", "", "Control network script"),
("enablerj45", ConfigDataTypes.BOOL.value, "1", "On,Off", "Enable RJ45s"),
("preservedir", ConfigDataTypes.BOOL.value, "0", "On,Off", "Preserve session dir"),
("enablesdt", ConfigDataTypes.BOOL.value, "0", "On,Off", "Enable SDT3D output"),
("sdturl", ConfigDataTypes.STRING.value, Sdt.DEFAULT_SDT_URL, "", "SDT3D URL"),
]
config_groups = "Options:1-%d" % len(config_matrix)
def __init__(self, session): @classmethod
""" def configurations(cls):
Creates a SessionConfig instance. return [
Configuration(_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network"),
Configuration(_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0"),
Configuration(_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1"),
Configuration(_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2"),
Configuration(_id="controlnet3", _type=ConfigDataTypes.STRING, label="Control Network 3"),
Configuration(_id="controlnet_updown_script", _type=ConfigDataTypes.STRING, label="Control Network Script"),
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")
]
:param core.session.Session session: session this manager is tied to @classmethod
:return: nothing def config_groups(cls):
""" return "Options:1-%d" % len(cls.configurations())
ConfigurableManager.__init__(self)
self.session = session
self.reset()
def reset(self): def __init__(self):
""" super(SessionConfig, self).__init__()
Reset the session configuration. config = self.default_values()
self.set_configs(config)
:return: nothing
"""
defaults = self.getdefaultvalues()
for key in self.getnames():
# value may come from config file
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(NewConfigurableManager):
""" """
Metadata is simply stored in a configs[] dict. Key=value pairs are Metadata is simply stored in a configs[] dict. Key=value pairs are
passed in from configure messages destined to the "metadata" object. passed in from configure messages destined to the "metadata" object.
@ -1363,92 +1264,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()

View file

@ -1,6 +1,7 @@
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
@ -369,6 +370,7 @@ class CoreDocumentParser0(object):
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
values = ConfigShim.str_to_dict(values)
self.session.services.setcustomservice(n.objid, svc, values) self.session.services.setcustomservice(n.objid, svc, values)
return True return True

View file

@ -4,6 +4,7 @@ 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
@ -615,6 +616,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,11 +631,15 @@ 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':
values = ConfigShim.str_to_dict(values)
self.session.services.setcustomservice(node.objid, session_service, values) self.session.services.setcustomservice(node.objid, session_service, values)
# 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:
@ -812,7 +818,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):
""" """

View file

@ -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):
@ -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)
@ -482,6 +478,7 @@ class NetworkElement(NamedXmlElement):
modelconfigs = network_object.session.mobility.getmodels(network_object) modelconfigs = network_object.session.mobility.getmodels(network_object)
modelconfigs += network_object.session.emane.getmodels(network_object) modelconfigs += network_object.session.emane.getmodels(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)