--- /dev/null
+#use glob syntax.
+syntax: glob
+
+*.pyc
+*~
+build
+
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from constants import TESTBED_ID
+from execute import TestbedInstance
+
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+TESTBED_ID = "planetlab"
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+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)
+
+
+
+