refactored configs back to be able to provide instance conifgurations for sessions
This commit is contained in:
parent
eb415aa4d4
commit
3a39432fc7
22 changed files with 560 additions and 319 deletions
|
@ -90,11 +90,57 @@ class Configuration(object):
|
||||||
self.__class__.__name__, self.id, self.type, self.default, self.options)
|
self.__class__.__name__, self.id, self.type, self.default, self.options)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurableManager(object):
|
||||||
|
_default_node = -1
|
||||||
|
_default_type = _default_node
|
||||||
|
|
||||||
|
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 has_configs(self, node_id):
|
||||||
|
return node_id in self._configuration_maps
|
||||||
|
|
||||||
|
def config_reset(self, node_id=None):
|
||||||
|
logger.debug("resetting all configurations: %s", self.__class__.__name__)
|
||||||
|
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_configs = self.get_all_configs(node_id)
|
||||||
|
if config_type in node_configs:
|
||||||
|
node_configs.pop(config_type)
|
||||||
|
node_configs[config_type] = config
|
||||||
|
|
||||||
|
def get_config(self, _id, node_id=_default_node, config_type=_default_type, default=None):
|
||||||
|
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, default)
|
||||||
|
|
||||||
|
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.get_all_configs(node_id)
|
||||||
|
return node_map.setdefault(config_type, {})
|
||||||
|
|
||||||
|
def get_all_configs(self, node_id=_default_node):
|
||||||
|
logger.debug("getting all configs for node(%s)", node_id)
|
||||||
|
return self._configuration_maps.setdefault(node_id, OrderedDict())
|
||||||
|
|
||||||
|
|
||||||
class ConfigurableOptions(object):
|
class ConfigurableOptions(object):
|
||||||
# unique name to receive configuration changes
|
# unique name to receive configuration changes
|
||||||
name = None
|
name = None
|
||||||
bitmap = None
|
bitmap = None
|
||||||
configuration_maps = None
|
|
||||||
_default_node = -1
|
_default_node = -1
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -108,42 +154,3 @@ class ConfigurableOptions(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_values(cls):
|
def default_values(cls):
|
||||||
return OrderedDict([(config.id, config.default) for config in cls.configurations()])
|
return OrderedDict([(config.id, config.default) for config in cls.configurations()])
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def nodes(cls):
|
|
||||||
return {node_id for node_id in cls.configuration_maps.iterkeys() if node_id != cls._default_node}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def config_reset(cls, node_id=None):
|
|
||||||
if not node_id:
|
|
||||||
logger.debug("resetting all configurations: %s", cls.__name__)
|
|
||||||
cls.configuration_maps.clear()
|
|
||||||
elif node_id in cls.configuration_maps:
|
|
||||||
logger.debug("resetting node(%s) configurations: %s", node_id, cls.__name__)
|
|
||||||
cls.configuration_maps.pop(node_id)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set_config(cls, _id, value, node_id=_default_node):
|
|
||||||
logger.debug("setting config for node(%s) type(%s): %s=%s", node_id, _id, value)
|
|
||||||
node_configs = cls.get_configs(node_id)
|
|
||||||
node_configs[_id] = value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_config(cls, _id, node_id=_default_node, default=None):
|
|
||||||
node_configs = cls.get_configs(node_id)
|
|
||||||
value = node_configs.get(_id, default)
|
|
||||||
logger.debug("getting config for node(%s): %s = %s", node_id, _id, value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set_configs(cls, config=None, node_id=_default_node):
|
|
||||||
logger.debug("setting config for node(%s): %s", node_id, config)
|
|
||||||
node_config = cls.get_configs(node_id)
|
|
||||||
if config:
|
|
||||||
for key, value in config.iteritems():
|
|
||||||
node_config[key] = value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_configs(cls, node_id=_default_node):
|
|
||||||
logger.debug("getting configs for node(%s)", node_id)
|
|
||||||
return cls.configuration_maps.setdefault(node_id, cls.default_values())
|
|
||||||
|
|
|
@ -1186,7 +1186,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
logger.warn("model class does not exist: %s", object_name)
|
logger.warn("model class does not exist: %s", object_name)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
config = model_class.get_configs(node_id=node_id)
|
config = self.session.mobility.get_model_config(node_id, object_name)
|
||||||
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
|
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
|
||||||
replies.append(config_response)
|
replies.append(config_response)
|
||||||
elif message_type != ConfigFlags.RESET:
|
elif message_type != ConfigFlags.RESET:
|
||||||
|
@ -1195,17 +1195,11 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
logger.warn("no configuration object for node: %s", node_id)
|
logger.warn("no configuration object for node: %s", node_id)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
model_class = self.session.mobility.get_model_class(object_name)
|
parsed_config = {}
|
||||||
if not model_class:
|
|
||||||
logger.warn("model class does not exist: %s", object_name)
|
|
||||||
return []
|
|
||||||
|
|
||||||
if values_str:
|
if values_str:
|
||||||
parsed_config = ConfigShim.str_to_dict(values_str)
|
parsed_config = ConfigShim.str_to_dict(values_str)
|
||||||
model_class.set_configs(parsed_config, node_id=node_id)
|
|
||||||
|
|
||||||
config = model_class.get_configs(node_id)
|
self.session.mobility.set_model_config(node_id, object_name, parsed_config)
|
||||||
model_class.set_configs(config, node_id=node_id)
|
|
||||||
|
|
||||||
return replies
|
return replies
|
||||||
|
|
||||||
|
@ -1213,7 +1207,6 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
replies = []
|
replies = []
|
||||||
node_id = config_data.node
|
node_id = config_data.node
|
||||||
object_name = config_data.object
|
object_name = config_data.object
|
||||||
config_type = config_data.type
|
|
||||||
interface_id = config_data.interface_number
|
interface_id = config_data.interface_number
|
||||||
values_str = config_data.data_values
|
values_str = config_data.data_values
|
||||||
|
|
||||||
|
@ -1263,7 +1256,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
logger.warn("model class does not exist: %s", object_name)
|
logger.warn("model class does not exist: %s", object_name)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
config = model_class.get_configs(node_id=node_id)
|
config = self.session.emane.get_model_config(node_id, object_name)
|
||||||
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
|
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
|
||||||
replies.append(config_response)
|
replies.append(config_response)
|
||||||
elif message_type != ConfigFlags.RESET:
|
elif message_type != ConfigFlags.RESET:
|
||||||
|
@ -1272,18 +1265,11 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
logger.warn("no configuration object for node: %s", node_id)
|
logger.warn("no configuration object for node: %s", node_id)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
model_class = self.session.emane.get_model_class(object_name)
|
parsed_config = {}
|
||||||
if not model_class:
|
|
||||||
logger.warn("model class does not exist: %s", object_name)
|
|
||||||
return []
|
|
||||||
|
|
||||||
if values_str:
|
if values_str:
|
||||||
parsed_config = ConfigShim.str_to_dict(values_str)
|
parsed_config = ConfigShim.str_to_dict(values_str)
|
||||||
model_class.set_configs(parsed_config, node_id=node_id)
|
|
||||||
|
|
||||||
config = model_class.get_configs(node_id)
|
self.session.emane.set_model_config(node_id, object_name, parsed_config)
|
||||||
model_class.set_configs(config, node_id=node_id)
|
|
||||||
self.session.emane.set_node_model(node_id, object_name)
|
|
||||||
|
|
||||||
return replies
|
return replies
|
||||||
|
|
||||||
|
@ -1435,7 +1421,8 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
return ()
|
return ()
|
||||||
elif event_type == EventTypes.FILE_SAVE:
|
elif event_type == EventTypes.FILE_SAVE:
|
||||||
filename = event_data.name
|
filename = event_data.name
|
||||||
self.session.save_xml(filename, self.session.config["xmlfilever"])
|
xml_version = self.session.options.get_config("xmlfilever")
|
||||||
|
self.session.save_xml(filename, xml_version)
|
||||||
elif event_type == EventTypes.SCHEDULED:
|
elif event_type == EventTypes.SCHEDULED:
|
||||||
etime = event_data.time
|
etime = event_data.time
|
||||||
node = event_data.node
|
node = event_data.node
|
||||||
|
@ -1600,10 +1587,9 @@ class CoreHandler(SocketServer.BaseRequestHandler):
|
||||||
self.session.broadcast_config(config_data)
|
self.session.broadcast_config(config_data)
|
||||||
|
|
||||||
# send emane model info
|
# send emane model info
|
||||||
for model_name in self.session.emane.emane_models():
|
for node_id in self.session.emane.nodes():
|
||||||
model_class = self.session.emane.get_model_class(model_name)
|
node = self.session.get_object(node_id)
|
||||||
for node_id in model_class.nodes():
|
for model_class, config in self.session.emane.getmodels(node):
|
||||||
config = model_class.get_configs(node_id)
|
|
||||||
logger.info("emane config: node(%s) class(%s) values(%s)", node_id, model_class, config)
|
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)
|
config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config)
|
||||||
self.session.broadcast_config(config_data)
|
self.session.broadcast_config(config_data)
|
||||||
|
|
|
@ -8,7 +8,6 @@ from core.enumerations import ConfigDataTypes
|
||||||
|
|
||||||
class EmaneBypassModel(emanemodel.EmaneModel):
|
class EmaneBypassModel(emanemodel.EmaneModel):
|
||||||
name = "emane_bypass"
|
name = "emane_bypass"
|
||||||
configuration_maps = {}
|
|
||||||
|
|
||||||
# values to ignore, when writing xml files
|
# values to ignore, when writing xml files
|
||||||
config_ignore = {"none"}
|
config_ignore = {"none"}
|
||||||
|
|
|
@ -29,7 +29,6 @@ def convert_none(x):
|
||||||
|
|
||||||
class EmaneCommEffectModel(emanemodel.EmaneModel):
|
class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
name = "emane_commeffect"
|
name = "emane_commeffect"
|
||||||
configuration_maps = {}
|
|
||||||
|
|
||||||
shim_library = "commeffectshim"
|
shim_library = "commeffectshim"
|
||||||
shim_xml = "/usr/share/emane/manifest/commeffectshim.xml"
|
shim_xml = "/usr/share/emane/manifest/commeffectshim.xml"
|
||||||
|
@ -55,7 +54,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
||||||
:param interface: interface for the emane node
|
:param interface: interface for the emane node
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
config = self.getifcconfig(self.object_id, interface)
|
config = emane_manager.getifcconfig(self.object_id, interface, self.name)
|
||||||
if not config:
|
if not config:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from core import constants
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.api import dataconversion
|
from core.api import dataconversion
|
||||||
from core.conf import ConfigShim
|
from core.conf import ConfigShim, ConfigurableManager
|
||||||
from core.conf import Configuration
|
from core.conf import Configuration
|
||||||
from core.emane import emanemanifest
|
from core.emane import emanemanifest
|
||||||
from core.emane.bypass import EmaneBypassModel
|
from core.emane.bypass import EmaneBypassModel
|
||||||
|
@ -53,7 +53,7 @@ EMANE_MODELS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class EmaneManager(object):
|
class EmaneManager(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
EMANE controller object. Lives in a Session instance and is used for
|
EMANE controller object. Lives in a Session instance and is used for
|
||||||
building EMANE config files from all of the EmaneNode objects in this
|
building EMANE config files from all of the EmaneNode objects in this
|
||||||
|
@ -73,6 +73,7 @@ class EmaneManager(object):
|
||||||
:param core.session.Session session: session this manager is tied to
|
:param core.session.Session session: session this manager is tied to
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
|
super(EmaneManager, self).__init__()
|
||||||
self.session = session
|
self.session = session
|
||||||
self._emane_nodes = {}
|
self._emane_nodes = {}
|
||||||
self._emane_node_lock = threading.Lock()
|
self._emane_node_lock = threading.Lock()
|
||||||
|
@ -86,6 +87,7 @@ class EmaneManager(object):
|
||||||
|
|
||||||
# model for global EMANE configuration options
|
# model for global EMANE configuration options
|
||||||
self.emane_config = EmaneGlobalModel(session)
|
self.emane_config = EmaneGlobalModel(session)
|
||||||
|
self.set_configs(self.emane_config.default_values())
|
||||||
|
|
||||||
# store the last configured model for a node, used during startup
|
# store the last configured model for a node, used during startup
|
||||||
self.node_models = {}
|
self.node_models = {}
|
||||||
|
@ -98,19 +100,106 @@ class EmaneManager(object):
|
||||||
self.service = None
|
self.service = None
|
||||||
self.emane_check()
|
self.emane_check()
|
||||||
|
|
||||||
def set_node_model(self, node_id, model_name):
|
def set_model_config(self, node_id, model_name, config):
|
||||||
if model_name not in self._modelclsmap:
|
"""
|
||||||
raise ValueError("unknown emane model: %s", model_name)
|
Set configuration data for a model.
|
||||||
|
|
||||||
|
:param int node_id: node id to set model configuration for
|
||||||
|
:param str model_name: model to set configuration for
|
||||||
|
:param dict config: configuration data to set for model
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# get model class to configure
|
||||||
|
model_class = self._modelclsmap.get(model_name)
|
||||||
|
if not model_class:
|
||||||
|
raise ValueError("%s is an invalid model" % model_name)
|
||||||
|
|
||||||
|
# retrieve default values
|
||||||
|
node_config = self.get_model_config(node_id, model_name)
|
||||||
|
for key, value in config.iteritems():
|
||||||
|
node_config[key] = value
|
||||||
|
|
||||||
|
# set as node model for startup
|
||||||
self.node_models[node_id] = model_name
|
self.node_models[node_id] = model_name
|
||||||
|
|
||||||
def config_reset(self, node_id=None):
|
# set configuration
|
||||||
# clear and reset current emane configuration
|
self.set_configs(node_config, node_id=node_id, config_type=model_name)
|
||||||
self.emane_config.config_reset()
|
|
||||||
self.emane_config.set_configs()
|
|
||||||
|
|
||||||
# reset model configurations
|
def get_model_config(self, node_id, model_name):
|
||||||
for model_class in self._modelclsmap.itervalues():
|
"""
|
||||||
model_class.config_reset(node_id=node_id)
|
Set configuration data for a model.
|
||||||
|
|
||||||
|
:param int node_id: node id to set model configuration for
|
||||||
|
:param str model_name: model to set configuration for
|
||||||
|
:return: current model configuration for node
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
# get model class to configure
|
||||||
|
model_class = self._modelclsmap.get(model_name)
|
||||||
|
if not model_class:
|
||||||
|
raise ValueError("%s is an invalid model" % model_name)
|
||||||
|
|
||||||
|
config = self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
|
if not config:
|
||||||
|
# set default values, when not already set
|
||||||
|
config = model_class.default_values()
|
||||||
|
self.set_configs(config, node_id=node_id, config_type=model_name)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
def getifcconfig(self, node_id, interface, model_name):
|
||||||
|
"""
|
||||||
|
Retrieve interface configuration or node configuration if not provided.
|
||||||
|
|
||||||
|
:param int node_id: node id
|
||||||
|
:param interface: node interface
|
||||||
|
:param str model_name: model to get configuration for
|
||||||
|
:return: node/interface model configuration
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
# use the network-wide config values or interface(NEM)-specific values?
|
||||||
|
if interface is None:
|
||||||
|
return self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
|
else:
|
||||||
|
# don"t use default values when interface config is the same as net
|
||||||
|
# note here that using ifc.node.objid as key allows for only one type
|
||||||
|
# of each model per node;
|
||||||
|
# TODO: use both node and interface as key
|
||||||
|
|
||||||
|
# Adamson change: first check for iface config keyed by "node:ifc.name"
|
||||||
|
# (so that nodes w/ multiple interfaces of same conftype can have
|
||||||
|
# different configs for each separate interface)
|
||||||
|
key = 1000 * interface.node.objid
|
||||||
|
if interface.netindex is not None:
|
||||||
|
key += interface.netindex
|
||||||
|
|
||||||
|
# try retrieve interface specific configuration, avoid getting defaults
|
||||||
|
config = {}
|
||||||
|
if self.has_configs(key):
|
||||||
|
config = self.get_configs(key)
|
||||||
|
|
||||||
|
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
||||||
|
if not config and self.has_configs(interface.node.objid):
|
||||||
|
config = self.get_configs(node_id=interface.node.objid, config_type=model_name)
|
||||||
|
|
||||||
|
if not config and interface.transport_type == "raw":
|
||||||
|
# with EMANE 0.9.2+, we need an extra NEM XML from
|
||||||
|
# model.buildnemxmlfiles(), so defaults are returned here
|
||||||
|
config = self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
def set_model(self, node, model_class, config=None):
|
||||||
|
logger.info("setting emane model(%s) for node(%s): %s", model_class.name, node.objid, config)
|
||||||
|
if not config:
|
||||||
|
config = {}
|
||||||
|
self.set_model_config(node.objid, model_class.name, config)
|
||||||
|
config = self.get_model_config(node.objid, model_class.name)
|
||||||
|
node.setmodel(model_class, config)
|
||||||
|
|
||||||
|
def config_reset(self, node_id=None):
|
||||||
|
super(EmaneManager, self).config_reset(node_id)
|
||||||
|
self.set_configs(self.emane_config.default_values())
|
||||||
|
|
||||||
def emane_models(self):
|
def emane_models(self):
|
||||||
return self._modelclsmap.keys()
|
return self._modelclsmap.keys()
|
||||||
|
@ -162,8 +251,8 @@ class EmaneManager(object):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get the control network to be used for events
|
# Get the control network to be used for events
|
||||||
group, port = self.emane_config.get_config("eventservicegroup").split(":")
|
group, port = self.get_config("eventservicegroup").split(":")
|
||||||
self.event_device = self.emane_config.get_config("eventservicedevice")
|
self.event_device = self.get_config("eventservicedevice")
|
||||||
eventnetidx = self.session.get_control_net_index(self.event_device)
|
eventnetidx = self.session.get_control_net_index(self.event_device)
|
||||||
if eventnetidx < 0:
|
if eventnetidx < 0:
|
||||||
logger.error("invalid emane event service device provided: %s", self.event_device)
|
logger.error("invalid emane event service device provided: %s", self.event_device)
|
||||||
|
@ -223,11 +312,15 @@ class EmaneManager(object):
|
||||||
Used with XML export.
|
Used with XML export.
|
||||||
"""
|
"""
|
||||||
models = []
|
models = []
|
||||||
for model_class in self._modelclsmap.itervalues():
|
all_configs = {}
|
||||||
if node.objid in model_class.configuration_maps:
|
if self.has_configs(node_id=node.objid):
|
||||||
config = model_class.get_configs(node_id=node.objid)
|
all_configs = self.get_all_configs(node_id=node.objid)
|
||||||
models.append((model_class, config))
|
|
||||||
logger.debug("emane models: %s", models)
|
for model_name in all_configs.iterkeys():
|
||||||
|
model_class = self._modelclsmap[model_name]
|
||||||
|
config = self.get_configs(node_id=node.objid, config_type=model_name)
|
||||||
|
models.append((model_class, config))
|
||||||
|
logger.debug("emane models for node(%s): %s", node.objid, models)
|
||||||
return models
|
return models
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
@ -254,7 +347,7 @@ class EmaneManager(object):
|
||||||
# - needs to be configured before checkdistributed() for distributed
|
# - needs to be configured before checkdistributed() for distributed
|
||||||
# - needs to exist when eventservice binds to it (initeventservice)
|
# - needs to exist when eventservice binds to it (initeventservice)
|
||||||
if self.session.master:
|
if self.session.master:
|
||||||
otadev = self.emane_config.get_config("otamanagerdevice")
|
otadev = self.get_config("otamanagerdevice")
|
||||||
netidx = self.session.get_control_net_index(otadev)
|
netidx = self.session.get_control_net_index(otadev)
|
||||||
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
|
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
|
||||||
if netidx < 0:
|
if netidx < 0:
|
||||||
|
@ -263,7 +356,7 @@ class EmaneManager(object):
|
||||||
|
|
||||||
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
|
||||||
self.distributedctrlnet(ctrlnet)
|
self.distributedctrlnet(ctrlnet)
|
||||||
eventdev = self.emane_config.get_config("eventservicedevice")
|
eventdev = self.get_config("eventservicedevice")
|
||||||
logger.debug("emane event service device: eventdev(%s)", eventdev)
|
logger.debug("emane event service device: eventdev(%s)", eventdev)
|
||||||
if eventdev != otadev:
|
if eventdev != otadev:
|
||||||
netidx = self.session.get_control_net_index(eventdev)
|
netidx = self.session.get_control_net_index(eventdev)
|
||||||
|
@ -279,7 +372,7 @@ class EmaneManager(object):
|
||||||
# we are slave, but haven't received a platformid yet
|
# we are slave, but haven't received a platformid yet
|
||||||
platform_id_start = "platform_id_start"
|
platform_id_start = "platform_id_start"
|
||||||
default_values = self.emane_config.default_values()
|
default_values = self.emane_config.default_values()
|
||||||
value = self.emane_config.get_config(platform_id_start)
|
value = self.get_config(platform_id_start)
|
||||||
if value == default_values[platform_id_start]:
|
if value == default_values[platform_id_start]:
|
||||||
return EmaneManager.NOT_READY
|
return EmaneManager.NOT_READY
|
||||||
|
|
||||||
|
@ -411,10 +504,10 @@ class EmaneManager(object):
|
||||||
emane_node = self._emane_nodes[key]
|
emane_node = self._emane_nodes[key]
|
||||||
nemcount += emane_node.numnetif()
|
nemcount += emane_node.numnetif()
|
||||||
|
|
||||||
nemid = int(self.emane_config.get_config("nem_id_start"))
|
nemid = int(self.get_config("nem_id_start"))
|
||||||
nemid += nemcount
|
nemid += nemcount
|
||||||
|
|
||||||
platformid = int(self.emane_config.get_config("platform_id_start"))
|
platformid = int(self.get_config("platform_id_start"))
|
||||||
|
|
||||||
# build an ordered list of servers so platform ID is deterministic
|
# build an ordered list of servers so platform ID is deterministic
|
||||||
servers = []
|
servers = []
|
||||||
|
@ -433,8 +526,8 @@ class EmaneManager(object):
|
||||||
|
|
||||||
platformid += 1
|
platformid += 1
|
||||||
typeflags = ConfigFlags.UPDATE.value
|
typeflags = ConfigFlags.UPDATE.value
|
||||||
self.emane_config.set_config("platform_id_start", str(platformid))
|
self.set_config("platform_id_start", str(platformid))
|
||||||
self.emane_config.set_config("nem_id_start", str(nemid))
|
self.set_config("nem_id_start", str(nemid))
|
||||||
config_data = ConfigShim.config_data(0, None, typeflags, self.emane_config, self.get_configs())
|
config_data = ConfigShim.config_data(0, None, typeflags, self.emane_config, self.get_configs())
|
||||||
message = dataconversion.convert_config(config_data)
|
message = dataconversion.convert_config(config_data)
|
||||||
server.sock.send(message)
|
server.sock.send(message)
|
||||||
|
@ -555,9 +648,9 @@ class EmaneManager(object):
|
||||||
logger.error("emane node(%s) has no node model", node_id)
|
logger.error("emane node(%s) has no node model", node_id)
|
||||||
raise ValueError("emane node has no model set")
|
raise ValueError("emane node has no model set")
|
||||||
|
|
||||||
|
config = self.get_model_config(node_id=node_id, model_name=model_name)
|
||||||
|
logger.debug("setting emane model(%s) config(%s)", model_name, config)
|
||||||
model_class = self._modelclsmap[model_name]
|
model_class = self._modelclsmap[model_name]
|
||||||
config = model_class.get_configs(node_id=node_id)
|
|
||||||
logger.debug("setting emane model(%s) config(%s)", model_class, config)
|
|
||||||
emane_node.setmodel(model_class, config)
|
emane_node.setmodel(model_class, config)
|
||||||
|
|
||||||
def nemlookup(self, nemid):
|
def nemlookup(self, nemid):
|
||||||
|
@ -597,10 +690,10 @@ class EmaneManager(object):
|
||||||
plat = doc.getElementsByTagName("platform").pop()
|
plat = doc.getElementsByTagName("platform").pop()
|
||||||
|
|
||||||
if otadev:
|
if otadev:
|
||||||
self.emane_config.set_config("otamanagerdevice", otadev)
|
self.set_config("otamanagerdevice", otadev)
|
||||||
|
|
||||||
if eventdev:
|
if eventdev:
|
||||||
self.emane_config.set_config("eventservicedevice", eventdev)
|
self.set_config("eventservicedevice", eventdev)
|
||||||
|
|
||||||
# append all platform options (except starting id) to doc
|
# append all platform options (except starting id) to doc
|
||||||
for configuration in self.emane_config.emulator_config:
|
for configuration in self.emane_config.emulator_config:
|
||||||
|
@ -608,7 +701,7 @@ class EmaneManager(object):
|
||||||
if name == "platform_id_start":
|
if name == "platform_id_start":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
value = self.emane_config.get_config(name)
|
value = self.get_config(name)
|
||||||
param = self.xmlparam(doc, name, value)
|
param = self.xmlparam(doc, name, value)
|
||||||
plat.appendChild(param)
|
plat.appendChild(param)
|
||||||
|
|
||||||
|
@ -618,7 +711,7 @@ class EmaneManager(object):
|
||||||
"""
|
"""
|
||||||
Build a platform.xml file now that all nodes are configured.
|
Build a platform.xml file now that all nodes are configured.
|
||||||
"""
|
"""
|
||||||
nemid = int(self.emane_config.get_config("nem_id_start"))
|
nemid = int(self.get_config("nem_id_start"))
|
||||||
platformxmls = {}
|
platformxmls = {}
|
||||||
|
|
||||||
# assume self._objslock is already held here
|
# assume self._objslock is already held here
|
||||||
|
@ -695,7 +788,7 @@ class EmaneManager(object):
|
||||||
default_values = self.emane_config.default_values()
|
default_values = self.emane_config.default_values()
|
||||||
for name in ["eventservicegroup", "eventservicedevice"]:
|
for name in ["eventservicegroup", "eventservicedevice"]:
|
||||||
a = default_values[name]
|
a = default_values[name]
|
||||||
b = self.emane_config.get_config(name)
|
b = self.get_config(name)
|
||||||
if a != b:
|
if a != b:
|
||||||
need_xml = True
|
need_xml = True
|
||||||
|
|
||||||
|
@ -705,12 +798,12 @@ class EmaneManager(object):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
group, port = self.emane_config.get_config("eventservicegroup").split(":")
|
group, port = self.get_config("eventservicegroup").split(":")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.exception("invalid eventservicegroup in EMANE config")
|
logger.exception("invalid eventservicegroup in EMANE config")
|
||||||
return
|
return
|
||||||
|
|
||||||
dev = self.emane_config.get_config("eventservicedevice")
|
dev = self.get_config("eventservicedevice")
|
||||||
doc = self.xmldoc("emaneeventmsgsvc")
|
doc = self.xmldoc("emaneeventmsgsvc")
|
||||||
es = doc.getElementsByTagName("emaneeventmsgsvc").pop()
|
es = doc.getElementsByTagName("emaneeventmsgsvc").pop()
|
||||||
kvs = (("group", group), ("port", port), ("device", dev), ("mcloop", "1"), ("ttl", "32"))
|
kvs = (("group", group), ("port", port), ("device", dev), ("mcloop", "1"), ("ttl", "32"))
|
||||||
|
@ -737,12 +830,12 @@ class EmaneManager(object):
|
||||||
if realtime:
|
if realtime:
|
||||||
emanecmd += "-r",
|
emanecmd += "-r",
|
||||||
|
|
||||||
otagroup, otaport = self.emane_config.get_config("otamanagergroup").split(":")
|
otagroup, otaport = self.get_config("otamanagergroup").split(":")
|
||||||
otadev = self.emane_config.get_config("otamanagerdevice")
|
otadev = self.get_config("otamanagerdevice")
|
||||||
otanetidx = self.session.get_control_net_index(otadev)
|
otanetidx = self.session.get_control_net_index(otadev)
|
||||||
|
|
||||||
eventgroup, eventport = self.emane_config.get_config("eventservicegroup").split(":")
|
eventgroup, eventport = self.get_config("eventservicegroup").split(":")
|
||||||
eventdev = self.emane_config.get_config("eventservicedevice")
|
eventdev = self.get_config("eventservicedevice")
|
||||||
eventservicenetidx = self.session.get_control_net_index(eventdev)
|
eventservicenetidx = self.session.get_control_net_index(eventdev)
|
||||||
|
|
||||||
run_emane_on_host = False
|
run_emane_on_host = False
|
||||||
|
|
|
@ -79,7 +79,7 @@ class EmaneModel(WirelessModel):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# retrieve configuration values
|
# retrieve configuration values
|
||||||
config = self.getifcconfig(self.object_id, interface)
|
config = emane_manager.getifcconfig(self.object_id, interface, self.name)
|
||||||
if not config:
|
if not config:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ class EmaneModel(WirelessModel):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# check if value is a multi param
|
# check if value is a multi param
|
||||||
value = config[name]
|
value = str(config[name])
|
||||||
param = value_to_params(mac_document, name, value)
|
param = value_to_params(mac_document, name, value)
|
||||||
if not param:
|
if not param:
|
||||||
param = emane_manager.xmlparam(mac_document, name, value)
|
param = emane_manager.xmlparam(mac_document, name, value)
|
||||||
|
@ -182,7 +182,7 @@ class EmaneModel(WirelessModel):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# check if value is a multi param
|
# check if value is a multi param
|
||||||
value = config[name]
|
value = str(config[name])
|
||||||
param = value_to_params(phy_document, name, value)
|
param = value_to_params(phy_document, name, value)
|
||||||
if not param:
|
if not param:
|
||||||
param = emane_manager.xmlparam(phy_document, name, value)
|
param = emane_manager.xmlparam(phy_document, name, value)
|
||||||
|
@ -269,7 +269,7 @@ class EmaneModel(WirelessModel):
|
||||||
|
|
||||||
if interface:
|
if interface:
|
||||||
node_id = interface.node.objid
|
node_id = interface.node.objid
|
||||||
if self.getifcconfig(node_id, interface):
|
if self.session.emane.getifcconfig(node_id, interface, self.name):
|
||||||
name = interface.localname.replace(".", "_")
|
name = interface.localname.replace(".", "_")
|
||||||
|
|
||||||
return "%s%s" % (name, self.name)
|
return "%s%s" % (name, self.name)
|
||||||
|
@ -348,43 +348,3 @@ class EmaneModel(WirelessModel):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logger.warn("emane model(%s) does not support link configuration", self.name)
|
logger.warn("emane model(%s) does not support link configuration", self.name)
|
||||||
|
|
||||||
def getifcconfig(self, node_id, ifc):
|
|
||||||
"""
|
|
||||||
Retrieve interface configuration or node configuration if not provided.
|
|
||||||
|
|
||||||
:param int node_id: node id
|
|
||||||
:param ifc: node interface
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
# use the network-wide config values or interface(NEM)-specific values?
|
|
||||||
if ifc is None:
|
|
||||||
return self.get_configs(node_id)
|
|
||||||
else:
|
|
||||||
# don"t use default values when interface config is the same as net
|
|
||||||
# note here that using ifc.node.objid as key allows for only one type
|
|
||||||
# of each model per node;
|
|
||||||
# TODO: use both node and interface as key
|
|
||||||
|
|
||||||
# Adamson change: first check for iface config keyed by "node:ifc.name"
|
|
||||||
# (so that nodes w/ multiple interfaces of same conftype can have
|
|
||||||
# different configs for each separate interface)
|
|
||||||
key = 1000 * ifc.node.objid
|
|
||||||
if ifc.netindex is not None:
|
|
||||||
key += ifc.netindex
|
|
||||||
|
|
||||||
# try retrieve interface specific configuration, avoid getting defaults
|
|
||||||
config = {}
|
|
||||||
if key in self.configuration_maps:
|
|
||||||
config = self.get_configs(key)
|
|
||||||
|
|
||||||
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
|
||||||
if not config and ifc.node.objid in self.configuration_maps:
|
|
||||||
config = self.get_configs(ifc.node.objid)
|
|
||||||
|
|
||||||
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
|
|
||||||
config = self.get_configs(node_id)
|
|
||||||
|
|
||||||
return config
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ from core.emane import emanemodel
|
||||||
class EmaneIeee80211abgModel(emanemodel.EmaneModel):
|
class EmaneIeee80211abgModel(emanemodel.EmaneModel):
|
||||||
# model name
|
# model name
|
||||||
name = "emane_ieee80211abg"
|
name = "emane_ieee80211abg"
|
||||||
configuration_maps = {}
|
|
||||||
|
|
||||||
# mac configuration
|
# mac configuration
|
||||||
mac_library = "ieee80211abgmaclayer"
|
mac_library = "ieee80211abgmaclayer"
|
||||||
|
|
|
@ -73,7 +73,7 @@ class EmaneNode(EmaneNet):
|
||||||
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
|
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
|
||||||
self.model.set_configs(config, node_id=self.objid)
|
self.model.set_configs(config, node_id=self.objid)
|
||||||
|
|
||||||
def setmodel(self, model, config=None):
|
def setmodel(self, model, config):
|
||||||
"""
|
"""
|
||||||
set the EmaneModel associated with this node
|
set the EmaneModel associated with this node
|
||||||
"""
|
"""
|
||||||
|
@ -81,9 +81,11 @@ class EmaneNode(EmaneNet):
|
||||||
if model.config_type == RegisterTlvs.WIRELESS.value:
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||||
# EmaneModel really uses values from ConfigurableManager
|
# EmaneModel really uses values from ConfigurableManager
|
||||||
# when buildnemxml() is called, not during init()
|
# when buildnemxml() is called, not during init()
|
||||||
self.model = model(session=self.session, object_id=self.objid, config=config)
|
self.model = model(session=self.session, object_id=self.objid)
|
||||||
|
self.model.update_config(config)
|
||||||
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, object_id=self.objid, config=config)
|
self.mobility = model(session=self.session, object_id=self.objid)
|
||||||
|
self.mobility.update_config(config)
|
||||||
|
|
||||||
def setnemid(self, netif, nemid):
|
def setnemid(self, netif, nemid):
|
||||||
"""
|
"""
|
||||||
|
@ -180,8 +182,7 @@ class EmaneNode(EmaneNet):
|
||||||
trans.setAttribute("library", "trans%s" % transport_type.lower())
|
trans.setAttribute("library", "trans%s" % transport_type.lower())
|
||||||
trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0"))
|
trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0"))
|
||||||
|
|
||||||
model_class = emane.get_model_class(self.model.name)
|
config = emane.get_configs(node_id=self.objid, config_type=self.model.name)
|
||||||
config = model_class.get_configs(self.objid)
|
|
||||||
logger.debug("transport xml config: %s", config)
|
logger.debug("transport xml config: %s", config)
|
||||||
flowcontrol = config.get("flowcontrolenable", "0") == "1"
|
flowcontrol = config.get("flowcontrolenable", "0") == "1"
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ from core.emane import emanemodel
|
||||||
class EmaneRfPipeModel(emanemodel.EmaneModel):
|
class EmaneRfPipeModel(emanemodel.EmaneModel):
|
||||||
# model name
|
# model name
|
||||||
name = "emane_rfpipe"
|
name = "emane_rfpipe"
|
||||||
configuration_maps = {}
|
|
||||||
|
|
||||||
# mac configuration
|
# mac configuration
|
||||||
mac_library = "rfpipemaclayer"
|
mac_library = "rfpipemaclayer"
|
||||||
|
|
|
@ -16,7 +16,6 @@ from core.misc import utils
|
||||||
class EmaneTdmaModel(emanemodel.EmaneModel):
|
class EmaneTdmaModel(emanemodel.EmaneModel):
|
||||||
# model name
|
# model name
|
||||||
name = "emane_tdma"
|
name = "emane_tdma"
|
||||||
configuration_maps = {}
|
|
||||||
|
|
||||||
# mac configuration
|
# mac configuration
|
||||||
mac_library = "tdmaeventschedulerradiomodel"
|
mac_library = "tdmaeventschedulerradiomodel"
|
||||||
|
@ -47,7 +46,7 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# get configured schedule
|
# get configured schedule
|
||||||
config = self.get_configs(self.object_id)
|
config = self.session.emane.get_configs(node_id=self.object_id, config_type=self.name)
|
||||||
if not config:
|
if not config:
|
||||||
return
|
return
|
||||||
schedule = config[self.schedule_name]
|
schedule = config[self.schedule_name]
|
||||||
|
|
|
@ -739,6 +739,7 @@ class EmuSession(Session):
|
||||||
self.delete_objects()
|
self.delete_objects()
|
||||||
self.del_hooks()
|
self.del_hooks()
|
||||||
self.broker.reset()
|
self.broker.reset()
|
||||||
|
self.emane.reset()
|
||||||
|
|
||||||
def start_events(self):
|
def start_events(self):
|
||||||
"""
|
"""
|
||||||
|
@ -778,7 +779,7 @@ class EmuSession(Session):
|
||||||
node_options.model = "mdr"
|
node_options.model = "mdr"
|
||||||
return self.add_node(_type=NodeTypes.DEFAULT, _id=_id, node_options=node_options)
|
return self.add_node(_type=NodeTypes.DEFAULT, _id=_id, node_options=node_options)
|
||||||
|
|
||||||
def create_emane_network(self, model, geo_reference, geo_scale=None, node_options=NodeOptions()):
|
def create_emane_network(self, model, geo_reference, geo_scale=None, node_options=NodeOptions(), config=None):
|
||||||
"""
|
"""
|
||||||
Convenience method for creating an emane network.
|
Convenience method for creating an emane network.
|
||||||
|
|
||||||
|
@ -786,6 +787,7 @@ class EmuSession(Session):
|
||||||
:param geo_reference: geo reference point to use for emane node locations
|
:param geo_reference: geo reference point to use for emane node locations
|
||||||
:param geo_scale: geo scale to use for emane node locations, defaults to 1.0
|
:param geo_scale: geo scale to use for emane node locations, defaults to 1.0
|
||||||
:param core.emulator.emudata.NodeOptions node_options: options for emane node being created
|
:param core.emulator.emudata.NodeOptions node_options: options for emane node being created
|
||||||
|
:param dict config: emane model configuration
|
||||||
:return: create emane network
|
:return: create emane network
|
||||||
"""
|
"""
|
||||||
# required to be set for emane to function properly
|
# required to be set for emane to function properly
|
||||||
|
@ -795,7 +797,7 @@ class EmuSession(Session):
|
||||||
|
|
||||||
# create and return network
|
# create and return network
|
||||||
emane_network = self.add_node(_type=NodeTypes.EMANE, node_options=node_options)
|
emane_network = self.add_node(_type=NodeTypes.EMANE, node_options=node_options)
|
||||||
emane_network.setmodel(model)
|
self.emane.set_model(emane_network, model, config)
|
||||||
return emane_network
|
return emane_network
|
||||||
|
|
||||||
def wireless_link_all(self, network, nodes):
|
def wireless_link_all(self, network, nodes):
|
||||||
|
|
|
@ -9,7 +9,7 @@ import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.conf import ConfigurableOptions
|
from core.conf import ConfigurableOptions, ConfigurableManager
|
||||||
from core.conf import Configuration
|
from core.conf import Configuration
|
||||||
from core.coreobj import PyCoreNode
|
from core.coreobj import PyCoreNode
|
||||||
from core.data import EventData
|
from core.data import EventData
|
||||||
|
@ -25,7 +25,7 @@ from core.misc import utils
|
||||||
from core.misc.ipaddress import IpAddress
|
from core.misc.ipaddress import IpAddress
|
||||||
|
|
||||||
|
|
||||||
class MobilityManager(object):
|
class MobilityManager(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
Member of session class for handling configuration data for mobility and
|
Member of session class for handling configuration data for mobility and
|
||||||
range models.
|
range models.
|
||||||
|
@ -39,6 +39,7 @@ class MobilityManager(object):
|
||||||
|
|
||||||
:param core.session.Session session: session this manager is tied to
|
:param core.session.Session session: session this manager is tied to
|
||||||
"""
|
"""
|
||||||
|
super(MobilityManager, self).__init__()
|
||||||
self.session = session
|
self.session = session
|
||||||
# configurations for basic range, indexed by WLAN node number, are stored in configurations
|
# configurations for basic range, indexed by WLAN node number, are stored in configurations
|
||||||
# mapping from model names to their classes
|
# mapping from model names to their classes
|
||||||
|
@ -52,6 +53,50 @@ class MobilityManager(object):
|
||||||
self.physnets = {}
|
self.physnets = {}
|
||||||
self.session.broker.handlers.add(self.physnodehandlelink)
|
self.session.broker.handlers.add(self.physnodehandlelink)
|
||||||
|
|
||||||
|
def set_model_config(self, node_id, model_name, config):
|
||||||
|
"""
|
||||||
|
Set configuration data for a model.
|
||||||
|
|
||||||
|
:param int node_id: node id to set model configuration for
|
||||||
|
:param str model_name: model to set configuration for
|
||||||
|
:param dict config: configuration data to set for model
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
# get model class to configure
|
||||||
|
model_class = self._modelclsmap.get(model_name)
|
||||||
|
if not model_class:
|
||||||
|
raise ValueError("%s is an invalid model" % model_name)
|
||||||
|
|
||||||
|
# retrieve default values
|
||||||
|
node_config = self.get_model_config(node_id, model_name)
|
||||||
|
for key, value in config.iteritems():
|
||||||
|
node_config[key] = value
|
||||||
|
|
||||||
|
# set configuration
|
||||||
|
self.set_configs(node_config, node_id=node_id, config_type=model_name)
|
||||||
|
|
||||||
|
def get_model_config(self, node_id, model_name):
|
||||||
|
"""
|
||||||
|
Set configuration data for a model.
|
||||||
|
|
||||||
|
:param int node_id: node id to set model configuration for
|
||||||
|
:param str model_name: model to set configuration for
|
||||||
|
:return: current model configuration for node
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
# get model class to configure
|
||||||
|
model_class = self._modelclsmap.get(model_name)
|
||||||
|
if not model_class:
|
||||||
|
raise ValueError("%s is an invalid model" % model_name)
|
||||||
|
|
||||||
|
config = self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
|
if not config:
|
||||||
|
# set default values, when not already set
|
||||||
|
config = model_class.default_values()
|
||||||
|
self.set_configs(config, node_id=node_id, config_type=model_name)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
def mobility_models(self):
|
def mobility_models(self):
|
||||||
return self._modelclsmap.keys()
|
return self._modelclsmap.keys()
|
||||||
|
|
||||||
|
@ -68,17 +113,17 @@ class MobilityManager(object):
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
models = []
|
models = []
|
||||||
for model_class in self._modelclsmap.itervalues():
|
all_configs = {}
|
||||||
if node.objid in model_class.configuration_maps:
|
if self.has_configs(node_id=node.objid):
|
||||||
config = model_class.get_configs(node_id=node.objid)
|
all_configs = self.get_all_configs(node_id=node.objid)
|
||||||
models.append((model_class, config))
|
|
||||||
return models
|
|
||||||
|
|
||||||
def nodes(self):
|
for model_name in all_configs.iterkeys():
|
||||||
node_ids = set()
|
model_class = self._modelclsmap[model_name]
|
||||||
for model_class in self._modelclsmap.itervalues():
|
config = self.get_configs(node_id=node.objid, config_type=model_name)
|
||||||
node_ids |= model_class.nodes()
|
models.append((model_class, config))
|
||||||
return node_ids
|
|
||||||
|
logger.debug("mobility models for node(%s): %s", node.objid, models)
|
||||||
|
return models
|
||||||
|
|
||||||
def startup(self, node_ids=None):
|
def startup(self, node_ids=None):
|
||||||
"""
|
"""
|
||||||
|
@ -93,6 +138,7 @@ class MobilityManager(object):
|
||||||
|
|
||||||
for node_id in node_ids:
|
for node_id in node_ids:
|
||||||
logger.info("checking mobility startup for node: %s", node_id)
|
logger.info("checking mobility startup for node: %s", node_id)
|
||||||
|
logger.info("node mobility configurations: %s", self.get_all_configs(node_id))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node = self.session.get_object(node_id)
|
node = self.session.get_object(node_id)
|
||||||
|
@ -100,13 +146,12 @@ class MobilityManager(object):
|
||||||
logger.warn("skipping mobility configuration for unknown node: %s", node_id)
|
logger.warn("skipping mobility configuration for unknown node: %s", node_id)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for model_class in self._modelclsmap.itervalues():
|
for model_name in self._modelclsmap.iterkeys():
|
||||||
logger.debug("model(%s) configurations: %s", model_class, model_class.configuration_maps)
|
config = self.get_configs(node_id=node_id, config_type=model_name)
|
||||||
if node_id not in model_class.configuration_maps:
|
if not config:
|
||||||
continue
|
continue
|
||||||
config = model_class.get_configs(node_id=node_id)
|
model_class = self._modelclsmap[model_name]
|
||||||
logger.info("setting mobility model(%s) to node: %s", model_class.name, config)
|
self.set_model(node, model_class, config)
|
||||||
node.setmodel(model_class, config)
|
|
||||||
|
|
||||||
if self.session.master:
|
if self.session.master:
|
||||||
self.installphysnodes(node)
|
self.installphysnodes(node)
|
||||||
|
@ -114,15 +159,13 @@ class MobilityManager(object):
|
||||||
if node.mobility:
|
if node.mobility:
|
||||||
self.session.event_loop.add_event(0.0, node.mobility.startup)
|
self.session.event_loop.add_event(0.0, node.mobility.startup)
|
||||||
|
|
||||||
def config_reset(self, node_id=None):
|
def set_model(self, node, model_class, config=None):
|
||||||
"""
|
logger.info("setting mobility model(%s) for node(%s): %s", model_class.name, node.objid, config)
|
||||||
Reset all configs.
|
if not config:
|
||||||
|
config = {}
|
||||||
:param int node_id: node configuration to reset or None for all configurations
|
self.set_model_config(node.objid, model_class.name, config)
|
||||||
:return: nothing
|
config = self.get_model_config(node.objid, model_class.name)
|
||||||
"""
|
node.setmodel(model_class, config)
|
||||||
for model in self._modelclsmap.itervalues():
|
|
||||||
model.config_reset(node_id=node_id)
|
|
||||||
|
|
||||||
def handleevent(self, event_data):
|
def handleevent(self, event_data):
|
||||||
"""
|
"""
|
||||||
|
@ -233,7 +276,8 @@ class MobilityManager(object):
|
||||||
else:
|
else:
|
||||||
self.physnets[netnum].append(node_id)
|
self.physnets[netnum].append(node_id)
|
||||||
|
|
||||||
# TODO: remove need for handling old style message
|
# TODO: remove need for handling old style message
|
||||||
|
|
||||||
def physnodehandlelink(self, message):
|
def physnodehandlelink(self, message):
|
||||||
"""
|
"""
|
||||||
Broker handler. Snoop Link add messages to get
|
Broker handler. Snoop Link add messages to get
|
||||||
|
@ -254,7 +298,8 @@ class MobilityManager(object):
|
||||||
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)
|
self.addphys(nn[0], dummy)
|
||||||
|
|
||||||
# TODO: remove need to handling old style messages
|
# TODO: remove need to handling old style messages
|
||||||
|
|
||||||
def physnodeupdateposition(self, message):
|
def physnodeupdateposition(self, message):
|
||||||
"""
|
"""
|
||||||
Snoop node messages belonging to physical nodes. The dummy object
|
Snoop node messages belonging to physical nodes. The dummy object
|
||||||
|
@ -303,7 +348,7 @@ class WirelessModel(ConfigurableOptions):
|
||||||
bitmap = None
|
bitmap = None
|
||||||
position_callback = None
|
position_callback = None
|
||||||
|
|
||||||
def __init__(self, session, object_id, config=None):
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Create a WirelessModel instance.
|
Create a WirelessModel instance.
|
||||||
|
|
||||||
|
@ -313,8 +358,6 @@ class WirelessModel(ConfigurableOptions):
|
||||||
"""
|
"""
|
||||||
self.session = session
|
self.session = session
|
||||||
self.object_id = object_id
|
self.object_id = object_id
|
||||||
if config:
|
|
||||||
self.set_configs(config, node_id=self.object_id)
|
|
||||||
|
|
||||||
def all_link_data(self, flags):
|
def all_link_data(self, flags):
|
||||||
"""
|
"""
|
||||||
|
@ -337,15 +380,15 @@ class WirelessModel(ConfigurableOptions):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def updateconfig(self):
|
def update_config(self, config):
|
||||||
"""
|
"""
|
||||||
For run-time updates of model config. Returns True when position callback and set link
|
For run-time updates of model config. Returns True when position callback and set link
|
||||||
parameters should be invoked.
|
parameters should be invoked.
|
||||||
|
|
||||||
:return: False
|
:param dict config: configuration values to update
|
||||||
:rtype: bool
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
return False
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BasicRangeModel(WirelessModel):
|
class BasicRangeModel(WirelessModel):
|
||||||
|
@ -355,7 +398,6 @@ class BasicRangeModel(WirelessModel):
|
||||||
the GUI.
|
the GUI.
|
||||||
"""
|
"""
|
||||||
name = "basic_range"
|
name = "basic_range"
|
||||||
configuration_maps = {}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configurations(cls):
|
def configurations(cls):
|
||||||
|
@ -372,7 +414,7 @@ class BasicRangeModel(WirelessModel):
|
||||||
def config_groups(cls):
|
def config_groups(cls):
|
||||||
return "Basic Range Parameters:1-%d" % len(cls.configurations())
|
return "Basic Range Parameters:1-%d" % len(cls.configurations())
|
||||||
|
|
||||||
def __init__(self, session, object_id, config=None):
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Create a BasicRangeModel instance.
|
Create a BasicRangeModel instance.
|
||||||
|
|
||||||
|
@ -386,17 +428,12 @@ class BasicRangeModel(WirelessModel):
|
||||||
self._netifs = {}
|
self._netifs = {}
|
||||||
self._netifslock = threading.Lock()
|
self._netifslock = threading.Lock()
|
||||||
|
|
||||||
# retrieve current configuration
|
|
||||||
config = self.get_configs(node_id=self.object_id)
|
|
||||||
|
|
||||||
self.range = None
|
self.range = None
|
||||||
self.bw = None
|
self.bw = None
|
||||||
self.delay = None
|
self.delay = None
|
||||||
self.loss = None
|
self.loss = None
|
||||||
self.jitter = None
|
self.jitter = None
|
||||||
|
|
||||||
self.values_from_config(config)
|
|
||||||
|
|
||||||
def values_from_config(self, config):
|
def values_from_config(self, config):
|
||||||
"""
|
"""
|
||||||
Values to convert to link parameters.
|
Values to convert to link parameters.
|
||||||
|
@ -546,15 +583,13 @@ class BasicRangeModel(WirelessModel):
|
||||||
c = p1[2] - p2[2]
|
c = p1[2] - p2[2]
|
||||||
return math.hypot(math.hypot(a, b), c)
|
return math.hypot(math.hypot(a, b), c)
|
||||||
|
|
||||||
def updateconfig(self):
|
def update_config(self, config):
|
||||||
"""
|
"""
|
||||||
Configuration has changed during runtime.
|
Configuration has changed during runtime.
|
||||||
|
|
||||||
:param dict config: values to update configuration
|
:param dict config: values to update configuration
|
||||||
:return: was update successful
|
:return: nothing
|
||||||
:rtype: bool
|
|
||||||
"""
|
"""
|
||||||
config = self.get_configs(node_id=self.object_id)
|
|
||||||
self.values_from_config(config)
|
self.values_from_config(config)
|
||||||
self.setlinkparams()
|
self.setlinkparams()
|
||||||
return True
|
return True
|
||||||
|
@ -655,16 +690,15 @@ class WayPointMobility(WirelessModel):
|
||||||
STATE_RUNNING = 1
|
STATE_RUNNING = 1
|
||||||
STATE_PAUSED = 2
|
STATE_PAUSED = 2
|
||||||
|
|
||||||
def __init__(self, session, object_id, config=None):
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Create a WayPointMobility instance.
|
Create a WayPointMobility instance.
|
||||||
|
|
||||||
:param core.session.Session session: CORE session instance
|
:param core.session.Session session: CORE session instance
|
||||||
:param int object_id: object id
|
:param int object_id: object id
|
||||||
:param config: values for this model
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
super(WayPointMobility, self).__init__(session=session, object_id=object_id, config=config)
|
super(WayPointMobility, self).__init__(session=session, object_id=object_id)
|
||||||
|
|
||||||
self.state = self.STATE_STOPPED
|
self.state = self.STATE_STOPPED
|
||||||
self.queue = []
|
self.queue = []
|
||||||
|
@ -957,7 +991,6 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
BonnMotion.
|
BonnMotion.
|
||||||
"""
|
"""
|
||||||
name = "ns2script"
|
name = "ns2script"
|
||||||
configuration_maps = {}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configurations(cls):
|
def configurations(cls):
|
||||||
|
@ -976,7 +1009,7 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
def config_groups(cls):
|
def config_groups(cls):
|
||||||
return "ns-2 Mobility Script Parameters:1-%d" % len(cls.configurations())
|
return "ns-2 Mobility Script Parameters:1-%d" % len(cls.configurations())
|
||||||
|
|
||||||
def __init__(self, session, object_id, config=None):
|
def __init__(self, session, object_id):
|
||||||
"""
|
"""
|
||||||
Creates a Ns2ScriptedMobility instance.
|
Creates a Ns2ScriptedMobility instance.
|
||||||
|
|
||||||
|
@ -984,14 +1017,22 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
:param int object_id: object id
|
:param int object_id: object id
|
||||||
:param config: values
|
:param config: values
|
||||||
"""
|
"""
|
||||||
super(Ns2ScriptedMobility, self).__init__(session=session, object_id=object_id, config=config)
|
super(Ns2ScriptedMobility, self).__init__(session=session, object_id=object_id)
|
||||||
self._netifs = {}
|
self._netifs = {}
|
||||||
self._netifslock = threading.Lock()
|
self._netifslock = threading.Lock()
|
||||||
|
|
||||||
# retrieve current configuration
|
self.file = None
|
||||||
config = self.get_configs(self.object_id)
|
self.refresh_ms = None
|
||||||
|
self.loop = None
|
||||||
|
self.autostart = None
|
||||||
|
self.nodemap = {}
|
||||||
|
self.script_start = None
|
||||||
|
self.script_pause = None
|
||||||
|
self.script_stop = None
|
||||||
|
|
||||||
|
def update_config(self, config):
|
||||||
self.file = config["file"]
|
self.file = config["file"]
|
||||||
|
logger.info("ns-2 scripted mobility configured for WLAN %d using file: %s", self.object_id, self.file)
|
||||||
self.refresh_ms = int(config["refresh_ms"])
|
self.refresh_ms = int(config["refresh_ms"])
|
||||||
self.loop = config["loop"].lower() == "on"
|
self.loop = config["loop"].lower() == "on"
|
||||||
self.autostart = config["autostart"]
|
self.autostart = config["autostart"]
|
||||||
|
@ -999,7 +1040,6 @@ class Ns2ScriptedMobility(WayPointMobility):
|
||||||
self.script_start = config["script_start"]
|
self.script_start = config["script_start"]
|
||||||
self.script_pause = config["script_pause"]
|
self.script_pause = config["script_pause"]
|
||||||
self.script_stop = config["script_stop"]
|
self.script_stop = config["script_stop"]
|
||||||
logger.info("ns-2 scripted mobility configured for WLAN %d using file: %s", object_id, self.file)
|
|
||||||
self.readscriptfile()
|
self.readscriptfile()
|
||||||
self.copywaypoints()
|
self.copywaypoints()
|
||||||
self.setendtime()
|
self.setendtime()
|
||||||
|
|
|
@ -377,17 +377,18 @@ class WlanNode(LxBrNet):
|
||||||
# invokes any netif.poshook
|
# invokes any netif.poshook
|
||||||
netif.setposition(x, y, z)
|
netif.setposition(x, y, z)
|
||||||
|
|
||||||
def setmodel(self, model, config=None):
|
def setmodel(self, model, config):
|
||||||
"""
|
"""
|
||||||
Sets the mobility and wireless model.
|
Sets the mobility and wireless model.
|
||||||
|
|
||||||
:param core.mobility.WirelessModel.cls model: wireless model to set to
|
:param core.mobility.WirelessModel.cls model: wireless model to set to
|
||||||
:param dict config: model configuration
|
:param dict config: configuration for model being set
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logger.info("adding model: %s", model.name)
|
logger.info("adding model: %s", model.name)
|
||||||
if model.config_type == RegisterTlvs.WIRELESS.value:
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
||||||
self.model = model(session=self.session, object_id=self.objid, config=config)
|
self.model = model(session=self.session, object_id=self.objid)
|
||||||
|
self.model.update_config(config)
|
||||||
if self.model.position_callback:
|
if self.model.position_callback:
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
netif.poshook = self.model.position_callback
|
netif.poshook = self.model.position_callback
|
||||||
|
@ -396,7 +397,8 @@ class WlanNode(LxBrNet):
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
self.model.setlinkparams()
|
self.model.setlinkparams()
|
||||||
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
self.mobility = model(session=self.session, object_id=self.objid, config=config)
|
self.mobility = model(session=self.session, object_id=self.objid)
|
||||||
|
self.mobility.update_config(config)
|
||||||
|
|
||||||
def update_mobility(self, config):
|
def update_mobility(self, config):
|
||||||
if not self.mobility:
|
if not self.mobility:
|
||||||
|
|
|
@ -17,6 +17,7 @@ from core import constants
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.api import coreapi
|
from core.api import coreapi
|
||||||
from core.broker import CoreBroker
|
from core.broker import CoreBroker
|
||||||
|
from core.conf import ConfigurableManager
|
||||||
from core.conf import ConfigurableOptions
|
from core.conf import ConfigurableOptions
|
||||||
from core.conf import Configuration
|
from core.conf import Configuration
|
||||||
from core.data import EventData
|
from core.data import EventData
|
||||||
|
@ -94,7 +95,8 @@ class Session(object):
|
||||||
self.options = SessionConfig()
|
self.options = SessionConfig()
|
||||||
if not config:
|
if not config:
|
||||||
config = {}
|
config = {}
|
||||||
self.options.set_configs(config)
|
for key, value in config.iteritems():
|
||||||
|
self.options.set_config(key, value)
|
||||||
self.metadata = SessionMetaData()
|
self.metadata = SessionMetaData()
|
||||||
|
|
||||||
# initialize session feature helpers
|
# initialize session feature helpers
|
||||||
|
@ -1044,7 +1046,7 @@ class Session(object):
|
||||||
node.cmd(data, wait=False)
|
node.cmd(data, wait=False)
|
||||||
|
|
||||||
|
|
||||||
class SessionConfig(ConfigurableOptions):
|
class SessionConfig(ConfigurableManager, ConfigurableOptions):
|
||||||
"""
|
"""
|
||||||
Session configuration object.
|
Session configuration object.
|
||||||
"""
|
"""
|
||||||
|
@ -1075,10 +1077,12 @@ class SessionConfig(ConfigurableOptions):
|
||||||
return "Options:1-%d" % len(cls.configurations())
|
return "Options:1-%d" % len(cls.configurations())
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.set_configs()
|
super(SessionConfig, self).__init__()
|
||||||
|
self.set_configs(self.default_values())
|
||||||
|
|
||||||
def get_config(cls, _id, node_id=ConfigurableOptions._default_node, default=None):
|
def get_config(self, _id, node_id=ConfigurableManager._default_node,
|
||||||
value = super(SessionConfig, cls).get_config(_id, node_id, default)
|
config_type=ConfigurableManager._default_type, default=None):
|
||||||
|
value = super(SessionConfig, self).get_config(_id, node_id, config_type, default)
|
||||||
if value == "":
|
if value == "":
|
||||||
value = default
|
value = default
|
||||||
return value
|
return value
|
||||||
|
@ -1096,7 +1100,7 @@ class SessionConfig(ConfigurableOptions):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class SessionMetaData(ConfigurableOptions):
|
class SessionMetaData(ConfigurableManager):
|
||||||
"""
|
"""
|
||||||
Metadata is simply stored in a configs[] dict. Key=value pairs are
|
Metadata is simply stored in a configs[] dict. Key=value pairs are
|
||||||
passed in from configure messages destined to the "metadata" object.
|
passed in from configure messages destined to the "metadata" object.
|
||||||
|
|
|
@ -72,7 +72,12 @@ class CoreDocumentParser0(object):
|
||||||
"""
|
"""
|
||||||
Helper to return tuple of attributes common to nodes and nets.
|
Helper to return tuple of attributes common to nodes and nets.
|
||||||
"""
|
"""
|
||||||
node_id = int(obj.getAttribute("id"))
|
node_id = obj.getAttribute("id")
|
||||||
|
try:
|
||||||
|
node_id = int(node_id)
|
||||||
|
except:
|
||||||
|
logger.debug("parsing node without integer id: %s", node_id)
|
||||||
|
|
||||||
name = str(obj.getAttribute("name"))
|
name = str(obj.getAttribute("name"))
|
||||||
node_type = str(obj.getAttribute("type"))
|
node_type = str(obj.getAttribute("type"))
|
||||||
return node_id, name, node_type
|
return node_id, name, node_type
|
||||||
|
@ -205,7 +210,8 @@ class CoreDocumentParser0(object):
|
||||||
|
|
||||||
# TODO: assign other config managers here
|
# TODO: assign other config managers here
|
||||||
if mgr:
|
if mgr:
|
||||||
mgr.setconfig_keyvalues(nodenum, name, kvs)
|
for k, v in kvs:
|
||||||
|
mgr.set_config(k, v, node_id=nodenum, config_type=name)
|
||||||
|
|
||||||
def parsenetem(self, model, obj, kvs):
|
def parsenetem(self, model, obj, kvs):
|
||||||
"""
|
"""
|
||||||
|
@ -218,7 +224,6 @@ class CoreDocumentParser0(object):
|
||||||
# nodes and interfaces do not exist yet, at this point of the parsing,
|
# nodes and interfaces do not exist yet, at this point of the parsing,
|
||||||
# save (key, value) pairs for later
|
# save (key, value) pairs for later
|
||||||
try:
|
try:
|
||||||
# kvs = map(lambda(k, v): (int(v)), kvs)
|
|
||||||
kvs = map(self.numericvalue, kvs)
|
kvs = map(self.numericvalue, kvs)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.warn("error parsing link parameters for '%s' on '%s'", ifname, peer)
|
logger.warn("error parsing link parameters for '%s' on '%s'", ifname, peer)
|
||||||
|
@ -394,7 +399,7 @@ class CoreDocumentParser0(object):
|
||||||
v = str(param.getAttribute("value"))
|
v = str(param.getAttribute("value"))
|
||||||
if v == '':
|
if v == '':
|
||||||
v = xmlutils.get_text_child(param) # allow attribute/text for newlines
|
v = xmlutils.get_text_child(param) # allow attribute/text for newlines
|
||||||
setattr(self.session.options, k, v)
|
self.session.options.set_config(k, v)
|
||||||
hooks = xmlutils.get_one_element(self.meta, "Hooks")
|
hooks = xmlutils.get_one_element(self.meta, "Hooks")
|
||||||
if hooks:
|
if hooks:
|
||||||
self.parsehooks(hooks)
|
self.parsehooks(hooks)
|
||||||
|
@ -405,4 +410,4 @@ class CoreDocumentParser0(object):
|
||||||
v = str(param.getAttribute("value"))
|
v = str(param.getAttribute("value"))
|
||||||
if v == '':
|
if v == '':
|
||||||
v = xmlutils.get_text_child(param)
|
v = xmlutils.get_text_child(param)
|
||||||
self.session.metadata.add_item(k, v)
|
self.session.metadata.set_config(k, v)
|
||||||
|
|
|
@ -210,12 +210,11 @@ class CoreDocumentParser1(object):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
logger.info("setting wireless link params: node(%s) model(%s) mobility_model(%s)",
|
logger.info("setting wireless link params: node(%s) model(%s) mobility_model(%s)",
|
||||||
nodenum, model_name, mobility_model_name)
|
nodenum, model_name, mobility_model_name)
|
||||||
model_class = mgr.get_model_class(model_name)
|
mgr.set_model_config(node_id=nodenum, model_name=model_name, config=link_params)
|
||||||
model_class.set_configs(link_params, node_id=nodenum)
|
|
||||||
|
|
||||||
if mobility_model_name and mobility_params:
|
if mobility_model_name and mobility_params:
|
||||||
model_class = mgr.get_model_class(mobility_model_name)
|
self.session.mobility.set_model_config(node_id=nodenum, model_name=mobility_model_name,
|
||||||
model_class.set_configs(mobility_params, node_id=nodenum)
|
config=mobility_params)
|
||||||
|
|
||||||
def link_layer2_devices(self, device1, ifname1, device2, ifname2):
|
def link_layer2_devices(self, device1, ifname1, device2, ifname2):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,7 +6,7 @@ import pwd
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.coreobj import PyCoreNet
|
from core.coreobj import PyCoreNet
|
||||||
from core.coreobj import PyCoreNode
|
from core.coreobj import PyCoreNode
|
||||||
from core.enumerations import RegisterTlvs
|
from core.enumerations import RegisterTlvs, EventTypes
|
||||||
from core.xml import xmlutils
|
from core.xml import xmlutils
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ class CoreDocumentWriter0(Document):
|
||||||
self.populatefromsession()
|
self.populatefromsession()
|
||||||
|
|
||||||
def populatefromsession(self):
|
def populatefromsession(self):
|
||||||
self.session.emane.setup() # not during runtime?
|
if self.session.state != EventTypes.RUNTIME_STATE.value:
|
||||||
|
self.session.emane.setup() # not during runtime?
|
||||||
self.addorigin()
|
self.addorigin()
|
||||||
self.adddefaultservices()
|
self.adddefaultservices()
|
||||||
self.addnets()
|
self.addnets()
|
||||||
|
@ -136,14 +137,14 @@ class CoreDocumentWriter0(Document):
|
||||||
for m, conf in configs:
|
for m, conf in configs:
|
||||||
model = self.createElement("model")
|
model = self.createElement("model")
|
||||||
n.appendChild(model)
|
n.appendChild(model)
|
||||||
model.setAttribute("name", m._name)
|
model.setAttribute("name", m.name)
|
||||||
type = "wireless"
|
type = "wireless"
|
||||||
if m._type == RegisterTlvs.MOBILITY.value:
|
if m.config_type == RegisterTlvs.MOBILITY.value:
|
||||||
type = "mobility"
|
type = "mobility"
|
||||||
model.setAttribute("type", type)
|
model.setAttribute("type", type)
|
||||||
for i, k in enumerate(m.getnames()):
|
|
||||||
|
for k, value in conf.iteritems():
|
||||||
key = self.createElement(k)
|
key = self.createElement(k)
|
||||||
value = conf[i]
|
|
||||||
if value is None:
|
if value is None:
|
||||||
value = ""
|
value = ""
|
||||||
key.appendChild(self.createTextNode("%s" % value))
|
key.appendChild(self.createTextNode("%s" % value))
|
||||||
|
@ -193,8 +194,8 @@ class CoreDocumentWriter0(Document):
|
||||||
# could use ifc.params, transport_type
|
# could use ifc.params, transport_type
|
||||||
self.addaddresses(i, ifc)
|
self.addaddresses(i, ifc)
|
||||||
# per-interface models
|
# per-interface models
|
||||||
if netmodel and netmodel._name[:6] == "emane_":
|
if netmodel and netmodel.name[:6] == "emane_":
|
||||||
cfg = netmodel.getifcconfig(node.objid, ifc)
|
cfg = self.session.emane.getifcconfig(node.objid, ifc, netmodel.name)
|
||||||
if cfg:
|
if cfg:
|
||||||
self.addmodels(i, ((netmodel, cfg),))
|
self.addmodels(i, ((netmodel, cfg),))
|
||||||
|
|
||||||
|
|
|
@ -654,7 +654,7 @@ class DeviceElement(NamedXmlElement):
|
||||||
# per-interface models
|
# per-interface models
|
||||||
# XXX Remove???
|
# XXX Remove???
|
||||||
if netmodel and netmodel.name[:6] == "emane_":
|
if netmodel and netmodel.name[:6] == "emane_":
|
||||||
cfg = netmodel.getifcconfig(device_object.objid, interface_object)
|
cfg = self.coreSession.emane.getifcconfig(device_object.objid, interface_object, netmodel.name)
|
||||||
if cfg:
|
if cfg:
|
||||||
interface_element.addModels(((netmodel, cfg),))
|
interface_element.addModels(((netmodel, cfg),))
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ def example(options):
|
||||||
|
|
||||||
# create wlan network node
|
# create wlan network node
|
||||||
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
wlan.setmodel(BasicRangeModel)
|
session.mobility.set_model(wlan, BasicRangeModel)
|
||||||
|
|
||||||
# create nodes
|
# create nodes
|
||||||
wireless_nodes = []
|
wireless_nodes = []
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from core.conf import ConfigurableOptions
|
from core.conf import ConfigurableOptions, ConfigurableManager
|
||||||
from core.conf import Configuration
|
from core.conf import Configuration
|
||||||
from core.enumerations import ConfigDataTypes
|
from core.enumerations import ConfigDataTypes
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class TestConf:
|
||||||
|
|
||||||
def test_nodes(self):
|
def test_nodes(self):
|
||||||
# given
|
# given
|
||||||
config_manager = TestConfigurableOptions()
|
config_manager = ConfigurableManager()
|
||||||
test_config = {1: 2}
|
test_config = {1: 2}
|
||||||
node_id = 1
|
node_id = 1
|
||||||
config_manager.set_configs(test_config)
|
config_manager.set_configs(test_config)
|
||||||
|
@ -58,7 +58,7 @@ class TestConf:
|
||||||
|
|
||||||
def test_config_reset_all(self):
|
def test_config_reset_all(self):
|
||||||
# given
|
# given
|
||||||
config_manager = TestConfigurableOptions()
|
config_manager = ConfigurableManager()
|
||||||
test_config = {1: 2}
|
test_config = {1: 2}
|
||||||
node_id = 1
|
node_id = 1
|
||||||
config_manager.set_configs(test_config)
|
config_manager.set_configs(test_config)
|
||||||
|
@ -68,11 +68,11 @@ class TestConf:
|
||||||
config_manager.config_reset()
|
config_manager.config_reset()
|
||||||
|
|
||||||
# then
|
# then
|
||||||
assert not config_manager.configuration_maps
|
assert not config_manager._configuration_maps
|
||||||
|
|
||||||
def test_config_reset_node(self):
|
def test_config_reset_node(self):
|
||||||
# given
|
# given
|
||||||
config_manager = TestConfigurableOptions()
|
config_manager = ConfigurableManager()
|
||||||
test_config = {1: 2}
|
test_config = {1: 2}
|
||||||
node_id = 1
|
node_id = 1
|
||||||
config_manager.set_configs(test_config)
|
config_manager.set_configs(test_config)
|
||||||
|
@ -82,12 +82,12 @@ class TestConf:
|
||||||
config_manager.config_reset(node_id)
|
config_manager.config_reset(node_id)
|
||||||
|
|
||||||
# then
|
# then
|
||||||
assert node_id not in config_manager.configuration_maps
|
assert node_id not in config_manager._configuration_maps
|
||||||
assert config_manager.get_configs()
|
assert config_manager.get_configs()
|
||||||
|
|
||||||
def test_configs_setget(self):
|
def test_configs_setget(self):
|
||||||
# given
|
# given
|
||||||
config_manager = TestConfigurableOptions()
|
config_manager = ConfigurableManager()
|
||||||
test_config = {1: 2}
|
test_config = {1: 2}
|
||||||
node_id = 1
|
node_id = 1
|
||||||
config_manager.set_configs(test_config)
|
config_manager.set_configs(test_config)
|
||||||
|
@ -103,7 +103,7 @@ class TestConf:
|
||||||
|
|
||||||
def test_config_setget(self):
|
def test_config_setget(self):
|
||||||
# given
|
# given
|
||||||
config_manager = TestConfigurableOptions()
|
config_manager = ConfigurableManager()
|
||||||
name = "test"
|
name = "test"
|
||||||
value = "1"
|
value = "1"
|
||||||
node_id = 1
|
node_id = 1
|
||||||
|
|
|
@ -5,24 +5,21 @@ Unit tests for testing basic CORE networks.
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import threading
|
import threading
|
||||||
from xml.etree import ElementTree
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from mock import MagicMock
|
from mock import MagicMock
|
||||||
|
|
||||||
from core.emulator.emudata import NodeOptions
|
from core.emulator.emudata import NodeOptions
|
||||||
from core.enumerations import MessageFlags, NodeTypes
|
from core.enumerations import MessageFlags
|
||||||
from core.mobility import BasicRangeModel, Ns2ScriptedMobility
|
from core.enumerations import NodeTypes
|
||||||
|
from core.mobility import BasicRangeModel
|
||||||
|
from core.mobility import Ns2ScriptedMobility
|
||||||
from core.netns.vnodeclient import VnodeClient
|
from core.netns.vnodeclient import VnodeClient
|
||||||
from core.service import ServiceManager
|
from core.service import ServiceManager
|
||||||
|
|
||||||
_PATH = os.path.abspath(os.path.dirname(__file__))
|
_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||||
_SERVICES_PATH = os.path.join(_PATH, "myservices")
|
_SERVICES_PATH = os.path.join(_PATH, "myservices")
|
||||||
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
|
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
|
||||||
_XML_VERSIONS = [
|
|
||||||
"0.0",
|
|
||||||
"1.0"
|
|
||||||
]
|
|
||||||
_WIRED = [
|
_WIRED = [
|
||||||
NodeTypes.PEER_TO_PEER,
|
NodeTypes.PEER_TO_PEER,
|
||||||
NodeTypes.HUB,
|
NodeTypes.HUB,
|
||||||
|
@ -93,60 +90,6 @@ class TestCore:
|
||||||
status = ping(node_one, node_two, ip_prefixes)
|
status = ping(node_one, node_two, ip_prefixes)
|
||||||
assert not status
|
assert not status
|
||||||
|
|
||||||
@pytest.mark.parametrize("version", _XML_VERSIONS)
|
|
||||||
def test_xml(self, session, tmpdir, version, ip_prefixes):
|
|
||||||
"""
|
|
||||||
Test xml client methods.
|
|
||||||
|
|
||||||
:param session: session for test
|
|
||||||
:param tmpdir: tmpdir to create data in
|
|
||||||
:param str version: xml version to write and parse
|
|
||||||
:param ip_prefixes: generates ip addresses for nodes
|
|
||||||
"""
|
|
||||||
# create ptp
|
|
||||||
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
|
|
||||||
|
|
||||||
# create nodes
|
|
||||||
node_one = session.add_node()
|
|
||||||
node_two = session.add_node()
|
|
||||||
|
|
||||||
# link nodes to ptp net
|
|
||||||
for node in [node_one, node_two]:
|
|
||||||
interface = ip_prefixes.create_interface(node)
|
|
||||||
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
|
|
||||||
|
|
||||||
# instantiate session
|
|
||||||
session.instantiate()
|
|
||||||
|
|
||||||
# get ids for nodes
|
|
||||||
n1_id = node_one.objid
|
|
||||||
n2_id = node_two.objid
|
|
||||||
|
|
||||||
# save xml
|
|
||||||
xml_file = tmpdir.join("session.xml")
|
|
||||||
file_path = xml_file.strpath
|
|
||||||
session.save_xml(file_path, version)
|
|
||||||
|
|
||||||
# verify xml file was created and can be parsed
|
|
||||||
assert xml_file.isfile()
|
|
||||||
assert ElementTree.parse(file_path)
|
|
||||||
|
|
||||||
# stop current session, clearing data
|
|
||||||
session.shutdown()
|
|
||||||
|
|
||||||
# verify nodes have been removed from session
|
|
||||||
with pytest.raises(KeyError):
|
|
||||||
assert not session.get_object(n1_id)
|
|
||||||
with pytest.raises(KeyError):
|
|
||||||
assert not session.get_object(n2_id)
|
|
||||||
|
|
||||||
# load saved xml
|
|
||||||
session.open_xml(file_path, start=True)
|
|
||||||
|
|
||||||
# verify nodes have been recreated
|
|
||||||
assert session.get_object(n1_id)
|
|
||||||
assert session.get_object(n2_id)
|
|
||||||
|
|
||||||
def test_vnode_client(self, session, ip_prefixes):
|
def test_vnode_client(self, session, ip_prefixes):
|
||||||
"""
|
"""
|
||||||
Test vnode client methods.
|
Test vnode client methods.
|
||||||
|
@ -257,7 +200,7 @@ class TestCore:
|
||||||
|
|
||||||
# create wlan
|
# create wlan
|
||||||
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
wlan_node.setmodel(BasicRangeModel)
|
session.mobility.set_model(wlan_node, BasicRangeModel)
|
||||||
|
|
||||||
# create nodes
|
# create nodes
|
||||||
node_options = NodeOptions()
|
node_options = NodeOptions()
|
||||||
|
@ -290,7 +233,7 @@ class TestCore:
|
||||||
|
|
||||||
# create wlan
|
# create wlan
|
||||||
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
wlan_node.setmodel(BasicRangeModel)
|
session.mobility.set_model(wlan_node, BasicRangeModel)
|
||||||
|
|
||||||
# create nodes
|
# create nodes
|
||||||
node_options = NodeOptions()
|
node_options = NodeOptions()
|
||||||
|
@ -317,7 +260,7 @@ class TestCore:
|
||||||
"script_pause": "",
|
"script_pause": "",
|
||||||
"script_stop": "",
|
"script_stop": "",
|
||||||
}
|
}
|
||||||
wlan_node.setmodel(Ns2ScriptedMobility, config)
|
session.mobility.set_model(wlan_node, Ns2ScriptedMobility, config)
|
||||||
|
|
||||||
# add handler for receiving node updates
|
# add handler for receiving node updates
|
||||||
event = threading.Event()
|
event = threading.Event()
|
||||||
|
|
203
daemon/tests/test_xml.py
Normal file
203
daemon/tests/test_xml.py
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.emane.ieee80211abg import EmaneIeee80211abgModel
|
||||||
|
from core.emulator.emudata import NodeOptions
|
||||||
|
from core.enumerations import NodeTypes
|
||||||
|
from core.mobility import BasicRangeModel
|
||||||
|
|
||||||
|
_XML_VERSIONS = [
|
||||||
|
"0.0",
|
||||||
|
"1.0"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestXml:
|
||||||
|
@pytest.mark.parametrize("version", _XML_VERSIONS)
|
||||||
|
def test_xml_ptp(self, session, tmpdir, version, ip_prefixes):
|
||||||
|
"""
|
||||||
|
Test xml client methods for a ptp neetwork.
|
||||||
|
|
||||||
|
:param session: session for test
|
||||||
|
:param tmpdir: tmpdir to create data in
|
||||||
|
:param str version: xml version to write and parse
|
||||||
|
:param ip_prefixes: generates ip addresses for nodes
|
||||||
|
"""
|
||||||
|
# create ptp
|
||||||
|
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
|
||||||
|
|
||||||
|
# create nodes
|
||||||
|
node_one = session.add_node()
|
||||||
|
node_two = session.add_node()
|
||||||
|
|
||||||
|
# link nodes to ptp net
|
||||||
|
for node in [node_one, node_two]:
|
||||||
|
interface = ip_prefixes.create_interface(node)
|
||||||
|
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
|
||||||
|
|
||||||
|
# instantiate session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# get ids for nodes
|
||||||
|
n1_id = node_one.objid
|
||||||
|
n2_id = node_two.objid
|
||||||
|
|
||||||
|
# save xml
|
||||||
|
xml_file = tmpdir.join("session.xml")
|
||||||
|
file_path = xml_file.strpath
|
||||||
|
session.save_xml(file_path, version)
|
||||||
|
|
||||||
|
# verify xml file was created and can be parsed
|
||||||
|
assert xml_file.isfile()
|
||||||
|
assert ElementTree.parse(file_path)
|
||||||
|
|
||||||
|
# stop current session, clearing data
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
# verify nodes have been removed from session
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n1_id)
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n2_id)
|
||||||
|
|
||||||
|
# load saved xml
|
||||||
|
session.open_xml(file_path, start=True)
|
||||||
|
|
||||||
|
# verify nodes have been recreated
|
||||||
|
assert session.get_object(n1_id)
|
||||||
|
assert session.get_object(n2_id)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", _XML_VERSIONS)
|
||||||
|
def test_xml_mobility(self, session, tmpdir, version, ip_prefixes):
|
||||||
|
"""
|
||||||
|
Test xml client methods for mobility.
|
||||||
|
|
||||||
|
:param session: session for test
|
||||||
|
:param tmpdir: tmpdir to create data in
|
||||||
|
:param str version: xml version to write and parse
|
||||||
|
:param ip_prefixes: generates ip addresses for nodes
|
||||||
|
"""
|
||||||
|
# create wlan
|
||||||
|
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
|
||||||
|
session.mobility.set_model(wlan_node, BasicRangeModel, {"test": "1"})
|
||||||
|
|
||||||
|
# create nodes
|
||||||
|
node_options = NodeOptions()
|
||||||
|
node_options.set_position(0, 0)
|
||||||
|
node_one = session.create_wireless_node(node_options=node_options)
|
||||||
|
node_two = session.create_wireless_node(node_options=node_options)
|
||||||
|
|
||||||
|
# link nodes
|
||||||
|
for node in [node_one, node_two]:
|
||||||
|
interface = ip_prefixes.create_interface(node)
|
||||||
|
session.add_link(node.objid, wlan_node.objid, interface_one=interface)
|
||||||
|
|
||||||
|
# link nodes in wlan
|
||||||
|
session.wireless_link_all(wlan_node, [node_one, node_two])
|
||||||
|
|
||||||
|
# instantiate session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# get ids for nodes
|
||||||
|
wlan_id = wlan_node.objid
|
||||||
|
n1_id = node_one.objid
|
||||||
|
n2_id = node_two.objid
|
||||||
|
|
||||||
|
# save xml
|
||||||
|
xml_file = tmpdir.join("session.xml")
|
||||||
|
file_path = xml_file.strpath
|
||||||
|
session.save_xml(file_path, version)
|
||||||
|
|
||||||
|
# verify xml file was created and can be parsed
|
||||||
|
assert xml_file.isfile()
|
||||||
|
assert ElementTree.parse(file_path)
|
||||||
|
|
||||||
|
# stop current session, clearing data
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
# verify nodes have been removed from session
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n1_id)
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n2_id)
|
||||||
|
|
||||||
|
# load saved xml
|
||||||
|
session.open_xml(file_path, start=True)
|
||||||
|
|
||||||
|
# retrieve configuration we set originally
|
||||||
|
value = str(session.mobility.get_config("test", wlan_id, BasicRangeModel.name))
|
||||||
|
|
||||||
|
# verify nodes and configuration were restored
|
||||||
|
assert session.get_object(n1_id)
|
||||||
|
assert session.get_object(n2_id)
|
||||||
|
assert session.get_object(wlan_id)
|
||||||
|
assert value == "1"
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("version", ["1.0"])
|
||||||
|
def test_xml_emane(self, session, tmpdir, version, ip_prefixes):
|
||||||
|
"""
|
||||||
|
Test xml client methods for emane.
|
||||||
|
|
||||||
|
:param session: session for test
|
||||||
|
:param tmpdir: tmpdir to create data in
|
||||||
|
:param str version: xml version to write and parse
|
||||||
|
:param ip_prefixes: generates ip addresses for nodes
|
||||||
|
"""
|
||||||
|
# create emane node for networking the core nodes
|
||||||
|
emane_network = session.create_emane_network(
|
||||||
|
EmaneIeee80211abgModel,
|
||||||
|
geo_reference=(47.57917, -122.13232, 2.00000),
|
||||||
|
config={"test": "1"}
|
||||||
|
)
|
||||||
|
emane_network.setposition(x=80, y=50)
|
||||||
|
|
||||||
|
# create nodes
|
||||||
|
node_options = NodeOptions()
|
||||||
|
node_options.set_position(150, 150)
|
||||||
|
node_one = session.create_wireless_node(node_options=node_options)
|
||||||
|
node_options.set_position(300, 150)
|
||||||
|
node_two = session.create_wireless_node(node_options=node_options)
|
||||||
|
|
||||||
|
for i, node in enumerate([node_one, node_two]):
|
||||||
|
node.setposition(x=150 * (i + 1), y=150)
|
||||||
|
interface = ip_prefixes.create_interface(node)
|
||||||
|
session.add_link(node.objid, emane_network.objid, interface_one=interface)
|
||||||
|
|
||||||
|
# instantiate session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# get ids for nodes
|
||||||
|
emane_id = emane_network.objid
|
||||||
|
n1_id = node_one.objid
|
||||||
|
n2_id = node_two.objid
|
||||||
|
|
||||||
|
# save xml
|
||||||
|
xml_file = tmpdir.join("session.xml")
|
||||||
|
file_path = xml_file.strpath
|
||||||
|
session.save_xml(file_path, version)
|
||||||
|
|
||||||
|
# verify xml file was created and can be parsed
|
||||||
|
assert xml_file.isfile()
|
||||||
|
assert ElementTree.parse(file_path)
|
||||||
|
|
||||||
|
# stop current session, clearing data
|
||||||
|
session.shutdown()
|
||||||
|
|
||||||
|
# verify nodes have been removed from session
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n1_id)
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
assert not session.get_object(n2_id)
|
||||||
|
|
||||||
|
# load saved xml
|
||||||
|
session.open_xml(file_path, start=True)
|
||||||
|
|
||||||
|
# retrieve configuration we set originally
|
||||||
|
value = str(session.emane.get_config("test", emane_id, EmaneIeee80211abgModel.name))
|
||||||
|
|
||||||
|
# verify nodes and configuration were restored
|
||||||
|
assert session.get_object(n1_id)
|
||||||
|
assert session.get_object(n2_id)
|
||||||
|
assert session.get_object(emane_id)
|
||||||
|
assert value == "1"
|
Loading…
Add table
Reference in a new issue