Revision f2f88abf lib/rapi/client.py
b/lib/rapi/client.py | ||
---|---|---|
98 | 98 |
USER_AGENT = "Ganeti RAPI Client" |
99 | 99 |
|
100 | 100 |
def __init__(self, master_hostname, port=5080, username=None, password=None, |
101 |
ssl_cert=None): |
|
101 |
ssl_cert_file=None):
|
|
102 | 102 |
"""Constructor. |
103 | 103 |
|
104 | 104 |
@type master_hostname: str |
... | ... | |
109 | 109 |
@param username: the username to connect with |
110 | 110 |
@type password: str |
111 | 111 |
@param password: the password to connect with |
112 |
@type ssl_cert: str or None |
|
113 |
@param ssl_cert: the expected SSL certificate. if None, SSL certificate
|
|
114 |
will not be verified |
|
112 |
@type ssl_cert_file: str or None
|
|
113 |
@param ssl_cert_file: path to the expected SSL certificate. if None, SSL
|
|
114 |
certificate will not be verified
|
|
115 | 115 |
|
116 | 116 |
""" |
117 | 117 |
self._master_hostname = master_hostname |
118 | 118 |
self._port = port |
119 |
if ssl_cert: |
|
120 |
_VerifyCertificate(self._master_hostname, self._port, ssl_cert) |
|
121 | 119 |
|
120 |
self._version = None |
|
122 | 121 |
self._http = httplib2.Http() |
122 |
|
|
123 |
# Older versions of httplib2 don't support the connection_type argument |
|
124 |
# to request(), so we have to manually specify the connection object in the |
|
125 |
# internal dict. |
|
126 |
base_url = self._MakeUrl("/", prepend_version=False) |
|
127 |
scheme, authority, _, _, _ = httplib2.parse_uri(base_url) |
|
128 |
conn_key = "%s:%s" % (scheme, authority) |
|
129 |
self._http.connections[conn_key] = \ |
|
130 |
HTTPSConnectionOpenSSL(master_hostname, port, cert_file=ssl_cert_file) |
|
131 |
|
|
123 | 132 |
self._headers = { |
124 | 133 |
"Accept": "text/plain", |
125 | 134 |
"Content-type": "application/x-www-form-urlencoded", |
126 | 135 |
"User-Agent": self.USER_AGENT} |
127 |
self._version = None |
|
128 |
if username and password:
|
|
136 |
|
|
137 |
if username is not None and password is not None:
|
|
129 | 138 |
self._http.add_credentials(username, password) |
130 | 139 |
|
131 | 140 |
def _MakeUrl(self, path, query=None, prepend_version=True): |
... | ... | |
144 | 153 |
|
145 | 154 |
""" |
146 | 155 |
if prepend_version: |
147 |
if not self._version: |
|
148 |
self._GetVersionInternal() |
|
149 |
path = "/%d%s" % (self._version, path) |
|
156 |
path = "/%d%s" % (self.GetVersion(), path) |
|
150 | 157 |
|
151 | 158 |
return "https://%(host)s:%(port)d%(path)s?%(query)s" % { |
152 | 159 |
"host": self._master_hostname, |
... | ... | |
176 | 183 |
@rtype: str |
177 | 184 |
@return: JSON-Decoded response |
178 | 185 |
|
186 |
@raises CertificateError: If an invalid SSL certificate is found |
|
179 | 187 |
@raises GanetiApiError: If an invalid response is returned |
180 | 188 |
|
181 | 189 |
""" |
182 | 190 |
if content: |
183 |
simplejson.JSONEncoder(sort_keys=True).encode(content) |
|
191 |
content = simplejson.JSONEncoder(sort_keys=True).encode(content)
|
|
184 | 192 |
|
185 | 193 |
url = self._MakeUrl(path, query, prepend_version) |
186 |
resp_headers, resp_content = self._http.request( |
|
187 |
url, method, body=content, headers=self._headers) |
|
194 |
try: |
|
195 |
resp_headers, resp_content = self._http.request(url, method, |
|
196 |
body=content, headers=self._headers) |
|
197 |
except (crypto.Error, SSL.Error): |
|
198 |
raise CertificateError("Invalid SSL certificate.") |
|
188 | 199 |
|
189 | 200 |
if resp_content: |
190 | 201 |
resp_content = simplejson.loads(resp_content) |
... | ... | |
201 | 212 |
|
202 | 213 |
return resp_content |
203 | 214 |
|
204 |
def _GetVersionInternal(self): |
|
205 |
"""Gets the Remote API version running on the cluster. |
|
206 |
|
|
207 |
@rtype: int |
|
208 |
@return: Ganeti version |
|
209 |
|
|
210 |
""" |
|
211 |
self._version = self._SendRequest(HTTP_GET, "/version", |
|
212 |
prepend_version=False) |
|
213 |
return self._version |
|
214 |
|
|
215 | 215 |
def GetVersion(self): |
216 | 216 |
"""Gets the Remote API version running on the cluster. |
217 | 217 |
|
218 | 218 |
@rtype: int |
219 |
@return: Ganeti version |
|
219 |
@return: Ganeti Remote API version
|
|
220 | 220 |
|
221 | 221 |
""" |
222 |
if not self._version: |
|
223 |
self._GetVersionInternal() |
|
222 |
if self._version is None: |
|
223 |
self._version = self._SendRequest(HTTP_GET, "/version", |
|
224 |
prepend_version=False) |
|
224 | 225 |
return self._version |
225 | 226 |
|
226 | 227 |
def GetOperatingSystems(self): |
... | ... | |
841 | 842 |
ssl = SSL.Connection(ctx, sock) |
842 | 843 |
ssl.connect((self.host, self.port)) |
843 | 844 |
self.sock = httplib.FakeSocket(sock, ssl) |
844 |
|
|
845 |
|
|
846 |
def _VerifyCertificate(hostname, port, cert_file): |
|
847 |
"""Verifies the SSL certificate for the given host/port. |
|
848 |
|
|
849 |
@type hostname: str |
|
850 |
@param hostname: the ganeti cluster master whose certificate to verify |
|
851 |
@type port: int |
|
852 |
@param port: the port on which the RAPI is running |
|
853 |
@type cert_file: str |
|
854 |
@param cert_file: filename of the expected SSL certificate |
|
855 |
|
|
856 |
@raises CertificateError: If an invalid SSL certificate is found |
|
857 |
|
|
858 |
""" |
|
859 |
https = HTTPSConnectionOpenSSL(hostname, port, cert_file=cert_file) |
|
860 |
try: |
|
861 |
try: |
|
862 |
https.request(HTTP_GET, "/version") |
|
863 |
except (crypto.Error, SSL.Error): |
|
864 |
raise CertificateError("Invalid SSL certificate.") |
|
865 |
finally: |
|
866 |
https.close() |
Also available in: Unified diff