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