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()
|