12 except (socket.error, IOError, OSError):
20 _expected_methods = set(
21 ['AddNodeTag', 'AddConfFile', 'DeletePersonTag', 'AddNodeType', 'DeleteBootState', 'SliceListNames', 'DeleteKey',
22 'SliceGetTicket', 'SliceUsersList', 'SliceUpdate', 'GetNodeGroups', 'SliceCreate', 'GetNetworkMethods', 'GetNodeFlavour',
23 'DeleteNode', 'BootNotifyOwners', 'AddPersonKey', 'AddNode', 'UpdateNodeGroup', 'GetAddressTypes', 'AddIlink', 'DeleteNetworkType',
24 'GetInitScripts', 'GenerateNodeConfFile', 'AddSite', 'BindObjectToPeer', 'SliceListUserSlices', 'GetPeers', 'AddPeer', 'DeletePeer',
25 'AddRole', 'DeleteRole', 'SetPersonPrimarySite', 'AddSiteAddress', 'SliceDelete', 'NotifyPersons', 'GetKeyTypes', 'GetConfFiles',
26 'GetIlinks', 'AddTagType', 'GetNodes', 'DeleteNodeTag', 'DeleteSliceFromNodesWhitelist', 'UpdateAddress', 'ResetPassword',
27 'AddSliceToNodesWhitelist', 'AddRoleToTagType', 'AddLeases', 'GetAddresses', 'AddInitScript', 'RebootNode', 'GetPCUTypes',
28 'RefreshPeer', 'GetBootMedium', 'UpdateKey', 'UpdatePCU', 'GetSession', 'AddInterfaceTag', 'UpdatePCUType', 'GetInterfaces',
29 'SliceExtendedInfo', 'SliceNodesList', 'DeleteRoleFromTagType', 'DeleteSlice', 'GetSites', 'DeleteMessage', 'GetSliceFamily',
30 'GetPlcRelease', 'UpdateTagType', 'AddSliceInstantiation', 'ResolveSlices', 'GetSlices', 'DeleteRoleFromPerson', 'GetSessions',
31 'UpdatePeer', 'VerifyPerson', 'GetPersonTags', 'DeleteKeyType', 'AddSlice', 'SliceUserAdd', 'DeleteSession', 'GetMessages',
32 'DeletePCU', 'GetPeerData', 'DeletePersonFromSite', 'DeleteTagType', 'GetPCUs', 'UpdateLeases', 'AddMessage',
33 'DeletePCUProtocolType', 'DeleteInterfaceTag', 'AddPersonToSite', 'GetSlivers', 'SliceNodesDel', 'DeleteAddressTypeFromAddress',
34 'AddNodeGroup', 'GetSliceTags', 'DeleteSite', 'GetSiteTags', 'UpdateMessage', 'DeleteSliceFromNodes', 'SliceRenew',
35 'UpdatePCUProtocolType', 'DeleteSiteTag', 'GetPCUProtocolTypes', 'GetEvents', 'GetSliceTicket', 'AddPersonTag', 'BootGetNodeDetails',
36 'DeleteInterface', 'DeleteNodeGroup', 'AddPCUProtocolType', 'BootCheckAuthentication', 'AddSiteTag', 'AddAddressTypeToAddress',
37 'DeleteConfFile', 'DeleteInitScript', 'DeletePerson', 'DeleteIlink', 'DeleteAddressType', 'AddBootState', 'AuthCheck',
38 'NotifySupport', 'GetSliceInstantiations', 'AddPCUType', 'AddPCU', 'AddSession', 'GetEventObjects', 'UpdateSiteTag',
39 'UpdateNodeTag', 'AddPerson', 'BlacklistKey', 'UpdateInitScript', 'AddSliceToNodes', 'RebootNodeWithPCU', 'GetNodeTags',
40 'GetSliceKeys', 'GetSliceSshKeys', 'AddNetworkMethod', 'SliceNodesAdd', 'DeletePersonFromSlice', 'ReportRunlevel',
41 'GetNetworkTypes', 'UpdateSite', 'DeleteConfFileFromNodeGroup', 'UpdateNode', 'DeleteSliceInstantiation', 'DeleteSliceTag',
42 'BootUpdateNode', 'UpdatePerson', 'UpdateConfFile', 'SliceUserDel', 'DeleteLeases', 'AddConfFileToNodeGroup', 'UpdatePersonTag',
43 'DeleteConfFileFromNode', 'AddPersonToSlice', 'UnBindObjectFromPeer', 'AddNodeToPCU', 'GetLeaseGranularity', 'DeletePCUType',
44 'GetTagTypes', 'GetNodeTypes', 'UpdateInterfaceTag', 'GetRoles', 'UpdateSlice', 'UpdateSliceTag', 'AddSliceTag', 'AddNetworkType',
45 'AddInterface', 'AddAddressType', 'AddRoleToPerson', 'DeleteNodeType', 'GetLeases', 'UpdateInterface', 'SliceInfo', 'DeleteAddress',
46 'SliceTicketGet', 'GetPersons', 'GetWhitelist', 'AddKeyType', 'UpdateAddressType', 'GetPeerName', 'DeleteNetworkMethod',
47 'UpdateIlink', 'AddConfFileToNode', 'GetKeys', 'DeleteNodeFromPCU', 'GetInterfaceTags', 'GetBootStates', 'SetInterfaceSens', 'SetNodeLoadm',
48 'GetInterfaceRate', 'GetNodeLoadw', 'SetInterfaceKey', 'GetNodeSlices', 'GetNodeLoadm', 'SetSliceVref', 'GetInterfaceIwpriv', 'SetNodeLoadw',
49 'SetNodeSerial', 'GetNodePlainBootstrapfs', 'SetNodeMEMw', 'GetNodeResponse', 'SetInterfaceRate', 'SetSliceInitscript',
50 'SetNodeFcdistro', 'GetNodeLoady', 'SetNodeArch', 'SetNodeKargs', 'SetNodeMEMm', 'SetNodeBWy', 'SetNodeBWw',
51 'SetInterfaceSecurityMode', 'SetNodeBWm', 'SetNodeASType', 'GetNodeKargs', 'GetPersonColumnconf', 'GetNodeResponsem',
52 'GetNodeCPUy', 'GetNodeCramfs', 'SetNodeSlicesw', 'SetPersonColumnconf', 'SetNodeSlicesy', 'GetNodeCPUw', 'GetNodeBWy',
53 'GetNodeCPUm', 'GetInterfaceDriver', 'GetNodeLoad', 'GetInterfaceMode', 'GetNodeSerial', 'SetNodeSlicesm', 'SetNodeLoady',
54 'GetNodeReliabilityw', 'SetSliceFcdistro', 'GetNodeReliabilityy', 'SetInterfaceEssid', 'SetSliceInitscriptCode',
55 'GetNodeExtensions', 'GetSliceOmfControl', 'SetNodeCity', 'SetInterfaceIfname', 'SetNodeHrn', 'SetNodeNoHangcheck',
56 'GetNodeNoHangcheck', 'GetSliceFcdistro', 'SetNodeCountry', 'SetNodeKvariant', 'GetNodeKvariant', 'GetNodeMEMy',
57 'SetInterfaceIwpriv', 'GetNodeMEMw', 'SetInterfaceBackdoor', 'GetInterfaceFreq', 'SetInterfaceChannel', 'SetInterfaceNw',
58 'GetPersonShowconf', 'GetSliceInitscriptCode', 'SetNodeMEM', 'GetInterfaceEssid', 'GetNodeMEMm', 'SetInterfaceMode',
59 'SetInterfaceIwconfig', 'GetNodeSlicesm', 'GetNodeBWm', 'SetNodePlainBootstrapfs', 'SetNodeRegion', 'SetNodeCPU',
60 'GetNodeSlicesw', 'SetNodeBW', 'SetNodeSlices', 'SetNodeCramfs', 'GetNodeSlicesy', 'GetInterfaceKey', 'GetSliceInitscript',
61 'SetNodeCPUm', 'SetSliceArch', 'SetNodeLoad', 'SetNodeResponse', 'GetSliceSliverHMAC', 'GetNodeBWw', 'GetNodeRegion',
62 'SetNodeMEMy', 'GetNodeASType', 'SetNodePldistro', 'GetSliceArch', 'GetNodeCountry', 'SetSliceOmfControl', 'GetNodeHrn',
63 'GetNodeCity', 'SetInterfaceAlias', 'GetNodeBW', 'GetNodePldistro', 'GetSlicePldistro', 'SetNodeASNumber', 'GetSliceHmac',
64 'SetSliceHmac', 'GetNodeMEM', 'GetNodeASNumber', 'GetInterfaceAlias', 'GetSliceVref', 'GetNodeArch', 'GetSliceSshKey',
65 'GetInterfaceKey4', 'GetInterfaceKey2', 'GetInterfaceKey3', 'GetInterfaceKey1', 'GetInterfaceBackdoor', 'GetInterfaceIfname',
66 'SetSliceSliverHMAC', 'SetNodeReliability', 'GetNodeCPU', 'SetPersonShowconf', 'SetNodeExtensions', 'SetNodeCPUy',
67 'SetNodeCPUw', 'GetNodeResponsew', 'SetNodeResponsey', 'GetInterfaceSens', 'SetNodeResponsew', 'GetNodeResponsey',
68 'GetNodeReliability', 'GetNodeReliabilitym', 'SetNodeResponsem', 'SetInterfaceDriver', 'GetInterfaceSecurityMode',
69 'SetNodeDeployment', 'SetNodeReliabilitym', 'GetNodeFcdistro', 'SetInterfaceFreq', 'GetInterfaceNw', 'SetNodeReliabilityy',
70 'SetNodeReliabilityw', 'GetInterfaceIwconfig', 'SetSlicePldistro', 'SetSliceSshKey', 'GetNodeDeployment', 'GetInterfaceChannel',
71 'SetInterfaceKey2', 'SetInterfaceKey3', 'SetInterfaceKey1', 'SetInterfaceKey4'])
73 _required_methods = set()
75 def __init__(self, username=None, password=None, sessionkey=None, proxy=None,
76 hostname = "www.planet-lab.eu",
77 urlpattern = "https://%(hostname)s:443/PLCAPI/",
78 localPeerName = "PLE"):
79 if sessionkey is not None:
80 self.auth = dict(AuthMethod='session', session=sessionkey)
81 elif username is not None and password is not None:
82 self.auth = dict(AuthMethod='password', Username=username, AuthString=password)
84 self.auth = dict(AuthMethod='anonymous')
86 self._localPeerName = localPeerName
87 self._url = urlpattern % {'hostname':hostname}
88 if (proxy is not None):
90 class HTTPSProxyTransport(xmlrpclib.Transport):
91 def __init__(self, proxy, use_datetime=0):
92 opener = urllib2.build_opener(urllib2.ProxyHandler({"https" : proxy}))
93 xmlrpclib.Transport.__init__(self, use_datetime)
95 def request(self, host, handler, request_body, verbose=0):
96 req = urllib2.Request('https://%s%s' % (host, handler), request_body)
97 req.add_header('User-agent', self.user_agent)
98 self.verbose = verbose
99 return self.parse_response(self.opener.open(req))
100 self._proxyTransport = lambda : HTTPSProxyTransport(proxy)
102 self._proxyTransport = lambda : None
104 self.threadlocal = threading.local()
108 # Cannot reuse same proxy in all threads, py2.7 is not threadsafe
109 return xmlrpclib.ServerProxy(
111 transport = self._proxyTransport(),
117 return self.threadlocal.mc
118 except AttributeError:
124 # validate XMLRPC server checking supported API calls
125 methods = set(_retry(self.mcapi.system.listMethods)())
126 if self._required_methods - methods:
127 warnings.warn("Unsupported REQUIRED methods: %s" % ( ", ".join(sorted(self._required_methods - methods)), ) )
129 if self._expected_methods - methods:
130 warnings.warn("Unsupported EXPECTED methods: %s" % ( ", ".join(sorted(self._expected_methods - methods)), ) )
134 network_types = _retry(self.mcapi.GetNetworkTypes)(self.auth)
135 except (xmlrpclib.ProtocolError, xmlrpclib.Fault),e:
136 warnings.warn(str(e))
142 def network_types(self):
144 return self._network_types
145 except AttributeError:
146 self._network_types = _retry(self.mcapi.GetNetworkTypes)(self.auth)
147 return self._network_types
152 return self._peer_map
153 except AttributeError:
154 peers = _retry(self.mcapi.GetPeers)(self.auth, {}, ['shortname','peername','peer_id'])
155 self._peer_map = dict(
156 (peer['shortname'], peer['peer_id'])
159 self._peer_map.update(
160 (peer['peername'], peer['peer_id'])
163 self._peer_map.update(
164 (peer['peer_id'], peer['shortname'])
167 self._peer_map[None] = self._localPeerName
168 return self._peer_map
171 def GetNodeFlavour(self, node):
173 Returns detailed information on a given node's flavour, i.e. its base installation.
175 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:
176 'arch', 'pldistro', 'fcdistro', 'deployment', 'extensions'
180 * node : int or string
181 - int, Node identifier
182 - string, Fully qualified hostname
187 * extensions : array of string, extensions to add to the base install
188 * fcdistro : string, the fcdistro this node should be based upon
189 * nodefamily : string, the nodefamily this node should be based upon
190 * plain : boolean, use plain bootstrapfs image if set (for tests)
192 if not isinstance(node, (str, int, long)):
193 raise ValueError, "Node must be either a non-unicode string or an int"
194 return _retry(self.mcapi.GetNodeFlavour)(self.auth, node)
196 def GetNodes(self, nodeIdOrName=None, fields=None, **kw):
198 Returns an array of structs containing details about nodes.
199 If nodeIdOrName is specified and is an array of node identifiers or hostnames,
200 or the filters keyword argument with struct of node attributes,
201 or node attributes by keyword argument,
202 only nodes matching the filter will be returned.
204 If fields is specified, only the specified details will be returned.
205 NOTE that if fields is unspecified, the complete set of native fields are returned,
206 which DOES NOT include tags at this time.
208 Some fields may only be viewed by admins.
212 fields: an optional list of fields to retrieve. The default is all.
214 filters: an optional mapping with custom filters, which is the only
215 way to support complex filters like negation and numeric comparisons.
217 peer: a string (or sequence of strings) with the name(s) of peers
218 to filter - or None for local nodes.
220 if fields is not None:
221 fieldstuple = (fields,)
224 if nodeIdOrName is not None:
225 return _retry(self.mcapi.GetNodes)(self.auth, nodeIdOrName, *fieldstuple)
227 filters = kw.pop('filters',{})
230 peer = kw.pop('peer')
232 nameToId = self.peer_map.get
234 if hasattr(peer, '__iter__'):
235 # we can't mix local and external nodes, so
236 # split and re-issue recursively in that case
237 if None in peer or self._localPeerName in peer:
240 if self._localPeerName in peer:
241 peer.remove(self._localPeerName)
243 self.GetNodes(nodeIdOrName, fields, filters=filters, peer=peer, **kw)
244 + self.GetNodes(nodeIdOrName, fields, filters=filters, peer=None, **kw)
247 peer_filter = map(nameToId, peer)
248 elif peer is None or peer == self._localPeerName:
251 peer_filter = nameToId(peer)
253 filters['peer_id'] = peer_filter
256 return _retry(self.mcapi.GetNodes)(self.auth, filters, *fieldstuple)
258 def GetNodeTags(self, nodeTagId=None, fields=None, **kw):
259 if fields is not None:
260 fieldstuple = (fields,)
263 if nodeTagId is not None:
264 return _retry(self.mcapi.GetNodeTags)(self.auth, nodeTagId, *fieldstuple)
266 filters = kw.pop('filters',{})
268 return _retry(self.mcapi.GetNodeTags)(self.auth, filters, *fieldstuple)
270 def GetSliceTags(self, sliceTagId=None, fields=None, **kw):
271 if fields is not None:
272 fieldstuple = (fields,)
275 if sliceTagId is not None:
276 return _retry(self.mcapi.GetSliceTags)(self.auth, sliceTagId, *fieldstuple)
278 filters = kw.pop('filters',{})
280 return _retry(self.mcapi.GetSliceTags)(self.auth, filters, *fieldstuple)
282 def GetInterfaces(self, interfaceIdOrIp=None, fields=None, **kw):
283 if fields is not None:
284 fieldstuple = (fields,)
287 if interfaceIdOrIp is not None:
288 return _retry(self.mcapi.GetInterfaces)(self.auth, interfaceIdOrIp, *fieldstuple)
290 filters = kw.pop('filters',{})
292 return _retry(self.mcapi.GetInterfaces)(self.auth, filters, *fieldstuple)
294 def GetSlices(self, sliceIdOrName=None, fields=None, **kw):
295 if fields is not None:
296 fieldstuple = (fields,)
299 if sliceIdOrName is not None:
300 return _retry(self.mcapi.GetSlices)(self.auth, sliceIdOrName, *fieldstuple)
302 filters = kw.pop('filters',{})
304 return _retry(self.mcapi.GetSlices)(self.auth, filters, *fieldstuple)
306 def UpdateSlice(self, sliceIdOrName, **kw):
307 return _retry(self.mcapi.UpdateSlice)(self.auth, sliceIdOrName, kw)
309 def StartMulticall(self):
310 self.threadlocal.mc = xmlrpclib.MultiCall(self.mcapi)
312 def FinishMulticall(self):
313 mc = self.threadlocal.mc
314 del self.threadlocal.mc
317 def GetSliceNodes(self, slicename):
318 return self.GetSlices(slicename, ['node_ids'])[0]['node_ids']
320 def AddSliceNodes(self, slicename, nodes = None):
321 self.UpdateSlice(slicename, nodes = nodes)
323 def GetNodeInfo(self, node_id):
324 self.StartMulticall()
325 info = self.GetNodes(node_id)
326 tags = self.GetNodeTags(node_id=node_id, fields=('tagname','value'))
327 info, tags = self.FinishMulticall()
330 def GetSliceId(self, slicename):
332 slices = self.GetSlices(slicename, fields=('slice_id',))
334 slice_id = slices[0]['slice_id']
335 # If it wasn't found, don't remember this failure, keep trying
338 def GetSliceVnetSysTag(self, slicename):
339 slicetags = self.GetSliceTags(
341 tagname = 'vsys_vnet',
344 return slicetags[0]['value']
348 def plcapi(auth_user, auth_string, plc_host, plc_url, proxy):
352 username = auth_user,
353 password = auth_string,
355 urlpattern = plc_url,
359 # anonymous access - may not be enough for much