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

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