From: Claudio-Daniel Freire Date: Thu, 21 Apr 2011 12:16:38 +0000 (+0200) Subject: Several execution fixes: X-Git-Tag: nepi_v2~137 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=0f6345e48f32e6080a2ed709d963722601a288ee;p=nepi.git Several execution fixes: * execution test case * testbed needs username/password to work * early assignment of nodes to an existing PL node if unambiguously available * other minor stuff --- diff --git a/src/nepi/testbeds/planetlab/execute.py b/src/nepi/testbeds/planetlab/execute.py index 97816612..8611834b 100644 --- a/src/nepi/testbeds/planetlab/execute.py +++ b/src/nepi/testbeds/planetlab/execute.py @@ -12,19 +12,49 @@ class TestbedController(testbed_impl.TestbedController): self.slicename = None self._traces = dict() - import node, interfaces + import node, interfaces, application self._node = node self._interfaces = interfaces + self._app = application @property def home_directory(self): return self._home_directory + + @property + def plapi(self): + if not hasattr(self, '_plapi'): + import plcapi + + if self.authUser: + self._plapi = plcapi.PLCAPI( + username = self.authUser, + password = self.authString) + else: + # anonymous access - may not be enough for much + self._plapi = plcapi.PLCAPI() + return self._plapi + + @property + def slice_id(self): + if not hasattr(self, '_slice_id'): + slices = self.plapi.GetSlices(self.slicename, fields=('slice_id',)) + if slices: + self._slice_id = slices[0]['slice_id'] + else: + # If it wasn't found, don't remember this failure, keep trying + return None + return self._slice_id def do_setup(self): self._home_directory = self._attributes.\ get_attribute_value("homeDirectory") self.slicename = self._attributes.\ get_attribute_value("slice") + self.authUser = self._attributes.\ + get_attribute_value("authUser") + self.authString = self._attributes.\ + get_attribute_value("authPass") def do_create(self): # Create node elements per XML data @@ -39,6 +69,18 @@ class TestbedController(testbed_impl.TestbedController): # Wait for all nodes to be ready self.wait_nodes() + + def do_resource_discovery(self): + # Do what? + pass + + def do_provisioning(self): + # Que te recontra? + pass + + def wait_nodes(self): + # Suuure... + pass def set(self, time, guid, name, value): super(TestbedController, self).set(time, guid, name, value) @@ -93,45 +135,45 @@ class TestbedController(testbed_impl.TestbedController): self._traces[trace_id] = trace def _make_node(self, parameters): - node = self._node.Node() + node = self._node.Node(self.plapi) # Note: there is 1-to-1 correspondence between attribute names # If that changes, this has to change as well - for attr in parameters.get_attribute_names(): - setattr(node, attr, parameters.get_attribute_value(attr)) + for attr,val in parameters.iteritems(): + setattr(node, attr, val) return node def _make_node_iface(self, parameters): - iface = self._interfaces.NodeIface() + iface = self._interfaces.NodeIface(self.plapi) # Note: there is 1-to-1 correspondence between attribute names # If that changes, this has to change as well - for attr in parameters.get_attribute_names(): - setattr(iface, attr, parameters.get_attribute_value(attr)) + for attr,val in parameters.iteritems(): + setattr(iface, attr, val) return iface def _make_tun_iface(self, parameters): - iface = self._interfaces.TunIface() + iface = self._interfaces.TunIface(self.plapi) # Note: there is 1-to-1 correspondence between attribute names # If that changes, this has to change as well - for attr in parameters.get_attribute_names(): - setattr(iface, attr, parameters.get_attribute_value(attr)) + for attr,val in parameters.iteritems(): + setattr(iface, attr, val) return iface def _make_internet(self, parameters): - return self._node.Internet() + return self._interfaces.Internet(self.plapi) def _make_application(self, parameters): - app = self._app.Application() + app = self._app.Application(self.plapi) # Note: there is 1-to-1 correspondence between attribute names # If that changes, this has to change as well - for attr in parameters.get_attribute_names(): - setattr(app, attr, parameters.get_attribute_value(attr)) + for attr,val in parameters.iteritems(): + setattr(app, attr, val) return app diff --git a/src/nepi/testbeds/planetlab/interfaces.py b/src/nepi/testbeds/planetlab/interfaces.py index d1dea809..4be8f538 100644 --- a/src/nepi/testbeds/planetlab/interfaces.py +++ b/src/nepi/testbeds/planetlab/interfaces.py @@ -18,6 +18,7 @@ class NodeIface(object): self.address = None self.lladdr = None self.netprefix = None + self.netmask = None self.broadcast = True self._interface_id = None @@ -27,6 +28,13 @@ class NodeIface(object): # These get initialized when the iface is connected to the internet self.has_internet = False + def __str__(self): + return "%s" % ( + self.__class__.__name__, + self.address, self.netmask, + self.lladdr, + ) + def add_address(self, address, netprefix, broadcast): raise RuntimeError, "Cannot add explicit addresses to public interface" @@ -40,13 +48,13 @@ class NodeIface(object): siblings: other NodeIface elements attached to the same node """ - if (self.node or self.node._node_id) is None: + if self.node is None or self.node._node_id is None: raise RuntimeError, "Cannot pick interface without an assigned node" avail = self._api.GetInterfaces( node_id=self.node._node_id, is_primary=self.primary, - fields=('interface_id','mac','netmask','ip') )) + fields=('interface_id','mac','netmask','ip') ) used = set([sibling._interface_id for sibling in siblings if sibling._interface_id is not None]) @@ -59,12 +67,13 @@ class NodeIface(object): self.address = candidate['ip'] self.lladdr = candidate['mac'] self.netprefix = candidate['netmask'] + self.netmask = ipaddr2.ipv4_dot2mask(self.netprefix) if self.netprefix else None return else: raise RuntimeError, "Cannot configure interface: cannot find suitable interface in PlanetLab node" def validate(self): - if not element.has_internet: + if not self.has_internet: raise RuntimeError, "All external interface devices must be connected to the Internet" @@ -87,6 +96,14 @@ class TunIface(object): # These get initialized when the iface is connected to its node self.node = None + def __str__(self): + return "%s" % ( + self.__class__.__name__, + self.address, self.netmask, + " up" if self.up else " down", + " snat" if self.snat else "", + ) + def add_address(self, address, netprefix, broadcast): if (self.address or self.netprefix or self.netmask) is not None: raise RuntimeError, "Cannot add more than one address to TUN interfaces" diff --git a/src/nepi/testbeds/planetlab/metadata_v01.py b/src/nepi/testbeds/planetlab/metadata_v01.py index d8879a13..2142da6e 100644 --- a/src/nepi/testbeds/planetlab/metadata_v01.py +++ b/src/nepi/testbeds/planetlab/metadata_v01.py @@ -50,7 +50,7 @@ def create_node(testbed_instance, guid): def create_nodeiface(testbed_instance, guid): parameters = testbed_instance._get_parameters(guid) - element = testbed_instance.create_node_iface(parameters) + element = testbed_instance._make_node_iface(parameters) testbed_instance.elements[guid] = element def create_tuniface(testbed_instance, guid): @@ -74,8 +74,6 @@ def start_application(testbed_instance, guid): parameters = testbed_instance._get_parameters(guid) traces = testbed_instance._get_traces(guid) app = testbed_instance.elements[guid] - sudo = parameters["sudo"] - command = parameters["command"] app.stdout = testbed_instance.trace_filename(guid, "stdout") app.stderr = testbed_instance.trace_filename(guid, "stderr") @@ -90,17 +88,16 @@ def status_application(testbed_instance, guid): return STATUS_NOT_STARTED app = testbed_instance.elements[guid] # TODO - return STATUS_NOT_STARTED + return STATUS_FINISHED ### Configure functions ### def configure_nodeiface(testbed_instance, guid): element = testbed_instance._elements[guid] - if not guid in testbed_instance._add_address: - return # Cannot explicitly configure addresses - del testbed_instance._add_address[guid] + if guid in testbed_instance._add_address: + del testbed_instance._add_address[guid] # Get siblings node_guid = testbed_instance.get_connected(guid, "node", "devs")[0] @@ -119,6 +116,7 @@ def configure_tuniface(testbed_instance, guid): element = testbed_instance._elements[guid] if not guid in testbed_instance._add_address: return + addresses = testbed_instance._add_address[guid] for address in addresses: (address, netprefix, broadcast) = address @@ -130,6 +128,12 @@ def configure_tuniface(testbed_instance, guid): def configure_node(testbed_instance, guid): element = testbed_instance._elements[guid] + # If we have only one candidate, simply use it + candidates = element.find_candidates( + filter_slice_id = testbed_instance.slice_id) + if len(candidates) == 1: + element.assign_node_id(iter(candidates).next()) + # Do some validations element.validate() @@ -354,9 +358,9 @@ traces = dict({ }) }) -create_order = [ NODE, NODEIFACE, TUNIFACE, APPLICATION ] +create_order = [ INTERNET, NODE, NODEIFACE, TUNIFACE, APPLICATION ] -configure_order = [ NODE, NODEIFACE, TUNIFACE, APPLICATION ] +configure_order = [ INTERNET, NODE, NODEIFACE, TUNIFACE, APPLICATION ] factories_info = dict({ NODE: dict({ @@ -425,6 +429,20 @@ testbed_attributes = dict({ "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, "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, + "validation_function": validation.is_string + }), + "auth_pass": dict({ + "name": "authPass", + "help": "The PlanetLab user's password.", + "type": Attribute.STRING, + "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue, + "validation_function": validation.is_string + }), }) class VersionedMetadataInfo(metadata.VersionedMetadataInfo): diff --git a/src/nepi/testbeds/planetlab/node.py b/src/nepi/testbeds/planetlab/node.py index e9a95579..e5fd483b 100644 --- a/src/nepi/testbeds/planetlab/node.py +++ b/src/nepi/testbeds/planetlab/node.py @@ -62,12 +62,14 @@ class Node(object): + filter(has, self.TAGFILTERS.iterkeys()) ) - def find_candidates(self): + def find_candidates(self, filter_slice_id=None): fields = ('node_id',) replacements = {'timeframe':self.timeframe} # get initial candidates (no tag filters) basefilters = self.build_filters({}, self.BASEFILTERS) + if filter_slice_id: + basefilters['|slice_ids'] = (filter_slice_id,) # keyword-only "pseudofilters" extra = {} @@ -115,6 +117,37 @@ class Node(object): return candidates + def assign_node_id(self, node_id): + self._node_id = node_id + self.fetch_node_info() + + def fetch_node_info(self): + info = self._api.GetNodes(self._node_id) + tags = dict( (t['tagname'],t['value']) + for t in self._api.GetNodeTags(node_id=self._node_id, fields=('tagname','value')) ) + + self.min_num_external_ifaces = None + self.max_num_external_ifaces = None + self.timeframe = 'm' + + replacements = {'timeframe':self.timeframe} + for attr, tag in self.BASEFILTERS.iteritems(): + if tag in info: + value = info[tag] + setattr(self, attr, value) + for attr, (tag,_) in self.TAGFILTERS.iteritems(): + tag = tag % replacements + if tag in tags: + value = tags[tag] + setattr(self, attr, value) + + if 'peer_id' in info: + self.site = self._api.peer_map[info['peer_id']] + + if 'interface_ids' in info: + self.min_num_external_ifaces = \ + self.max_num_external_ifaces = len(info['interface_ids']) + def validate(self): pass diff --git a/src/nepi/testbeds/planetlab/plcapi.py b/src/nepi/testbeds/planetlab/plcapi.py index ab8fd2ec..2c1752e1 100644 --- a/src/nepi/testbeds/planetlab/plcapi.py +++ b/src/nepi/testbeds/planetlab/plcapi.py @@ -115,6 +115,10 @@ class PLCAPI(object): (peer['peername'], peer['peer_id']) for peer in peers ) + self._peer_map = dict( + (peer['peer_id'], peer['shortname']) + for peer in peers + ) return self._peer_map @@ -224,10 +228,22 @@ class PLCAPI(object): else: fieldstuple = () if interfaceIdOrIp is not None: - return self.api.GetNodeTags(self.auth, interfaceIdOrIp, *fieldstuple) + return self.api.GetInterfaces(self.auth, interfaceIdOrIp, *fieldstuple) else: filters = kw.pop('filters',{}) filters.update(kw) - return self.api.GetNodeTags(self.auth, filters, *fieldstuple) + return self.api.GetInterfaces(self.auth, filters, *fieldstuple) + + def GetSlices(self, sliceIdOrName=None, fields=None, **kw): + if fields is not None: + fieldstuple = (fields,) + else: + fieldstuple = () + if sliceIdOrName is not None: + return self.api.GetSlices(self.auth, sliceIdOrName, *fieldstuple) + else: + filters = kw.pop('filters',{}) + filters.update(kw) + return self.api.GetSlices(self.auth, filters, *fieldstuple) diff --git a/test/testbeds/planetlab/execute.py b/test/testbeds/planetlab/execute.py index b02f5427..bb3c77f3 100755 --- a/test/testbeds/planetlab/execute.py +++ b/test/testbeds/planetlab/execute.py @@ -3,13 +3,14 @@ import getpass from nepi.util.constants import STATUS_FINISHED -from nepi.testbeds import netns +from nepi.testbeds import planetlab import os import shutil import tempfile import test_util import time import unittest +import re class NetnsExecuteTestCase(unittest.TestCase): def setUp(self): @@ -18,6 +19,53 @@ class NetnsExecuteTestCase(unittest.TestCase): def tearDown(self): shutil.rmtree(self.root_dir) + def test_simple(self): + testbed_version = "01" + instance = planetlab.TestbedController(testbed_version) + + instance.defer_configure("homeDirectory", self.root_dir) + instance.defer_configure("slice", "inria_nepi12") + instance.defer_configure("authUser", "claudio-daniel.freire@inria.fr") + instance.defer_configure("authPass", getpass.getpass()) + + instance.defer_create(2, "Node") + instance.defer_create_set(2, "hostname", "onelab11.pl.sophia.inria.fr") + instance.defer_create(3, "Node") + instance.defer_create_set(3, "hostname", "onelab10.pl.sophia.inria.fr") + instance.defer_create(4, "NodeInterface") + instance.defer_connect(2, "devs", 4, "node") + instance.defer_create(5, "NodeInterface") + instance.defer_connect(3, "devs", 5, "node") + instance.defer_create(6, "Internet") + instance.defer_connect(4, "inet", 6, "devs") + instance.defer_connect(5, "inet", 6, "devs") + instance.defer_create(7, "Application") + instance.defer_create_set(7, "command", "ping -qc1 {#GUID-3.addr[0].[ip]#}") + instance.defer_add_trace(7, "stdout") + instance.defer_connect(7, "node", 2, "apps") + + instance.do_setup() + instance.do_create() + instance.do_connect() + instance.do_configure() + + print instance.elements[4] + print instance.elements[5] + + instance.start() + while instance.status(7) != STATUS_FINISHED: + time.sleep(0.5) + ping_result = instance.trace(7, "stdout") + comp_result = r"""PING .* \(.*) \d*\(\d*\) bytes of data. + +--- .* ping statistics --- +1 packets transmitted, 1 received, 0% packet loss, time \d*ms.* +""" + self.assertTrue(re.match(comp_result, ping_result, re.MULTILINE)) + instance.stop() + instance.shutdown() + + if __name__ == '__main__': unittest.main()