From: Alina Quereilhac Date: Fri, 8 Jul 2011 13:57:29 +0000 (+0200) Subject: code clean up X-Git-Tag: nepi-3.0.0~380 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=6d9e95a0b1fc4f20dedc1bdadb4ccb50e8beac80;p=nepi.git code clean up --- diff --git a/src/nepi/core/attributes.py b/src/nepi/core/attributes.py index 272cbef2..c2bc6b5e 100644 --- a/src/nepi/core/attributes.py +++ b/src/nepi/core/attributes.py @@ -26,17 +26,21 @@ class Attribute(object): } ### Attribute Flags - NoFlags = 0x00 - # Attribute is only modifiable during experiment design - DesignOnly = 0x01 - # Attribute is read only and can't be modified by the user - # Note: ReadOnly implies DesignOnly - ReadOnly = 0x03 - # Attribute is invisible to the user but can be modified - Invisible = 0x04 - # Attribute has no default value in the testbed instance. - # So it needs to be set explicitely - HasNoDefaultValue = 0x08 + NoFlags = 0x00 + # Read-only attribute at design time + DesignReadOnly = 0x01 + # Invisible attribute at design time + DesignInvisible = 0x02 + # Read-only attribute at execution time + ExecReadOnly = 0x04 + # Invisible attribute at execution time + ExecInvisible = 0x08 + # Attribute doesn't change value during execution time + ExecImmutable = 0x10 + # Attribute has no default value in the testbed + NoDefaultValue = 0x20 + # Metadata attribute (is not directly reflected by a real object attribute) + Metadata = 0x30 def __init__(self, name, help, type, value = None, range = None, allowed = None, flags = None, validation_function = None, @@ -73,21 +77,32 @@ class Attribute(object): return self._flags @property - def invisible(self): - return (self._flags & Attribute.Invisible) == Attribute.Invisible + def is_design_invisible(self): + return self.has_flag(Attribute.DesignInvisible) @property - def read_only(self): - return (self._flags & Attribute.ReadOnly) == Attribute.ReadOnly + def is_design_read_only(self): + return self.has_flag(Attribute.DesignReadOnly) @property - def has_no_default_value(self): - return (self._flags & Attribute.HasNoDefaultValue) == \ - Attribute.HasNoDefaultValue + def is_exec_invisible(self): + return self.has_flag(Attribute.ExecInvisible) + + @property + def is_exec_read_only(self): + return self.has_flag(Attribute.ExecReadOnly) + + @property + def is_exec_immutable(self): + return self.has_flag(Attribute.ExecImmutable) + + @property + def is_metadata(self): + return self.has_flag(Attribute.Metadata) @property - def design_only(self): - return (self._flags & Attribute.DesignOnly) == Attribute.DesignOnly + def has_no_default_value(self): + return self.has_flag(Attribute.NoDefaultValue) @property def modified(self): @@ -109,6 +124,9 @@ class Attribute(object): def validation_function(self): return self._validation_function + def has_flag(self, flag): + return (self._flags & flag) == flag + def get_value(self): return self._value @@ -153,9 +171,9 @@ class AttributesMap(object): def get_attribute_list(self, filter_flags = None): attributes = self._attributes if filter_flags != None: - def filter_attrs(attr): - (attr_id, attr_data) = attr - return not ((attr_data.flags & filter_flags) == filter_flags) + def filter_attrs(attr_data): + (attr_id, attr) = attr_data + return not attr.has_flag(filter_flags) attributes = dict(filter(filter_attrs, attributes.iteritems())) return attributes.keys() @@ -182,14 +200,23 @@ class AttributesMap(object): def get_attribute_category(self, name): return self._attributes[name].category - def is_attribute_read_only(self, name): - return self._attributes[name].read_only + def is_attribute_design_invisible(self, name): + return self._attributes[name].is_design_invisible + + def is_attribute_design_read_only(self, name): + return self._attributes[name].is_design_read_only + + def is_attribute_exec_invisible(self, name): + return self._attributes[name].is_exec_invisible + + def is_attribute_exec_read_only(self, name): + return self._attributes[name].is_exec_read_only - def is_attribute_invisible(self, name): - return self._attributes[name].invisible + def is_attribute_exec_immutable(self, name): + return self._attributes[name].is_exec_immutable - def is_attribute_design_only(self, name): - return self._attributes[name].design_only + def is_attribute_metadata(self, name): + return self._attributes[name].is_metadata def has_attribute_no_default_value(self, name): return self._attributes[name].has_no_default_value diff --git a/src/nepi/core/design.py b/src/nepi/core/design.py index 602667c6..f69959f0 100644 --- a/src/nepi/core/design.py +++ b/src/nepi/core/design.py @@ -114,14 +114,14 @@ class Address(AttributesMap): self.add_attribute(name = "Address", help = "Address number", type = Attribute.STRING, - flags = Attribute.HasNoDefaultValue, + flags = Attribute.NoDefaultValue, validation_function = validation.is_ip_address) self.add_attribute(name = "NetPrefix", help = "Network prefix for the address", type = Attribute.INTEGER, range = (0, 128), value = 24, - flags = Attribute.HasNoDefaultValue, + flags = Attribute.NoDefaultValue, validation_function = validation.is_integer) self.add_attribute(name = "Broadcast", help = "Broadcast address", @@ -140,18 +140,18 @@ class Route(AttributesMap): type = Attribute.INTEGER, range = (0, 128), value = 24, - flags = Attribute.HasNoDefaultValue, + flags = Attribute.NoDefaultValue, validation_function = validation.is_integer) self.add_attribute(name = "NextHop", help = "Address for the next hop", type = Attribute.STRING, - flags = Attribute.HasNoDefaultValue, + flags = Attribute.NoDefaultValue, validation_function = validation.is_ref_address) self.add_attribute(name = "Metric", help = "Routing metric", type = Attribute.INTEGER, value = 0, - flags = Attribute.HasNoDefaultValue, + flags = Attribute.NoDefaultValue, validation_function = validation.is_integer) class Box(AttributesMap, Taggable): @@ -187,7 +187,7 @@ class Box(AttributesMap, Taggable): attr.range, attr.allowed, attr.flags, attr.validation_function, attr.category) for attr in factory.attributes: - if attr.modified or attr.invisible: + if attr.modified or attr.is_metadata: self._factory_attributes[attr.name] = attr.value def __str__(self): diff --git a/src/nepi/core/execute.py b/src/nepi/core/execute.py index 5e88e1c2..9de12788 100644 --- a/src/nepi/core/execute.py +++ b/src/nepi/core/execute.py @@ -268,6 +268,8 @@ class ExperimentController(object): def start(self): parser = XmlExperimentParser() data = parser.from_xml_to_data(self._experiment_xml) + + # instantiate testbed controllers self._init_testbed_controllers(data) # persist testbed connection data, for potential recovery @@ -319,8 +321,6 @@ class ExperimentController(object): # final netref step, fail if anything's left unresolved self.do_netrefs(data, fail_if_undefined=True) - self._program_testbed_cross_connections(data) - # perform do_configure in parallel for al testbeds # (it's internal configuration for each) self._parallel([testbed.do_configure @@ -683,72 +683,109 @@ class ExperimentController(object): self._netreffed_testbeds.remove(guid) def _program_testbed_controllers(self, element_guids, data): + deferred = dict() + + def add_deferred_testbed(deferred, testbed_guid): + if not testbed_guid in deferred: + deferred[testbed_guid] = dict() + deferred[testbed_guid]["connections"] = set() + deferred[testbed_guid]["cross_connections"] = set() + + def add_deferred_connection(deferred, data, guid, connector_type_name, + other_guid, other_connector_type_name): + + (testbed_guid, factory_id) = data.get_box_data(guid) + (other_testbed_guid, other_factory_id) = data.get_box_data( + other_guid) + testbed = self._testbeds[testbed_guid] + testbed_id = testbed.testbed_id + add_deferred_testbed(deferred, testbed_guid) + + if testbed_guid == other_testbed_guid: + # each testbed should take care of enforcing internal + # connection simmetry, so each connection is only + # added in one direction + c = (guid, connector_type_name, other_guid, + other_connector_type_name) + deferred[testbed_guid]["connections"].add(c) + else: + # the controller takes care of cross_connection simmetry + # so cross_connections are added in both directions + other_testbed = self._testbeds[other_testbed_guid] + other_testbed_id = other_testbed.testbed_id + add_deferred_testbed(deferred, other_testbed_guid) + c1 = (testbed_guid, testbed_id, guid, factory_id, + connector_type_name, other_testbed_guid, + other_testbed_id, other_guid, other_factory_id, + other_connector_type_name) + c2 = (other_testbed_guid, other_testbed_id, other_guid, + other_factory_id, other_connector_type_name, + testbed_guid, testbed_id, guid, factory_id, + connector_type_name) + deferred[testbed_guid]["cross_connections"].add(c1) + deferred[other_testbed_guid]["cross_connections"].add(c2) + + def resolve_create_netref(data, guid, name, value): + # Try to resolve create-time netrefs, if possible + if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value): + try: + nuvalue = self.resolve_netref_value(value) + except: + # Any trouble means we're not in shape to resolve the netref yet + nuvalue = None + if nuvalue is not None: + # Only if we succeed we remove the netref deferral entry + value = nuvalue + data.set_attribute_data(guid, name, value) + if (testbed_guid, guid) in self._netrefs: + self._netrefs[(testbed_guid, guid)].discard(name) + return value + for guid in element_guids: (testbed_guid, factory_id) = data.get_box_data(guid) testbed = self._testbeds.get(testbed_guid) - if testbed: - testbed.defer_create(guid, factory_id) - for (name, value) in data.get_attribute_data(guid): - # Try to resolve create-time netrefs, if possible - if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value): - try: - nuvalue = self.resolve_netref_value(value) - except: - # Any trouble means we're not in shape to resolve the netref yet - nuvalue = None - if nuvalue is not None: - # Only if we succeed we remove the netref deferral entry - value = nuvalue - data.set_attribute_data(guid, name, value) - if (testbed_guid, guid) in self._netrefs: - self._netrefs[(testbed_guid, guid)].discard(name) - testbed.defer_create_set(guid, name, value) - - for guid in element_guids: - (testbed_guid, factory_id) = data.get_box_data(guid) + # create + testbed.defer_create(guid, factory_id) + # set attributes + for (name, value) in data.get_attribute_data(guid): + value = resolve_create_netref(data, guid, name, value) + testbed.defer_create_set(guid, name, value) + # traces + for trace_id in data.get_trace_data(guid): + testbed.defer_add_trace(guid, trace_id) + # addresses + for (address, netprefix, broadcast) in data.get_address_data(guid): + if address != None: + testbed.defer_add_address(guid, address, netprefix, + broadcast) + # routes + for (destination, netprefix, nexthop, metric) in data.get_route_data(guid): + testbed.defer_add_route(guid, destination, netprefix, nexthop, metric) + # store connections data + for (connector_type_name, other_guid, other_connector_type_name) \ + in data.get_connection_data(guid): + add_deferred_connection(deferred, data, guid, + connector_type_name, other_guid, + other_connector_type_name) + + # connections + for testbed_guid, data in deferred.iteritems(): testbed = self._testbeds.get(testbed_guid) - if testbed: - for (connector_type_name, cross_guid, cross_connector_type_name) \ - in data.get_connection_data(guid): - (testbed_guid, factory_id) = data.get_box_data(guid) - (cross_testbed_guid, cross_factory_id) = data.get_box_data( - cross_guid) - if testbed_guid == cross_testbed_guid: - testbed.defer_connect(guid, connector_type_name, - cross_guid, cross_connector_type_name) - for trace_id in data.get_trace_data(guid): - testbed.defer_add_trace(guid, trace_id) - for (address, netprefix, broadcast) in \ - data.get_address_data(guid): - if address != None: - testbed.defer_add_address(guid, address, netprefix, - broadcast) - for (destination, netprefix, nexthop, metric) in data.get_route_data(guid): - testbed.defer_add_route(guid, destination, netprefix, nexthop, metric) - - def _program_testbed_cross_connections(self, data): - data_guids = data.guids + for (guid, connector_type_name, other_guid, + other_connector_type_name) in data["connections"]: + testbed.defer_connect(guid, connector_type_name, + other_guid, other_connector_type_name) + for (testbed_guid, testbed_id, guid, factory_id, + connector_type_name, other_testbed_guid, + other_testbed_id, other_guid, other_factory_id, + other_connector_type_name) in data["cross_connections"]: + testbed.defer_cross_connect(guid, connector_type_name, other_guid, + other_testbed_guid, other_testbed_id, other_factory_id, + other_connector_type_name) + # save cross data for later + self._add_crossdata(testbed_guid, guid, other_testbed_guid, + other_guid) - for guid in data_guids: - if not data.is_testbed_data(guid): - (testbed_guid, factory_id) = data.get_box_data(guid) - testbed = self._testbeds.get(testbed_guid) - if testbed: - for (connector_type_name, cross_guid, cross_connector_type_name) \ - in data.get_connection_data(guid): - (testbed_guid, factory_id) = data.get_box_data(guid) - (cross_testbed_guid, cross_factory_id) = data.get_box_data( - cross_guid) - if testbed_guid != cross_testbed_guid: - cross_testbed = self._testbeds[cross_testbed_guid] - cross_testbed_id = cross_testbed.testbed_id - testbed.defer_cross_connect(guid, connector_type_name, cross_guid, - cross_testbed_guid, cross_testbed_id, cross_factory_id, - cross_connector_type_name) - # save cross data for later - self._add_crossdata(testbed_guid, guid, cross_testbed_guid, - cross_guid) - def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid): if testbed_guid not in self._cross_data: self._cross_data[testbed_guid] = dict() @@ -771,10 +808,9 @@ class ExperimentController(object): _testbed_id = cross_testbed.testbed_id, _testbed_version = cross_testbed.testbed_version) cross_data[cross_testbed_guid][cross_guid] = elem_cross_data - attribute_list = cross_testbed.get_attribute_list(cross_guid, - filter_flags = Attribute.DesignOnly) + attribute_list = cross_testbed.get_attribute_list(cross_guid) for attr_name in attribute_list: - attr_value = cross_testbed.get(cross_guid, attr_name) + attr_value = cross_testbed.get_deferred(cross_guid, attr_name) elem_cross_data[attr_name] = attr_value return cross_data diff --git a/src/nepi/core/factory.py b/src/nepi/core/factory.py index 7e3869d3..eee07f0b 100644 --- a/src/nepi/core/factory.py +++ b/src/nepi/core/factory.py @@ -10,7 +10,7 @@ class AddressableMixin(object): super(AddressableMixin, self).__init__(guid, factory, testbed_guid, container) max_addr = self._factory_attributes["maxAddresses"] - self._max_addresses = max_addr + self.set_attribute_value("maxAddresses", max_addr) self._addresses = list() @property @@ -19,7 +19,7 @@ class AddressableMixin(object): @property def max_addresses(self): - return self._max_addresses + return self.get_attribute_value("maxAddresses") class UserAddressableMixin(AddressableMixin): def __init__(self, guid, factory, testbed_guid, container = None): diff --git a/src/nepi/core/metadata.py b/src/nepi/core/metadata.py index cd7ea9a6..4148d0f5 100644 --- a/src/nepi/core/metadata.py +++ b/src/nepi/core/metadata.py @@ -162,7 +162,9 @@ class Metadata(object): "name" : "label", "validation_function" : validation.is_string, "type" : Attribute.STRING, - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "help" : "A unique identifier for referring to this box", }), }) @@ -174,7 +176,9 @@ class Metadata(object): "validation_function" : validation.is_integer, "type" : Attribute.INTEGER, "value" : 1, - "flags" : Attribute.Invisible, + "flags" : Attribute.DesignReadOnly |\ + Attribute.ExecInvisible |\ + Attribute.Metadata, "help" : "The maximum allowed number of addresses", }), }) @@ -193,13 +197,17 @@ class Metadata(object): "help" : "Path to the directory where traces and other files will be stored", "type" : Attribute.STRING, "value" : "", - "flags" : Attribute.DesignOnly + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, }), "label" : dict({ "name" : "label", "validation_function" : validation.is_string, "type" : Attribute.STRING, - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "help" : "A unique identifier for referring to this testbed", }), }) @@ -212,7 +220,9 @@ class Metadata(object): "validation_function" : validation.is_string, "help" : "Shell commands to run before spawning TestbedController processes", "type" : Attribute.STRING, - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "category" : AC.CATEGORY_DEPLOYMENT, }), DC.DEPLOYMENT_MODE: dict({ @@ -224,7 +234,9 @@ class Metadata(object): DC.MODE_DAEMON, DC.MODE_SINGLE_PROCESS ], - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_enum, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -237,7 +249,9 @@ class Metadata(object): DC.ACCESS_LOCAL, DC.ACCESS_SSH ], - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_enum, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -246,7 +260,9 @@ class Metadata(object): "help" : "Host where the testbed will be executed", "type" : Attribute.STRING, "value" : "localhost", - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_string, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -255,7 +271,9 @@ class Metadata(object): "help" : "User on the Host to execute the testbed", "type" : Attribute.STRING, "value" : getpass.getuser(), - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_string, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -263,7 +281,9 @@ class Metadata(object): "name" : DC.DEPLOYMENT_KEY, "help" : "Path to SSH key to use for connecting", "type" : Attribute.STRING, - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_string, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -272,7 +292,9 @@ class Metadata(object): "help" : "Port on the Host", "type" : Attribute.INTEGER, "value" : 22, - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_integer, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -281,7 +303,9 @@ class Metadata(object): "help" : "Root directory for storing process files", "type" : Attribute.STRING, "value" : ".", - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_string, # TODO: validation.is_path "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -290,7 +314,9 @@ class Metadata(object): "help" : "Use -A option for forwarding of the authentication agent, if ssh access is used", "type" : Attribute.BOOL, "value" : False, - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_bool, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -303,7 +329,9 @@ class Metadata(object): DC.ERROR_LEVEL, DC.DEBUG_LEVEL ], - "flags" : Attribute.DesignOnly, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_enum, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -312,7 +340,9 @@ class Metadata(object): "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, + "flags" : Attribute.ExecReadOnly |\ + Attribute.ExecImmutable |\ + Attribute.Metadata, "validation_function" : validation.is_bool, "category" : AC.CATEGORY_DEPLOYMENT, }), @@ -324,7 +354,10 @@ class Metadata(object): "name" : "tun_proto", "help" : "TUNneling protocol used", "type" : Attribute.STRING, - "flags" : Attribute.Invisible, + "flags" : Attribute.DesignInvisible | \ + Attribute.ExecInvisible | \ + Attribute.ExecImmutable | \ + Attribute.Metadata, "validation_function" : validation.is_string, }), "tun_key" : dict({ @@ -333,28 +366,40 @@ class Metadata(object): "Endpoints must agree to use the minimum (in lexicographic order) " "of both the remote and local sides.", "type" : Attribute.STRING, - "flags" :Attribute.Invisible, + "flags" : Attribute.DesignInvisible | \ + Attribute.ExecInvisible | \ + Attribute.ExecImmutable | \ + Attribute.Metadata, "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, + "flags" : Attribute.DesignInvisible | \ + Attribute.ExecInvisible | \ + Attribute.ExecImmutable | \ + Attribute.Metadata, "validation_function" : validation.is_string, }), "tun_port" : dict({ "name" : "tun_port", "help" : "IP port of the tunnel endpoint", "type" : Attribute.INTEGER, - "flags" : Attribute.Invisible, + "flags" : Attribute.DesignInvisible | \ + Attribute.ExecInvisible | \ + Attribute.ExecImmutable | \ + Attribute.Metadata, "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, + "flags" : Attribute.DesignInvisible | \ + Attribute.ExecInvisible | \ + Attribute.ExecImmutable | \ + Attribute.Metadata, "validation_function" : validation.is_string }), }) @@ -493,7 +538,6 @@ class Metadata(object): range = attr_info.get("range") allowed = attr_info.get("allowed") flags = attr_info.get("flags") - flags = Attribute.NoFlags if flags == None else flags validation_function = attr_info["validation_function"] category = attr_info.get("category") add_attr_func(name, help, type, value, range, allowed, flags, diff --git a/src/nepi/core/testbed_impl.py b/src/nepi/core/testbed_impl.py index 0c19d627..4b200565 100644 --- a/src/nepi/core/testbed_impl.py +++ b/src/nepi/core/testbed_impl.py @@ -58,69 +58,45 @@ class TestbedController(execute.TestbedController): def elements(self): return self._elements - def _get_factory_id(self, guid): - """ Returns the factory ID of the (perhaps not yet) created object """ - return self._create.get(guid, None) - def defer_configure(self, name, value): - if not self._attributes.has_attribute(name): - raise AttributeError("Invalid attribute %s for testbed" % name) - # Validation + self._validate_testbed_attribute(name) + self._validate_testbed_value(name, value) self._attributes.set_attribute_value(name, value) self._configure[name] = value def defer_create(self, guid, factory_id): - if factory_id not in self._factories: - raise AttributeError("Invalid element type %s for testbed version %s" % - (factory_id, self._testbed_version)) - if guid in self._create: - raise AttributeError("Cannot add elements with the same guid: %d" % - guid) + self._validate_factory_id(factory_id) + self._validate_not_guid(guid) self._create[guid] = factory_id def defer_create_set(self, guid, name, value): - if not guid in self._create: - raise RuntimeError("Element guid %d doesn't exist" % guid) - factory = self._get_factory(guid) - if not factory.box_attributes.has_attribute(name): - raise AttributeError("Invalid attribute %s for element type %s" % - (name, factory.factory_id)) - if not factory.box_attributes.is_attribute_value_valid(name, value): - raise AttributeError("Invalid value %s for attribute %s" % \ - (value, name)) + self._validate_guid(guid) + self._validate_box_attribute(guid, name) + self._validate_box_value(guid, name, value) if guid not in self._create_set: self._create_set[guid] = dict() self._create_set[guid][name] = value def defer_factory_set(self, guid, name, value): - if not guid in self._create: - raise RuntimeError("Element guid %d doesn't exist" % guid) - factory = self._get_factory(guid) - if not factory.has_attribute(name): - raise AttributeError("Invalid attribute %s for element type %s" % - (name, factory.factory_id)) - if not factory.is_attribute_value_valid(name, value): - raise AttributeError("Invalid value %s for attribute %s" % \ - (value, name)) + self._validate_guid(guid) + self._validate_factory_attribute(guid, name) + self._validate_factory_value(guid, name, value) if guid not in self._factory_set: self._factory_set[guid] = dict() self._factory_set[guid][name] = value def defer_connect(self, guid1, connector_type_name1, guid2, connector_type_name2): + self._validate_guid(guid1) + self._validate_guid(guid2) factory1 = self._get_factory(guid1) factory_id2 = self._create[guid2] - # TODO VALIDATE!!! - #if self.box.guid == connector.box.guid: - # return False - #if self.is_full() or connector.is_full(): - # return False - #if self.is_connected(connector): - # return False - #count = self._get_connection_count(guid1, connector_type_name1) connector_type = factory1.connector_type(connector_type_name1) connector_type.can_connect(self._testbed_id, factory_id2, connector_type_name2, False) + self._validate_connection(guid1, connector_type_name1, guid2, + connector_type_name2) + if not guid1 in self._connect: self._connect[guid1] = dict() if not connector_type_name1 in self._connect[guid1]: @@ -132,23 +108,19 @@ class TestbedController(execute.TestbedController): if not connector_type_name2 in self._connect[guid2]: self._connect[guid2][connector_type_name2] = dict() self._connect[guid2][connector_type_name2][guid1] = \ - connector_type_name1 + connector_type_name1 def defer_cross_connect(self, guid, connector_type_name, cross_guid, cross_testbed_guid, cross_testbed_id, cross_factory_id, cross_connector_type_name): + self._validate_guid(guid) factory = self._get_factory(guid) - # TODO VALIDATE!!! - #if self.box.guid == connector.box.guid: - # return False - #if self.is_full() or connector.is_full(): - # return False - #if self.is_connected(connector): - # return False - #count = self._get_connection_count(guid, connector_type_name) connector_type = factory.connector_type(connector_type_name) connector_type.can_connect(cross_testbed_id, cross_factory_id, cross_connector_type_name, True) + self._validate_connection(guid, connector_type_name, cross_guid, + cross_connector_type_name) + if not guid in self._cross_connect: self._cross_connect[guid] = dict() if not connector_type_name in self._cross_connect[guid]: @@ -158,40 +130,22 @@ class TestbedController(execute.TestbedController): cross_factory_id, cross_connector_type_name) def defer_add_trace(self, guid, trace_name): - if not guid in self._create: - raise RuntimeError("Element guid %d doesn't exist" % guid) - factory = self._get_factory(guid) - if not trace_name in factory.traces_list: - raise RuntimeError("Element type '%s' has no trace '%s'" % - (factory.factory_id, trace_name)) + self._validate_guid(guid) + self._validate_trace(guid, trace_name) if not guid in self._add_trace: self._add_trace[guid] = list() self._add_trace[guid].append(trace_name) def defer_add_address(self, guid, address, netprefix, broadcast): - if not guid in self._create: - raise RuntimeError("Element guid %d doesn't exist" % guid) - factory = self._get_factory(guid) - if not factory.allow_addresses: - raise RuntimeError("Element type '%s' doesn't support addresses" % - factory.factory_id) - max_addresses = 1 # TODO: MAKE THIS PARAMETRIZABLE - if guid in self._add_address: - count_addresses = len(self._add_address[guid]) - if max_addresses == count_addresses: - raise RuntimeError("Element guid %d of type '%s' can't accept \ - more addresses" % (guid, factory.factory_id)) - else: + self._validate_guid(guid) + self._validate_allow_addresses(guid) + if guid not in self._add_address: self._add_address[guid] = list() self._add_address[guid].append((address, netprefix, broadcast)) def defer_add_route(self, guid, destination, netprefix, nexthop, metric = 0): - if not guid in self._create: - raise RuntimeError("Element guid %d doesn't exist" % guid) - factory = self._get_factory(guid) - if not factory.allow_routes: - raise RuntimeError("Element type '%s' doesn't support routes" % - factory.factory_id) + self._validate_guid(guid) + self._validate_allow_routes(guid) if not guid in self._add_route: self._add_route[guid] = list() self._add_route[guid].append((destination, netprefix, nexthop, metric)) @@ -335,18 +289,10 @@ class TestbedController(execute.TestbedController): self._status = TS.STATUS_CROSS_CONNECTED def set(self, guid, name, value, time = TIME_NOW): - if not guid in self._create: - raise RuntimeError("Element guid %d doesn't exist" % guid) - factory = self._get_factory(guid) - if not factory.box_attributes.has_attribute(name): - raise AttributeError("Invalid attribute %s for element type %s" % - (name, factory.factory_id)) - if self._status > TS.STATUS_STARTED and \ - factory.box_attributes.is_attribute_design_only(name): - raise AttributeError("Attribute %s can only be modified during experiment design" % name) - if not factory.box_attributes.is_attribute_value_valid(name, value): - raise AttributeError("Invalid value %s for attribute %s" % \ - (value, name)) + self._validate_guid(guid) + self._validate_box_attribute(guid, name) + self._validate_box_value(guid, name, value) + self._validate_modify_box_value(guid, name) if guid not in self._set: self._set[guid] = dict() self._setlog[guid] = dict() @@ -362,16 +308,14 @@ class TestbedController(execute.TestbedController): through the defer_create interface, and AttributeError if the attribute isn't available (doesn't exist or is design-only) """ - if not guid in self._create: - raise KeyError, "Element guid %d doesn't exist" % guid - factory = self._get_factory(guid) - if not factory.box_attributes.has_attribute(name): - raise AttributeError, "Invalid attribute %s for element type %s" % \ - (name, factory.factory_id) + self._validate_guid(guid) + self._validate_box_attribute(guid, name) if guid in self._set and name in self._set[guid]: return self._set[guid][name] if guid in self._create_set and name in self._create_set[guid]: return self._create_set[guid][name] + # if nothing else found, returns the factory default value + factory = self._get_factory(guid) return factory.box_attributes.get_attribute_value(name) def get_route(self, guid, index, attribute): @@ -456,8 +400,7 @@ class TestbedController(execute.TestbedController): def status(self, guid = None): if not guid: return self._status - if not guid in self._create: - raise RuntimeError("Element guid %d doesn't exist" % guid) + self._validate_guid(guid) factory = self._get_factory(guid) status_function = factory.status_function if status_function: @@ -537,3 +480,109 @@ class TestbedController(execute.TestbedController): factory_id = self._create[guid] return self._factories[factory_id] + def _get_factory_id(self, guid): + """ Returns the factory ID of the (perhaps not yet) created object """ + return self._create.get(guid, None) + + def _validate_guid(self, guid): + if not guid in self._create: + raise RuntimeError("Element guid %d doesn't exist" % guid) + + def _validate_not_guid(self, guid): + if guid in self._create: + raise AttributeError("Cannot add elements with the same guid: %d" % + guid) + + def _validate_factory_id(self, factory_id): + if factory_id not in self._factories: + raise AttributeError("Invalid element type %s for testbed version %s" % + (factory_id, self._testbed_version)) + + def _validate_testbed_attribute(self, name): + if not self._attributes.has_attribute(name): + raise AttributeError("Invalid testbed attribute %s for testbed" % \ + name) + + def _validate_testbed_value(self, name, value): + if not self._attributes.is_attribute_value_valid(name, value): + raise AttributeError("Invalid value %s for testbed attribute %s" % \ + (value, name)) + + def _validate_box_attribute(self, guid, name): + factory = self._get_factory(guid) + if not factory.box_attributes.has_attribute(name): + raise AttributeError("Invalid attribute %s for element type %s" % + (name, factory.factory_id)) + + def _validate_box_value(self, guid, name, value): + factory = self._get_factory(guid) + if not factory.box_attributes.is_attribute_value_valid(name, value): + raise AttributeError("Invalid value %s for attribute %s" % \ + (value, name)) + + def _validate_factory_attribute(self, guid, name): + factory = self._get_factory(guid) + if not factory.has_attribute(name): + raise AttributeError("Invalid attribute %s for element type %s" % + (name, factory.factory_id)) + + def _validate_factory_value(self, guid, name, value): + factory = self._get_factory(guid) + if not factory.is_attribute_value_valid(name, value): + raise AttributeError("Invalid value %s for attribute %s" % \ + (value, name)) + + def _validate_trace(self, guid, trace_name): + factory = self._get_factory(guid) + if not trace_name in factory.traces_list: + raise RuntimeError("Element type '%s' has no trace '%s'" % + (factory.factory_id, trace_name)) + + def _validate_allow_addresses(self, guid): + factory = self._get_factory(guid) + if not factory.allow_addresses: + raise RuntimeError("Element type '%s' doesn't support addresses" % + factory.factory_id) + attr_name = "maxAddresses" + if guid in self._create_set and attr_name in self._create_set[guid]: + max_addresses = self._create_set[guid][attr_name] + else: + factory = self._get_factory(guid) + max_addresses = factory.box_attributes.get_attribute_value(attr_name) + if guid in self._add_address: + count_addresses = len(self._add_address[guid]) + if max_addresses == count_addresses: + raise RuntimeError("Element guid %d of type '%s' can't accept \ + more addresses" % (guid, factory.factory_id)) + + def _validate_allow_routes(self, guid): + factory = self._get_factory(guid) + if not factory.allow_routes: + raise RuntimeError("Element type '%s' doesn't support routes" % + factory.factory_id) + + def _validate_connection(self, guid1, connector_type_name1, guid2, + connector_type_name2, cross = False): + # can't connect with self + if guid1 == guid2: + raise AttributeError("Can't connect guid %d to self" % \ + (guid1)) + # the connection is already done, so ignore + connected = self.get_connected(guid1, connector_type_name1, + connector_type_name2) + if guid2 in connected: + return + count1 = self._get_connection_count(guid1, connector_type_name1) + factory1 = self._get_factory(guid1) + connector_type1 = factory1.connector_type(connector_type_name1) + if count1 == connector_type1.max: + raise AttributeError("Connector %s is full for guid %d" % \ + (connector_type_name1, guid1)) + + def _validate_modify_box_value(self, guid, name): + factory = self._get_factory(guid) + if self._status > TS.STATUS_STARTED and \ + (factory.box_attributes.is_attribute_exec_read_only(name) or \ + factory.box_attributes.is_attribute_exec_immutable(name)): + raise AttributeError("Attribute %s can only be modified during experiment design" % name) + diff --git a/src/nepi/testbeds/netns/execute.py b/src/nepi/testbeds/netns/execute.py index 4bfff535..b98f1cb5 100644 --- a/src/nepi/testbeds/netns/execute.py +++ b/src/nepi/testbeds/netns/execute.py @@ -86,8 +86,7 @@ class TestbedController(testbed_impl.TestbedController): # TODO: take on account schedule time for the task factory_id = self._create[guid] factory = self._factories[factory_id] - if factory.box_attributes.is_attribute_design_only(name) or \ - factory.box_attributes.is_attribute_invisible(name): + if factory.box_attributes.is_attribute_metadata(name): return element = self._elements.get(guid) if element: @@ -98,8 +97,7 @@ class TestbedController(testbed_impl.TestbedController): # TODO: take on account schedule time for the task factory_id = self._create[guid] factory = self._factories[factory_id] - if factory.box_attributes.is_attribute_design_only(name) or \ - factory.box_attributes.is_attribute_invisible(name): + if factory.box_attributes.is_attribute_metadata(name): return value element = self._elements.get(guid) try: diff --git a/src/nepi/testbeds/netns/metadata_v01.py b/src/nepi/testbeds/netns/metadata_v01.py index 539175fb..2e10da17 100644 --- a/src/nepi/testbeds/netns/metadata_v01.py +++ b/src/nepi/testbeds/netns/metadata_v01.py @@ -362,14 +362,14 @@ attributes = dict({ "help": "Forward x11 from main namespace to the node", "type": Attribute.BOOL, "value": False, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_bool }), "lladdr": dict({ "name": "lladdr", "help": "Mac address", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_mac_address }), "up": dict({ @@ -383,7 +383,7 @@ attributes = dict({ "name": "name", "help": "Device name", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "mtu": dict({ @@ -416,21 +416,21 @@ attributes = dict({ "name": "command", "help": "Command line string", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "user": dict({ "name": "user", "help": "System user", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "stdin": dict({ "name": "stdin", "help": "Standard input", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), }) diff --git a/src/nepi/testbeds/ns3/attributes_metadata_v3_9.py b/src/nepi/testbeds/ns3/attributes_metadata_v3_9.py index 85127d60..f4e200bd 100644 --- a/src/nepi/testbeds/ns3/attributes_metadata_v3_9.py +++ b/src/nepi/testbeds/ns3/attributes_metadata_v3_9.py @@ -12,7 +12,7 @@ testbed_attributes = dict({ "name": "SimulatorImplementationType", "help": "The object class to use as the simulator implementation", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "checksum": dict({ @@ -20,7 +20,7 @@ testbed_attributes = dict({ "help": "A global switch to enable all checksums for all protocols", "type": Attribute.BOOL, "value": False, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_bool }), "simu_stop": dict({ @@ -931,7 +931,7 @@ attributes = dict({ "name": "WaypointsLeft", "validation_function": validation.is_integer, "value": 0, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.INTEGER, "help": "The number of waypoints remaining." }), @@ -1560,7 +1560,7 @@ attributes = dict({ "name": "Library", "validation_function": validation.is_string, "value": "liblinux2.6.26.so", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "Set the linux library to be used to create the stack" }), @@ -2165,8 +2165,7 @@ attributes = dict({ "name": "ProtocolNumber", "validation_function": validation.is_integer, "value": 0, - - "flags": Attribute.ReadOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.INTEGER, "help": "The Ipv4 protocol number." }), @@ -2181,9 +2180,7 @@ attributes = dict({ "name": "Velocity", "validation_function": validation.is_string, "value": "0:0:0", - - "flags": Attribute.DesignOnly, - + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "The current velocity of the mobility model." }), @@ -2191,9 +2188,7 @@ attributes = dict({ "name": "StartTime", "validation_function": validation.is_string, "value": "0ns", - - "flags": Attribute.DesignOnly, - + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "Time at which the application will start" }), @@ -2415,7 +2410,7 @@ attributes = dict({ "name": "Standard", "validation_function": validation.is_string, "value": "WIFI_PHY_STANDARD_80211a", - "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue, "type": Attribute.ENUM, "allowed": wifi_standards.keys(), "help": "Wifi PHY standard" @@ -2424,7 +2419,7 @@ attributes = dict({ "name": "LinuxSocketAddress", "validation_function": None, "value": "", - "flags": Attribute.Invisible, + "flags": Attribute.DesignInvisible | Attribute.ExecInvisible | Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "Socket address assigned to the Linux socket created to recive file descriptor" }), @@ -2432,7 +2427,7 @@ attributes = dict({ "name": "SrcAddress", "validation_function": validation.is_string, # TODO:! Address + Netref "value": "", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "The source ip address for the IpcsClassifierRecord" }), @@ -2440,7 +2435,7 @@ attributes = dict({ "name": "SrcMask", "validation_function": validation.is_string, # TODO:! NetworkMask "value": "", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "The mask to apply on the source ip address for the IpcsClassifierRecord" }), @@ -2448,7 +2443,7 @@ attributes = dict({ "name": "DstAddress", "validation_function": validation.is_string, # TODO:! Address + Netref "value": "", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "The destination ip address for the IpcsClassifierRecord" }), @@ -2456,7 +2451,7 @@ attributes = dict({ "name": "DstMask", "validation_function": validation.is_string, # TODO:! NetworkMask "value": "", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "The mask to apply on the destination ip address for the IpcsClassifierRecord" }), @@ -2464,7 +2459,7 @@ attributes = dict({ "name": "SrcPortLow", "validation_function": validation.is_integer, "value": 0, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.INTEGER, "help": "The lower boundary of the source port range for the IpcsClassifierRecord" }), @@ -2472,7 +2467,7 @@ attributes = dict({ "name": "SrcPortHigh", "validation_function": validation.is_integer, "value": 65000, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.INTEGER, "help": "The higher boundary of the source port range for the IpcsClassifierRecord" }), @@ -2480,7 +2475,7 @@ attributes = dict({ "name": "DstPortLow", "validation_function": validation.is_integer, "value": 0, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.INTEGER, "help": "The lower boundary of the destination port range for the IpcsClassifierRecord" }), @@ -2488,7 +2483,7 @@ attributes = dict({ "name": "DstPortHigh", "validation_function": validation.is_integer, "value": 65000, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.INTEGER, "help": "The higher boundary of the destination port range for the IpcsClassifierRecord" }), @@ -2497,7 +2492,7 @@ attributes = dict({ "validation_function": validation.is_string, "value": "UdpL4Protocol", "allowed": l4_protocols.keys(), - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.ENUM, "help": "The L4 protocol for the IpcsClassifierRecord" }), @@ -2505,7 +2500,7 @@ attributes = dict({ "name": "Priority", "validation_function": validation.is_integer, "value": 1, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.INTEGER, "help": "The priority of the IpcsClassifierRecord" }), @@ -2514,7 +2509,7 @@ attributes = dict({ "validation_function": validation.is_string, "value": "SF_DIRECTION_UP", "allowed": service_flow_direction.keys(), - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.ENUM, "help": "Service flow direction as described by the IEEE-802.16 standard" }), @@ -2523,7 +2518,7 @@ attributes = dict({ "validation_function": validation.is_string, "value": "SF_TYPE_RTPS", "allowed": service_flow_scheduling_type.keys(), - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.ENUM, "help": "Service flow scheduling type", }), @@ -2531,7 +2526,7 @@ attributes = dict({ "name": "WaypointList", "validation_function": validation.is_string, # TODO: SPECIAL VALIDATION FUNC "value": "", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "type": Attribute.STRING, "help": "Comma separated list of waypoints in format t:x:y:z. Ex: 0s:0:0:0, 1s:1:0:0" }), diff --git a/src/nepi/testbeds/ns3/execute.py b/src/nepi/testbeds/ns3/execute.py index 0f0b29ff..a0c1b66e 100644 --- a/src/nepi/testbeds/ns3/execute.py +++ b/src/nepi/testbeds/ns3/execute.py @@ -103,14 +103,10 @@ class TestbedController(testbed_impl.TestbedController): # TODO: take on account schedule time for the task factory_id = self._create[guid] factory = self._factories[factory_id] - if factory.box_attributes.is_attribute_design_only(name): - return element = self._elements[guid] if factory_id in self.LOCAL_FACTORIES: setattr(element, name, value) - elif factory.box_attributes.is_attribute_invisible(name): - return - else: + elif not factory.box_attributes.is_attribute_metadata(name): ns3_value = self._to_ns3_value(guid, name, value) self._set_attribute(name, ns3_value, element) @@ -125,9 +121,9 @@ class TestbedController(testbed_impl.TestbedController): return getattr(element, name) else: return value - if factory.box_attributes.is_attribute_design_only(name) or \ - factory.box_attributes.is_attribute_invisible(name): + if factory.box_attributes.is_attribute_metadata(name): return value + TypeId = self.ns3.TypeId() typeid = TypeId.LookupByName(factory_id) info = TypeId.AttributeInfo() diff --git a/src/nepi/testbeds/ns3/factories_metadata_v3_9.py b/src/nepi/testbeds/ns3/factories_metadata_v3_9.py index 2ba13369..d2f30090 100644 --- a/src/nepi/testbeds/ns3/factories_metadata_v3_9.py +++ b/src/nepi/testbeds/ns3/factories_metadata_v3_9.py @@ -54,16 +54,14 @@ def _get_ipv4_protocol_guid(testbed_instance, node_guid): ipv4_guid = proto_guid break if not ipv4_guid: - raise RuntimeError("No Ipv4L3Protocol associated to node %d. \ - can't add Ipv4 addresses" % node_guid) + raise RuntimeError("No Ipv4L3Protocol associated to node %d. Can't add Ipv4 addresses" % node_guid) return ipv4_guid def _get_node_guid(testbed_instance, guid): # search for the node asociated with the device node_guids = testbed_instance.get_connected(guid, "node", "devs") if len(node_guids) == 0: - raise RuntimeError("Can't instantiate interface %d outside netns \ - node" % guid) + raise RuntimeError("Can't instantiate interface %d outside node" % guid) node_guid = node_guids[0] return node_guid diff --git a/src/nepi/testbeds/planetlab/execute.py b/src/nepi/testbeds/planetlab/execute.py index 0728c170..61bda109 100644 --- a/src/nepi/testbeds/planetlab/execute.py +++ b/src/nepi/testbeds/planetlab/execute.py @@ -439,8 +439,6 @@ class TestbedController(testbed_impl.TestbedController): # TODO: take on account schedule time for the task factory_id = self._create[guid] factory = self._factories[factory_id] - if factory.box_attributes.is_attribute_design_only(name): - return value element = self._elements.get(guid) try: return getattr(element, name) diff --git a/src/nepi/testbeds/planetlab/metadata_v01.py b/src/nepi/testbeds/planetlab/metadata_v01.py index 9f2fd3c4..ceea03d1 100644 --- a/src/nepi/testbeds/planetlab/metadata_v01.py +++ b/src/nepi/testbeds/planetlab/metadata_v01.py @@ -621,21 +621,21 @@ attributes = dict({ "help": "Forward x11 from main namespace to the node", "type": Attribute.BOOL, "value": False, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_bool, }), "hostname": dict({ "name": "hostname", "help": "Constrain hostname during resource discovery. May use wildcards.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string, }), "architecture": dict({ "name": "architecture", "help": "Constrain architexture during resource discovery.", "type": Attribute.ENUM, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "allowed": ["x86_64", "i386"], "validation_function": validation.is_enum, @@ -644,7 +644,7 @@ attributes = dict({ "name": "operatingSystem", "help": "Constrain operating system during resource discovery.", "type": Attribute.ENUM, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "allowed": ["f8", "f12", "f14", @@ -656,7 +656,7 @@ attributes = dict({ "name": "site", "help": "Constrain the PlanetLab site this node should reside on.", "type": Attribute.ENUM, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "allowed": ["PLE", "PLC", "PLJ"], @@ -667,7 +667,7 @@ attributes = dict({ "help": "Enable emulation on this node. Enables NetfilterRoutes, bridges, and a host of other functionality.", "type": Attribute.BOOL, "value": False, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_bool, }), "min_reliability": dict({ @@ -675,7 +675,7 @@ attributes = dict({ "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.", "type": Attribute.DOUBLE, "range": (0,100), - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_double, }), "max_reliability": dict({ @@ -683,7 +683,7 @@ attributes = dict({ "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.", "type": Attribute.DOUBLE, "range": (0,100), - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_double, }), "min_bandwidth": dict({ @@ -691,7 +691,7 @@ attributes = dict({ "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.", "type": Attribute.DOUBLE, "range": (0,2**31), - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_double, }), "max_bandwidth": dict({ @@ -699,7 +699,7 @@ attributes = dict({ "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.", "type": Attribute.DOUBLE, "range": (0,2**31), - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_double, }), @@ -721,7 +721,7 @@ attributes = dict({ "name": "name", "help": "Device name", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "mtu": dict({ @@ -749,14 +749,14 @@ attributes = dict({ "help": "If the interface is a P2P link, the remote endpoint's IP " "should be set on this attribute.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "txqueuelen": dict({ "name": "mask", "help": "Transmission queue length (in packets)", "type": Attribute.INTEGER, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "range" : (1,10000), "validation_function": validation.is_integer }), @@ -765,14 +765,14 @@ attributes = dict({ "name": "command", "help": "Command line string", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "sudo": dict({ "name": "sudo", "help": "Run with root privileges", "type": Attribute.BOOL, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "value": False, "validation_function": validation.is_bool }), @@ -780,7 +780,7 @@ attributes = dict({ "name": "stdin", "help": "Standard input", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), @@ -788,21 +788,21 @@ attributes = dict({ "name": "depends", "help": "Space-separated list of packages required to run the application", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "build-depends": dict({ "name": "buildDepends", "help": "Space-separated list of packages required to build the application", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "rpm-fusion": dict({ "name": "rpmFusion", "help": "True if required packages can be found in the RpmFusion repository", "type": Attribute.BOOL, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "value": False, "validation_function": validation.is_bool }), @@ -811,7 +811,7 @@ attributes = dict({ "help": "Space-separated list of regular files to be deployed in the working path prior to building. " "Archives won't be expanded automatically.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "build": dict({ @@ -826,7 +826,7 @@ attributes = dict({ "make sure to clean up temporary files, to reduce bandwidth usage between " "nodes when transferring built packages.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "install": dict({ @@ -842,7 +842,7 @@ attributes = dict({ "make and other tools to install, be sure to provide them as " "actual dependencies instead.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), @@ -853,7 +853,7 @@ attributes = dict({ " * CLIENT: applies to outgoing connections\n" " * SERVICE: applies to both", "type": Attribute.ENUM, - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "allowed": ["SERVER", "CLIENT", "SERVICE"], @@ -1078,21 +1078,21 @@ testbed_attributes = dict({ "name": "slice", "help": "The name of the PlanetLab slice to use", "type": Attribute.STRING, - "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue, "validation_function": validation.is_string }), "auth_user": dict({ "name": "authUser", "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue, "validation_function": validation.is_string }), "auth_pass": dict({ "name": "authPass", "help": "The PlanetLab user's password.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue, "validation_function": validation.is_string }), "plc_host": dict({ @@ -1100,7 +1100,7 @@ testbed_attributes = dict({ "help": "The PlanetLab PLC API host", "type": Attribute.STRING, "value": "www.planet-lab.eu", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "plc_url": dict({ @@ -1108,7 +1108,7 @@ testbed_attributes = dict({ "help": "The PlanetLab PLC API url pattern - %(hostname)s is replaced by plcHost.", "type": Attribute.STRING, "value": "https://%(hostname)s:443/PLCAPI/", - "flags": Attribute.DesignOnly, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, "validation_function": validation.is_string }), "slice_ssh_key": dict({ @@ -1119,7 +1119,7 @@ testbed_attributes = dict({ "It is recommended that a NEPI-specific user be created for this purpose and " "this purpose alone.", "type": Attribute.STRING, - "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue, "validation_function": validation.is_string }), }) diff --git a/test/lib/mock/metadata_v01.py b/test/lib/mock/metadata_v01.py index b2f2b670..49ece6e2 100644 --- a/test/lib/mock/metadata_v01.py +++ b/test/lib/mock/metadata_v01.py @@ -119,7 +119,9 @@ attributes = dict({ "help": "Attribute that indicates the maximum number of addresses for an interface", "type": Attribute.INTEGER, "value": 3, - "flags": Attribute.Invisible, + "flags" : Attribute.DesignReadOnly |\ + Attribute.ExecInvisible |\ + Attribute.Metadata, "validation_function": validation.is_integer }) })