refactored configuration managers and options into a single class, now the models that can be configured will deal with and handle configurations for nodes themselves

This commit is contained in:
Blake J. Harnden 2018-06-11 12:26:12 -07:00
parent 1b843e2868
commit 52230bc026
22 changed files with 284 additions and 422 deletions

View file

@ -94,80 +94,55 @@ class ConfigurableOptions(object):
# unique name to receive configuration changes
name = None
bitmap = None
configuration_maps = None
_default_node = -1
@classmethod
def configurations(cls):
"""
Returns configuration options supported by this class.
:return: list of configuration options
:rtype: list[Configuration]
"""
return []
@classmethod
def config_groups(cls):
"""
String formatted to specify configuration groupings, using list index positions.
Example:
"Group1:start-stop|Group2:start-stop"
:return: config groups
:rtype: str
"""
return None
@classmethod
def default_values(cls):
"""
Retrieves default values for configurations.
:return: mapping of configuration options that can also be iterated in order of definition
:rtype: OrderedDict
"""
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}
class ConfigurableManager(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):
logger.debug("resetting all configurations: %s", self.__class__.__name__)
@classmethod
def config_reset(cls, node_id=None):
if not node_id:
self._configuration_maps.clear()
elif node_id in self._configuration_maps:
self._configuration_maps.pop(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)
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
@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
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
@classmethod
def get_config(cls, _id, node_id=_default_node):
logger.debug("getting config for node(%s): %s", node_id, _id)
node_configs = cls.get_configs(node_id)
return node_configs.get(_id)
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)
@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
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())
@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())

View file

@ -955,7 +955,6 @@ class CoreHandler(SocketServer.BaseRequestHandler):
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:
@ -1139,7 +1138,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
logger.error(error_message)
else:
if opaque is None:
values = values.split('|')
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)
@ -1151,7 +1150,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
# 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)
logger.warn("request for unknown service: %s", u)
if services:
svc = services[0]
@ -1187,10 +1186,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
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 = model_class.get_configs(node_id=node_id)
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
replies.append(config_response)
elif message_type == ConfigFlags.RESET:
@ -1207,15 +1203,12 @@ class CoreHandler(SocketServer.BaseRequestHandler):
logger.warn("model class does not exist: %s", object_name)
return []
config = model_class.default_values()
if values_str:
parsed_config = ConfigShim.str_to_dict(values_str)
for name, value in parsed_config.iteritems():
config[name] = value
else:
config = self.session.mobility.get_configs(node_id, object_name) or config
model_class.set_configs(parsed_config, node_id=node_id)
self.session.mobility.set_configs(config, node_id, object_name)
config = model_class.get_configs(node_id)
model_class.set_configs(config, node_id=node_id)
return replies
@ -1282,10 +1275,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
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 = model_class.get_configs(node_id=node_id)
config_response = ConfigShim.config_data(0, node_id, typeflags, model_class, config)
replies.append(config_response)
elif message_type == ConfigFlags.RESET:
@ -1302,15 +1292,13 @@ class CoreHandler(SocketServer.BaseRequestHandler):
logger.warn("model class does not exist: %s", object_name)
return []
config = model_class.default_values()
if values_str:
parsed_config = ConfigShim.str_to_dict(values_str)
for name, value in parsed_config.iteritems():
config[name] = value
else:
config = self.session.emane.get_configs(node_id, object_name) or config
model_class.set_configs(parsed_config, node_id=node_id)
self.session.emane.set_configs(config, node_id, object_name)
config = model_class.get_configs(node_id)
model_class.set_configs(config, node_id=node_id)
self.session.emane.set_node_model(node_id, object_name)
return replies

View file

@ -8,6 +8,7 @@ from core.enumerations import ConfigDataTypes
class EmaneBypassModel(emanemodel.EmaneModel):
name = "emane_bypass"
configuration_maps = {}
# values to ignore, when writing xml files
config_ignore = {"none"}

View file

@ -29,6 +29,7 @@ def convert_none(x):
class EmaneCommEffectModel(emanemodel.EmaneModel):
name = "emane_commeffect"
configuration_maps = {}
shim_library = "commeffectshim"
shim_xml = "/usr/share/emane/manifest/commeffectshim.xml"
@ -54,8 +55,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
:param interface: interface for the emane node
:return: nothing
"""
default_values = self.default_values()
config = emane_manager.getifcconfig(self.object_id, self.name, default_values, interface)
config = self.getifcconfig(self.object_id, interface)
if not config:
return

View file

@ -12,7 +12,6 @@ from core import logger
from core.api import coreapi
from core.api import dataconversion
from core.conf import ConfigShim
from core.conf import ConfigurableManager
from core.conf import Configuration
from core.emane import emanemanifest
from core.emane.bypass import EmaneBypassModel
@ -54,7 +53,7 @@ EMANE_MODELS = [
]
class EmaneManager(ConfigurableManager):
class EmaneManager(object):
"""
EMANE controller object. Lives in a Session instance and is used for
building EMANE config files from all of the EmaneNode objects in this
@ -74,7 +73,6 @@ class EmaneManager(ConfigurableManager):
:param core.session.Session session: session this manager is tied to
:return: nothing
"""
super(EmaneManager, self).__init__()
self.session = session
self._emane_nodes = {}
self._emane_node_lock = threading.Lock()
@ -87,8 +85,10 @@ class EmaneManager(ConfigurableManager):
self.eventmonthread = None
# model for global EMANE configuration options
self.emane_config = EmaneGlobalModel(session, None)
self.set_configs(self.emane_config.default_values())
self.emane_config = EmaneGlobalModel(session)
# store the last configured model for a node, used during startup
self.node_models = {}
session.broker.handlers.add(self.handledistributed)
self.service = None
@ -98,9 +98,19 @@ class EmaneManager(ConfigurableManager):
self.service = None
self.emane_check()
def set_node_model(self, node_id, model_name):
if model_name not in self._modelclsmap:
raise ValueError("unknown emane model: %s", model_name)
self.node_models[node_id] = model_name
def config_reset(self, node_id=None):
super(EmaneManager, self).config_reset(node_id)
self.set_configs(self.emane_config.default_values())
# clear and reset current emane configuration
self.emane_config.config_reset()
self.emane_config.set_configs()
# reset model configurations
for model_class in self._modelclsmap.itervalues():
model_class.config_reset(node_id=node_id)
def emane_models(self):
return self._modelclsmap.keys()
@ -152,8 +162,8 @@ class EmaneManager(ConfigurableManager):
return
# Get the control network to be used for events
group, port = self.get_config("eventservicegroup").split(":")
self.event_device = self.get_config("eventservicedevice")
group, port = self.emane_config.get_config("eventservicegroup").split(":")
self.event_device = self.emane_config.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)
@ -212,54 +222,14 @@ class EmaneManager(ConfigurableManager):
"""
Used with XML export.
"""
configs = self.get_all_configs(node.objid)
models = []
for model_name, config in configs.iteritems():
model_class = self._modelclsmap[model_name]
models.append((model_class, config))
for model_class in self._modelclsmap.itervalues():
if node.objid in model_class.configuration_maps:
config = model_class.get_configs(node_id=node.objid)
models.append((model_class, config))
logger.debug("emane models: %s", models)
return models
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.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
# 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
config = self.get_configs(key, config_type)
# 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
config = self.get_configs(node_id, config_type) or default_values
return config
def setup(self):
"""
Populate self._objs with EmaneNodes; perform distributed setup;
@ -284,7 +254,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:
otadev = self.get_config("otamanagerdevice")
otadev = self.emane_config.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:
@ -293,7 +263,7 @@ class EmaneManager(ConfigurableManager):
ctrlnet = self.session.add_remove_control_net(net_index=netidx, remove=False, conf_required=False)
self.distributedctrlnet(ctrlnet)
eventdev = self.get_config("eventservicedevice")
eventdev = self.emane_config.get_config("eventservicedevice")
logger.debug("emane event service device: eventdev(%s)", eventdev)
if eventdev != otadev:
netidx = self.session.get_control_net_index(eventdev)
@ -309,11 +279,11 @@ class EmaneManager(ConfigurableManager):
# 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)
value = self.emane_config.get_config(platform_id_start)
if value == default_values[platform_id_start]:
return EmaneManager.NOT_READY
self.setnodemodels()
self.check_node_models()
return EmaneManager.SUCCESS
def startup(self):
@ -441,10 +411,10 @@ class EmaneManager(ConfigurableManager):
emane_node = self._emane_nodes[key]
nemcount += emane_node.numnetif()
nemid = int(self.get_config("nem_id_start"))
nemid = int(self.emane_config.get_config("nem_id_start"))
nemid += nemcount
platformid = int(self.get_config("platform_id_start"))
platformid = int(self.emane_config.get_config("platform_id_start"))
# build an ordered list of servers so platform ID is deterministic
servers = []
@ -463,8 +433,8 @@ class EmaneManager(ConfigurableManager):
platformid += 1
typeflags = ConfigFlags.UPDATE.value
self.set_config("platform_id_start", str(platformid))
self.set_config("nem_id_start", str(nemid))
self.emane_config.set_config("platform_id_start", str(platformid))
self.emane_config.set_config("nem_id_start", str(nemid))
config_data = ConfigShim.config_data(0, None, typeflags, self.emane_config, self.get_configs())
message = dataconversion.convert_config(config_data)
server.sock.send(message)
@ -489,6 +459,7 @@ class EmaneManager(ConfigurableManager):
self.buildnemxml()
self.buildeventservicexml()
# TODO: remove need for tlv messaging
def distributedctrlnet(self, ctrlnet):
"""
Distributed EMANE requires multiple control network prefixes to
@ -566,33 +537,30 @@ class EmaneManager(ConfigurableManager):
with open(pathname, "w") as xml_file:
doc.writexml(writer=xml_file, indent="", addindent=" ", newl="\n", encoding="UTF-8")
def setnodemodels(self):
def check_node_models(self):
"""
Associate EmaneModel classes with EmaneNode nodes. The model
configurations are stored in self.configs.
"""
for key in self._emane_nodes:
self.setnodemodel(key)
for node_id in self._emane_nodes:
emane_node = self._emane_nodes[node_id]
logger.debug("checking emane model for node: %s", node_id)
def setnodemodel(self, node_id):
logger.debug("setting emane models for node: %s", node_id)
node_config_types = self.get_all_configs(node_id)
if not node_config_types:
logger.debug("no emane node model configuration, leaving: %s", node_id)
return False
# skip nodes that already have a model set
if emane_node.model:
logger.debug("node(%s) already has model(%s)", emane_node.objid, emane_node.model.name)
continue
# retrieve and use the last set model configured for this node
# this supports behavior from the legacy gui due to there not being a formal set model message
emane_node = self._emane_nodes[node_id]
models = self.getmodels(emane_node)
if models:
model_class, config = models[-1]
# set model configured for node, due to legacy messaging configuration before nodes exist
model_name = self.node_models.get(node_id)
if not model_name:
logger.error("emane node(%s) has no node model", node_id)
raise ValueError("emane node has no model set")
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)
return True
# no model has been configured for this EmaneNode
return False
def nemlookup(self, nemid):
"""
@ -631,10 +599,10 @@ class EmaneManager(ConfigurableManager):
plat = doc.getElementsByTagName("platform").pop()
if otadev:
self.set_config("otamanagerdevice", otadev)
self.emane_config.set_config("otamanagerdevice", otadev)
if eventdev:
self.set_config("eventservicedevice", eventdev)
self.emane_config.set_config("eventservicedevice", eventdev)
# append all platform options (except starting id) to doc
for configuration in self.emane_config.emulator_config:
@ -642,7 +610,7 @@ class EmaneManager(ConfigurableManager):
if name == "platform_id_start":
continue
value = self.get_config(name)
value = self.emane_config.get_config(name)
param = self.xmlparam(doc, name, value)
plat.appendChild(param)
@ -652,7 +620,7 @@ class EmaneManager(ConfigurableManager):
"""
Build a platform.xml file now that all nodes are configured.
"""
nemid = int(self.get_config("nem_id_start"))
nemid = int(self.emane_config.get_config("nem_id_start"))
platformxmls = {}
# assume self._objslock is already held here
@ -729,7 +697,7 @@ class EmaneManager(ConfigurableManager):
default_values = self.emane_config.default_values()
for name in ["eventservicegroup", "eventservicedevice"]:
a = default_values[name]
b = self.get_config(name)
b = self.emane_config.get_config(name)
if a != b:
need_xml = True
@ -739,12 +707,12 @@ class EmaneManager(ConfigurableManager):
return
try:
group, port = self.get_config("eventservicegroup").split(":")
group, port = self.emane_config.get_config("eventservicegroup").split(":")
except ValueError:
logger.exception("invalid eventservicegroup in EMANE config")
return
dev = self.get_config("eventservicedevice")
dev = self.emane_config.get_config("eventservicedevice")
doc = self.xmldoc("emaneeventmsgsvc")
es = doc.getElementsByTagName("emaneeventmsgsvc").pop()
kvs = (("group", group), ("port", port), ("device", dev), ("mcloop", "1"), ("ttl", "32"))
@ -771,12 +739,12 @@ class EmaneManager(ConfigurableManager):
if realtime:
emanecmd += "-r",
otagroup, otaport = self.get_config("otamanagergroup").split(":")
otadev = self.get_config("otamanagerdevice")
otagroup, otaport = self.emane_config.get_config("otamanagergroup").split(":")
otadev = self.emane_config.get_config("otamanagerdevice")
otanetidx = self.session.get_control_net_index(otadev)
eventgroup, eventport = self.get_config("eventservicegroup").split(":")
eventdev = self.get_config("eventservicedevice")
eventgroup, eventport = self.emane_config.get_config("eventservicegroup").split(":")
eventdev = self.emane_config.get_config("eventservicedevice")
eventservicenetidx = self.session.get_control_net_index(eventdev)
run_emane_on_host = False
@ -1013,6 +981,7 @@ class EmaneGlobalModel(EmaneModel):
_DEFAULT_DEV = "ctrl0"
name = "emane"
configuration_maps = {}
emulator_xml = "/usr/share/emane/manifest/nemmanager.xml"
emulator_defaults = {

View file

@ -70,9 +70,6 @@ class EmaneModel(WirelessModel):
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):
super(EmaneModel, self).__init__(session, object_id)
def build_xml_files(self, emane_manager, interface):
"""
Builds xml files for emane. Includes a nem.xml file that points to both mac.xml and phy.xml definitions.
@ -82,7 +79,7 @@ class EmaneModel(WirelessModel):
:return: nothing
"""
# retrieve configuration values
config = emane_manager.getifcconfig(self.object_id, self.name, self.default_values(), interface)
config = self.getifcconfig(self.object_id, interface)
if not config:
return
@ -270,11 +267,10 @@ class EmaneModel(WirelessModel):
:rtype: str
"""
name = "n%s" % self.object_id
emane_manager = self.session.emane
if interface:
node_id = interface.node.objid
if emane_manager.getifcconfig(node_id, self.name, {}, interface):
if self.getifcconfig(node_id, interface):
name = interface.localname.replace(".", "_")
return "%s%s" % (name, self.name)
@ -353,3 +349,43 @@ class EmaneModel(WirelessModel):
:return: nothing
"""
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

View file

@ -9,6 +9,7 @@ from core.emane import emanemodel
class EmaneIeee80211abgModel(emanemodel.EmaneModel):
# model name
name = "emane_ieee80211abg"
configuration_maps = {}
# mac configuration
mac_library = "ieee80211abgmaclayer"

View file

@ -67,7 +67,13 @@ class EmaneNode(EmaneNet):
def unlink(self, netif1, netif2):
pass
def setmodel(self, model, config):
def updatemodel(self, config):
if not self.model:
raise ValueError("no model set to update for node(%s)", self.objid)
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
self.model.set_configs(config, node_id=self.objid)
def setmodel(self, model, config=None):
"""
set the EmaneModel associated with this node
"""
@ -75,7 +81,7 @@ class EmaneNode(EmaneNet):
if model.config_type == RegisterTlvs.WIRELESS.value:
# EmaneModel really uses values from ConfigurableManager
# when buildnemxml() is called, not during init()
self.model = model(session=self.session, object_id=self.objid)
self.model = model(session=self.session, object_id=self.objid, config=config)
elif model.config_type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, object_id=self.objid, config=config)
@ -174,7 +180,8 @@ class EmaneNode(EmaneNet):
trans.setAttribute("library", "trans%s" % transport_type.lower())
trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0"))
config = emane.get_configs(self.objid, self.model.name)
model_class = emane.get_model_class(self.model.name)
config = model_class.get_configs(self.objid)
logger.debug("transport xml config: %s", config)
flowcontrol = config.get("flowcontrolenable", "0") == "1"

View file

@ -9,6 +9,7 @@ from core.emane import emanemodel
class EmaneRfPipeModel(emanemodel.EmaneModel):
# model name
name = "emane_rfpipe"
configuration_maps = {}
# mac configuration
mac_library = "rfpipemaclayer"

View file

@ -16,6 +16,7 @@ from core.misc import utils
class EmaneTdmaModel(emanemodel.EmaneModel):
# model name
name = "emane_tdma"
configuration_maps = {}
# mac configuration
mac_library = "tdmaeventschedulerradiomodel"
@ -47,7 +48,7 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
:return: nothing
"""
# get configured schedule
config = emane_manager.get_configs(self.object_id, self.name)
config = self.get_configs(self.object_id)
if not config:
return
schedule = config[self.schedule_name]

View file

@ -325,9 +325,6 @@ class EmuSession(Session):
:param core.enumerations.LinkTypes link_type: link type to delete
:return: nothing
"""
# interface data
# interface_one_data, interface_two_data = get_interfaces(link_data)
# get node objects identified by link data
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
@ -390,9 +387,6 @@ class EmuSession(Session):
:param core.emulator.emudata.LinkOptions link_options: data to update link with
:return: nothing
"""
# interface data
# interface_one_data, interface_two_data = get_interfaces(link_data)
# get node objects identified by link data
node_one, node_two, net_one, net_two, tunnel = self._link_nodes(node_one_id, node_two_id)
@ -801,33 +795,9 @@ class EmuSession(Session):
# create and return network
emane_network = self.add_node(_type=NodeTypes.EMANE, node_options=node_options)
self.set_emane_model(emane_network, model)
emane_network.setmodel(model)
return emane_network
def set_emane_model(self, emane_node, emane_model):
"""
Set emane model for a given emane node.
:param emane_node: emane node to set model for
:param emane_model: emane model to set
:return: nothing
"""
config = emane_model.default_values()
emane_node.setmodel(emane_model, config)
self.emane.set_configs(config, emane_node.objid, emane_model.name)
def set_wireless_model(self, node, model):
"""
Convenience method for setting a wireless model.
:param node: node to set wireless model for
:param core.mobility.WirelessModel model: wireless model to set node to
:return: nothing
"""
config = model.default_values()
node.setmodel(model, config)
self.mobility.set_configs(config, node.objid, model.name)
def wireless_link_all(self, network, nodes):
"""
Link all nodes to the provided wireless network.

View file

@ -11,7 +11,6 @@ import time
from core import logger
from core.conf import ConfigurableOptions
from core.conf import Configuration
from core.conf import ConfigurableManager
from core.coreobj import PyCoreNode
from core.data import EventData
from core.data import LinkData
@ -26,7 +25,7 @@ from core.misc import utils
from core.misc.ipaddress import IpAddress
class MobilityManager(ConfigurableManager):
class MobilityManager(object):
"""
Member of session class for handling configuration data for mobility and
range models.
@ -40,7 +39,6 @@ class MobilityManager(ConfigurableManager):
:param core.session.Session session: session this manager is tied to
"""
super(MobilityManager, self).__init__()
self.session = session
# configurations for basic range, indexed by WLAN node number, are stored in configurations
# mapping from model names to their classes
@ -69,13 +67,19 @@ class MobilityManager(ConfigurableManager):
:return: list of model and values tuples for the network node
:rtype: list
"""
configs = self.get_all_configs(node.objid)
models = []
for model_name, config in configs.iteritems():
model_class = self._modelclsmap[model_name]
models.append((model_class, config))
for model_class in self._modelclsmap.itervalues():
if node.objid in model_class.configuration_maps:
config = model_class.get_configs(node_id=node.objid)
models.append((model_class, config))
return models
def nodes(self):
node_ids = set()
for model_class in self._modelclsmap.itervalues():
node_ids |= model_class.nodes()
return node_ids
def startup(self, node_ids=None):
"""
Session is transitioning from instantiation to runtime state.
@ -96,20 +100,13 @@ class MobilityManager(ConfigurableManager):
logger.warn("skipping mobility configuration for unknown node: %s", node_id)
continue
node_configs = self.get_all_configs(node_id)
if not node_configs:
logger.warn("missing mobility configuration for node: %s", node_id)
continue
for model_name in node_configs.iterkeys():
try:
clazz = self._modelclsmap[model_name]
model_config = self.get_configs(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.exception("skipping mobility configuration for unknown model: %s", model_name)
for model_class in self._modelclsmap.itervalues():
logger.debug("model(%s) configurations: %s", model_class, model_class.configuration_maps)
if node_id not in model_class.configuration_maps:
continue
config = model_class.get_configs(node_id=node_id)
logger.info("setting mobility model(%s) to node: %s", model_class.name, config)
node.setmodel(model_class, config)
if self.session.master:
self.installphysnodes(node)
@ -117,37 +114,15 @@ class MobilityManager(ConfigurableManager):
if node.mobility:
self.session.event_loop.add_event(0.0, node.mobility.startup)
def reset(self):
def config_reset(self, node_id=None):
"""
Reset all configs.
:param int node_id: node configuration to reset or None for all configurations
:return: nothing
"""
self.config_reset()
def set_configs(self, config, node_id=None, config_type=None):
"""
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 str config_type: configuration type
:return: nothing
"""
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, config)
except KeyError:
logger.exception("skipping mobility configuration for unknown node %s", node_id)
for model in self._modelclsmap.itervalues():
model.config_reset(node_id=node_id)
def handleevent(self, event_data):
"""
@ -338,6 +313,8 @@ class WirelessModel(ConfigurableOptions):
"""
self.session = session
self.object_id = object_id
if config:
self.set_configs(config, node_id=self.object_id)
def all_link_data(self, flags):
"""
@ -360,12 +337,11 @@ class WirelessModel(ConfigurableOptions):
"""
raise NotImplementedError
def updateconfig(self, config):
def updateconfig(self):
"""
For run-time updates of model config. Returns True when position callback and set link
parameters should be invoked.
:param dict config: value to update
:return: False
:rtype: bool
"""
@ -379,6 +355,7 @@ class BasicRangeModel(WirelessModel):
the GUI.
"""
name = "basic_range"
configuration_maps = {}
@classmethod
def configurations(cls):
@ -409,10 +386,8 @@ class BasicRangeModel(WirelessModel):
self._netifs = {}
self._netifslock = threading.Lock()
# TODO: can this be handled in a better spot
if not config:
config = self.default_values()
self.session.mobility.set_configs(config, node_id=object_id, config_type=self.name)
# retrieve current configuration
config = self.get_configs(node_id=self.object_id)
self.range = None
self.bw = None
@ -571,17 +546,17 @@ class BasicRangeModel(WirelessModel):
c = p1[2] - p2[2]
return math.hypot(math.hypot(a, b), c)
def updateconfig(self, config):
def updateconfig(self):
"""
Configuration has changed during runtime.
MobilityManager.setconfig() -> WlanNode.updatemodel() ->
WirelessModel.updateconfig()
:param dict config: values to update configuration
:return: was update successful
:rtype: bool
"""
config = self.get_configs(node_id=self.object_id)
self.values_from_config(config)
self.setlinkparams()
return True
def create_link_data(self, interface1, interface2, message_type):
@ -840,7 +815,6 @@ class WayPointMobility(WirelessModel):
self.setnodeposition(node, x, y, z)
moved.append(node)
moved_netifs.append(netif)
# self.wlan.model.update(moved)
self.session.mobility.updatewlans(moved, moved_netifs)
def addwaypoint(self, time, nodenum, x, y, z, speed):
@ -914,7 +888,6 @@ class WayPointMobility(WirelessModel):
:return: nothing
"""
# this would cause PyCoreNetIf.poshook() callback (range calculation)
# node.setposition(x, y, z)
node.position.set(x, y, z)
node_data = node.data(message_type=0)
self.session.broadcast_node(node_data)
@ -984,6 +957,7 @@ class Ns2ScriptedMobility(WayPointMobility):
BonnMotion.
"""
name = "ns2script"
configuration_maps = {}
@classmethod
def configurations(cls):
@ -1014,9 +988,8 @@ class Ns2ScriptedMobility(WayPointMobility):
self._netifs = {}
self._netifslock = threading.Lock()
if not config:
config = self.default_values()
self.session.mobility.set_configs(config, node_id=object_id, config_type=self.name)
# retrieve current configuration
config = self.get_configs(self.object_id)
self.file = config["file"]
self.refresh_ms = int(config["refresh_ms"])
@ -1041,9 +1014,9 @@ class Ns2ScriptedMobility(WayPointMobility):
"""
filename = self.findfile(self.file)
try:
f = open(filename, 'r')
f = open(filename, "r")
except IOError:
logger.exception("ns-2 scripted mobility failed to load file '%s'", self.file)
logger.exception("ns-2 scripted mobility failed to load file: %s", self.file)
return
logger.info("reading ns-2 script file: %s" % filename)
ln = 0

View file

@ -377,7 +377,7 @@ class WlanNode(LxBrNet):
# invokes any netif.poshook
netif.setposition(x, y, z)
def setmodel(self, model, config):
def setmodel(self, model, config=None):
"""
Sets the mobility and wireless model.
@ -398,31 +398,23 @@ class WlanNode(LxBrNet):
elif model.config_type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, object_id=self.objid, config=config)
def updatemodel(self, model_name, config):
"""
Allow for model updates during runtime (similar to setmodel().)
def update_mobility(self, config):
if not self.mobility:
raise ValueError("no mobility set to update for node(%s)", self.objid)
self.mobility.set_configs(config, node_id=self.objid)
: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)
if self.model is None or self.model.name != model_name:
return
model = self.model
if model.config_type == RegisterTlvs.WIRELESS.value:
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()
netif.poshook(netif, x, y, z)
self.model.setlinkparams()
def updatemodel(self, config):
if not self.model:
raise ValueError("no model set to update for node(%s)", self.objid)
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
self.model.set_configs(config, node_id=self.objid)
if self.model.position_callback:
for netif in self.netifs():
netif.poshook = self.model.position_callback
if netif.node is not None:
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.updateconfig()
def all_link_data(self, flags):
"""

View file

@ -305,7 +305,7 @@ class OvsNet(PyCoreNet):
utils.check_cmd([constants.OVS_BIN, "add-port", network.bridge_name, interface.name])
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
# TODO: is there a native method for this? see if this causes issues
# TODO: is there a native method for this? see if this causes issues
# i = network.newifindex()
# network._netif[i] = interface
# with network._linked_lock:
@ -593,7 +593,7 @@ class OvsWlanNode(OvsNet):
interface.setposition(x, y, z)
# self.model.setlinkparams()
def setmodel(self, model, config):
def setmodel(self, model, config=None):
"""
Mobility and wireless model.
"""
@ -611,29 +611,18 @@ class OvsWlanNode(OvsNet):
elif model.type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, object_id=self.objid, config=config)
def updatemodel(self, model_name, values):
"""
Allow for model updates during runtime (similar to setmodel().)
"""
logger.info("updating model %s", model_name)
if self.model is None or self.model.name != model_name:
logger.info(
"failure to update model, model doesn't exist or invalid name: model(%s) - name(%s)",
self.model, model_name
)
return
model = self.model
if model.type == RegisterTlvs.WIRELESS.value:
if not model.updateconfig(values):
return
if self.model.position_callback:
for interface in self.netifs():
interface.poshook = self.model.position_callback
if interface.node is not None:
x, y, z = interface.node.position.get()
interface.poshook(interface, x, y, z)
self.model.setlinkparams()
def updatemodel(self, config):
if not self.model:
raise ValueError("no model set to update for node(%s)", self.objid)
logger.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
self.model.set_configs(config, node_id=self.objid)
if self.model.position_callback:
for netif in self.netifs():
netif.poshook = self.model.position_callback
if netif.node is not None:
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.updateconfig()
def all_link_data(self, flags):
all_links = OvsNet.all_link_data(self, flags)

View file

@ -19,10 +19,8 @@ from core import constants
from core import logger
from core.api import coreapi
from core.broker import CoreBroker
from core.conf import ConfigShim
from core.conf import ConfigurableOptions
from core.conf import ConfigShim, ConfigurableOptions
from core.conf import Configuration
from core.conf import ConfigurableManager
from core.data import ConfigData
from core.data import EventData
from core.data import ExceptionData
@ -1149,16 +1147,24 @@ class Session(object):
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):
for model_name in self.emane.emane_models():
model_class = self.emane.get_model_class(model_name)
for node_id in model_class.nodes():
config = model_class.get_configs(node_id)
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)
# 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_id, service in service_configs:
@ -1221,11 +1227,12 @@ class Session(object):
logger.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data))
class SessionConfig(ConfigurableManager, ConfigurableOptions):
class SessionConfig(ConfigurableOptions):
"""
Session configuration object.
"""
name = "session"
configuration_maps = {}
config_type = RegisterTlvs.UTILITY.value
@classmethod
@ -1251,16 +1258,16 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
return "Options:1-%d" % len(cls.configurations())
def __init__(self):
super(SessionConfig, self).__init__()
config = self.default_values()
self.set_configs(config)
self.set_configs()
class SessionMetaData(ConfigurableManager):
class SessionMetaData(ConfigurableOptions):
"""
Metadata is simply stored in a configs[] dict. Key=value pairs are
passed in from configure messages destined to the "metadata" object.
The data is not otherwise interpreted or processed.
"""
name = "metadata"
configuration_maps = {}
config_type = RegisterTlvs.UTILITY.value

View file

@ -210,9 +210,12 @@ class CoreDocumentParser1(object):
raise NotImplementedError
logger.info("setting wireless link params: node(%s) model(%s) mobility_model(%s)",
nodenum, model_name, mobility_model_name)
mgr.setconfig_keyvalues(nodenum, model_name, link_params.items())
model_class = mgr.get_model_class(model_name)
model_class.set_configs(link_params, node_id=nodenum)
if mobility_model_name and mobility_params:
mgr.setconfig_keyvalues(nodenum, mobility_model_name, mobility_params.items())
model_class = mgr.get_model_class(mobility_model_name)
model_class.set_configs(mobility_params, node_id=nodenum)
def link_layer2_devices(self, device1, ifname1, device2, ifname2):
"""
@ -843,7 +846,7 @@ class CoreDocumentParser1(object):
params = self.parse_parameter_children(metadata)
for name, value in params.iteritems():
if name and value:
self.session.metadata.add_item(str(name), str(value))
self.session.metadata.set_config(str(name), str(value))
def parse_session_config(self):
session_config = xmlutils.get_first_child_by_tag_name(self.scenario, 'CORE:sessionconfig')

View file

@ -194,8 +194,7 @@ class CoreDocumentWriter0(Document):
self.addaddresses(i, ifc)
# per-interface models
if netmodel and netmodel._name[:6] == "emane_":
cfg = self.session.emane.getifcconfig(node.objid, netmodel._name,
None, ifc)
cfg = netmodel.getifcconfig(node.objid, ifc)
if cfg:
self.addmodels(i, ((netmodel, cfg),))

View file

@ -654,8 +654,7 @@ class DeviceElement(NamedXmlElement):
# per-interface models
# XXX Remove???
if netmodel and netmodel.name[:6] == "emane_":
cfg = self.coreSession.emane.getifcconfig(device_object.objid, netmodel.name,
None, interface_object)
cfg = netmodel.getifcconfig(device_object.objid, interface_object)
if cfg:
interface_element.addModels(((netmodel, cfg),))

View file

@ -27,7 +27,7 @@ def example(options):
# create wlan network node
wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN)
session.set_wireless_model(wlan, BasicRangeModel)
wlan.setmodel(BasicRangeModel)
# create nodes
wireless_nodes = []

View file

@ -214,6 +214,12 @@ def session():
# return created session
yield session_fixture
# clear session configurations
session_fixture.location.reset()
session_fixture.services.reset()
session_fixture.mobility.config_reset()
session_fixture.emane.config_reset()
# shutdown coreemu
coreemu.shutdown()

View file

@ -1,8 +1,3 @@
from random import shuffle
import pytest
from core.conf import ConfigurableManager
from core.conf import ConfigurableOptions
from core.conf import Configuration
from core.enumerations import ConfigDataTypes
@ -11,6 +6,7 @@ from core.enumerations import ConfigDataTypes
class TestConfigurableOptions(ConfigurableOptions):
name_one = "value1"
name_two = "value2"
configuration_maps = {}
@classmethod
def configurations(cls):
@ -47,7 +43,7 @@ class TestConf:
def test_nodes(self):
# given
config_manager = ConfigurableManager()
config_manager = TestConfigurableOptions()
test_config = {1: 2}
node_id = 1
config_manager.set_configs(test_config)
@ -58,11 +54,11 @@ class TestConf:
# then
assert len(nodes) == 1
assert nodes[0] == node_id
assert node_id in nodes
def test_config_reset_all(self):
# given
config_manager = ConfigurableManager()
config_manager = TestConfigurableOptions()
test_config = {1: 2}
node_id = 1
config_manager.set_configs(test_config)
@ -72,11 +68,11 @@ class TestConf:
config_manager.config_reset()
# then
assert not config_manager._configuration_maps
assert not config_manager.configuration_maps
def test_config_reset_node(self):
# given
config_manager = ConfigurableManager()
config_manager = TestConfigurableOptions()
test_config = {1: 2}
node_id = 1
config_manager.set_configs(test_config)
@ -86,11 +82,12 @@ class TestConf:
config_manager.config_reset(node_id)
# then
assert not config_manager.get_configs(node_id)
assert node_id not in config_manager.configuration_maps
assert config_manager.get_configs()
def test_configs_setget(self):
# given
config_manager = ConfigurableManager()
config_manager = TestConfigurableOptions()
test_config = {1: 2}
node_id = 1
config_manager.set_configs(test_config)
@ -106,7 +103,7 @@ class TestConf:
def test_config_setget(self):
# given
config_manager = ConfigurableManager()
config_manager = TestConfigurableOptions()
name = "test"
value = "1"
node_id = 1
@ -120,56 +117,3 @@ class TestConf:
# then
assert defaults_value == value
assert node_value == value
def test_all_configs(self):
# given
config_manager = ConfigurableManager()
name = "test"
value_one = "1"
value_two = "2"
node_id = 1
config_one = "config1"
config_two = "config2"
config_manager.set_config(name, value_one, config_type=config_one)
config_manager.set_config(name, value_two, config_type=config_two)
config_manager.set_config(name, value_one, node_id=node_id, config_type=config_one)
config_manager.set_config(name, value_two, node_id=node_id, config_type=config_two)
# when
defaults_value_one = config_manager.get_config(name, config_type=config_one)
defaults_value_two = config_manager.get_config(name, config_type=config_two)
node_value_one = config_manager.get_config(name, node_id=node_id, config_type=config_one)
node_value_two = config_manager.get_config(name, node_id=node_id, config_type=config_two)
default_all_configs = config_manager.get_all_configs()
node_all_configs = config_manager.get_all_configs(node_id=node_id)
# then
assert defaults_value_one == value_one
assert defaults_value_two == value_two
assert node_value_one == value_one
assert node_value_two == value_two
assert len(default_all_configs) == 2
assert config_one in default_all_configs
assert config_two in default_all_configs
assert len(node_all_configs) == 2
assert config_one in node_all_configs
assert config_two in node_all_configs
@pytest.mark.parametrize("_", xrange(10))
def test_config_last_key(self, _):
# given
config_manager = ConfigurableManager()
config = {1: 2}
node_id = 1
config_types = [1, 2, 3]
shuffle(config_types)
config_manager.set_configs(config, node_id=node_id, config_type=config_types[0])
config_manager.set_configs(config, node_id=node_id, config_type=config_types[1])
config_manager.set_configs(config, node_id=node_id, config_type=config_types[2])
# when
keys = config_manager.get_all_configs(node_id=node_id).keys()
# then
assert keys
assert keys[-1] == config_types[2]

View file

@ -12,15 +12,17 @@ from mock import MagicMock
from core.emulator.emudata import NodeOptions
from core.enumerations import MessageFlags, NodeTypes
from core.mobility import BasicRangeModel
from core.mobility import Ns2ScriptedMobility
from core.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.netns.vnodeclient import VnodeClient
from core.service import ServiceManager
_PATH = os.path.abspath(os.path.dirname(__file__))
_SERVICES_PATH = os.path.join(_PATH, "myservices")
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
_XML_VERSIONS = ["0.0", "1.0"]
_XML_VERSIONS = [
"0.0",
"1.0"
]
_WIRED = [
NodeTypes.PEER_TO_PEER,
NodeTypes.HUB,
@ -101,7 +103,6 @@ class TestCore:
: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)
@ -256,7 +257,7 @@ class TestCore:
# create wlan
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
session.set_wireless_model(wlan_node, BasicRangeModel)
wlan_node.setmodel(BasicRangeModel)
# create nodes
node_options = NodeOptions()
@ -289,7 +290,7 @@ class TestCore:
# create wlan
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
session.set_wireless_model(wlan_node, BasicRangeModel)
wlan_node.setmodel(BasicRangeModel)
# create nodes
node_options = NodeOptions()
@ -316,7 +317,7 @@ class TestCore:
"script_pause": "",
"script_stop": "",
}
session.mobility.set_configs(config, wlan_node.objid, Ns2ScriptedMobility.name)
wlan_node.setmodel(Ns2ScriptedMobility, config)
# add handler for receiving node updates
event = threading.Event()