}
### 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,
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):
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
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()
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
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",
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):
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):
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
# 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
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()
_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
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
@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):
"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",
}),
})
"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",
}),
})
"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",
}),
})
"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({
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,
}),
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,
}),
"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,
}),
"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,
}),
"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,
}),
"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,
}),
"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,
}),
"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,
}),
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,
}),
"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,
}),
"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({
"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
}),
})
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,
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]:
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]:
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))
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()
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):
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:
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)
+
# 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:
# 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:
"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({
"name": "name",
"help": "Device name",
"type": Attribute.STRING,
- "flags": Attribute.DesignOnly,
+ "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
"validation_function": validation.is_string
}),
"mtu": 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
}),
})
"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({
"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({
"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."
}),
"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"
}),
"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."
}),
"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."
}),
"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"
}),
"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"
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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"
}),
"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",
}),
"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"
}),
# 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)
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()
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
# 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)
"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,
"name": "operatingSystem",
"help": "Constrain operating system during resource discovery.",
"type": Attribute.ENUM,
- "flags": Attribute.DesignOnly,
+ "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
"allowed": ["f8",
"f12",
"f14",
"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"],
"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({
"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({
"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({
"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({
"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,
}),
"name": "name",
"help": "Device name",
"type": Attribute.STRING,
- "flags": Attribute.DesignOnly,
+ "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
"validation_function": validation.is_string
}),
"mtu": 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
}),
"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
}),
"name": "stdin",
"help": "Standard input",
"type": Attribute.STRING,
- "flags": Attribute.DesignOnly,
+ "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
"validation_function": validation.is_string
}),
"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
}),
"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({
"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({
"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
}),
" * 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"],
"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({
"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({
"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({
"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
}),
})
"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
})
})