added modelmanager for both mobility and emane to leverage and reduce duplicate logic

This commit is contained in:
Blake J. Harnden 2018-06-13 16:17:47 -07:00
parent a52e454111
commit 25cfb21586
5 changed files with 171 additions and 192 deletions

View file

@ -265,3 +265,87 @@ class ConfigurableOptions(object):
:rtype: OrderedDict
"""
return OrderedDict([(config.id, config.default) for config in cls.configurations()])
class ModelManager(ConfigurableManager):
def __init__(self):
super(ModelManager, self).__init__()
self.models = {}
self.node_models = {}
def set_model_config(self, node_id, model_name, config=None):
"""
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.models.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)
if not config:
config = {}
for key, value in config.iteritems():
node_config[key] = value
# set as node model for startup
self.node_models[node_id] = model_name
# 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.models.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 set_model(self, node, model_class, config=None):
logger.info("setting mobility model(%s) for node(%s): %s", model_class.name, node.objid, 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 getmodels(self, node):
"""
Return a list of model classes and values for a net if one has been
configured. This is invoked when exporting a session to XML.
:param node: network node to get models for
:return: list of model and values tuples for the network node
:rtype: list
"""
models = []
all_configs = {}
if self.has_configs(node_id=node.objid):
all_configs = self.get_all_configs(node_id=node.objid)
for model_name in all_configs.iterkeys():
model_class = self.models[model_name]
config = self.get_configs(node_id=node.objid, config_type=model_name)
models.append((model_class, config))
logger.debug("mobility models for node(%s): %s", node.objid, models)
return models

View file

@ -42,8 +42,6 @@ from core.enumerations import SessionTlvs
from core.misc import nodeutils
from core.misc import structutils
from core.misc import utils
from core.mobility import BasicRangeModel
from core.mobility import Ns2ScriptedMobility
from core.service import CoreService
from core.service import ServiceManager
@ -380,13 +378,11 @@ class CoreHandler(SocketServer.BaseRequestHandler):
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.broker.config_type, self.session.broker.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.location.config_type, self.session.location.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.mobility.config_type, self.session.mobility.name)
for model_name in self.session.mobility.mobility_models():
model_class = self.session.mobility.get_model_class(model_name)
for model_class in self.session.mobility.models.itervalues():
tlv_data += coreapi.CoreRegisterTlv.pack(model_class.config_type, model_class.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.services.config_type, self.session.services.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.emane.config_type, self.session.emane.name)
for model_name in self.session.emane.emane_models():
model_class = self.session.emane.get_model_class(model_name)
for model_class in self.session.emane.models.itervalues():
tlv_data += coreapi.CoreRegisterTlv.pack(model_class.config_type, model_class.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.options.config_type, self.session.options.name)
tlv_data += coreapi.CoreRegisterTlv.pack(self.session.metadata.config_type, self.session.metadata.name)
@ -937,11 +933,11 @@ class CoreHandler(SocketServer.BaseRequestHandler):
replies = self.handle_config_services(message_type, config_data)
elif config_data.object == self.session.mobility.name:
self.handle_config_mobility(message_type, config_data)
elif config_data.object in [BasicRangeModel.name, Ns2ScriptedMobility.name]:
elif config_data.object in self.session.mobility.models:
replies = self.handle_config_mobility_models(message_type, config_data)
elif config_data.object == self.session.emane.name:
replies = self.handle_config_emane(message_type, config_data)
elif config_data.object in self.session.emane.emane_models():
elif config_data.object in self.session.emane.models:
replies = self.handle_config_emane_models(message_type, config_data)
else:
raise Exception("no handler for configuration: %s", config_data.object)
@ -1181,7 +1177,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
logger.info("replying to configure request for model: %s", object_name)
typeflags = ConfigFlags.NONE.value
model_class = self.session.mobility.get_model_class(object_name)
model_class = self.session.mobility.models.get(object_name)
if not model_class:
logger.warn("model class does not exist: %s", object_name)
return []
@ -1251,7 +1247,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
logger.info("replying to configure request for model: %s", object_name)
typeflags = ConfigFlags.NONE.value
model_class = self.session.emane.get_model_class(object_name)
model_class = self.session.emane.models.get(object_name)
if not model_class:
logger.warn("model class does not exist: %s", object_name)
return []

View file

@ -11,8 +11,9 @@ from core import constants
from core import logger
from core.api import coreapi
from core.api import dataconversion
from core.conf import ConfigShim, ConfigurableManager
from core.conf import ConfigShim
from core.conf import Configuration
from core.conf import ModelManager
from core.emane import emanemanifest
from core.emane.bypass import EmaneBypassModel
from core.emane.commeffect import EmaneCommEffectModel
@ -53,7 +54,7 @@ EMANE_MODELS = [
]
class EmaneManager(ConfigurableManager):
class EmaneManager(ModelManager):
"""
EMANE controller object. Lives in a Session instance and is used for
building EMANE config files from all of the EmaneNode objects in this
@ -89,64 +90,11 @@ class EmaneManager(ConfigurableManager):
self.emane_config = EmaneGlobalModel(session)
self.set_configs(self.emane_config.default_values())
# store the last configured model for a node, used during startup
self.node_models = {}
session.broker.handlers.add(self.handledistributed)
self.service = None
self.event_device = None
self._modelclsmap = {}
self.service = None
self.emane_check()
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 as node model for startup
self.node_models[node_id] = model_name
# 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 getifcconfig(self, node_id, interface, model_name):
"""
Retrieve interface configuration or node configuration if not provided.
@ -189,24 +137,10 @@ class EmaneManager(ConfigurableManager):
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):
return self._modelclsmap.keys()
def get_model_class(self, model_name):
return self._modelclsmap[model_name]
def emane_check(self):
"""
Check if emane is installed and load models.
@ -281,7 +215,7 @@ class EmaneManager(ConfigurableManager):
"""
for emane_model in emane_models:
logger.info("loading emane model: %s", emane_model.__name__)
self._modelclsmap[emane_model.name] = emane_model
self.models[emane_model.name] = emane_model
def add_node(self, emane_node):
"""
@ -307,22 +241,6 @@ class EmaneManager(ConfigurableManager):
nodes.add(netif.node)
return nodes
def getmodels(self, node):
"""
Used with XML export.
"""
models = []
all_configs = {}
if self.has_configs(node_id=node.objid):
all_configs = self.get_all_configs(node_id=node.objid)
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
def setup(self):
"""
Populate self._objs with EmaneNodes; perform distributed setup;
@ -650,7 +568,7 @@ class EmaneManager(ConfigurableManager):
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.models[model_name]
emane_node.setmodel(model_class, config)
def nemlookup(self, nemid):

View file

@ -9,8 +9,9 @@ import threading
import time
from core import logger
from core.conf import ConfigurableOptions, ConfigurableManager
from core.conf import ConfigurableOptions
from core.conf import Configuration
from core.conf import ModelManager
from core.coreobj import PyCoreNode
from core.data import EventData
from core.data import LinkData
@ -25,7 +26,7 @@ from core.misc import utils
from core.misc.ipaddress import IpAddress
class MobilityManager(ConfigurableManager):
class MobilityManager(ModelManager):
"""
Member of session class for handling configuration data for mobility and
range models.
@ -41,90 +42,14 @@ class MobilityManager(ConfigurableManager):
"""
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
self._modelclsmap = {
BasicRangeModel.name: BasicRangeModel,
Ns2ScriptedMobility.name: Ns2ScriptedMobility
}
self.models[BasicRangeModel.name] = BasicRangeModel
self.models[Ns2ScriptedMobility.name] = Ns2ScriptedMobility
# dummy node objects for tracking position of nodes on other servers
self.phys = {}
self.physnets = {}
self.session.broker.handlers.add(self.physnodehandlelink)
def 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):
return self._modelclsmap.keys()
def get_model_class(self, model_name):
return self._modelclsmap[model_name]
def getmodels(self, node):
"""
Return a list of model classes and values for a net if one has been
configured. This is invoked when exporting a session to XML.
:param node: network node to get models for
:return: list of model and values tuples for the network node
:rtype: list
"""
models = []
all_configs = {}
if self.has_configs(node_id=node.objid):
all_configs = self.get_all_configs(node_id=node.objid)
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("mobility models for node(%s): %s", node.objid, models)
return models
def startup(self, node_ids=None):
"""
Session is transitioning from instantiation to runtime state.
@ -146,11 +71,11 @@ class MobilityManager(ConfigurableManager):
logger.warn("skipping mobility configuration for unknown node: %s", node_id)
continue
for model_name in self._modelclsmap.iterkeys():
for model_name in self.models.iterkeys():
config = self.get_configs(node_id=node_id, config_type=model_name)
if not config:
continue
model_class = self._modelclsmap[model_name]
model_class = self.models[model_name]
self.set_model(node, model_class, config)
if self.session.master:
@ -159,14 +84,6 @@ class MobilityManager(ConfigurableManager):
if node.mobility:
self.session.event_loop.add_event(0.0, node.mobility.startup)
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)
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 handleevent(self, event_data):
"""
Handle an Event Message used to start, stop, or pause
@ -189,7 +106,7 @@ class MobilityManager(ConfigurableManager):
models = name[9:].split(',')
for model in models:
try:
cls = self._modelclsmap[model]
cls = self.models[model]
except KeyError:
logger.warn("Ignoring event for unknown model '%s'", model)
continue
@ -299,7 +216,6 @@ class MobilityManager(ConfigurableManager):
self.addphys(nn[0], dummy)
# TODO: remove need to handling old style messages
def physnodeupdateposition(self, message):
"""
Snoop node messages belonging to physical nodes. The dummy object

View file

@ -1,6 +1,10 @@
from core.conf import ConfigurableOptions, ConfigurableManager
import pytest
from core.conf import ConfigurableOptions, ConfigurableManager, ModelManager
from core.conf import Configuration
from core.enumerations import ConfigDataTypes
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.enumerations import ConfigDataTypes, NodeTypes
from core.mobility import BasicRangeModel
class TestConfigurableOptions(ConfigurableOptions):
@ -117,3 +121,64 @@ class TestConf:
# then
assert defaults_value == value
assert node_value == value
def test_model_setget_config(self):
# given
manager = ModelManager()
manager.models[BasicRangeModel.name] = BasicRangeModel
# when
manager.set_model_config(1, BasicRangeModel.name)
# then
assert manager.get_model_config(1, BasicRangeModel.name)
def test_model_set_config_error(self):
# given
manager = ModelManager()
manager.models[BasicRangeModel.name] = BasicRangeModel
bad_name = "bad-model"
# when/then
with pytest.raises(ValueError):
manager.set_model_config(1, bad_name)
def test_model_get_config_error(self):
# given
manager = ModelManager()
manager.models[BasicRangeModel.name] = BasicRangeModel
bad_name = "bad-model"
# when/then
with pytest.raises(ValueError):
manager.get_model_config(1, bad_name)
def test_model_set(self, session):
# given
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
# when
session.mobility.set_model(wlan_node, BasicRangeModel)
# then
assert session.mobility.get_model_config(wlan_node.objid, BasicRangeModel.name)
def test_model_set_error(self, session):
# given
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
# when / then
with pytest.raises(ValueError):
session.mobility.set_model(wlan_node, EmaneIeee80211abgModel)
def test_get_models(self, session):
# given
wlan_node = session.add_node(_type=NodeTypes.WIRELESS_LAN)
session.mobility.set_model(wlan_node, BasicRangeModel)
# when
models = session.mobility.getmodels(wlan_node)
# then
assert models
assert len(models) == 1