blob: 41b4b4e522e84500d8a78daa419ef0d755063fc4 [file] [log] [blame]
Soren Hansenbc1d3a02011-09-08 13:33:17 +02001import json
2import logging
3import subprocess
4
5import kong.common.http
6from kong import exceptions
7
8
9class API(kong.common.http.Client):
10 """Barebones Nova HTTP API client."""
11
12 def __init__(self, host, port, base_url, user, api_key, project_id=''):
13 """Initialize Nova HTTP API client.
14
15 :param host: Hostname/IP of the Nova API to test.
16 :param port: Port of the Nova API to test.
17 :param base_url: Version identifier (normally /v1.0 or /v1.1)
18 :param user: The username to use for tests.
19 :param api_key: The API key of the user.
20 :returns: None
21
22 """
23 super(API, self).__init__(host, port, base_url)
24 self.user = user
25 self.api_key = api_key
26 self.project_id = project_id
27 # Default to same as base_url, but will be change on auth
28 self.management_url = self.base_url
29
30 def authenticate(self, user, api_key, project_id):
31 """Request and return an authentication token from Nova.
32
33 :param user: The username we're authenticating.
34 :param api_key: The API key for the user we're authenticating.
35 :returns: Authentication token (string)
36 :raises: KeyError if authentication fails.
37
38 """
39 headers = {
40 'X-Auth-User': user,
41 'X-Auth-Key': api_key,
42 'X-Auth-Project-Id': project_id,
43 }
44 resp, body = super(API, self).request('GET', '', headers=headers,
45 base_url=self.base_url)
46
47 try:
48 self.management_url = resp['x-server-management-url']
49 return resp['x-auth-token']
50 except KeyError:
51 print "Failed to authenticate user"
52 raise
53
54 def _wait_for_entity_status(self, url, entity_name, status, **kwargs):
55 """Poll the provided url until expected entity status is returned"""
56
57 def check_response(resp, body):
58 try:
59 data = json.loads(body)
60 return data[entity_name]['status'] == status
61 except (ValueError, KeyError):
62 return False
63
64 try:
65 self.poll_request('GET', url, check_response, **kwargs)
66 except exceptions.TimeoutException:
67 msg = "%s failed to reach status %s" % (entity_name, status)
68 raise AssertionError(msg)
69
70 def wait_for_server_status(self, server_id, status='ACTIVE', **kwargs):
71 """Wait for the server status to be equal to the status passed in.
72
73 :param server_id: Server ID to query.
74 :param status: The status string to look for.
75 :returns: None
76 :raises: AssertionError if request times out
77
78 """
79 url = '/servers/%s' % server_id
80 return self._wait_for_entity_status(url, 'server', status, **kwargs)
81
82 def wait_for_image_status(self, image_id, status='ACTIVE', **kwargs):
83 """Wait for the image status to be equal to the status passed in.
84
85 :param image_id: Image ID to query.
86 :param status: The status string to look for.
87 :returns: None
88 :raises: AssertionError if request times out
89
90 """
91 url = '/images/%s' % image_id
92 return self._wait_for_entity_status(url, 'image', status, **kwargs)
93
94 def request(self, method, url, **kwargs):
95 """Generic HTTP request on the Nova API.
96
97 :param method: Request verb to use (GET, PUT, POST, etc.)
98 :param url: The API resource to request.
99 :param kwargs: Additional keyword arguments to pass to the request.
100 :returns: HTTP response object.
101
102 """
103 headers = kwargs.get('headers', {})
104 project_id = kwargs.get('project_id', self.project_id)
105
106 headers['X-Auth-Token'] = self.authenticate(self.user, self.api_key,
107 self.project_id)
108 kwargs['headers'] = headers
109 return super(API, self).request(method, url, **kwargs)
110
111 def get_server(self, server_id):
112 """Fetch a server by id
113
114 :param server_id: dict of server attributes
115 :returns: dict of server attributes
116 :raises: ServerNotFound if server does not exist
117
118 """
119 resp, body = self.request('GET', '/servers/%s' % server_id)
120 try:
121 assert resp['status'] == '200'
122 data = json.loads(body)
123 return data['server']
124 except (AssertionError, ValueError, TypeError, KeyError):
125 raise exceptions.ServerNotFound(server_id)
126
127 def create_server(self, entity):
128 """Attempt to create a new server.
129
130 :param entity: dict of server attributes
131 :returns: dict of server attributes after creation
132 :raises: AssertionError if server creation fails
133
134 """
135 post_body = json.dumps({
136 'server': entity,
137 })
138
139 resp, body = self.request('POST', '/servers', body=post_body)
140 try:
141 assert resp['status'] == '202'
142 data = json.loads(body)
143 return data['server']
144 except (AssertionError, ValueError, TypeError, KeyError):
145 raise AssertionError("Failed to create server")
146
147 def delete_server(self, server_id):
148 """Attempt to delete a server.
149
150 :param server_id: server identifier
151 :returns: None
152
153 """
154 url = '/servers/%s' % server_id
155 response, body = self.request('DELETE', url)