156 lines
5.3 KiB
Python
156 lines
5.3 KiB
Python
import json
|
|
import logging
|
|
import subprocess
|
|
|
|
import kong.common.http
|
|
from kong import exceptions
|
|
|
|
|
|
class API(kong.common.http.Client):
|
|
"""Barebones Nova HTTP API client."""
|
|
|
|
def __init__(self, host, port, base_url, user, api_key, project_id=''):
|
|
"""Initialize Nova HTTP API client.
|
|
|
|
:param host: Hostname/IP of the Nova API to test.
|
|
:param port: Port of the Nova API to test.
|
|
:param base_url: Version identifier (normally /v1.0 or /v1.1)
|
|
:param user: The username to use for tests.
|
|
:param api_key: The API key of the user.
|
|
:returns: None
|
|
|
|
"""
|
|
super(API, self).__init__(host, port, base_url)
|
|
self.user = user
|
|
self.api_key = api_key
|
|
self.project_id = project_id
|
|
# Default to same as base_url, but will be change on auth
|
|
self.management_url = self.base_url
|
|
|
|
def authenticate(self, user, api_key, project_id):
|
|
"""Request and return an authentication token from Nova.
|
|
|
|
:param user: The username we're authenticating.
|
|
:param api_key: The API key for the user we're authenticating.
|
|
:returns: Authentication token (string)
|
|
:raises: KeyError if authentication fails.
|
|
|
|
"""
|
|
headers = {
|
|
'X-Auth-User': user,
|
|
'X-Auth-Key': api_key,
|
|
'X-Auth-Project-Id': project_id,
|
|
}
|
|
resp, body = super(API, self).request('GET', '', headers=headers,
|
|
base_url=self.base_url)
|
|
|
|
try:
|
|
self.management_url = resp['x-server-management-url']
|
|
return resp['x-auth-token']
|
|
except KeyError:
|
|
print "Failed to authenticate user"
|
|
raise
|
|
|
|
def _wait_for_entity_status(self, url, entity_name, status, **kwargs):
|
|
"""Poll the provided url until expected entity status is returned"""
|
|
|
|
def check_response(resp, body):
|
|
try:
|
|
data = json.loads(body)
|
|
return data[entity_name]['status'] == status
|
|
except (ValueError, KeyError):
|
|
return False
|
|
|
|
try:
|
|
self.poll_request('GET', url, check_response, **kwargs)
|
|
except exceptions.TimeoutException:
|
|
msg = "%s failed to reach status %s" % (entity_name, status)
|
|
raise AssertionError(msg)
|
|
|
|
def wait_for_server_status(self, server_id, status='ACTIVE', **kwargs):
|
|
"""Wait for the server status to be equal to the status passed in.
|
|
|
|
:param server_id: Server ID to query.
|
|
:param status: The status string to look for.
|
|
:returns: None
|
|
:raises: AssertionError if request times out
|
|
|
|
"""
|
|
url = '/servers/%s' % server_id
|
|
return self._wait_for_entity_status(url, 'server', status, **kwargs)
|
|
|
|
def wait_for_image_status(self, image_id, status='ACTIVE', **kwargs):
|
|
"""Wait for the image status to be equal to the status passed in.
|
|
|
|
:param image_id: Image ID to query.
|
|
:param status: The status string to look for.
|
|
:returns: None
|
|
:raises: AssertionError if request times out
|
|
|
|
"""
|
|
url = '/images/%s' % image_id
|
|
return self._wait_for_entity_status(url, 'image', status, **kwargs)
|
|
|
|
def request(self, method, url, **kwargs):
|
|
"""Generic HTTP request on the Nova API.
|
|
|
|
:param method: Request verb to use (GET, PUT, POST, etc.)
|
|
:param url: The API resource to request.
|
|
:param kwargs: Additional keyword arguments to pass to the request.
|
|
:returns: HTTP response object.
|
|
|
|
"""
|
|
headers = kwargs.get('headers', {})
|
|
project_id = kwargs.get('project_id', self.project_id)
|
|
|
|
headers['X-Auth-Token'] = self.authenticate(self.user, self.api_key,
|
|
self.project_id)
|
|
kwargs['headers'] = headers
|
|
return super(API, self).request(method, url, **kwargs)
|
|
|
|
def get_server(self, server_id):
|
|
"""Fetch a server by id
|
|
|
|
:param server_id: dict of server attributes
|
|
:returns: dict of server attributes
|
|
:raises: ServerNotFound if server does not exist
|
|
|
|
"""
|
|
resp, body = self.request('GET', '/servers/%s' % server_id)
|
|
try:
|
|
assert resp['status'] == '200'
|
|
data = json.loads(body)
|
|
return data['server']
|
|
except (AssertionError, ValueError, TypeError, KeyError):
|
|
raise exceptions.ServerNotFound(server_id)
|
|
|
|
def create_server(self, entity):
|
|
"""Attempt to create a new server.
|
|
|
|
:param entity: dict of server attributes
|
|
:returns: dict of server attributes after creation
|
|
:raises: AssertionError if server creation fails
|
|
|
|
"""
|
|
post_body = json.dumps({
|
|
'server': entity,
|
|
})
|
|
|
|
resp, body = self.request('POST', '/servers', body=post_body)
|
|
try:
|
|
assert resp['status'] == '202'
|
|
data = json.loads(body)
|
|
return data['server']
|
|
except (AssertionError, ValueError, TypeError, KeyError):
|
|
raise AssertionError("Failed to create server")
|
|
|
|
def delete_server(self, server_id):
|
|
"""Attempt to delete a server.
|
|
|
|
:param server_id: server identifier
|
|
:returns: None
|
|
|
|
"""
|
|
url = '/servers/%s' % server_id
|
|
response, body = self.request('DELETE', url)
|