Several execution fixes:
[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 = dict(
119                 (peer['peer_id'], peer['shortname'])
120                 for peer in peers
121             )
122             return self._peer_map
123     
124
125     def GetNodeFlavour(self, node):
126         """
127         Returns detailed information on a given node's flavour, i.e. its base installation.
128
129         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:
130         'arch', 'pldistro', 'fcdistro', 'deployment', 'extensions'
131         
132         Params:
133         
134             * node : int or string
135                 - int, Node identifier
136                 - string, Fully qualified hostname
137         
138         Returns:
139
140             struct
141                 * extensions : array of string, extensions to add to the base install
142                 * fcdistro : string, the fcdistro this node should be based upon
143                 * nodefamily : string, the nodefamily this node should be based upon
144                 * plain : boolean, use plain bootstrapfs image if set (for tests)  
145         """
146         if not isinstance(node, (str, int, long)):
147             raise ValueError, "Node must be either a non-unicode string or an int"
148         return self.api.GetNodeFlavour(self.auth, node)
149     
150     def GetNodes(self, nodeIdOrName=None, fields=None, **kw):
151         """
152         Returns an array of structs containing details about nodes. 
153         If nodeIdOrName is specified and is an array of node identifiers or hostnames, 
154         or the filters keyword argument with struct of node attributes, 
155         or node attributes by keyword argument,
156         only nodes matching the filter will be returned.
157
158         If fields is specified, only the specified details will be returned. 
159         NOTE that if fields is unspecified, the complete set of native fields are returned, 
160         which DOES NOT include tags at this time.
161
162         Some fields may only be viewed by admins.
163         
164         Special params:
165             
166             fields: an optional list of fields to retrieve. The default is all.
167             
168             filters: an optional mapping with custom filters, which is the only
169                 way to support complex filters like negation and numeric comparisons.
170                 
171             peer: a string (or sequence of strings) with the name(s) of peers
172                 to filter - or None for local nodes.
173         """
174         if fields is not None:
175             fieldstuple = (fields,)
176         else:
177             fieldstuple = ()
178         if nodeIdOrName is not None:
179             return self.api.GetNodes(self.auth, nodeIdOrName, *fieldstuple)
180         else:
181             filters = kw.pop('filters',{})
182             
183             if 'peer' in kw:
184                 peer = kw.pop('peer')
185                 
186                 nameToId = self.peer_map.get
187                 
188                 if hasattr(peer, '__iter__'):
189                     # we can't mix local and external nodes, so
190                     # split and re-issue recursively in that case
191                     if None in peer or self._localPeerName in peer:
192                         if None in peer:    
193                             peer.remove(None)
194                         if self._localPeerName in peer:
195                             peer.remove(self._localPeerName)
196                         return (
197                             self.GetNodes(nodeIdOrName, fields, filters=filters, peer=peer, **kw)
198                             + self.GetNodes(nodeIdOrName, fields, filters=filters, peer=None, **kw)
199                         )
200                     else:
201                         peer_filter = map(nameToId, peer)
202                 elif peer is None or peer == self._localPeerName:
203                     peer_filter = None
204                 else:
205                     peer_filter = nameToId(peer)
206                 
207                 filters['peer_id'] = peer_filter
208             
209             filters.update(kw)
210             return self.api.GetNodes(self.auth, filters, *fieldstuple)
211     
212     def GetNodeTags(self, nodeTagId=None, fields=None, **kw):
213         if fields is not None:
214             fieldstuple = (fields,)
215         else:
216             fieldstuple = ()
217         if nodeTagId is not None:
218             return self.api.GetNodeTags(self.auth, nodeTagId, *fieldstuple)
219         else:
220             filters = kw.pop('filters',{})
221             filters.update(kw)
222             return self.api.GetNodeTags(self.auth, filters, *fieldstuple)
223         
224     
225     def GetInterfaces(self, interfaceIdOrIp=None, fields=None, **kw):
226         if fields is not None:
227             fieldstuple = (fields,)
228         else:
229             fieldstuple = ()
230         if interfaceIdOrIp is not None:
231             return self.api.GetInterfaces(self.auth, interfaceIdOrIp, *fieldstuple)
232         else:
233             filters = kw.pop('filters',{})
234             filters.update(kw)
235             return self.api.GetInterfaces(self.auth, filters, *fieldstuple)
236         
237     def GetSlices(self, sliceIdOrName=None, fields=None, **kw):
238         if fields is not None:
239             fieldstuple = (fields,)
240         else:
241             fieldstuple = ()
242         if sliceIdOrName is not None:
243             return self.api.GetSlices(self.auth, sliceIdOrName, *fieldstuple)
244         else:
245             filters = kw.pop('filters',{})
246             filters.update(kw)
247             return self.api.GetSlices(self.auth, filters, *fieldstuple)
248         
249