blob: f79c3d5df1600881328c0ce7172d4f666fed16b8 [file] [log] [blame]
Vincent Hou6b8a7b72012-08-25 01:24:33 +08001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2#
3# Copyright 2012 IBM
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
Matthew Treinisha83a16e2012-12-07 13:44:02 -050018import httplib2
19import json
Vincent Hou6b8a7b72012-08-25 01:24:33 +080020import logging
Matthew Treinisha83a16e2012-12-07 13:44:02 -050021
Vincent Hou6b8a7b72012-08-25 01:24:33 +080022from lxml import etree
Matthew Treinisha83a16e2012-12-07 13:44:02 -050023
Vincent Hou6b8a7b72012-08-25 01:24:33 +080024from tempest.common.rest_client import RestClient
25from tempest.common.rest_client import RestClientXML
Matthew Treinisha83a16e2012-12-07 13:44:02 -050026from tempest import exceptions
dwallecke62b9f02012-10-10 23:34:42 -050027from tempest.services.compute.xml.common import Document
28from tempest.services.compute.xml.common import Element
29from tempest.services.compute.xml.common import Text
30from tempest.services.compute.xml.common import xml_to_json
Matthew Treinisha83a16e2012-12-07 13:44:02 -050031
Vincent Hou6b8a7b72012-08-25 01:24:33 +080032
33XMLNS = "http://docs.openstack.org/identity/api/v2.0"
34
35
Attila Fazekas407b6db2013-01-19 12:48:36 +010036class IdentityClientXML(RestClientXML):
Vincent Hou6b8a7b72012-08-25 01:24:33 +080037
38 def __init__(self, config, username, password, auth_url, tenant_name=None):
Attila Fazekas407b6db2013-01-19 12:48:36 +010039 super(IdentityClientXML, self).__init__(config, username, password,
40 auth_url, tenant_name)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080041 self.service = self.config.identity.catalog_type
42 self.endpoint_url = 'adminURL'
43
44 def _parse_array(self, node):
45 array = []
46 for child in node.getchildren():
47 array.append(xml_to_json(child))
48 return array
49
50 def _parse_body(self, body):
51 json = xml_to_json(body)
52 return json
53
54 def has_admin_extensions(self):
55 """
56 Returns True if the KSADM Admin Extensions are supported
57 False otherwise
58 """
59 if hasattr(self, '_has_admin_extensions'):
60 return self._has_admin_extensions
61 resp, body = self.list_roles()
62 self._has_admin_extensions = ('status' in resp and resp.status != 503)
63 return self._has_admin_extensions
64
65 def create_role(self, name):
Sean Daguef237ccb2013-01-04 15:19:14 -050066 """Create a role."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +080067 create_role = Element("role", xmlns=XMLNS, name=name)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080068 resp, body = self.post('OS-KSADM/roles', str(Document(create_role)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +080069 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080070 body = self._parse_body(etree.fromstring(body))
71 return resp, body
72
73 def create_tenant(self, name, **kwargs):
74 """
75 Create a tenant
76 name (required): New tenant name
77 description: Description of new tenant (default is none)
78 enabled <true|false>: Initial tenant status (default is true)
79 """
80 en = kwargs.get('enabled', 'true')
81 create_tenant = Element("tenant",
82 xmlns=XMLNS,
83 name=name,
84 description=kwargs.get('description', ''),
85 enabled=str(en).lower())
86 resp, body = self.post('tenants', str(Document(create_tenant)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +080087 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080088 body = self._parse_body(etree.fromstring(body))
89 return resp, body
90
91 def delete_role(self, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -050092 """Delete a role."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +080093 resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id),
94 self.headers)
95 return resp, body
96
97 def list_user_roles(self, tenant_id, user_id):
Sean Daguef237ccb2013-01-04 15:19:14 -050098 """Returns a list of roles assigned to a user for a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +080099 url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
100 resp, body = self.get(url, self.headers)
101 body = self._parse_array(etree.fromstring(body))
102 return resp, body
103
104 def assign_user_role(self, tenant_id, user_id, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500105 """Add roles to a user on a tenant."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800106 resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
107 (tenant_id, user_id, role_id), '', self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800108 body = self._parse_body(etree.fromstring(body))
109 return resp, body
110
111 def remove_user_role(self, tenant_id, user_id, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500112 """Removes a role assignment for a user on a tenant."""
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800113 return self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
114 (tenant_id, user_id, role_id), self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800115
116 def delete_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500117 """Delete a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800118 resp, body = self.delete('tenants/%s' % str(tenant_id), self.headers)
119 return resp, body
120
121 def get_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500122 """Get tenant details."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800123 resp, body = self.get('tenants/%s' % str(tenant_id), self.headers)
124 body = self._parse_body(etree.fromstring(body))
125 return resp, body
126
127 def list_roles(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500128 """Returns roles."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800129 resp, body = self.get('OS-KSADM/roles', self.headers)
130 body = self._parse_array(etree.fromstring(body))
131 return resp, body
132
133 def list_tenants(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500134 """Returns tenants."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800135 resp, body = self.get('tenants', self.headers)
136 body = self._parse_array(etree.fromstring(body))
137 return resp, body
138
Matthew Treinishbf41e102013-01-08 15:56:28 -0500139 def get_tenant_by_name(self, tenant_name):
140 resp, tenants = self.list_tenants()
141 for tenant in tenants:
142 if tenant['name'] == tenant_name:
143 return tenant
144 raise exceptions.NotFound('No such tenant')
145
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800146 def update_tenant(self, tenant_id, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500147 """Updates a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800148 resp, body = self.get_tenant(tenant_id)
149 name = kwargs.get('name', body['name'])
150 desc = kwargs.get('description', body['description'])
151 en = kwargs.get('enabled', body['enabled'])
152 update_tenant = Element("tenant",
153 xmlns=XMLNS,
154 id=tenant_id,
155 name=name,
156 description=desc,
157 enabled=str(en).lower())
158
159 resp, body = self.post('tenants/%s' % tenant_id,
160 str(Document(update_tenant)),
161 self.headers)
162 body = self._parse_body(etree.fromstring(body))
163 return resp, body
164
165 def create_user(self, name, password, tenant_id, email):
Sean Daguef237ccb2013-01-04 15:19:14 -0500166 """Create a user."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800167 create_user = Element("user",
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800168 xmlns=XMLNS,
169 name=name,
170 password=password,
171 tenantId=tenant_id,
172 email=email)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800173 resp, body = self.post('users', str(Document(create_user)),
174 self.headers)
175 body = self._parse_body(etree.fromstring(body))
176 return resp, body
177
178 def delete_user(self, user_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500179 """Delete a user."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800180 resp, body = self.delete("users/%s" % user_id, self.headers)
181 return resp, body
182
183 def get_users(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500184 """Get the list of users."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800185 resp, body = self.get("users", self.headers)
186 body = self._parse_array(etree.fromstring(body))
187 return resp, body
188
189 def enable_disable_user(self, user_id, enabled):
Sean Daguef237ccb2013-01-04 15:19:14 -0500190 """Enables or disables a user."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800191 enable_user = Element("user", enabled=str(enabled).lower())
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800192 resp, body = self.put('users/%s/enabled' % user_id,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800193 str(Document(enable_user)), self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800194 body = self._parse_array(etree.fromstring(body))
195 return resp, body
196
197 def delete_token(self, token_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500198 """Delete a token."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800199 resp, body = self.delete("tokens/%s" % token_id, self.headers)
200 return resp, body
201
202 def list_users_for_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500203 """List users for a Tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800204 resp, body = self.get('/tenants/%s/users' % tenant_id, self.headers)
205 body = self._parse_array(etree.fromstring(body))
206 return resp, body
207
Matthew Treinishbf41e102013-01-08 15:56:28 -0500208 def get_user_by_username(self, tenant_id, username):
209 resp, users = self.list_users_for_tenant(tenant_id)
210 for user in users:
211 if user['name'] == username:
212 return user
213 raise exceptions.NotFound('No such user')
214
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800215 def create_service(self, name, type, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500216 """Create a service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800217 OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
218 create_service = Element("service",
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800219 xmlns=OS_KSADM,
220 name=name,
221 type=type,
222 description=kwargs.get('description'))
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800223 resp, body = self.post('OS-KSADM/services',
224 str(Document(create_service)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800225 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800226 body = self._parse_body(etree.fromstring(body))
227 return resp, body
228
umamohanb51ad002013-01-24 18:13:15 +0000229 def list_services(self):
230 """Returns services."""
231 resp, body = self.get('OS-KSADM/services', self.headers)
232 body = self._parse_array(etree.fromstring(body))
233 return resp, body
234
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800235 def get_service(self, service_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500236 """Get Service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800237 url = '/OS-KSADM/services/%s' % service_id
238 resp, body = self.get(url, self.headers)
239 body = self._parse_body(etree.fromstring(body))
240 return resp, body
241
242 def delete_service(self, service_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500243 """Delete Service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800244 url = '/OS-KSADM/services/%s' % service_id
245 return self.delete(url, self.headers)
246
247
248class TokenClientXML(RestClientXML):
249
250 def __init__(self, config):
Jay Pipes7c88eb22013-01-16 21:32:43 -0500251 auth_url = config.identity.uri
252
253 # TODO(jaypipes) Why is this all repeated code in here?
254 # Normalize URI to ensure /tokens is in it.
255 if 'tokens' not in auth_url:
256 auth_url = auth_url.rstrip('/') + '/tokens'
257
258 self.auth_url = auth_url
Jay Pipescd8eaec2013-01-16 21:03:48 -0500259 self.config = config
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800260
261 def auth(self, user, password, tenant):
262 passwordCreds = Element("passwordCredentials",
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800263 username=user,
264 password=password)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800265 auth = Element("auth", tenantName=tenant)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800266 auth.append(passwordCreds)
267 headers = {'Content-Type': 'application/xml'}
268 resp, body = self.post(self.auth_url, headers=headers,
269 body=str(Document(auth)))
270 return resp, body
271
272 def request(self, method, url, headers=None, body=None):
273 """A simple HTTP request interface."""
Jay Pipescd8eaec2013-01-16 21:03:48 -0500274 dscv = self.config.identity.disable_ssl_certificate_validation
275 self.http_obj = httplib2.Http(disable_ssl_certificate_validation=dscv)
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800276 if headers is None:
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800277 headers = {}
278
279 resp, resp_body = self.http_obj.request(url, method,
280 headers=headers, body=body)
281
282 if resp.status in (401, 403):
283 resp_body = json.loads(resp_body)
284 raise exceptions.Unauthorized(resp_body['error']['message'])
285
286 return resp, resp_body
287
288 def get_token(self, user, password, tenant):
289 resp, body = self.auth(user, password, tenant)
290 if resp['status'] != '202':
291 body = json.loads(body)
292 access = body['access']
293 token = access['token']
294 return token['id']