7ba75f81ebf9002777dc4334f199c5ccea720dc7
[nepi.git] / src / nepi / testbeds / planetlab / plcapi.py
1 import xmlrpclib
2
3 class PLCAPI(object):
4     _expected_methods = set(
5         ['AddNodeTag', 'AddConfFile', 'DeletePersonTag', 'AddNodeType', 'DeleteBootState', 'SliceListNames', 'DeleteKey', 
6          'SliceGetTicket', 'SliceUsersList', 'SliceUpdate', 'GetNodeGroups', 'SliceCreate', 'GetNetworkMethods', 'GetNodeFlavour', 
7          'DeleteNode', 'BootNotifyOwners', 'AddPersonKey', 'AddNode', 'UpdateNodeGroup', 'GetAddressTypes', 'AddIlink', 'DeleteNetworkType', 
8          'GetInitScripts', 'GenerateNodeConfFile', 'AddSite', 'BindObjectToPeer', 'SliceListUserSlices', 'GetPeers', 'AddPeer', 'DeletePeer', 
9          'AddRole', 'DeleteRole', 'SetPersonPrimarySite', 'AddSiteAddress', 'SliceDelete', 'NotifyPersons', 'GetKeyTypes', 'GetConfFiles', 
10          'GetIlinks', 'AddTagType', 'GetNodes', 'DeleteNodeTag', 'DeleteSliceFromNodesWhitelist', 'UpdateAddress', 'ResetPassword', 
11          'AddSliceToNodesWhitelist', 'AddRoleToTagType', 'AddLeases', 'GetAddresses', 'AddInitScript', 'RebootNode', 'GetPCUTypes', 
12          'RefreshPeer', 'GetBootMedium', 'UpdateKey', 'UpdatePCU', 'GetSession', 'AddInterfaceTag', 'UpdatePCUType', 'GetInterfaces', 
13          'SliceExtendedInfo', 'SliceNodesList', 'DeleteRoleFromTagType', 'DeleteSlice', 'GetSites', 'DeleteMessage', 'GetSliceFamily', 
14          'GetPlcRelease', 'UpdateTagType', 'AddSliceInstantiation', 'ResolveSlices', 'GetSlices', 'DeleteRoleFromPerson', 'GetSessions', 
15          'UpdatePeer', 'VerifyPerson', 'GetPersonTags', 'DeleteKeyType', 'AddSlice', 'SliceUserAdd', 'DeleteSession', 'GetMessages', 
16          'DeletePCU', 'GetPeerData', 'DeletePersonFromSite', 'DeleteTagType', 'GetPCUs', 'UpdateLeases', 'AddMessage', 
17          'DeletePCUProtocolType', 'DeleteInterfaceTag', 'AddPersonToSite', 'GetSlivers', 'SliceNodesDel', 'DeleteAddressTypeFromAddress', 
18          'AddNodeGroup', 'GetSliceTags', 'DeleteSite', 'GetSiteTags', 'UpdateMessage', 'DeleteSliceFromNodes', 'SliceRenew', 
19          'UpdatePCUProtocolType', 'DeleteSiteTag', 'GetPCUProtocolTypes', 'GetEvents', 'GetSliceTicket', 'AddPersonTag', 'BootGetNodeDetails', 
20          'DeleteInterface', 'DeleteNodeGroup', 'AddPCUProtocolType', 'BootCheckAuthentication', 'AddSiteTag', 'AddAddressTypeToAddress', 
21          'DeleteConfFile', 'DeleteInitScript', 'DeletePerson', 'DeleteIlink', 'DeleteAddressType', 'AddBootState', 'AuthCheck', 
22          'NotifySupport', 'GetSliceInstantiations', 'AddPCUType', 'AddPCU', 'AddSession', 'GetEventObjects', 'UpdateSiteTag', 
23          'UpdateNodeTag', 'AddPerson', 'BlacklistKey', 'UpdateInitScript', 'AddSliceToNodes', 'RebootNodeWithPCU', 'GetNodeTags', 
24          'GetSliceKeys', 'GetSliceSshKeys', 'AddNetworkMethod', 'SliceNodesAdd', 'DeletePersonFromSlice', 'ReportRunlevel', 
25          'GetNetworkTypes', 'UpdateSite', 'DeleteConfFileFromNodeGroup', 'UpdateNode', 'DeleteSliceInstantiation', 'DeleteSliceTag', 
26          'BootUpdateNode', 'UpdatePerson', 'UpdateConfFile', 'SliceUserDel', 'DeleteLeases', 'AddConfFileToNodeGroup', 'UpdatePersonTag', 
27          'DeleteConfFileFromNode', 'AddPersonToSlice', 'UnBindObjectFromPeer', 'AddNodeToPCU', 'GetLeaseGranularity', 'DeletePCUType', 
28          'GetTagTypes', 'GetNodeTypes', 'UpdateInterfaceTag', 'GetRoles', 'UpdateSlice', 'UpdateSliceTag', 'AddSliceTag', 'AddNetworkType', 
29          'AddInterface', 'AddAddressType', 'AddRoleToPerson', 'DeleteNodeType', 'GetLeases', 'UpdateInterface', 'SliceInfo', 'DeleteAddress', 
30          'SliceTicketGet', 'GetPersons', 'GetWhitelist', 'AddKeyType', 'UpdateAddressType', 'GetPeerName', 'DeleteNetworkMethod', 
31          'UpdateIlink', 'AddConfFileToNode', 'GetKeys', 'DeleteNodeFromPCU', 'GetInterfaceTags', 'GetBootStates', 'SetInterfaceSens', 'SetNodeLoadm', 
32          'GetInterfaceRate', 'GetNodeLoadw', 'SetInterfaceKey', 'GetNodeSlices', 'GetNodeLoadm', 'SetSliceVref', 'GetInterfaceIwpriv', 'SetNodeLoadw', 
33          'SetNodeSerial', 'GetNodePlainBootstrapfs', 'SetNodeMEMw', 'GetNodeResponse', 'SetInterfaceRate', 'SetSliceInitscript', 
34          'SetNodeFcdistro', 'GetNodeLoady', 'SetNodeArch', 'SetNodeKargs', 'SetNodeMEMm', 'SetNodeBWy', 'SetNodeBWw', 
35          'SetInterfaceSecurityMode', 'SetNodeBWm', 'SetNodeASType', 'GetNodeKargs', 'GetPersonColumnconf', 'GetNodeResponsem', 
36          'GetNodeCPUy', 'GetNodeCramfs', 'SetNodeSlicesw', 'SetPersonColumnconf', 'SetNodeSlicesy', 'GetNodeCPUw', 'GetNodeBWy', 
37          'GetNodeCPUm', 'GetInterfaceDriver', 'GetNodeLoad', 'GetInterfaceMode', 'GetNodeSerial', 'SetNodeSlicesm', 'SetNodeLoady', 
38          'GetNodeReliabilityw', 'SetSliceFcdistro', 'GetNodeReliabilityy', 'SetInterfaceEssid', 'SetSliceInitscriptCode', 
39          'GetNodeExtensions', 'GetSliceOmfControl', 'SetNodeCity', 'SetInterfaceIfname', 'SetNodeHrn', 'SetNodeNoHangcheck', 
40          'GetNodeNoHangcheck', 'GetSliceFcdistro', 'SetNodeCountry', 'SetNodeKvariant', 'GetNodeKvariant', 'GetNodeMEMy', 
41          'SetInterfaceIwpriv', 'GetNodeMEMw', 'SetInterfaceBackdoor', 'GetInterfaceFreq', 'SetInterfaceChannel', 'SetInterfaceNw', 
42          'GetPersonShowconf', 'GetSliceInitscriptCode', 'SetNodeMEM', 'GetInterfaceEssid', 'GetNodeMEMm', 'SetInterfaceMode', 
43          'SetInterfaceIwconfig', 'GetNodeSlicesm', 'GetNodeBWm', 'SetNodePlainBootstrapfs', 'SetNodeRegion', 'SetNodeCPU', 
44          'GetNodeSlicesw', 'SetNodeBW', 'SetNodeSlices', 'SetNodeCramfs', 'GetNodeSlicesy', 'GetInterfaceKey', 'GetSliceInitscript', 
45          'SetNodeCPUm', 'SetSliceArch', 'SetNodeLoad', 'SetNodeResponse', 'GetSliceSliverHMAC', 'GetNodeBWw', 'GetNodeRegion', 
46          'SetNodeMEMy', 'GetNodeASType', 'SetNodePldistro', 'GetSliceArch', 'GetNodeCountry', 'SetSliceOmfControl', 'GetNodeHrn', 
47          'GetNodeCity', 'SetInterfaceAlias', 'GetNodeBW', 'GetNodePldistro', 'GetSlicePldistro', 'SetNodeASNumber', 'GetSliceHmac', 
48          'SetSliceHmac', 'GetNodeMEM', 'GetNodeASNumber', 'GetInterfaceAlias', 'GetSliceVref', 'GetNodeArch', 'GetSliceSshKey', 
49          'GetInterfaceKey4', 'GetInterfaceKey2', 'GetInterfaceKey3', 'GetInterfaceKey1', 'GetInterfaceBackdoor', 'GetInterfaceIfname', 
50          'SetSliceSliverHMAC', 'SetNodeReliability', 'GetNodeCPU', 'SetPersonShowconf', 'SetNodeExtensions', 'SetNodeCPUy', 
51          'SetNodeCPUw', 'GetNodeResponsew', 'SetNodeResponsey', 'GetInterfaceSens', 'SetNodeResponsew', 'GetNodeResponsey', 
52          'GetNodeReliability', 'GetNodeReliabilitym', 'SetNodeResponsem', 'SetInterfaceDriver', 'GetInterfaceSecurityMode', 
53          'SetNodeDeployment', 'SetNodeReliabilitym', 'GetNodeFcdistro', 'SetInterfaceFreq', 'GetInterfaceNw', 'SetNodeReliabilityy', 
54          'SetNodeReliabilityw', 'GetInterfaceIwconfig', 'SetSlicePldistro', 'SetSliceSshKey', 'GetNodeDeployment', 'GetInterfaceChannel', 
55          'SetInterfaceKey2', 'SetInterfaceKey3', 'SetInterfaceKey1', 'SetInterfaceKey4'])
56      
57     _required_methods = set()
58
59     def __init__(self, username=None, password=None, sessionkey=None,
60             hostname = "www.planet-lab.eu",
61             urlpattern = "https://%(hostname)s:443/PLCAPI/",
62             localPeerName = "PLE"):
63         if sessionkey is not None:
64             self.auth = dict(AuthMethod='session', session=sessionkey)
65         elif username is not None and password is not None:
66             self.auth = dict(AuthMethod='password', Username=username, AuthString=password)
67         else:
68             self.auth = dict(AuthMethod='anonymous')
69         
70         self._localPeerName = localPeerName
71         
72         self.api = xmlrpclib.ServerProxy(
73             urlpattern % {'hostname':hostname},
74             allow_none = True)
75         
76     def test(self):
77         import warnings
78         
79         # validate XMLRPC server checking supported API calls
80         methods = set(self.api.system.listMethods())
81         if self._required_methods - methods:
82             warnings.warn("Unsupported REQUIRED methods: %s" % ( ", ".join(sorted(self._required_methods - methods)), ) )
83             return False
84         if self._expected_methods - methods:
85             warnings.warn("Unsupported EXPECTED methods: %s" % ( ", ".join(sorted(self._expected_methods - methods)), ) )
86         
87         try:
88             # test authorization
89             network_types = self.api.GetNetworkTypes(self.auth)
90         except (xmlrpclib.ProtocolError, xmlrpclib.Fault),e:
91             warnings.warn(str(e))
92         
93         return True
94     
95     
96     @property
97     def network_types(self):
98         try:
99             return self._network_types
100         except AttributeError:
101             self._network_types = self.api.GetNetworkTypes(self.auth)
102             return self._network_types
103     
104     @property
105     def peer_map(self):
106         try:
107             return self._peer_map
108         except AttributeError:
109             peers = self.api.GetPeers(self.auth, {}, ['shortname','peername','peer_id'])
110             self._peer_map = dict(
111                 (peer['shortname'], peer['peer_id'])
112                 for peer in peers
113             )
114             self._peer_map.update(
115                 (peer['peername'], peer['peer_id'])
116                 for peer in peers
117             )
118             self._peer_map.update(
119                 (peer['peer_id'], peer['shortname'])
120                 for peer in peers
121             )
122             self._peer_map[None] = self._localPeerName
123             return self._peer_map
124     
125
126     def GetNodeFlavour(self, node):
127         """
128         Returns detailed information on a given node's flavour, i.e. its base installation.
129
130         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:
131         'arch', 'pldistro', 'fcdistro', 'deployment', 'extensions'
132         
133         Params:
134         
135             * node : int or string
136                 - int, Node identifier
137                 - string, Fully qualified hostname
138         
139         Returns:
140
141             struct
142                 * extensions : array of string, extensions to add to the base install
143                 * fcdistro : string, the fcdistro this node should be based upon
144                 * nodefamily : string, the nodefamily this node should be based upon
145                 * plain : boolean, use plain bootstrapfs image if set (for tests)  
146         """
147         if not isinstance(node, (str, int, long)):
148             raise ValueError, "Node must be either a non-unicode string or an int"
149         return self.api.GetNodeFlavour(self.auth, node)
150     
151     def GetNodes(self, nodeIdOrName=None, fields=None, **kw):
152         """
153         Returns an array of structs containing details about nodes. 
154         If nodeIdOrName is specified and is an array of node identifiers or hostnames, 
155         or the filters keyword argument with struct of node attributes, 
156         or node attributes by keyword argument,
157         only nodes matching the filter will be returned.
158
159         If fields is specified, only the specified details will be returned. 
160         NOTE that if fields is unspecified, the complete set of native fields are returned, 
161         which DOES NOT include tags at this time.
162
163         Some fields may only be viewed by admins.
164         
165         Special params:
166             
167             fields: an optional list of fields to retrieve. The default is all.
168             
169             filters: an optional mapping with custom filters, which is the only
170                 way to support complex filters like negation and numeric comparisons.
171                 
172             peer: a string (or sequence of strings) with the name(s) of peers
173                 to filter - or None for local nodes.
174         """
175         if fields is not None:
176             fieldstuple = (fields,)
177         else:
178             fieldstuple = ()
179         if nodeIdOrName is not None:
180             return self.api.GetNodes(self.auth, nodeIdOrName, *fieldstuple)
181         else:
182             filters = kw.pop('filters',{})
183             
184             if 'peer' in kw:
185                 peer = kw.pop('peer')
186                 
187                 nameToId = self.peer_map.get
188                 
189                 if hasattr(peer, '__iter__'):
190                     # we can't mix local and external nodes, so
191                     # split and re-issue recursively in that case
192                     if None in peer or self._localPeerName in peer:
193                         if None in peer:    
194                             peer.remove(None)
195                         if self._localPeerName in peer:
196                             peer.remove(self._localPeerName)
197                         return (
198                             self.GetNodes(nodeIdOrName, fields, filters=filters, peer=peer, **kw)
199                             + self.GetNodes(nodeIdOrName, fields, filters=filters, peer=None, **kw)
200                         )
201                     else:
202                         peer_filter = map(nameToId, peer)
203                 elif peer is None or peer == self._localPeerName:
204                     peer_filter = None
205                 else:
206                     peer_filter = nameToId(peer)
207                 
208                 filters['peer_id'] = peer_filter
209             
210             filters.update(kw)
211             return self.api.GetNodes(self.auth, filters, *fieldstuple)
212     
213     def GetNodeTags(self, nodeTagId=None, fields=None, **kw):
214         if fields is not None:
215             fieldstuple = (fields,)
216         else:
217             fieldstuple = ()
218         if nodeTagId is not None:
219             return self.api.GetNodeTags(self.auth, nodeTagId, *fieldstuple)
220         else:
221             filters = kw.pop('filters',{})
222             filters.update(kw)
223             return self.api.GetNodeTags(self.auth, filters, *fieldstuple)
224
225     def GetSliceTags(self, sliceTagId=None, fields=None, **kw):
226         if fields is not None:
227             fieldstuple = (fields,)
228         else:
229             fieldstuple = ()
230         if sliceTagId is not None:
231             return self.api.GetSliceTags(self.auth, sliceTagId, *fieldstuple)
232         else:
233             filters = kw.pop('filters',{})
234             filters.update(kw)
235             return self.api.GetSliceTags(self.auth, filters, *fieldstuple)
236         
237     
238     def GetInterfaces(self, interfaceIdOrIp=None, fields=None, **kw):
239         if fields is not None:
240             fieldstuple = (fields,)
241         else:
242             fieldstuple = ()
243         if interfaceIdOrIp is not None:
244             return self.api.GetInterfaces(self.auth, interfaceIdOrIp, *fieldstuple)
245         else:
246             filters = kw.pop('filters',{})
247             filters.update(kw)
248             return self.api.GetInterfaces(self.auth, filters, *fieldstuple)
249         
250     def GetSlices(self, sliceIdOrName=None, fields=None, **kw):
251         if fields is not None:
252             fieldstuple = (fields,)
253         else:
254             fieldstuple = ()
255         if sliceIdOrName is not None:
256             return self.api.GetSlices(self.auth, sliceIdOrName, *fieldstuple)
257         else:
258             filters = kw.pop('filters',{})
259             filters.update(kw)
260             return self.api.GetSlices(self.auth, filters, *fieldstuple)
261         
262     def UpdateSlice(self, sliceIdOrName, **kw):
263         return self.api.UpdateSlice(self.auth, sliceIdOrName, kw)
264         
265