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.api import coreapi
from core.conf import ConfigurableManager
from core.coreobj import PyCoreNet
from core.coreobj import PyCoreNode
from core.enumerations import ConfigDataTypes
@ -81,7 +80,7 @@ class CoreDistributedServer(object):
self.sock = None
class CoreBroker(ConfigurableManager):
class CoreBroker(object):
"""
Helps with brokering messages between CORE daemon servers.
"""
@ -100,7 +99,7 @@ class CoreBroker(ConfigurableManager):
:return: nothing
"""
ConfigurableManager.__init__(self)
# ConfigurableManager.__init__(self)
self.session = session
self.session_clients = []
self.session_id_master = None
@ -611,62 +610,6 @@ class CoreBroker(ConfigurableManager):
"""
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):
"""
Handle an API message. Determine whether this needs to be handled
@ -733,6 +676,7 @@ class CoreBroker(ConfigurableManager):
if server is None:
logger.warn("ignoring unknown server: %s", servername)
return
if server.sock is None or server.host is None or server.port is None:
logger.info("ignoring disconnected server: %s", servername)
return

View file

@ -2,384 +2,24 @@
Common support for configurable CORE objects.
"""
import string
from collections import OrderedDict
from core import logger
from core.data import ConfigData
from core.enumerations import ConfigDataTypes
from core.enumerations import ConfigFlags
class ConfigurableManager(object):
"""
A generic class for managing Configurables. This class can register
with a session to receive Config Messages for setting some parameters
for itself or for the Configurables that it manages.
"""
# name corresponds to configuration object field
name = ""
# type corresponds with register message types
config_type = None
def __init__(self):
"""
Creates a ConfigurableManager instance.
"""
# configurable key=values, indexed by node number
self.configs = {}
# TODO: fix the need for this and isolate to the mobility class that wants it
self._modelclsmap = {}
def configure(self, session, config_data):
"""
Handle configure messages. The configuration message sent to a
ConfigurableManager usually is used to:
1. Request a list of Configurables (request flag)
2. Reset manager and clear configs (reset flag)
3. Send values that configure the manager or one of its Configurables
:param core.session.Session session: CORE session object
:param ConfigData config_data: configuration data for carrying out a configuration
:return: response messages
"""
if config_data.type == ConfigFlags.REQUEST.value:
return self.configure_request(config_data)
elif config_data.type == ConfigFlags.RESET.value:
return self.configure_reset(config_data)
else:
return self.configure_values(config_data)
def configure_request(self, config_data):
"""
Request configuration data.
:param ConfigData config_data: configuration data for carrying out a configuration
:return: nothing
"""
return None
def configure_reset(self, config_data):
"""
By default, resets this manager to clear configs.
:param ConfigData config_data: configuration data for carrying out a configuration
:return: reset response messages, or None
"""
return self.reset()
def configure_values(self, config_data):
"""
Values have been sent to this manager.
:param ConfigData config_data: configuration data for carrying out a configuration
:return: nothing
"""
return None
def configure_values_keyvalues(self, config_data, target, keys):
"""
Helper that can be used for configure_values for parsing in
'key=value' strings from a values field. The key name must be
in the keys list, and target.key=value is set.
:param ConfigData config_data: configuration data for carrying out a configuration
:param target: target to set attribute values on
:param keys: list of keys to verify validity
:return: nothing
"""
values = config_data.data_values
if values is None:
return None
kvs = values.split('|')
for kv in kvs:
try:
key, value = kv.split('=', 1)
if value is not None and not value.strip():
value = None
except ValueError:
# value only
key = keys[kvs.index(kv)]
value = kv
if key not in keys:
raise ValueError("invalid key: %s" % key)
if value is not None:
setattr(target, key, value)
return None
def reset(self):
"""
Reset functionality for the configurable class.
:return: nothing
"""
return None
def setconfig(self, nodenum, conftype, values):
"""
Add configuration values for a node to a dictionary; values are
usually received from a Configuration Message, and may refer to a
node for which no object exists yet
:param int nodenum: node id
:param conftype: configuration types
:param values: configuration values
:return: nothing
"""
logger.info("setting config for node(%s): %s - %s", nodenum, conftype, values)
conflist = []
if nodenum in self.configs:
oldlist = self.configs[nodenum]
found = False
for t, v in oldlist:
if t == conftype:
# replace existing config
found = True
conflist.append((conftype, values))
else:
conflist.append((t, v))
if not found:
conflist.append((conftype, values))
else:
conflist.append((conftype, values))
self.configs[nodenum] = conflist
def getconfig(self, nodenum, conftype, defaultvalues):
"""
Get configuration values for a node; if the values don't exist in
our dictionary then return the default values supplied
:param int nodenum: node id
:param conftype: configuration type
:param defaultvalues: default values
:return: configuration type and default values
:type: tuple
"""
logger.info("getting config for node(%s): %s - default(%s)",
nodenum, conftype, defaultvalues)
if nodenum in self.configs:
# return configured values
conflist = self.configs[nodenum]
for t, v in conflist:
if conftype is None or t == conftype:
return t, v
# return default values provided (may be None)
return conftype, defaultvalues
def getallconfigs(self, use_clsmap=True):
"""
Return (nodenum, conftype, values) tuples for all stored configs.
Used when reconnecting to a session.
:param bool use_clsmap: should a class map be used, default to True
:return: list of all configurations
:rtype: list
"""
r = []
for nodenum in self.configs:
for t, v in self.configs[nodenum]:
if use_clsmap:
t = self._modelclsmap[t]
r.append((nodenum, t, v))
return r
def clearconfig(self, nodenum):
"""
remove configuration values for the specified node;
when nodenum is None, remove all configuration values
:param int nodenum: node id
:return: nothing
"""
if nodenum is None:
self.configs = {}
return
if nodenum in self.configs:
self.configs.pop(nodenum)
def setconfig_keyvalues(self, nodenum, conftype, keyvalues):
"""
Key values list of tuples for a node.
:param int nodenum: node id
:param conftype: configuration type
:param keyvalues: key valyes
:return: nothing
"""
if conftype not in self._modelclsmap:
logger.warn("unknown model type '%s'", conftype)
return
model = self._modelclsmap[conftype]
keys = model.getnames()
# defaults are merged with supplied values here
values = list(model.getdefaultvalues())
for key, value in keyvalues:
if key not in keys:
logger.warn("Skipping unknown configuration key for %s: '%s'", conftype, key)
continue
i = keys.index(key)
values[i] = value
self.setconfig(nodenum, conftype, values)
def getmodels(self, n):
"""
Return a list of model classes and values for a net if one has been
configured. This is invoked when exporting a session to XML.
This assumes self.configs contains an iterable of (model-names, values)
and a self._modelclsmapdict exists.
:param n: network node to get models for
:return: list of model and values tuples for the network node
:rtype: list
"""
r = []
if n.objid in self.configs:
v = self.configs[n.objid]
for model in v:
cls = self._modelclsmap[model[0]]
vals = model[1]
r.append((cls, vals))
return r
class Configurable(object):
"""
A generic class for managing configuration parameters.
Parameters are sent via Configuration Messages, which allow the GUI
to build dynamic dialogs depending on what is being configured.
"""
name = ""
# Configuration items:
# ('name', 'type', 'default', 'possible-value-list', 'caption')
config_matrix = []
config_groups = None
bitmap = None
def __init__(self, session=None, object_id=None):
"""
Creates a Configurable instance.
:param core.session.Session session: session for this configurable
:param object_id:
"""
self.session = session
self.object_id = object_id
def reset(self):
"""
Reset method.
:return: nothing
"""
pass
def register(self):
"""
Register method.
:return: nothing
"""
pass
class ConfigShim(object):
@classmethod
def str_to_dict(cls, key_values):
key_values = key_values.split("|")
values = OrderedDict()
for key_value in key_values:
key, value = key_value.split("=", 1)
values[key] = value
return values
@classmethod
def getdefaultvalues(cls):
"""
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):
def config_data(cls, flags, node_id, type_flags, configurable_options, config):
"""
Convert this class to a Config API message. Some TLVs are defined
by the class, but node number, conf type flags, and values must
@ -388,106 +28,143 @@ class Configurable(object):
:param flags: message flags
:param int node_id: node id
: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
:rtype: ConfigData
"""
keys = cls.getnames()
keyvalues = map(lambda a, b: "%s=%s" % (a, b), keys, values)
values_str = string.join(keyvalues, '|')
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
possible_valuess = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
key_values = None
captions = None
data_types = []
possible_values = []
logger.debug("configurable: %s", configurable_options)
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(
message_type=flags,
node=node_id,
object=cls.name,
object=configurable_options.name,
type=type_flags,
data_types=datatypes,
data_values=values_str,
data_types=tuple(data_types),
data_values=key_values,
captions=captions,
possible_values=possible_valuess,
bitmap=cls.bitmap,
groups=cls.config_groups
possible_values="|".join(possible_values),
bitmap=configurable_options.bitmap,
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
:return: on or off string
:rtype: str
"""
if value == "1" or value == "true" or value == "on":
return "on"
else:
return "off"
class Configuration(object):
def __init__(self, _id, _type, label, default="", options=None):
self.id = _id
self.type = _type
self.default = default
if not options:
options = []
self.options = options
if not label:
label = _id
self.label = label
@staticmethod
def offontobool(value):
"""
Convenience helper for converting an on/off string to a integer.
def __str__(self):
return "%s(id=%s, type=%s, default=%s, options=%s)" % (
self.__class__.__name__, self.id, self.type, self.default, self.options)
:param str value: on/off string
:return: on/off integer value
:rtype: int
"""
if type(value) == str:
if value.lower() == "on":
return 1
elif value.lower() == "off":
return 0
return value
class ConfigurableOptions(object):
# unique name to receive configuration changes
name = None
bitmap = None
@classmethod
def valueof(cls, name, values):
def configurations(cls):
"""
Helper to return a value by the name defined in confmatrix.
Checks if it is boolean
Returns configuration options supported by this class.
:param str name: name to get value of
:param values: values to get value from
:return: value for name
:return: list of configuration options
:rtype: list[Configuration]
"""
i = cls.getnames().index(name)
if cls.config_matrix[i][1] == ConfigDataTypes.BOOL.value and values[i] != "":
return cls.booltooffon(values[i])
else:
return values[i]
return []
@staticmethod
def haskeyvalues(values):
@classmethod
def config_groups(cls):
"""
Helper to check for list of key=value pairs versus a plain old
list of values. Returns True if all elements are "key=value".
String formatted to specify configuration groupings, using list index positions.
:param values: items to check for key/value pairs
:return: True if all values are key/value pairs, False otherwise
:rtype: bool
Example:
"Group1:start-stop|Group2:start-stop"
:return: config groups
:rtype: str
"""
if len(values) == 0:
return False
for v in values:
if "=" not in v:
return False
return True
return None
def getkeyvaluelist(self):
@classmethod
def default_values(cls):
"""
Helper to return a list of (key, value) tuples. Keys come from
configuration matrix and values are instance attributes.
Retrieves default values for configurations.
:return: tuples of key value pairs
:rtype: list
:return: mapping of configuration options that can also be iterated in order of definition
: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 threading
import time
from itertools import repeat
from core import logger
from core.api import coreapi
from core.conf import ConfigShim
from core.data import ConfigData
from core.data import EventData
from core.emulator.emudata import InterfaceData
from core.emulator.emudata import LinkOptions
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 EventTypes
from core.enumerations import ExceptionTlvs
@ -35,6 +37,8 @@ from core.enumerations import SessionTlvs
from core.misc import nodeutils
from core.misc import structutils
from core.misc import utils
from core.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.service import ServiceManager
class CoreHandler(SocketServer.BaseRequestHandler):
@ -407,12 +411,19 @@ class CoreHandler(SocketServer.BaseRequestHandler):
tlv_data = ""
tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.EXECUTE_SERVER.value, "core-daemon")
tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.EMULATION_SERVER.value, "core-daemon")
# get config objects for session
for name in self.session.config_objects:
config_type, callback = self.session.config_objects[name]
# type must be in coreapi.reg_tlvs
tlv_data += coreapi.CoreRegisterTlv.pack(config_type, name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.broker.config_type, self.session.broker.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.location.config_type, self.session.location.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.mobility.config_type, self.session.mobility.name)
for model_name in self.session.mobility.mobility_models():
model_class = self.session.mobility.get_model_class(model_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)
@ -941,15 +952,399 @@ class CoreHandler(SocketServer.BaseRequestHandler):
opaque=message.get_tlv(ConfigTlvs.OPAQUE.value)
)
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 = self.session.config_object(config_data)
replies = []
# 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:
self.handle_broadcast_config(reply)
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):
"""
File Message handler

View file

@ -305,8 +305,8 @@ class PyCoreNode(PyCoreObj):
:return: nothing
"""
preserve = getattr(self.session.options, "preservedir", None)
if preserve == "1":
preserve = self.session.options.get_config("preservedir") == "1"
if preserve:
return
if self.tmpnodedir:

View file

@ -1,7 +1,7 @@
"""
EMANE Bypass model for CORE
"""
from core.conf import Configuration
from core.emane import emanemodel
from core.enumerations import ConfigDataTypes
@ -15,13 +15,20 @@ class EmaneBypassModel(emanemodel.EmaneModel):
# mac definitions
mac_library = "bypassmaclayer"
mac_config = [
("none", ConfigDataTypes.BOOL.value, "0", "True,False",
"There are no parameters for the bypass model."),
Configuration(
_id="none",
_type=ConfigDataTypes.BOOL,
default="0",
options=["True", "False"],
label="There are no parameters for the bypass model."
)
]
# phy definitions
phy_library = "bypassphylayer"
phy_config = []
# override gui display tabs
config_groups_override = "Bypass Parameters:1-1"
# override config groups
@classmethod
def config_groups(cls):
return "Bypass Parameters:1-1"

View file

@ -35,8 +35,13 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
shim_defaults = {}
config_shim = emanemanifest.parse(shim_xml, shim_defaults)
config_groups_override = "CommEffect SHIM Parameters:1-%d" % len(config_shim)
config_matrix_override = config_shim
@classmethod
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):
"""
@ -49,8 +54,9 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
:param interface: interface for the emane node
:return: nothing
"""
values = emane_manager.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), interface)
if values is None:
default_values = self.default_values()
config = emane_manager.getifcconfig(self.object_id, self.name, default_values, interface)
if not config:
return
# retrieve xml names
@ -67,23 +73,22 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
nem_element.appendChild(shim_xml)
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_element = shim_document.getElementsByTagName("shim").pop()
shim_element.setAttribute("name", "%s SHIM" % self.name)
shim_element.setAttribute("library", self.shim_library)
# append all shim options (except filterfile) to shimdoc
for name in shim_names:
value = self.valueof(name, values)
for configuration in self.config_shim:
name = configuration.id
if name == "filterfile":
continue
value = config[name]
param = emane_manager.xmlparam(shim_document, name, value)
shim_element.appendChild(param)
# empty filterfile is not allowed
ff = self.valueof("filterfile", values)
ff = config["filterfile"]
if ff.strip() != "":
shim_element.appendChild(emane_manager.xmlparam(shim_document, "filterfile", ff))
emane_manager.xmlwrite(shim_document, shim_name)

View file

@ -10,7 +10,9 @@ from core import CoreCommandError
from core import constants
from core import logger
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.bypass import EmaneBypassModel
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
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
:return: nothing
"""
ConfigurableManager.__init__(self)
super(EmaneManager, self).__init__()
self.session = session
self._emane_nodes = {}
self._emane_node_lock = threading.Lock()
@ -84,16 +86,22 @@ class EmaneManager(ConfigurableManager):
# model for global EMANE configuration options
self.emane_config = EmaneGlobalModel(session, None)
self.set_configs(self.emane_config.default_values())
session.broker.handlers.add(self.handledistributed)
self.service = None
self.event_device = None
self._modelclsmap = {
self.emane_config.name: self.emane_config
}
self._modelclsmap = {}
self.service = None
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):
"""
Check if emane is installed and load models.
@ -138,9 +146,8 @@ class EmaneManager(ConfigurableManager):
return
# Get the control network to be used for events
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
group, port = self.emane_config.valueof("eventservicegroup", values).split(":")
self.event_device = self.emane_config.valueof("eventservicedevice", values)
group, port = self.get_config("eventservicegroup").split(":")
self.event_device = self.get_config("eventservicedevice")
eventnetidx = self.session.get_control_net_index(self.event_device)
if eventnetidx < 0:
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:
logger.info("loading emane model: %s", emane_model.__name__)
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):
"""
@ -196,26 +202,31 @@ class EmaneManager(ConfigurableManager):
nodes.add(netif.node)
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)
# EMANE global params are stored with first EMANE node (if non-default
# values are configured)
sorted_ids = sorted(self.configs.keys())
if None in self.configs and len(sorted_ids) > 1 and n.objid == sorted_ids[1]:
v = self.configs[None]
for model in v:
cls = self._modelclsmap[model[0]]
vals = model[1]
r.append((cls, vals))
return r
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))
logger.debug("emane models: %s", models)
return models
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?
if ifc is None:
return self.getconfig(nodenum, conftype, defaultvalues)[1]
return self.get_configs(node_id, config_type) or default_values
else:
# don"t use default values when interface config is the same as net
# note here that using ifc.node.objid as key allows for only one type
@ -229,16 +240,19 @@ class EmaneManager(ConfigurableManager):
if ifc.netindex is not None:
key += ifc.netindex
values = self.getconfig(key, conftype, None)[1]
if not values:
values = self.getconfig(ifc.node.objid, conftype, None)[1]
# try retrieve interface specific configuration
config = self.get_configs(key, config_type)
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
# 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):
"""
@ -264,9 +278,7 @@ class EmaneManager(ConfigurableManager):
# - needs to be configured before checkdistributed() for distributed
# - needs to exist when eventservice binds to it (initeventservice)
if self.session.master:
values = self.getconfig(None, self.emane_config.name, self.emane_config.getdefaultvalues())[1]
logger.debug("emane config default values: %s", values)
otadev = self.emane_config.valueof("otamanagerdevice", values)
otadev = self.get_config("otamanagerdevice")
netidx = self.session.get_control_net_index(otadev)
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
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)
self.distributedctrlnet(ctrlnet)
eventdev = self.emane_config.valueof("eventservicedevice", values)
eventdev = self.get_config("eventservicedevice")
logger.debug("emane event service device: eventdev(%s)", eventdev)
if eventdev != otadev:
netidx = self.session.get_control_net_index(eventdev)
@ -288,10 +300,11 @@ class EmaneManager(ConfigurableManager):
self.distributedctrlnet(ctrlnet)
if self.checkdistributed():
# we are slave, but haven"t received a platformid yet
cfgval = self.getconfig(None, self.emane_config.name, self.emane_config.getdefaultvalues())[1]
i = self.emane_config.getnames().index("platform_id_start")
if cfgval[i] == self.emane_config.getdefaultvalues()[i]:
# we are slave, but haven't received a platformid yet
platform_id_start = "platform_id_start"
default_values = self.emane_config.default_values()
value = self.get_config(platform_id_start)
if value == default_values[platform_id_start]:
return EmaneManager.NOT_READY
self.setnodemodels()
@ -359,7 +372,7 @@ class EmaneManager(ConfigurableManager):
with self._emane_node_lock:
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.transformport = self.session.get_config_item_int("emane_transform_port", 8200)
@ -416,20 +429,16 @@ class EmaneManager(ConfigurableManager):
if not master:
return True
cfgval = self.getconfig(None, self.emane_config.name, self.emane_config.getdefaultvalues())[1]
values = list(cfgval)
nemcount = 0
with self._emane_node_lock:
for key in self._emane_nodes:
emane_node = self._emane_nodes[key]
nemcount += emane_node.numnetif()
nemid = int(self.emane_config.valueof("nem_id_start", values))
nemid = int(self.get_config("nem_id_start"))
nemid += nemcount
platformid = int(self.emane_config.valueof("platform_id_start", values))
names = list(self.emane_config.getnames())
platformid = int(self.get_config("platform_id_start"))
# build an ordered list of servers so platform ID is deterministic
servers = []
@ -448,9 +457,10 @@ class EmaneManager(ConfigurableManager):
platformid += 1
typeflags = ConfigFlags.UPDATE.value
values[names.index("platform_id_start")] = str(platformid)
values[names.index("nem_id_start")] = str(nemid)
msg = EmaneGlobalModel.config_data(flags=0, node_id=None, type_flags=typeflags, values=values)
self.set_config("platform_id_start", str(platformid))
self.set_config("nem_id_start", str(nemid))
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)
# increment nemid for next server by number of interfaces
with self._ifccountslock:
@ -489,8 +499,9 @@ class EmaneManager(ConfigurableManager):
if len(servers) < 2:
return
prefix = session.config.get("controlnet")
prefix = getattr(session.options, "controlnet", prefix)
prefix = session.options.get_config("controlnet")
if not prefix:
prefix = session.config.get("controlnet")
prefixes = prefix.split()
# normal Config messaging will distribute controlnets
if len(prefixes) >= len(servers):
@ -557,24 +568,17 @@ class EmaneManager(ConfigurableManager):
for key in self._emane_nodes:
self.setnodemodel(key)
def setnodemodel(self, key):
logger.debug("setting emane node model: %s", key)
emane_node = self._emane_nodes[key]
if key not in self.configs:
logger.debug("no emane node model configuration, leaving")
def setnodemodel(self, node_id):
logger.debug("setting emane models for node: %s", node_id)
node_config_types = self.get_config_types(node_id)
if not node_config_types:
logger.debug("no emane node model configuration, leaving: %s", node_id)
return False
for t, v in self.configs[key]:
logger.debug("configuration: key(%s) value(%s)", t, v)
if t is None:
continue
if t == self.emane_config.name:
continue
# 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)
emane_node = self._emane_nodes[node_id]
for model_class, config in self.getmodels(emane_node):
logger.debug("setting emane model(%s) config(%s)", model_class, config)
emane_node.setmodel(model_class, config)
return True
# no model has been configured for this EmaneNode
@ -588,8 +592,8 @@ class EmaneManager(ConfigurableManager):
emane_node = None
netif = None
for key in self._emane_nodes:
emane_node = self._emane_nodes[key]
for node_id in self._emane_nodes:
emane_node = self._emane_nodes[node_id]
netif = emane_node.getnemnetif(nemid)
if netif is not None:
break
@ -607,7 +611,7 @@ class EmaneManager(ConfigurableManager):
count += len(emane_node.netifs())
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
as keys. Override OTA manager and event service devices if
@ -615,21 +619,20 @@ class EmaneManager(ConfigurableManager):
"""
doc = self.xmldoc("platform")
plat = doc.getElementsByTagName("platform").pop()
names = list(self.emane_config.getnames())
platform_names = names[:len(self.emane_config.emulator_config)]
platform_names.remove("platform_id_start")
platform_values = list(values)
if otadev:
i = platform_names.index("otamanagerdevice")
platform_values[i] = otadev
self.set_config("otamanagerdevice", otadev)
if eventdev:
i = platform_names.index("eventservicedevice")
platform_values[i] = eventdev
self.set_config("eventservicedevice", eventdev)
# append all platform options (except starting id) to doc
for name in platform_names:
value = self.emane_config.valueof(name, platform_values)
for configuration in self.emane_config.emulator_config:
name = configuration.id
if name == "platform_id_start":
continue
value = self.get_config(name)
param = self.xmlparam(doc, name, value)
plat.appendChild(param)
@ -639,8 +642,7 @@ class EmaneManager(ConfigurableManager):
"""
Build a platform.xml file now that all nodes are configured.
"""
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
nemid = int(self.emane_config.valueof("nem_id_start", values))
nemid = int(self.get_config("nem_id_start"))
platformxmls = {}
# assume self._objslock is already held here
@ -660,7 +662,7 @@ class EmaneManager(ConfigurableManager):
eventdev = None
if key not in platformxmls:
platformxmls[key] = self.newplatformxmldoc(values, otadev, eventdev)
platformxmls[key] = self.newplatformxmldoc(otadev, eventdev)
doc = platformxmls[key]
plat = doc.getElementsByTagName("platform").pop()
@ -713,13 +715,11 @@ class EmaneManager(ConfigurableManager):
Build the libemaneeventservice.xml file if event service options
were changed in the global config.
"""
defaults = self.emane_config.getdefaultvalues()
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
need_xml = False
keys = ("eventservicegroup", "eventservicedevice")
for k in keys:
a = self.emane_config.valueof(k, defaults)
b = self.emane_config.valueof(k, values)
default_values = self.emane_config.default_values()
for name in ["eventservicegroup", "eventservicedevice"]:
a = default_values[name]
b = self.get_config(name)
if a != b:
need_xml = True
@ -729,12 +729,12 @@ class EmaneManager(ConfigurableManager):
return
try:
group, port = self.emane_config.valueof("eventservicegroup", values).split(":")
group, port = self.get_config("eventservicegroup").split(":")
except ValueError:
logger.exception("invalid eventservicegroup in EMANE config")
return
dev = self.emane_config.valueof("eventservicedevice", values)
dev = self.get_config("eventservicedevice")
doc = self.xmldoc("emaneeventmsgsvc")
es = doc.getElementsByTagName("emaneeventmsgsvc").pop()
kvs = (("group", group), ("port", port), ("device", dev), ("mcloop", "1"), ("ttl", "32"))
@ -761,13 +761,12 @@ class EmaneManager(ConfigurableManager):
if realtime:
emanecmd += "-r",
values = self.getconfig(None, "emane", self.emane_config.getdefaultvalues())[1]
otagroup, otaport = self.emane_config.valueof("otamanagergroup", values).split(":")
otadev = self.emane_config.valueof("otamanagerdevice", values)
otagroup, otaport = self.get_config("otamanagergroup").split(":")
otadev = self.get_config("otamanagerdevice")
otanetidx = self.session.get_control_net_index(otadev)
eventgroup, eventport = self.emane_config.valueof("eventservicegroup", values).split(":")
eventdev = self.emane_config.valueof("eventservicedevice", values)
eventgroup, eventport = self.get_config("eventservicegroup").split(":")
eventdev = self.get_config("eventservicedevice")
eventservicenetidx = self.session.get_control_net_index(eventdev)
run_emane_on_host = False
@ -799,8 +798,7 @@ class EmaneManager(ConfigurableManager):
node.check_cmd(args)
# start emane
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n),
os.path.join(path, "platform%d.xml" % n)]
args = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n), os.path.join(path, "platform%d.xml" % n)]
output = node.check_cmd(args)
logger.info("node(%s) emane daemon running: %s", node.name, args)
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.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):
"""
Returns boolean whether or not EMANE events will be monitored.
@ -900,11 +881,10 @@ class EmaneManager(ConfigurableManager):
return
if self.service is None:
errmsg = "Warning: EMANE events will not be generated " \
"because the emaneeventservice\n binding was " \
"unable to load " \
"(install the python-emaneeventservice bindings)"
logger.error(errmsg)
logger.error("Warning: EMANE events will not be generated "
"because the emaneeventservice\n binding was "
"unable to load "
"(install the python-emaneeventservice bindings)")
return
self.doeventloop = True
self.eventmonthread = threading.Thread(target=self.eventmonitorloop)
@ -1019,6 +999,7 @@ class EmaneGlobalModel(EmaneModel):
"""
Global EMANE configuration options.
"""
_DEFAULT_DEV = "ctrl0"
name = "emane"
@ -1033,19 +1014,26 @@ class EmaneGlobalModel(EmaneModel):
emulator_config = emanemanifest.parse(emulator_xml, emulator_defaults)
emulator_config.insert(
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_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
config_groups_override = "Platform Attributes:1-%d|NEM Parameters:%d-%d" % (
len(emulator_config), len(emulator_config) + 1, len(config_matrix_override))
@classmethod
def configurations(cls):
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):
EmaneModel.__init__(self, session, object_id)
super(EmaneGlobalModel, self).__init__(session, object_id)
def build_xml_files(self, emane_manager, interface):
"""

View file

@ -1,4 +1,5 @@
from core import logger
from core.conf import Configuration
from core.enumerations import ConfigDataTypes
manifest = None
@ -23,7 +24,7 @@ def _type_value(config_type):
config_type = "FLOAT"
elif config_type == "INETADDR":
config_type = "STRING"
return ConfigDataTypes[config_type].value
return ConfigDataTypes[config_type]
def _get_possible(config_type, config_regex):
@ -36,14 +37,13 @@ def _get_possible(config_type, config_regex):
:rtype: str
"""
if config_type == "bool":
return "On,Off"
return ["On", "Off"]
if config_type == "string" and config_regex:
possible = config_regex[2:-2]
possible = possible.replace("|", ",")
return possible
return possible.split("|")
return ""
return []
def _get_default(config_type_name, config_value):
@ -116,7 +116,13 @@ def parse(manifest_path, defaults):
if config_name.endswith("uri"):
config_descriptions = "%s file" % config_descriptions
config_tuple = (config_name, config_type_value, config_default, possible, config_descriptions)
configurations.append(config_tuple)
configuration = Configuration(
_id=config_name,
_type=config_type_value,
default=config_default,
options=possible,
label=config_descriptions
)
configurations.append(configuration)
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)
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):
"""
EMANE models inherit from this parent class, which takes care of
handling configuration messages based on the list of
configurable parameters. Helper functions also live here.
"""
__metaclass__ = EmaneModelMetaClass
# default mac configuration settings
mac_library = None
mac_xml = None
@ -97,10 +58,20 @@ class EmaneModel(WirelessModel):
config_ignore = set()
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):
WirelessModel.__init__(self, session, object_id)
super(EmaneModel, self).__init__(session, object_id)
def build_xml_files(self, emane_manager, interface):
"""
@ -111,8 +82,8 @@ class EmaneModel(WirelessModel):
:return: nothing
"""
# retrieve configuration values
values = emane_manager.getifcconfig(self.object_id, self.name, self.getdefaultvalues(), interface)
if values is None:
config = emane_manager.getifcconfig(self.object_id, self.name, self.default_values(), interface)
if not config:
return
# create document and write to disk
@ -122,12 +93,12 @@ class EmaneModel(WirelessModel):
# create mac document and write to disk
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)
# create phy document and write to disk
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)
def create_nem_doc(self, emane_manager, interface):
@ -157,18 +128,15 @@ class EmaneModel(WirelessModel):
return nem_document
def create_mac_doc(self, emane_manager, values):
def create_mac_doc(self, emane_manager, config):
"""
Create the mac xml document.
: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
:rtype: xml.dom.minidom.Document
"""
names = list(self.getnames())
mac_names = names[:len(self.mac_config)]
mac_document = emane_manager.xmldoc("mac")
mac_element = mac_document.getElementsByTagName("mac").pop()
mac_element.setAttribute("name", "%s MAC" % self.name)
@ -177,13 +145,14 @@ class EmaneModel(WirelessModel):
raise ValueError("must define emane model library")
mac_element.setAttribute("library", self.mac_library)
for name in mac_names:
for mac_configuration in self.mac_config:
# ignore custom configurations
name = mac_configuration.id
if name in self.config_ignore:
continue
# check if value is a multi param
value = self.valueof(name, values)
value = config[name]
param = value_to_params(mac_document, name, value)
if not param:
param = emane_manager.xmlparam(mac_document, name, value)
@ -192,18 +161,15 @@ class EmaneModel(WirelessModel):
return mac_document
def create_phy_doc(self, emane_manager, values):
def create_phy_doc(self, emane_manager, config):
"""
Create the phy xml document.
: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
:rtype: xml.dom.minidom.Document
"""
names = list(self.getnames())
phy_names = names[len(self.mac_config):]
phy_document = emane_manager.xmldoc("phy")
phy_element = phy_document.getElementsByTagName("phy").pop()
phy_element.setAttribute("name", "%s PHY" % self.name)
@ -212,13 +178,14 @@ class EmaneModel(WirelessModel):
phy_element.setAttribute("library", self.phy_library)
# append all phy options
for name in phy_names:
for phy_configuration in self.phy_config:
# ignore custom configurations
name = phy_configuration.id
if name in self.config_ignore:
continue
# check if value is a multi param
value = self.valueof(name, values)
value = config[name]
param = value_to_params(phy_document, name, value)
if not param:
param = emane_manager.xmlparam(phy_document, name, value)
@ -227,16 +194,6 @@ class EmaneModel(WirelessModel):
return phy_document
@classmethod
def configure_emane(cls, session, config_data):
"""
Handle configuration messages for configuring an emane model.
:param core.session.Session session: session to configure emane
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
"""
return cls.configure(session.emane, config_data)
def post_startup(self, emane_manager):
"""
Logic to execute after the emane manager is finished with startup.
@ -317,7 +274,7 @@ class EmaneModel(WirelessModel):
if interface:
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(".", "_")
return "%s%s" % (name, self.name)

View file

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

View file

@ -6,6 +6,7 @@ import os
from core import constants
from core import logger
from core.conf import Configuration
from core.emane import emanemanifest
from core.emane import emanemodel
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")
mac_config.insert(
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}
@ -41,10 +47,10 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
:return: nothing
"""
# get configured schedule
values = emane_manager.getconfig(self.object_id, self.name, self.getdefaultvalues())[1]
if values is None:
config = emane_manager.get_configs()
if not config:
return
schedule = self.valueof(self.schedule_name, values)
schedule = config[self.schedule_name]
event_device = emane_manager.event_device

View file

@ -378,7 +378,8 @@ class EmuSession(Session):
if node_two:
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.
@ -480,7 +481,7 @@ class EmuSession(Session):
# set node start based on current session state, override and check when rj45
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:
start = False
@ -851,7 +852,7 @@ class CoreEmu(object):
"""
# set umask 0
os.umask(0)
# configuration
self.config = config

View file

@ -6,19 +6,15 @@ https://pypi.python.org/pypi/utm (version 0.3.0).
"""
from core import logger
from core.conf import ConfigurableManager
from core.enumerations import RegisterTlvs
from core.misc import utm
class CoreLocation(ConfigurableManager):
class CoreLocation(object):
"""
Member of session class for handling global location data. This keeps
track of a latitude/longitude/altitude reference point and scale in
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"
config_type = RegisterTlvs.UTILITY.value
@ -29,7 +25,7 @@ class CoreLocation(ConfigurableManager):
:return: nothing
"""
ConfigurableManager.__init__(self)
# ConfigurableManager.__init__(self)
self.reset()
self.zonemap = {}
self.refxyz = (0.0, 0.0, 0.0)
@ -52,35 +48,6 @@ class CoreLocation(ConfigurableManager):
# cached distance to refpt in other zones
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):
"""
Convert the specified value in pixels to meters using the

View file

@ -9,10 +9,12 @@ import threading
import time
from core import logger
from core.conf import Configurable
from core.conf import ConfigurableManager
from core.conf import ConfigurableOptions
from core.conf import Configuration
from core.conf import NewConfigurableManager
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 EventTypes
from core.enumerations import LinkTypes
@ -24,7 +26,7 @@ from core.misc import utils
from core.misc.ipaddress import IpAddress
class MobilityManager(ConfigurableManager):
class MobilityManager(NewConfigurableManager):
"""
Member of session class for handling configuration data for mobility and
range models.
@ -38,20 +40,42 @@ class MobilityManager(ConfigurableManager):
:param core.session.Session session: session this manager is tied to
"""
ConfigurableManager.__init__(self)
super(MobilityManager, self).__init__()
self.session = session
# configurations for basic range, indexed by WLAN node number, are
# stored in self.configs
# configurations for basic range, indexed by WLAN node number, are stored in configurations
# mapping from model names to their classes
self._modelclsmap = {
BasicRangeModel.name: BasicRangeModel,
Ns2ScriptedMobility.name: Ns2ScriptedMobility
}
# dummy node objects for tracking position of nodes on other servers
self.phys = {}
self.physnets = {}
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):
"""
Session is transitioning from instantiation to runtime state.
@ -61,7 +85,7 @@ class MobilityManager(ConfigurableManager):
:return: nothing
"""
if node_ids is None:
node_ids = self.configs.keys()
node_ids = self.nodes()
for node_id in node_ids:
logger.info("checking mobility startup for node: %s", node_id)
@ -69,22 +93,22 @@ class MobilityManager(ConfigurableManager):
try:
node = self.session.get_object(node_id)
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
if node_id not in self.configs:
logger.warn("missing mobility configuration for node %d." % node_id)
node_configs = self.get_config_types(node_id)
if not node_configs:
logger.warn("missing mobility configuration for node: %s", node_id)
continue
v = self.configs[node_id]
for model in v:
for model_name in node_configs.iterkeys():
try:
logger.info("setting mobility model to node: %s", model)
cls = self._modelclsmap[model[0]]
node.setmodel(cls, model[1])
clazz = self._modelclsmap[model_name]
model_config = self.get_config(node_id, model_name)
logger.info("setting mobility model(%s) to node: %s", model_name, model_config)
node.setmodel(clazz, model_config)
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
if self.session.master:
@ -99,26 +123,32 @@ class MobilityManager(ConfigurableManager):
: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 config_type: configuration type
:param values: configuration value
:param str config_type: configuration type
: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:
return
if self.session.state == EventTypes.RUNTIME_STATE.value:
try:
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:
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):
"""
@ -206,13 +236,13 @@ class MobilityManager(ConfigurableManager):
:param list moved_netifs: moved network interfaces
:return: nothing
"""
for nodenum in self.configs:
for node_id in self.nodes():
try:
n = self.session.get_object(nodenum)
node = self.session.get_object(node_id)
except KeyError:
continue
if n.model:
n.model.update(moved, moved_netifs)
if node.model:
node.model.update(moved, moved_netifs)
def addphys(self, netnum, node):
"""
@ -222,12 +252,12 @@ class MobilityManager(ConfigurableManager):
:param core.coreobj.PyCoreNode node: node to add physical network to
:return: nothing
"""
nodenum = node.objid
self.phys[nodenum] = node
node_id = node.objid
self.phys[node_id] = node
if netnum not in self.physnets:
self.physnets[netnum] = [nodenum, ]
self.physnets[netnum] = [node_id, ]
else:
self.physnets[netnum].append(nodenum)
self.physnets[netnum].append(node_id)
# TODO: remove need for handling old style message
def physnodehandlelink(self, message):
@ -247,8 +277,7 @@ class MobilityManager(ConfigurableManager):
return
if nn[1] in self.session.broker.physical_nodes:
# record the fact that this PhysicalNode is linked to a net
dummy = PyCoreNode(session=self.session, objid=nn[1],
name="n%d" % nn[1], start=False)
dummy = PyCoreNode(session=self.session, objid=nn[1], name="n%d" % nn[1], start=False)
self.addphys(nn[0], dummy)
# TODO: remove need to handling old style messages
@ -291,7 +320,7 @@ class MobilityManager(ConfigurableManager):
netif.poshook(netif, x, y, z)
class WirelessModel(Configurable):
class WirelessModel(ConfigurableOptions):
"""
Base class used by EMANE models and the basic range model.
Used for managing arbitrary configuration parameters.
@ -308,9 +337,8 @@ class WirelessModel(Configurable):
:param int object_id: object id
:param values: values
"""
Configurable.__init__(self, session, object_id)
# 'values' can be retrieved from a ConfigurableManager, or used here
# during initialization, depending on the model.
self.session = session
self.object_id = object_id
def all_link_data(self, flags):
"""
@ -333,12 +361,12 @@ class WirelessModel(Configurable):
"""
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
parameters should be invoked.
:param values: value to update
:param dict config: value to update
:return: False
:rtype: bool
"""
@ -353,23 +381,20 @@ class BasicRangeModel(WirelessModel):
"""
name = "basic_range"
# configuration parameters are
# ( 'name', 'type', 'default', 'possible-value-list', 'caption')
config_matrix = [
("range", ConfigDataTypes.UINT32.value, '275',
'', 'wireless range (pixels)'),
("bandwidth", ConfigDataTypes.UINT32.value, '54000',
'', 'bandwidth (bps)'),
("jitter", ConfigDataTypes.FLOAT.value, '0.0',
'', 'transmission jitter (usec)'),
("delay", ConfigDataTypes.FLOAT.value, '5000.0',
'', 'transmission delay (usec)'),
("error", ConfigDataTypes.FLOAT.value, '0.0',
'', 'error rate (%)'),
]
@classmethod
def configurations(cls):
return [
Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"),
Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT32, default="54000", label="bandwidth (bps)"),
Configuration(_id="jitter", _type=ConfigDataTypes.FLOAT, default="0.0", label="transmission jitter (usec)"),
Configuration(_id="delay", _type=ConfigDataTypes.FLOAT, default="5000.0",
label="transmission delay (usec)"),
Configuration(_id="error", _type=ConfigDataTypes.FLOAT, default="0.0", label="error rate (%)")
]
# value groupings
config_groups = "Basic Range Parameters:1-%d" % len(config_matrix)
@classmethod
def config_groups(cls):
return "Basic Range Parameters:1-%d" % len(cls.configurations())
def __init__(self, session, object_id, values=None):
"""
@ -377,57 +402,49 @@ class BasicRangeModel(WirelessModel):
:param core.session.Session session: related core session
:param int object_id: object id
:param values: values
:param dict values: values
"""
super(BasicRangeModel, self).__init__(session=session, object_id=object_id)
self.session = session
self.wlan = session.get_object(object_id)
self._netifs = {}
self._netifslock = threading.Lock()
if values is None:
values = session.mobility.getconfig(object_id, self.name, self.getdefaultvalues())[1]
self.range = float(self.valueof("range", values))
logger.info("Basic range model configured for WLAN %d using range %d", object_id, self.range)
self.valuestolinkparams(values)
if not values:
values = self.default_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.delay = None
self.loss = 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.
:param values: values to convert
:param dict config: values to convert
: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:
self.bw = None
self.delay = float(self.valueof("delay", values))
self.delay = float(config["delay"])
if self.delay == 0.0:
self.delay = None
self.loss = float(self.valueof("error", values))
self.loss = float(config["error"])
if self.loss == 0.0:
self.loss = None
self.jitter = float(self.valueof("jitter", values))
self.jitter = float(config["jitter"])
if self.jitter == 0.0:
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):
"""
Apply link parameters to all interfaces. This is invoked from
@ -435,8 +452,7 @@ class BasicRangeModel(WirelessModel):
"""
with self._netifslock:
for netif in self._netifs:
self.wlan.linkconfig(netif, bw=self.bw, delay=self.delay,
loss=self.loss, duplicate=None,
self.wlan.linkconfig(netif, bw=self.bw, delay=self.delay, loss=self.loss, duplicate=None,
jitter=self.jitter)
def get_position(self, netif):
@ -461,7 +477,6 @@ class BasicRangeModel(WirelessModel):
:param z: z position
:return: nothing
"""
# print "set_position(%s, x=%s, y=%s, z=%s)" % (netif.localname, x, y, z)
self._netifslock.acquire()
self._netifs[netif] = (x, y, z)
if x is None or y is None:
@ -487,7 +502,7 @@ class BasicRangeModel(WirelessModel):
with self._netifslock:
while len(moved_netifs):
netif = moved_netifs.pop()
(nx, ny, nz) = netif.node.getposition()
nx, ny, nz = netif.node.getposition()
if netif in self._netifs:
self._netifs[netif] = (nx, ny, nz)
for netif2 in self._netifs:
@ -557,18 +572,17 @@ class BasicRangeModel(WirelessModel):
c = p1[2] - p2[2]
return math.hypot(math.hypot(a, b), c)
def updateconfig(self, values):
def updateconfig(self, config):
"""
Configuration has changed during runtime.
MobilityManager.setconfig() -> WlanNode.updatemodel() ->
WirelessModel.updateconfig()
:param values: values to update configuration
:param dict config: values to update configuration
:return: was update successful
:rtype: bool
"""
self.valuestolinkparams(values)
self.range = float(self.valueof("range", values))
self.values_from_config(config)
return True
def create_link_data(self, interface1, interface2, message_type):
@ -581,7 +595,6 @@ class BasicRangeModel(WirelessModel):
:return: link data
:rtype: LinkData
"""
return LinkData(
message_type=message_type,
node1_id=interface1.node.objid,
@ -678,6 +691,7 @@ class WayPointMobility(WirelessModel):
:return:
"""
super(WayPointMobility, self).__init__(session=session, object_id=object_id, values=values)
self.state = self.STATE_STOPPED
self.queue = []
self.queue_copy = []
@ -705,7 +719,6 @@ class WayPointMobility(WirelessModel):
self.lasttime = time.time()
now = self.lasttime - self.timezero
dt = self.lasttime - t
# print "runround(now=%.2f, dt=%.2f)" % (now, dt)
# keep current waypoints up-to-date
self.updatepoints(now)
@ -741,7 +754,6 @@ class WayPointMobility(WirelessModel):
moved_netifs.append(netif)
# calculate all ranges after moving nodes; this saves calculations
# self.wlan.model.update(moved)
self.session.mobility.updatewlans(moved, moved_netifs)
# TODO: check session state
@ -806,7 +818,6 @@ class WayPointMobility(WirelessModel):
self.endtime = self.lasttime - self.timezero
del self.points[node.objid]
return False
# print "node %s dx,dy= <%s, %d>" % (node.name, dx, dy)
if (x1 + dx) < 0.0:
dx = 0.0 - x1
if (y1 + dy) < 0.0:
@ -826,7 +837,7 @@ class WayPointMobility(WirelessModel):
node = netif.node
if node.objid not in self.initial:
continue
(x, y, z) = self.initial[node.objid].coords
x, y, z = self.initial[node.objid].coords
self.setnodeposition(node, x, y, z)
moved.append(node)
moved_netifs.append(netif)
@ -845,7 +856,6 @@ class WayPointMobility(WirelessModel):
:param speed: speed
: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)
heapq.heappush(self.queue, wp)
@ -976,25 +986,22 @@ class Ns2ScriptedMobility(WayPointMobility):
"""
name = "ns2script"
config_matrix = [
("file", ConfigDataTypes.STRING.value, '',
'', 'mobility script file'),
("refresh_ms", ConfigDataTypes.UINT32.value, '50',
'', 'refresh time (ms)'),
("loop", ConfigDataTypes.BOOL.value, '1',
'On,Off', 'loop'),
("autostart", ConfigDataTypes.STRING.value, '',
'', 'auto-start seconds (0.0 for runtime)'),
("map", ConfigDataTypes.STRING.value, '',
'', 'node mapping (optional, e.g. 0:1,1:2,2:3)'),
("script_start", ConfigDataTypes.STRING.value, '',
'', 'script file to run upon start'),
("script_pause", ConfigDataTypes.STRING.value, '',
'', 'script file to run upon pause'),
("script_stop", ConfigDataTypes.STRING.value, '',
'', 'script file to run upon stop'),
]
config_groups = "ns-2 Mobility Script Parameters:1-%d" % len(config_matrix)
@classmethod
def configurations(cls):
return [
Configuration(_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"),
Configuration(_id="refresh_ms", _type=ConfigDataTypes.UINT32, default="50", label="mobility script file"),
Configuration(_id="loop", _type=ConfigDataTypes.BOOL, default="1", options=["On", "Off"], label="loop"),
Configuration(_id="autostart", _type=ConfigDataTypes.STRING, label="auto-start seconds (0.0 for runtime)"),
Configuration(_id="map", _type=ConfigDataTypes.STRING, label="node mapping (optional, e.g. 0:1,1:2,2:3)"),
Configuration(_id="script_start", _type=ConfigDataTypes.STRING, label="script file to run upon start"),
Configuration(_id="script_pause", _type=ConfigDataTypes.STRING, label="script file to run upon pause"),
Configuration(_id="script_stop", _type=ConfigDataTypes.STRING, label="script file to run upon stop")
]
@classmethod
def config_groups(cls):
return "ns-2 Mobility Script Parameters:1-%d" % len(cls.configurations())
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)
self._netifs = {}
self._netifslock = threading.Lock()
if values is None:
values = session.mobility.getconfig(object_id, self.name, self.getdefaultvalues())[1]
self.file = self.valueof("file", values)
self.refresh_ms = int(self.valueof("refresh_ms", values))
self.loop = self.valueof("loop", values).lower() == "on"
self.autostart = self.valueof("autostart", values)
self.parsemap(self.valueof("map", values))
self.script_start = self.valueof("script_start", values)
self.script_pause = self.valueof("script_pause", values)
self.script_stop = self.valueof("script_stop", values)
if not values:
values = self.default_values()
self.session.mobility.set_configs(values, node_id=object_id, config_type=self.name)
self.file = values["file"]
self.refresh_ms = int(values["refresh_ms"])
self.loop = values["loop"].lower() == "on"
self.autostart = values["autostart"]
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)
self.readscriptfile()
self.copywaypoints()
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):
"""
Read in mobility script from a file. This adds waypoints to a
@ -1234,4 +1233,4 @@ class Ns2ScriptedMobility(WayPointMobility):
return
filename = self.findfile(filename)
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:
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().)
:param model_name: model name to update
:param values: values to update model with
:param str model_name: model name to update
:param dict config: values to update model with
:return: nothing
"""
logger.info("updating model %s" % model_name)
@ -412,14 +412,14 @@ class WlanNode(LxBrNet):
model = self.model
if model.config_type == RegisterTlvs.WIRELESS.value:
if not model.updateconfig(values):
if not model.updateconfig(config):
return
if self.model.position_callback:
for netif in self.netifs():
netif.poshook = self.model.position_callback
if netif.node is not None:
(x, y, z) = netif.node.position.get()
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.setlinkparams()

View file

@ -118,12 +118,7 @@ class Sdt(object):
:return: True if enabled, False otherwise
:rtype: bool
"""
if not hasattr(self.session.options, "enablesdt"):
return False
enabled = self.session.options.enablesdt
if enabled in ("1", "true", 1, True):
return True
return False
return self.session.options.get_config("enablesdt") == "1"
def seturl(self):
"""
@ -132,11 +127,8 @@ class Sdt(object):
:return: nothing
"""
url = None
if hasattr(self.session.options, "sdturl"):
if self.session.options.sdturl != "":
url = self.session.options.sdturl
if url is None or url == "":
url = self.session.options.get_config("stdurl")
if not url:
url = self.DEFAULT_SDT_URL
self.url = urlparse(url)
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.
"""
import time
from itertools import repeat
from core import CoreCommandError
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 FileData
from core.enumerations import ConfigDataTypes
from core.enumerations import ConfigFlags
from core.enumerations import EventTypes
from core.enumerations import MessageFlags
from core.enumerations import RegisterTlvs
@ -78,7 +72,7 @@ class ServiceManager(object):
cls.add(service)
class CoreServices(ConfigurableManager):
class CoreServices(object):
"""
Class for interacting with a list of available startup services for
nodes. Mostly used to convert a CoreService into a Config API
@ -88,7 +82,6 @@ class CoreServices(ConfigurableManager):
"""
name = "services"
config_type = RegisterTlvs.UTILITY.value
_invalid_custom_names = ("core", "api", "emane", "misc", "netns", "phys", "services")
def __init__(self, session):
@ -98,7 +91,7 @@ class CoreServices(ConfigurableManager):
:param core.session.Session session: session this manager is tied to
:return: nothing
"""
ConfigurableManager.__init__(self)
# ConfigurableManager.__init__(self)
self.session = session
# dict of default services tuples, key is node type
self.defaultservices = {}
@ -154,34 +147,31 @@ class CoreServices(ConfigurableManager):
return s
return service
def setcustomservice(self, object_id, service, values):
def setcustomservice(self, object_id, service, config):
"""
Store service customizations in an instantiated service object
using a list of values that came from a config message.
:param int object_id: object id to set custom service for
:param class service: service to set
:param list values: values to
:param dict config: values to
:return:
"""
logger.debug("setting custom service(%s) for node(%s): %s", object_id, service, config)
if service._custom:
s = service
else:
# instantiate the class, for storing config customization
s = service()
# values are new key=value format; not all keys need to be present
# a missing key means go with the default
if Configurable.haskeyvalues(values):
for v in values:
key, value = v.split('=', 1)
s.setvalue(key, value)
# old-style config, list of values
else:
s.fromvaluelist(values)
# set custom service configuration
for name, value in config.iteritems():
s.setvalue(name, value)
# assume custom service already in dict
if service._custom:
return
# add the custom service to dict
if object_id in self.customservices:
self.customservices[object_id] += (s,)
@ -436,130 +426,6 @@ class CoreServices(ConfigurableManager):
status = "-1"
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):
"""
Build a list of services from an opaque data string.

View file

@ -11,6 +11,7 @@ import subprocess
import tempfile
import threading
import time
from itertools import repeat
import pwd
@ -18,8 +19,10 @@ from core import constants
from core import logger
from core.api import coreapi
from core.broker import CoreBroker
from core.conf import Configurable
from core.conf import ConfigurableManager
from core.conf import ConfigShim
from core.conf import ConfigurableOptions
from core.conf import Configuration
from core.conf import NewConfigurableManager
from core.data import ConfigData
from core.data import EventData
from core.data import ExceptionData
@ -37,11 +40,10 @@ from core.misc import nodeutils
from core.misc import utils
from core.misc.event import EventLoop
from core.misc.ipaddress import MacAddress
from core.mobility import BasicRangeModel
from core.mobility import MobilityManager
from core.mobility import Ns2ScriptedMobility
from core.netns import nodes
from core.sdt import Sdt
from core.service import CoreService
from core.service import CoreServices
from core.xml.xmlsession import save_session_xml
@ -81,10 +83,6 @@ class Session(object):
self.objects = {}
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?
self.state = EventTypes.NONE.value
self._state_time = time.time()
@ -106,60 +104,30 @@ class Session(object):
self.config_handlers = []
self.shutdown_handlers = []
# setup broker
# initialize feature helpers
self.broker = CoreBroker(session=self)
self.add_config_object(CoreBroker.name, CoreBroker.config_type, self.broker.configure)
# setup location
self.location = CoreLocation()
self.add_config_object(CoreLocation.name, CoreLocation.config_type, self.location.configure)
# setup mobiliy
self.mobility = MobilityManager(session=self)
self.add_config_object(MobilityManager.name, MobilityManager.config_type, self.mobility.configure)
self.add_config_object(BasicRangeModel.name, BasicRangeModel.config_type, BasicRangeModel.configure_mob)
self.add_config_object(Ns2ScriptedMobility.name, Ns2ScriptedMobility.config_type,
Ns2ScriptedMobility.configure_mob)
# setup services
self.services = CoreServices(session=self)
self.add_config_object(CoreServices.name, CoreServices.config_type, self.services.configure)
# setup emane
self.emane = EmaneManager(session=self)
self.add_config_object(EmaneManager.name, EmaneManager.config_type, self.emane.configure)
# setup sdt
self.sdt = Sdt(session=self)
# future parameters set by the GUI may go here
self.options = SessionConfig(session=self)
self.add_config_object(SessionConfig.name, SessionConfig.config_type, self.options.configure)
self.options = SessionConfig()
self.metadata = SessionMetaData()
self.add_config_object(SessionMetaData.name, SessionMetaData.config_type, self.metadata.configure)
self.sdt = Sdt(session=self)
def shutdown(self):
"""
Shutdown all emulation objects and remove the session directory.
"""
# shutdown emane
# shutdown/cleanup feature helpers
self.emane.shutdown()
# shutdown broker
self.broker.shutdown()
# shutdown NRL's SDT3D
self.sdt.shutdown()
# delete all current objects
self.delete_objects()
preserve = False
if hasattr(self.options, "preservedir") and self.options.preservedir == "1":
preserve = True
# remove this sessions working directory
preserve = self.options.get_config("preservedir") == "1"
if not preserve:
shutil.rmtree(self.session_dir, ignore_errors=True)
@ -379,12 +347,7 @@ class Session(object):
except:
message = "exception occured when running %s state hook: %s" % (coreapi.state_name(state), hook)
logger.exception(message)
self.exception(
ExceptionLevels.ERROR,
"Session.run_state_hooks",
None,
message
)
self.exception(ExceptionLevels.ERROR, "Session.run_state_hooks", None, message)
def add_state_hook(self, state, hook):
"""
@ -422,7 +385,7 @@ class Session(object):
if state == EventTypes.RUNTIME_STATE.value:
self.emane.poststartup()
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")
save_session_xml(self, xml_file_name, xml_file_version)
@ -597,64 +560,6 @@ class Session(object):
except IOError:
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):
"""
Log information about the session in its current state.
@ -742,10 +647,8 @@ class Session(object):
if self.emane.startup() == self.emane.NOT_READY:
return
# startup broker
# start feature helpers
self.broker.startup()
# startup mobility
self.mobility.startup()
# boot the services on each node
@ -901,11 +804,25 @@ class Session(object):
:return: control net prefix list
:rtype: list
"""
p = getattr(self.options, "controlnet", self.config.get("controlnet"))
p0 = getattr(self.options, "controlnet0", self.config.get("controlnet0"))
p1 = getattr(self.options, "controlnet1", self.config.get("controlnet1"))
p2 = getattr(self.options, "controlnet2", self.config.get("controlnet2"))
p3 = getattr(self.options, "controlnet3", self.config.get("controlnet3"))
p = self.options.get_config("controlnet")
if not p:
p = self.config.get("controlnet")
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:
p0 = p
@ -1000,7 +917,7 @@ class Session(object):
logger.warning("controlnet updown script not configured")
# check if session option set, overwrite if so
options_updown_script = getattr(self.options, "controlnet_updown_script", None)
options_updown_script = self.options.get_config("controlnet_updown_script")
if options_updown_script:
updown_script = options_updown_script
@ -1194,6 +1111,7 @@ class Session(object):
node = self.get_object(node_id)
node.cmd(data, wait=False)
# TODO: move to core handlers
def send_objects(self):
"""
Return API messages that describe the current session.
@ -1222,36 +1140,48 @@ class Session(object):
logger.info(pprint.pformat(dict(link_data._asdict())))
self.broadcast_link(link_data)
# send model info
configs = self.mobility.getallconfigs()
configs += self.emane.getallconfigs()
logger.info("sending model configs:")
for node_number, cls, values in configs:
logger.info("config: node(%s) class(%s) values(%s)", node_number, cls, values)
config_data = cls.config_data(
flags=0,
node_id=node_number,
type_flags=ConfigFlags.UPDATE.value,
values=values
)
logger.info(pprint.pformat(dict(config_data._asdict())))
self.broadcast_config(config_data)
# send mobility model info
for node_id in self.mobility.nodes():
node = self.get_object(node_id)
for model_class, config in self.mobility.getmodels(node):
logger.info("mobility config: node(%s) class(%s) values(%s)", node_id, model_class, config)
config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config)
self.broadcast_config(config_data)
# send emane model info
for node_id in self.emane.nodes():
if node_id not in self.objects:
continue
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_configs = self.services.getallconfigs()
for node_number, service in service_configs:
for node_id, service in service_configs:
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(
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
)
config_response = self.services.configure_request(config_data)
self.broadcast_config(config_response)
self.broadcast_config(config_data)
for file_name, config_data in self.services.getallfiles(service):
file_data = FileData(
message_type=MessageFlags.ADD.value,
node=node_number,
node=node_id,
name=str(file_name),
type=opaque,
data=str(config_data)
@ -1271,91 +1201,62 @@ class Session(object):
)
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
options_config = self.options.configure_request(config_data, type_flags=ConfigFlags.UPDATE.value)
self.broadcast_config(options_config)
# retrieve session metadata
metadata_config = self.metadata.configure_request(config_data, type_flags=ConfigFlags.UPDATE.value)
self.broadcast_config(metadata_config)
# send session metadata
data_values = "|".join(["%s=%s" % item for item in self.metadata.get_configs().iteritems()])
data_types = tuple(ConfigDataTypes.STRING.value for _ in self.metadata.get_configs())
config_data = ConfigData(
message_type=0,
object=self.metadata.name,
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))
class SessionConfig(ConfigurableManager, Configurable):
class SessionConfig(NewConfigurableManager, ConfigurableOptions):
"""
Session configuration object.
"""
name = "session"
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):
"""
Creates a SessionConfig instance.
@classmethod
def configurations(cls):
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
:return: nothing
"""
ConfigurableManager.__init__(self)
self.session = session
self.reset()
@classmethod
def config_groups(cls):
return "Options:1-%d" % len(cls.configurations())
def reset(self):
"""
Reset the session configuration.
: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)
def __init__(self):
super(SessionConfig, self).__init__()
config = self.default_values()
self.set_configs(config)
class SessionMetaData(ConfigurableManager):
class SessionMetaData(NewConfigurableManager):
"""
Metadata is simply stored in a configs[] dict. Key=value pairs are
passed in from configure messages destined to the "metadata" object.
@ -1363,92 +1264,3 @@ class SessionMetaData(ConfigurableManager):
"""
name = "metadata"
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 core import logger
from core.conf import ConfigShim
from core.enumerations import NodeTypes
from core.misc import nodeutils
from core.service import ServiceManager
@ -369,6 +370,7 @@ class CoreDocumentParser0(object):
values.append("files=%s" % files)
if not bool(service.getAttribute("custom")):
return True
values = ConfigShim.str_to_dict(values)
self.session.services.setcustomservice(n.objid, svc, values)
return True

View file

@ -4,6 +4,7 @@ from xml.dom.minidom import parse
from core import constants
from core import logger
from core.conf import ConfigShim
from core.enumerations import NodeTypes
from core.misc import nodeutils
from core.misc.ipaddress import MacAddress
@ -615,6 +616,7 @@ class CoreDocumentParser1(object):
values.append('cmddown=%s' % shutdown)
if validate:
values.append('cmdval=%s' % validate)
filenames = []
files = []
for f in xmlutils.iter_children_with_name(service, 'file'):
@ -629,11 +631,15 @@ class CoreDocumentParser1(object):
data = None
typestr = 'service:%s:%s' % (name, filename)
files.append((typestr, filename, data))
if filenames:
values.append('files=%s' % filenames)
custom = service.getAttribute('custom')
if custom and custom.lower() == 'true':
values = ConfigShim.str_to_dict(values)
self.session.services.setcustomservice(node.objid, session_service, values)
# NOTE: if a custom service is used, setservicefile() must be
# called after the custom service exists
for typestr, filename, data in files:
@ -812,7 +818,7 @@ class CoreDocumentParser1(object):
params = self.parse_parameter_children(options)
for name, value in params.iteritems():
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):
"""

View file

@ -217,13 +217,7 @@ class ScenarioPlan(XmlElement):
self.last_network_id = 0
self.addNetworks()
self.addDevices()
# XXX Do we need these?
# self.session.emane.setup() # not during runtime?
# self.addorigin()
self.addDefaultServices()
self.addSessionConfiguration()
def addNetworks(self):
@ -318,10 +312,12 @@ class ScenarioPlan(XmlElement):
# options
options = self.createElement("options")
defaults = self.coreSession.options.getdefaultvalues()
for i, (k, v) in enumerate(self.coreSession.options.getkeyvaluelist()):
if str(v) != str(defaults[i]):
XmlElement.add_parameter(self.document, options, k, v)
options_config = self.coreSession.options.get_configs()
for _id, default_value in self.coreSession.options.default_values().iteritems():
value = options_config[_id]
if value != default_value:
XmlElement.add_parameter(self.document, options, _id, value)
if options.hasChildNodes():
config.appendChild(options)
@ -340,7 +336,7 @@ class ScenarioPlan(XmlElement):
# 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)
if meta.hasChildNodes():
config.appendChild(meta)
@ -482,6 +478,7 @@ class NetworkElement(NamedXmlElement):
modelconfigs = network_object.session.mobility.getmodels(network_object)
modelconfigs += network_object.session.emane.getmodels(network_object)
chan = None
for model, conf in modelconfigs:
# Handle mobility parameters below
if model.config_type == RegisterTlvs.MOBILITY.value:
@ -496,10 +493,9 @@ class NetworkElement(NamedXmlElement):
channel_domain="CORE")
# Add wireless model parameters
for i, key in enumerate(model.getnames()):
value = conf[i]
for key, value in conf.iteritems():
if value is not None:
chan.addParameter(key, model.valueof(key, conf))
chan.addParameter(key, value)
for model, conf in modelconfigs:
if model.config_type == RegisterTlvs.MOBILITY.value:
@ -509,8 +505,8 @@ class NetworkElement(NamedXmlElement):
type_element = self.createElement("type")
type_element.appendChild(self.createTextNode(model.name))
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:
mobility.addParameter(key, value)