from nepi.util import validation
from nepi.util.constants import ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP, DeploymentConfiguration
+# Attribute categories
+CATEGORY_DEPLOYMENT = "Deployment"
+
class VersionedMetadataInfo(object):
@property
def connector_types(self):
"allowed": array of posible values,
"flags": attributes flags,
"validation_function": validation function for the attribute
+ "category": category for the attribute
})
"""
raise NotImplementedError
"""
return self.configure_order
+ @property
+ def prestart_order(self):
+ """ list of factory ids that indicates the order in which the elements
+ should be prestart-configured.
+
+ Default: same as configure_order
+ """
+ return self.configure_order
+
+ @property
+ def start_order(self):
+ """ list of factory ids that indicates the order in which the elements
+ should be started.
+
+ Default: same as configure_order
+ """
+ return self.configure_order
+
@property
def factories_info(self):
""" dictionary of dictionaries of factory specific information
(just after connections are made,
just before netrefs are resolved)
"configure_function": function for element configuration,
+ "prestart_function": function for pre-start
+ element configuration (just before starting applications),
+ useful for synchronization of background setup tasks or
+ lazy instantiation or configuration of attributes
+ that require connection/cross-connection state before
+ being created.
+ After this point, all applications should be able to run.
"factory_attributes": list of references to attribute_ids,
"box_attributes": list of regerences to attribute_ids,
"traces": list of references to trace_id
+ "tags": list of references to tag_id
"connector_types": list of references to connector_types
})
"""
"allowed": array of posible values,
"flags": attributes flags,
"validation_function": validation function for the attribute
+ "category": category for the attribute
})
]
"""
help = "Path to the directory where traces and other files will be stored",
type = Attribute.STRING,
value = "",
- flags = Attribute.DesignOnly,
+ flags = Attribute.DesignOnly
)),
)
help = "Shell commands to run before spawning TestbedController processes",
type = Attribute.STRING,
flags = Attribute.DesignOnly,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.DEPLOYMENT_MODE, dict(name = DC.DEPLOYMENT_MODE,
help = "Instance execution mode",
DC.MODE_SINGLE_PROCESS
],
flags = Attribute.DesignOnly,
- validation_function = validation.is_enum
+ validation_function = validation.is_enum,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.DEPLOYMENT_COMMUNICATION, dict(name = DC.DEPLOYMENT_COMMUNICATION,
help = "Instance communication mode",
DC.ACCESS_SSH
],
flags = Attribute.DesignOnly,
- validation_function = validation.is_enum
+ validation_function = validation.is_enum,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.DEPLOYMENT_HOST, dict(name = DC.DEPLOYMENT_HOST,
help = "Host where the testbed will be executed",
type = Attribute.STRING,
value = "localhost",
flags = Attribute.DesignOnly,
- validation_function = validation.is_string
+ validation_function = validation.is_string,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.DEPLOYMENT_USER, dict(name = DC.DEPLOYMENT_USER,
help = "User on the Host to execute the testbed",
type = Attribute.STRING,
value = getpass.getuser(),
flags = Attribute.DesignOnly,
- validation_function = validation.is_string
+ validation_function = validation.is_string,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.DEPLOYMENT_KEY, dict(name = DC.DEPLOYMENT_KEY,
help = "Path to SSH key to use for connecting",
type = Attribute.STRING,
flags = Attribute.DesignOnly,
- validation_function = validation.is_string
+ validation_function = validation.is_string,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.DEPLOYMENT_PORT, dict(name = DC.DEPLOYMENT_PORT,
help = "Port on the Host",
type = Attribute.INTEGER,
value = 22,
flags = Attribute.DesignOnly,
- validation_function = validation.is_integer
+ validation_function = validation.is_integer,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.ROOT_DIRECTORY, dict(name = DC.ROOT_DIRECTORY,
help = "Root directory for storing process files",
type = Attribute.STRING,
value = ".",
flags = Attribute.DesignOnly,
- validation_function = validation.is_string # TODO: validation.is_path
+ validation_function = validation.is_string, # TODO: validation.is_path
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.USE_AGENT, dict(name = DC.USE_AGENT,
help = "Use -A option for forwarding of the authentication agent, if ssh access is used",
type = Attribute.BOOL,
value = False,
flags = Attribute.DesignOnly,
- validation_function = validation.is_bool
+ validation_function = validation.is_bool,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.LOG_LEVEL, dict(name = DC.LOG_LEVEL,
help = "Log level for instance",
DC.DEBUG_LEVEL
],
flags = Attribute.DesignOnly,
- validation_function = validation.is_enum
+ validation_function = validation.is_enum,
+ category = CATEGORY_DEPLOYMENT,
)),
(DC.RECOVER, dict(name = DC.RECOVER,
help = "Do not intantiate testbeds, rather, reconnect to already-running instances. Used to recover from a dead controller.",
type = Attribute.BOOL,
value = False,
flags = Attribute.DesignOnly,
- validation_function = validation.is_bool
+ validation_function = validation.is_bool,
+ category = CATEGORY_DEPLOYMENT,
)),
)
del DC
+
+ STANDARD_ATTRIBUTE_BUNDLES = {
+ "tun_proto" : dict({
+ "name": "tun_proto",
+ "help": "TUNneling protocol used",
+ "type": Attribute.STRING,
+ "flags": Attribute.Invisible,
+ "validation_function": validation.is_string,
+ }),
+ "tun_key" : dict({
+ "name": "tun_key",
+ "help": "Randomly selected TUNneling protocol cryptographic key. "
+ "Endpoints must agree to use the minimum (in lexicographic order) "
+ "of both the remote and local sides.",
+ "type": Attribute.STRING,
+ "flags": Attribute.Invisible,
+ "validation_function": validation.is_string,
+ }),
+ "tun_addr" : dict({
+ "name": "tun_addr",
+ "help": "Address (IP, unix socket, whatever) of the tunnel endpoint",
+ "type": Attribute.STRING,
+ "flags": Attribute.Invisible,
+ "validation_function": validation.is_string,
+ }),
+ "tun_port" : dict({
+ "name": "tun_port",
+ "help": "IP port of the tunnel endpoint",
+ "type": Attribute.INTEGER,
+ "flags": Attribute.Invisible,
+ "validation_function": validation.is_integer,
+ }),
+ ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP : dict({
+ "name": ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
+ "help": "Commands to set up the environment needed to run NEPI testbeds",
+ "type": Attribute.STRING,
+ "flags": Attribute.Invisible,
+ "validation_function": validation.is_string
+ }),
+ }
+
def __init__(self, testbed_id, version):
self._version = version
def preconfigure_order(self):
return self._metadata.preconfigure_order
+ @property
+ def prestart_order(self):
+ return self._metadata.prestart_order
+
+ @property
+ def start_order(self):
+ return self._metadata.start_order
+
def testbed_attributes(self):
attributes = AttributesMap()
flags = attr_info["flags"] if "flags" in attr_info \
else Attribute.NoFlags
validation_function = attr_info["validation_function"]
+ category = attr_info["category"] if "category" in attr_info else None
attributes.add_attribute(name, help, type, value,
- range, allowed, flags, validation_function)
+ range, allowed, flags, validation_function, category)
return attributes
self._add_attributes(factory, info, "box_attributes", True)
self._add_design_traces(factory, info)
+ self._add_tags(factory, info)
self._add_design_connector_types(factory, info)
factories.append(factory)
return factories
status_function = info.get("status_function")
configure_function = info.get("configure_function")
preconfigure_function = info.get("preconfigure_function")
+ prestart_function = info.get("prestart_function")
allow_addresses = info.get("allow_addresses", False)
allow_routes = info.get("allow_routes", False)
has_addresses = info.get("has_addresses", False)
factory = Factory(factory_id, create_function, start_function,
stop_function, status_function,
configure_function, preconfigure_function,
+ prestart_function,
allow_addresses, has_addresses,
allow_routes, has_routes)
self._add_attributes(factory, info, "box_attributes", True)
self._add_execute_traces(factory, info)
+ self._add_tags(factory, info)
self._add_execute_connector_types(factory, info)
factories.append(factory)
return factories
def _add_attributes(self, factory, info, attr_key, box_attributes = False, attr_bundle = ()):
if not attr_bundle and info and attr_key in info:
- attr_bundle = [ (attr_id, self._metadata.attributes[attr_id])
+ definitions = self.STANDARD_ATTRIBUTE_BUNDLES.copy()
+ definitions.update(self._metadata.attributes)
+ attr_bundle = [ (attr_id, definitions[attr_id])
for attr_id in info[attr_key] ]
for attr_id, attr_info in attr_bundle:
name = attr_info["name"]
and attr_info["flags"] != None \
else Attribute.NoFlags
validation_function = attr_info["validation_function"]
+ category = attr_info["category"] if "category" in attr_info else None
if box_attributes:
factory.add_box_attribute(name, help, type, value, range,
- allowed, flags, validation_function)
+ allowed, flags, validation_function, category)
else:
factory.add_attribute(name, help, type, value, range,
- allowed, flags, validation_function)
+ allowed, flags, validation_function, category)
def _add_design_traces(self, factory, info):
if "traces" in info:
trace_id = trace_info["name"]
factory.add_trace(trace_id)
+ def _add_tags(self, factory, info):
+ if "tags" in info:
+ for tag_id in info["tags"]:
+ factory.add_tag(tag_id)
+
def _add_design_connector_types(self, factory, info):
from nepi.core.design import ConnectorType
if "connector_types" in info: