From: Claudio-Daniel Freire Date: Fri, 8 Apr 2011 13:54:12 +0000 (+0200) Subject: Add .hgignore: pyc and ~ files X-Git-Tag: nepi_v2~171 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=5c71f7058cc1cf8641da8510bef3cc16ea1a7c18;p=nepi.git Add .hgignore: pyc and ~ files --- diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..163d7b02 --- /dev/null +++ b/.hgignore @@ -0,0 +1,7 @@ +#use glob syntax. +syntax: glob + +*.pyc +*~ +build + diff --git a/src/nepi/testbeds/planetlab/__init__.py b/src/nepi/testbeds/planetlab/__init__.py new file mode 100644 index 00000000..3b3f8534 --- /dev/null +++ b/src/nepi/testbeds/planetlab/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from constants import TESTBED_ID +from execute import TestbedInstance + diff --git a/src/nepi/testbeds/planetlab/constants.py b/src/nepi/testbeds/planetlab/constants.py new file mode 100644 index 00000000..3580509b --- /dev/null +++ b/src/nepi/testbeds/planetlab/constants.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +TESTBED_ID = "planetlab" + diff --git a/src/nepi/testbeds/planetlab/execute.py b/src/nepi/testbeds/planetlab/execute.py new file mode 100644 index 00000000..c6fa941a --- /dev/null +++ b/src/nepi/testbeds/planetlab/execute.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from constants import TESTBED_ID +from nepi.core import testbed_impl +import os + +class TestbedInstance(testbed_impl.TestbedInstance): + def __init__(self, testbed_version): + super(TestbedInstance, self).__init__(TESTBED_ID, testbed_version) + self._netns = None + self._home_directory = None + self._traces = dict() + + @property + def home_directory(self): + return self._home_directory + + @property + def netns(self): + return self._netns + + def do_setup(self): + self._home_directory = self._attributes.\ + get_attribute_value("homeDirectory") + self._netns = self._load_netns_module() + + def set(self, time, guid, name, value): + super(TestbedInstance, self).set(time, guid, name, value) + # TODO: take on account schedule time for the task + element = self._elements[guid] + if element: + setattr(element, name, value) + + def get(self, time, guid, name): + # TODO: take on account schedule time for the task + element = self._elements[guid] + return getattr(element, name) + + def action(self, time, guid, action): + raise NotImplementedError + + def trace(self, guid, trace_id): + fd = open("%s" % self.trace_filename(guid, trace_id), "r") + content = fd.read() + fd.close() + return content + + def shutdown(self): + for trace in self._traces.values(): + trace.close() + for element in self._elements.values(): + element.destroy() + + def trace_filename(self, guid, trace_id): + # TODO: Need to be defined inside a home!!!! with and experiment id_code + return os.path.join(self.home_directory, "%d_%s" % (guid, trace_id)) + + def follow_trace(self, trace_id, trace): + self._traces[trace_id] = trace + + def _load_netns_module(self): + # TODO: Do something with the configuration!!! + import sys + __import__("netns") + netns_mod = sys.modules["netns"] + # enable debug + enable_debug = self._attributes.get_attribute_value("enableDebug") + if enable_debug: + netns_mod.environ.set_log_level(netns_mod.environ.LOG_DEBUG) + return netns_mod + diff --git a/src/nepi/testbeds/planetlab/metadata_v01.py b/src/nepi/testbeds/planetlab/metadata_v01.py new file mode 100644 index 00000000..2a0666ce --- /dev/null +++ b/src/nepi/testbeds/planetlab/metadata_v01.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from constants import TESTBED_ID +from nepi.core import metadata +from nepi.core.attributes import Attribute +from nepi.util import validation +from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \ + STATUS_FINISHED + +NODE = "Node" +NODEIFACE = "NodeInterface" +APPLICATION = "Application" + +PL_TESTBED_ID = "planetlab" + +### Connection functions #### + +### Creation functions ### + +def create_node(testbed_instance, guid): + parameters = testbed_instance._get_parameters(guid) + element = testbed_instance.pl.Node() + testbed_instance.elements[guid] = element + +def create_nodeiface(testbed_instance, guid): + parameters = testbed_instance._get_parameters(guid) + element = testbed_instance.pl.Iface() + testbed_instance.elements[guid] = element + +def create_application(testbed_instance, guid): + testbed_instance.elements[guid] = None # Delayed construction + +### Start/Stop functions ### + +def start_application(testbed_instance, guid): + parameters = testbed_instance._get_parameters(guid) + traces = testbed_instance._get_traces(guid) + user = parameters["user"] + command = parameters["command"] + stdout = stderr = None + if "stdout" in traces: + filename = testbed_instance.trace_filename(guid, "stdout") + stdout = open(filename, "wb") + testbed_instance.follow_trace("stdout", stdout) + if "stderr" in traces: + filename = testbed_instance.trace_filename(guid, "stderr") + stderr = open(filename, "wb") + testbed_instance.follow_trace("stderr", stderr) + + node_guid = testbed_instance.get_connected(guid, "node", "apps") + if len(node_guid) == 0: + raise RuntimeError("Can't instantiate interface %d outside netns \ + node" % guid) + node = testbed_instance.elements[node_guid[0]] + element = node.Popen(command, shell = True, stdout = stdout, + stderr = stderr, user = user) + testbed_instance.elements[guid] = element + +### Status functions ### + +def status_application(testbed_instance, guid): + if guid not in testbed_instance.elements.keys(): + return STATUS_NOT_STARTED + app = testbed_instance.elements[guid] + if app.poll() == None: + return STATUS_RUNNING + return STATUS_FINISHED + +### Configure functions ### + +def configure_device(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 + # TODO: Decide if we should add a ipv4 or ipv6 address + element.add_v4_address(address, netprefix) + +def configure_node(testbed_instance, guid): + element = testbed_instance._elements[guid] + if not guid in testbed_instance._add_route: + return + routes = testbed_instance._add_route[guid] + for route in routes: + (destination, netprefix, nexthop) = route + element.add_route(prefix = destination, prefix_len = netprefix, + nexthop = nexthop) + +### Factory information ### + +connector_types = dict({ + "apps": dict({ + "help": "Connector from node to applications", + "name": "apps", + "max": -1, + "min": 0 + }), + "devs": dict({ + "help": "Connector from node to network interfaces", + "name": "devs", + "max": -1, + "min": 0 + }), + "node": dict({ + "help": "Connector to a Node", + "name": "node", + "max": 1, + "min": 1 + }), + "p2p": dict({ + "help": "Connector to a P2PInterface", + "name": "p2p", + "max": 1, + "min": 0 + }), + "fd": dict({ + "help": "Connector to a network interface that can receive a file descriptor", + "name": "fd", + "max": 1, + "min": 0 + }), + "switch": dict({ + "help": "Connector to a switch", + "name": "switch", + "max": 1, + "min": 0 + }) + }) + +connections = [ + dict({ + "from": (TESTBED_ID, NODE, "devs"), + "to": (TESTBED_ID, P2PIFACE, "node"), + "code": None, + "can_cross": False + }), + dict({ + "from": (TESTBED_ID, NODE, "devs"), + "to": (TESTBED_ID, TAPIFACE, "node"), + "code": None, + "can_cross": False + }), + dict({ + "from": (TESTBED_ID, NODE, "devs"), + "to": (TESTBED_ID, NODEIFACE, "node"), + "code": None, + "can_cross": False + }), + dict({ + "from": (TESTBED_ID, P2PIFACE, "p2p"), + "to": (TESTBED_ID, P2PIFACE, "p2p"), + "code": None, + "can_cross": False + }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "fd"), + "to": (NS3_TESTBED_ID, FDNETDEV, "fd"), + "code": connect_fd_local, + "can_cross": True + }), + dict({ + "from": (TESTBED_ID, SWITCH, "devs"), + "to": (TESTBED_ID, NODEIFACE, "switch"), + "code": connect_switch, + "can_cross": False + }), + dict({ + "from": (TESTBED_ID, NODE, "apps"), + "to": (TESTBED_ID, APPLICATION, "node"), + "code": None, + "can_cross": False + }) +] + +attributes = dict({ + "forward_X11": dict({ + "name": "forward_X11", + "help": "Forward x11 from main namespace to the node", + "type": Attribute.BOOL, + "value": False, + "flags": Attribute.DesignOnly, + "validation_function": validation.is_bool + }), + "lladdr": dict({ + "name": "lladdr", + "help": "Mac address", + "type": Attribute.STRING, + "flags": Attribute.DesignOnly, + "validation_function": validation.is_mac_address + }), + "up": dict({ + "name": "up", + "help": "Link up", + "type": Attribute.BOOL, + "value": False, + "validation_function": validation.is_bool + }), + "device_name": dict({ + "name": "name", + "help": "Device name", + "type": Attribute.STRING, + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }), + "mtu": dict({ + "name": "mtu", + "help": "Maximum transmition unit for device", + "type": Attribute.INTEGER, + "validation_function": validation.is_integer + }), + "broadcast": dict({ + "name": "broadcast", + "help": "Broadcast address", + "type": Attribute.STRING, + "validation_function": validation.is_string # TODO: should be is address! + }), + "multicast": dict({ + "name": "multicast", + "help": "Multicast enabled", + "type": Attribute.BOOL, + "value": False, + "validation_function": validation.is_bool + }), + "arp": dict({ + "name": "arp", + "help": "ARP enabled", + "type": Attribute.BOOL, + "value": False, + "validation_function": validation.is_bool + }), + "command": dict({ + "name": "command", + "help": "Command line string", + "type": Attribute.STRING, + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }), + "user": dict({ + "name": "user", + "help": "System user", + "type": Attribute.STRING, + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }), + "stdin": dict({ + "name": "stdin", + "help": "Standard input", + "type": Attribute.STRING, + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }), + }) + +traces = dict({ + "stdout": dict({ + "name": "stdout", + "help": "Standard output stream" + }), + "stderr": dict({ + "name": "stderr", + "help": "Application standard error", + }) + }) + +create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH, + APPLICATION ] + +configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE, SWITCH, NODE, + APPLICATION ] + +factories_info = dict({ + NODE: dict({ + "allow_routes": True, + "help": "Emulated Node with virtualized network stack", + "category": "topology", + "create_function": create_node, + "configure_function": configure_node, + "box_attributes": ["forward_X11"], + "connector_types": ["devs", "apps"] + }), + P2PIFACE: dict({ + "allow_addresses": True, + "help": "Point to point network interface", + "category": "devices", + "create_function": create_p2piface, + "configure_function": configure_device, + "box_attributes": ["lladdr", "up", "device_name", "mtu", + "multicast", "broadcast", "arp"], + "connector_types": ["node", "p2p"] + }), + TAPIFACE: dict({ + "allow_addresses": True, + "help": "Tap device network interface", + "category": "devices", + "create_function": create_tapiface, + "configure_function": configure_device, + "box_attributes": ["lladdr", "up", "device_name", "mtu", + "multicast", "broadcast", "arp"], + "connector_types": ["node", "fd"] + }), + NODEIFACE: dict({ + "allow_addresses": True, + "help": "Node network interface", + "category": "devices", + "create_function": create_nodeiface, + "configure_function": configure_device, + "box_attributes": ["lladdr", "up", "device_name", "mtu", + "multicast", "broadcast", "arp"], + "connector_types": ["node", "switch"] + }), + SWITCH: dict({ + "display_name": "Switch", + "help": "Switch interface", + "category": "devices", + "create_function": create_switch, + "box_attributes": ["up", "device_name", "mtu", "multicast"], + #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function), + #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function), + #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function), + #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function), + #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function) + "connector_types": ["devs"] + }), + APPLICATION: dict({ + "help": "Generic executable command line application", + "category": "applications", + "create_function": create_application, + "start_function": start_application, + "status_function": status_application, + "box_attributes": ["command", "user"], + "connector_types": ["node"], + "traces": ["stdout", "stderr"] + }), +}) + +testbed_attributes = dict({ + "enable_debug": dict({ + "name": "enableDebug", + "help": "Enable netns debug output", + "type": Attribute.BOOL, + "value": False, + "validation_function": validation.is_bool + }), + "home_directory": dict({ + "name": "homeDirectory", + "help": "Path to the directory where traces and other files \ + will be stored", + "type": Attribute.STRING, + "value": "", + "flags": Attribute.DesignOnly, + "validation_function": validation.is_string + }) + }) + +class VersionedMetadataInfo(metadata.VersionedMetadataInfo): + @property + def connector_types(self): + return connector_types + + @property + def connections(self): + return connections + + @property + def attributes(self): + return attributes + + @property + def traces(self): + return traces + + @property + def create_order(self): + return create_order + + @property + def configure_order(self): + return configure_order + + @property + def factories_info(self): + return factories_info + + @property + def testbed_attributes(self): + return testbed_attributes + diff --git a/src/nepi/testbeds/planetlab/plcapi.py b/src/nepi/testbeds/planetlab/plcapi.py new file mode 100644 index 00000000..a31e100e --- /dev/null +++ b/src/nepi/testbeds/planetlab/plcapi.py @@ -0,0 +1,210 @@ +import xmlrpclib + +class PLCAPI(object): + _expected_methods = set( + ['AddNodeTag', 'AddConfFile', 'DeletePersonTag', 'AddNodeType', 'DeleteBootState', 'SliceListNames', 'DeleteKey', + 'SliceGetTicket', 'SliceUsersList', 'SliceUpdate', 'GetNodeGroups', 'SliceCreate', 'GetNetworkMethods', 'GetNodeFlavour', + 'DeleteNode', 'BootNotifyOwners', 'AddPersonKey', 'AddNode', 'UpdateNodeGroup', 'GetAddressTypes', 'AddIlink', 'DeleteNetworkType', + 'GetInitScripts', 'GenerateNodeConfFile', 'AddSite', 'BindObjectToPeer', 'SliceListUserSlices', 'GetPeers', 'AddPeer', 'DeletePeer', + 'AddRole', 'DeleteRole', 'SetPersonPrimarySite', 'AddSiteAddress', 'SliceDelete', 'NotifyPersons', 'GetKeyTypes', 'GetConfFiles', + 'GetIlinks', 'AddTagType', 'GetNodes', 'DeleteNodeTag', 'DeleteSliceFromNodesWhitelist', 'UpdateAddress', 'ResetPassword', + 'AddSliceToNodesWhitelist', 'AddRoleToTagType', 'AddLeases', 'GetAddresses', 'AddInitScript', 'RebootNode', 'GetPCUTypes', + 'RefreshPeer', 'GetBootMedium', 'UpdateKey', 'UpdatePCU', 'GetSession', 'AddInterfaceTag', 'UpdatePCUType', 'GetInterfaces', + 'SliceExtendedInfo', 'SliceNodesList', 'DeleteRoleFromTagType', 'DeleteSlice', 'GetSites', 'DeleteMessage', 'GetSliceFamily', + 'GetPlcRelease', 'UpdateTagType', 'AddSliceInstantiation', 'ResolveSlices', 'GetSlices', 'DeleteRoleFromPerson', 'GetSessions', + 'UpdatePeer', 'VerifyPerson', 'GetPersonTags', 'DeleteKeyType', 'AddSlice', 'SliceUserAdd', 'DeleteSession', 'GetMessages', + 'DeletePCU', 'GetPeerData', 'DeletePersonFromSite', 'DeleteTagType', 'GetPCUs', 'UpdateLeases', 'AddMessage', + 'DeletePCUProtocolType', 'DeleteInterfaceTag', 'AddPersonToSite', 'GetSlivers', 'SliceNodesDel', 'DeleteAddressTypeFromAddress', + 'AddNodeGroup', 'GetSliceTags', 'DeleteSite', 'GetSiteTags', 'UpdateMessage', 'DeleteSliceFromNodes', 'SliceRenew', + 'UpdatePCUProtocolType', 'DeleteSiteTag', 'GetPCUProtocolTypes', 'GetEvents', 'GetSliceTicket', 'AddPersonTag', 'BootGetNodeDetails', + 'DeleteInterface', 'DeleteNodeGroup', 'AddPCUProtocolType', 'BootCheckAuthentication', 'AddSiteTag', 'AddAddressTypeToAddress', + 'DeleteConfFile', 'DeleteInitScript', 'DeletePerson', 'DeleteIlink', 'DeleteAddressType', 'AddBootState', 'AuthCheck', + 'NotifySupport', 'GetSliceInstantiations', 'AddPCUType', 'AddPCU', 'AddSession', 'GetEventObjects', 'UpdateSiteTag', + 'UpdateNodeTag', 'AddPerson', 'BlacklistKey', 'UpdateInitScript', 'AddSliceToNodes', 'RebootNodeWithPCU', 'GetNodeTags', + 'GetSliceKeys', 'GetSliceSshKeys', 'AddNetworkMethod', 'SliceNodesAdd', 'DeletePersonFromSlice', 'ReportRunlevel', + 'GetNetworkTypes', 'UpdateSite', 'DeleteConfFileFromNodeGroup', 'UpdateNode', 'DeleteSliceInstantiation', 'DeleteSliceTag', + 'BootUpdateNode', 'UpdatePerson', 'UpdateConfFile', 'SliceUserDel', 'DeleteLeases', 'AddConfFileToNodeGroup', 'UpdatePersonTag', + 'DeleteConfFileFromNode', 'AddPersonToSlice', 'UnBindObjectFromPeer', 'AddNodeToPCU', 'GetLeaseGranularity', 'DeletePCUType', + 'GetTagTypes', 'GetNodeTypes', 'UpdateInterfaceTag', 'GetRoles', 'UpdateSlice', 'UpdateSliceTag', 'AddSliceTag', 'AddNetworkType', + 'AddInterface', 'AddAddressType', 'AddRoleToPerson', 'DeleteNodeType', 'GetLeases', 'UpdateInterface', 'SliceInfo', 'DeleteAddress', + 'SliceTicketGet', 'GetPersons', 'GetWhitelist', 'AddKeyType', 'UpdateAddressType', 'GetPeerName', 'DeleteNetworkMethod', + 'UpdateIlink', 'AddConfFileToNode', 'GetKeys', 'DeleteNodeFromPCU', 'GetInterfaceTags', 'GetBootStates', 'SetInterfaceSens', 'SetNodeLoadm', + 'GetInterfaceRate', 'GetNodeLoadw', 'SetInterfaceKey', 'GetNodeSlices', 'GetNodeLoadm', 'SetSliceVref', 'GetInterfaceIwpriv', 'SetNodeLoadw', + 'SetNodeSerial', 'GetNodePlainBootstrapfs', 'SetNodeMEMw', 'GetNodeResponse', 'SetInterfaceRate', 'SetSliceInitscript', + 'SetNodeFcdistro', 'GetNodeLoady', 'SetNodeArch', 'SetNodeKargs', 'SetNodeMEMm', 'SetNodeBWy', 'SetNodeBWw', + 'SetInterfaceSecurityMode', 'SetNodeBWm', 'SetNodeASType', 'GetNodeKargs', 'GetPersonColumnconf', 'GetNodeResponsem', + 'GetNodeCPUy', 'GetNodeCramfs', 'SetNodeSlicesw', 'SetPersonColumnconf', 'SetNodeSlicesy', 'GetNodeCPUw', 'GetNodeBWy', + 'GetNodeCPUm', 'GetInterfaceDriver', 'GetNodeLoad', 'GetInterfaceMode', 'GetNodeSerial', 'SetNodeSlicesm', 'SetNodeLoady', + 'GetNodeReliabilityw', 'SetSliceFcdistro', 'GetNodeReliabilityy', 'SetInterfaceEssid', 'SetSliceInitscriptCode', + 'GetNodeExtensions', 'GetSliceOmfControl', 'SetNodeCity', 'SetInterfaceIfname', 'SetNodeHrn', 'SetNodeNoHangcheck', + 'GetNodeNoHangcheck', 'GetSliceFcdistro', 'SetNodeCountry', 'SetNodeKvariant', 'GetNodeKvariant', 'GetNodeMEMy', + 'SetInterfaceIwpriv', 'GetNodeMEMw', 'SetInterfaceBackdoor', 'GetInterfaceFreq', 'SetInterfaceChannel', 'SetInterfaceNw', + 'GetPersonShowconf', 'GetSliceInitscriptCode', 'SetNodeMEM', 'GetInterfaceEssid', 'GetNodeMEMm', 'SetInterfaceMode', + 'SetInterfaceIwconfig', 'GetNodeSlicesm', 'GetNodeBWm', 'SetNodePlainBootstrapfs', 'SetNodeRegion', 'SetNodeCPU', + 'GetNodeSlicesw', 'SetNodeBW', 'SetNodeSlices', 'SetNodeCramfs', 'GetNodeSlicesy', 'GetInterfaceKey', 'GetSliceInitscript', + 'SetNodeCPUm', 'SetSliceArch', 'SetNodeLoad', 'SetNodeResponse', 'GetSliceSliverHMAC', 'GetNodeBWw', 'GetNodeRegion', + 'SetNodeMEMy', 'GetNodeASType', 'SetNodePldistro', 'GetSliceArch', 'GetNodeCountry', 'SetSliceOmfControl', 'GetNodeHrn', + 'GetNodeCity', 'SetInterfaceAlias', 'GetNodeBW', 'GetNodePldistro', 'GetSlicePldistro', 'SetNodeASNumber', 'GetSliceHmac', + 'SetSliceHmac', 'GetNodeMEM', 'GetNodeASNumber', 'GetInterfaceAlias', 'GetSliceVref', 'GetNodeArch', 'GetSliceSshKey', + 'GetInterfaceKey4', 'GetInterfaceKey2', 'GetInterfaceKey3', 'GetInterfaceKey1', 'GetInterfaceBackdoor', 'GetInterfaceIfname', + 'SetSliceSliverHMAC', 'SetNodeReliability', 'GetNodeCPU', 'SetPersonShowconf', 'SetNodeExtensions', 'SetNodeCPUy', + 'SetNodeCPUw', 'GetNodeResponsew', 'SetNodeResponsey', 'GetInterfaceSens', 'SetNodeResponsew', 'GetNodeResponsey', + 'GetNodeReliability', 'GetNodeReliabilitym', 'SetNodeResponsem', 'SetInterfaceDriver', 'GetInterfaceSecurityMode', + 'SetNodeDeployment', 'SetNodeReliabilitym', 'GetNodeFcdistro', 'SetInterfaceFreq', 'GetInterfaceNw', 'SetNodeReliabilityy', + 'SetNodeReliabilityw', 'GetInterfaceIwconfig', 'SetSlicePldistro', 'SetSliceSshKey', 'GetNodeDeployment', 'GetInterfaceChannel', + 'SetInterfaceKey2', 'SetInterfaceKey3', 'SetInterfaceKey1', 'SetInterfaceKey4']) + + _required_methods = set() + + def __init__(self, username=None, password=None, sessionkey=None, + hostname = "www.planet-lab.eu", + urlpattern = "https://%(hostname)s:443/PLCAPI/", + localPeerName = "PLE"): + if sessionkey is not None: + self.auth = dict(AuthMethod='session', session=sessionkey) + elif username is not None and password is not None: + self.auth = dict(AuthMethod='password', Username=username, AuthString=password) + else: + self.auth = dict(AuthMethod='anonymous') + + self._localPeerName = localPeerName + + self.api = xmlrpclib.ServerProxy( + urlpattern % {'hostname':hostname}, + allow_none = True) + + def test(self): + import warnings + + # validate XMLRPC server checking supported API calls + methods = set(self.api.system.listMethods()) + if self._required_methods - methods: + warnings.warn("Unsupported REQUIRED methods: %s" % ( ", ".join(sorted(self._required_methods - methods)), ) ) + return False + if self._expected_methods - methods: + warnings.warn("Unsupported EXPECTED methods: %s" % ( ", ".join(sorted(self._expected_methods - methods)), ) ) + + try: + # test authorization + network_types = self.api.GetNetworkTypes(self.auth) + except (xmlrpclib.ProtocolError, xmlrpclib.Fault),e: + warnings.warn(str(e)) + + return True + + + @property + def network_types(self): + try: + return self._network_types + except AttributeError: + self._network_types = self.api.GetNetworkTypes(self.auth) + return self._network_types + + @property + def peer_map(self): + try: + return self._peer_map + except AttributeError: + peers = self.api.GetPeers(self.auth, {}, ['shortname','peername','peer_id']) + self._peer_map = dict( + (peer['shortname'], peer['peer_id']) + for peer in peers + ) + self._peer_map.update( + (peer['peername'], peer['peer_id']) + for peer in peers + ) + return self._peer_map + + + def GetNodeFlavour(self, node): + """ + Returns detailed information on a given node's flavour, i.e. its base installation. + + This depends on the global PLC settings in the PLC_FLAVOUR area, optionnally overridden by any of the following tags if set on that node: + 'arch', 'pldistro', 'fcdistro', 'deployment', 'extensions' + + Params: + + * node : int or string + - int, Node identifier + - string, Fully qualified hostname + + Returns: + + struct + * extensions : array of string, extensions to add to the base install + * fcdistro : string, the fcdistro this node should be based upon + * nodefamily : string, the nodefamily this node should be based upon + * plain : boolean, use plain bootstrapfs image if set (for tests) + """ + if not isinstance(node, (str, int, long)): + raise ValueError, "Node must be either a non-unicode string or an int" + return self.api.GetNodeFlavour(self.auth, node) + + def GetNodes(self, nodeIdOrName=None, fields=None, **kw): + """ + Returns an array of structs containing details about nodes. + If nodeIdOrName is specified and is an array of node identifiers or hostnames, + or the filters keyword argument with struct of node attributes, + or node attributes by keyword argument, + only nodes matching the filter will be returned. + + If fields is specified, only the specified details will be returned. + NOTE that if fields is unspecified, the complete set of native fields are returned, + which DOES NOT include tags at this time. + + Some fields may only be viewed by admins. + + Special params: + + fields: an optional list of fields to retrieve. The default is all. + + filters: an optional mapping with custom filters, which is the only + way to support complex filters like negation and numeric comparisons. + + peer: a string (or sequence of strings) with the name(s) of peers + to filter - or None for local nodes. + """ + if fields is not None: + fieldstuple = (fields,) + else: + fieldstuple = () + if nodeIdOrName is not None: + return self.api.GetNodes(self.auth, nodeIdOrName, *fieldstuple) + else: + filters = kw.pop('filters',{}) + + if 'peer' in kw: + peer = kw.pop('peer') + + nameToId = self.peer_map.get + + if hasattr(peer, '__iter__'): + # we can't mix local and external nodes, so + # split and re-issue recursively in that case + if None in peer or self._localPeerName in peer: + if None in peer: + peer.remove(None) + if self._localPeerName: + peer.remove(self._localPeerName) + return ( + self.GetNodes(nodeIdOrName, fields, filters=filters, peer=peer, **kw) + + self.GetNodes(nodeIdOrName, fields, filters=filters, peer=None, **kw) + ) + else: + peer_filter = map(nameToId, peer) + elif peer is None or peer == self._localPeerName: + peer_filter = None + else: + peer_filter = nameToId(peer) + + filters['peer_id'] = peer_filter + + filters.update(kw) + return self.api.GetNodes(self.auth, filters, *fieldstuple) + + + +