self.do_connect_init()
self.do_connect_compl()
+ # Manually recover nodes, to mark dependencies installed
+ # and clean up mutable attributes
+ self._do_in_factory_order(
+ lambda self, guid : self._elements[guid].recover(),
+ [
+ metadata.NODE,
+ ])
+
# Assign nodes - since we're working off exeucte XML, nodes
# have specific hostnames assigned and we don't need to do
# real assignment, only find out node ids and check liveliness
# Execute configuration steps only for those object
# kinds that do not have side effects
- # Manually recover nodes, to mark dependencies installed
- self._do_in_factory_order(
- lambda self, guid : self._elements[guid].recover(),
- [
- metadata.NODE,
- ])
-
# Do the ones without side effects,
# including nodes that need to set up home
# folders and all that
self.peer_addr = None
self.peer_port = None
self.peer_proto_impl = None
+ self._delay_recover = False
# same as peer proto, but for execute-time standard attribute lookups
self.tun_proto = None
return impl
def recover(self):
- self.peer_proto_impl = self._impl_instance(
- self._home_path,
- False) # no way to know, no need to know
- self.peer_proto_impl.recover()
+ if self.peer_proto:
+ self.peer_proto_impl = self._impl_instance(
+ self._home_path,
+ False) # no way to know, no need to know
+ self.peer_proto_impl.recover()
+ else:
+ self._delay_recover = True
def prepare(self, home_path, listening):
if not self.peer_iface and (self.peer_proto and (listening or (self.peer_addr and self.peer_port))):
if self.peer_iface:
if not self.peer_proto_impl:
self.peer_proto_impl = self._impl_instance(home_path, listening)
- self.peer_proto_impl.prepare()
+ if self._delay_recover:
+ self.peer_proto_impl.recover()
+ else:
+ self.peer_proto_impl.prepare()
def setup(self):
if self.peer_proto_impl:
"type": Attribute.DOUBLE,
"range": (0,100),
"flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"max_reliability": dict({
"name": "maxReliability",
"type": Attribute.DOUBLE,
"range": (0,100),
"flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"min_bandwidth": dict({
"name": "minBandwidth",
"type": Attribute.DOUBLE,
"range": (0,2**31),
"flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"max_bandwidth": dict({
"name": "maxBandwidth",
"type": Attribute.DOUBLE,
"range": (0,2**31),
"flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"up": dict({
"name": "bwIn",
"help": "Inbound bandwidth limit (in Mbit/s)",
"type": Attribute.DOUBLE,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"bw_out": dict({
"name": "bwOut",
"help": "Outbound bandwidth limit (in Mbit/s)",
"type": Attribute.DOUBLE,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"plr_in": dict({
"name": "plrIn",
"help": "Inbound packet loss rate (0 = no loss, 1 = 100% loss)",
"type": Attribute.DOUBLE,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"plr_out": dict({
"name": "plrOut",
"help": "Outbound packet loss rate (0 = no loss, 1 = 100% loss)",
"type": Attribute.DOUBLE,
- "validation_function": validation.is_double,
+ "validation_function": validation.is_number,
}),
"delay_in": dict({
"name": "delayIn",
class UnresponsiveNodeError(RuntimeError):
pass
+def _castproperty(typ, propattr):
+ def _get(self):
+ return getattr(self, propattr)
+ def _set(self, value):
+ if value is not None or (isinstance(value, basestring) and not value):
+ value = typ(value)
+ return setattr(self, propattr, value)
+ def _del(self, value):
+ return delattr(self, propattr)
+ _get.__name__ = propattr + '_get'
+ _set.__name__ = propattr + '_set'
+ _del.__name__ = propattr + '_del'
+ return property(_get, _set, _del)
+
class Node(object):
BASEFILTERS = {
# Map Node attribute to plcapi filter name
RPM_FUSION_URL = 'http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-stable.noarch.rpm'
RPM_FUSION_URL_F12 = 'http://download1.rpmfusion.org/free/fedora/releases/12/Everything/x86_64/os/rpmfusion-free-release-12-1.noarch.rpm'
+ minReliability = _castproperty(float, '_minReliability')
+ maxReliability = _castproperty(float, '_maxReliability')
+ minBandwidth = _castproperty(float, '_minBandwidth')
+ maxBandwidth = _castproperty(float, '_maxBandwidth')
+
def __init__(self, api=None):
if not api:
api = plcapi.PLCAPI()
raise AssertionError, "Misconfigured node: unspecified slice"
def recover(self):
- # Just mark dependencies installed
+ # Mark dependencies installed
self._installed = True
+
+ # Clear load attributes, they impair re-discovery
+ self.minReliability = \
+ self.maxReliability = \
+ self.minBandwidth = \
+ self.maxBandwidth = None
def install_dependencies(self):
if self.required_packages and not self._installed:
return node1, iface1, tap1, tap1ip, inet
- def _test_plpl_crossconnect(self, proto):
+ def _test_plpl_crossconnect(self, proto, recover = False):
pl, pl2, exp = self.make_experiment_desc()
+ if recover:
+ pl.set_attribute_value(DC.RECOVERY_POLICY, DC.POLICY_RECOVER)
+ pl2.set_attribute_value(DC.RECOVERY_POLICY, DC.POLICY_RECOVER)
+
# Create PL node, ifaces, assign addresses
node1, iface1, tap1, tap1ip, inet1 = self.make_pl_tapnode(pl,
"192.168.2.2", self.host1pl1, "node1")
xml = exp.to_xml()
- controller = ExperimentController(xml, self.root_dir)
- controller.start()
-
- while not controller.is_finished(ping.guid):
- time.sleep(0.5)
-
- ping_result = controller.trace(ping.guid, "stdout")
- tap_trace = controller.trace(tap1.guid, "packets")
- tap2_trace = controller.trace(tap2.guid, "packets")
-
- controller.stop()
- controller.shutdown()
+ try:
+ controller = ExperimentController(xml, self.root_dir)
+ controller.start()
+
+ if recover:
+ controller = None
+ controller = ExperimentController(None, self.root_dir)
+ controller.recover()
+
+ while not controller.is_finished(ping.guid):
+ time.sleep(0.5)
+
+ ping_result = controller.trace(ping.guid, "stdout")
+ tap_trace = controller.trace(tap1.guid, "packets")
+ tap2_trace = controller.trace(tap2.guid, "packets")
+
+ finally:
+ if controller is not None:
+ controller.stop()
+ controller.shutdown()
# asserts at the end, to make sure there's proper cleanup
self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE),
def test_plpl_crossconnect_tcp(self):
self._test_plpl_crossconnect("tcp")
+ @test_util.skipUnless(test_util.pl_auth() is not None,
+ "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)")
+ def test_plpl_crossconnect_udp_recover(self):
+ self._test_plpl_crossconnect("udp",
+ recover = True)
+
if __name__ == '__main__':
unittest.main()