blob: 7c3668087977c6ef32d76bf1a90d4b478cdf68db [file] [log] [blame]
Kurt Taylor6a6f5be2013-04-02 18:53:47 -04001# Copyright 2012 IBM Corp.
Vincent Hou6b8a7b72012-08-25 01:24:33 +08002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Matthew Treinisha83a16e2012-12-07 13:44:02 -050016import json
Matthew Treinisha83a16e2012-12-07 13:44:02 -050017
Vincent Hou6b8a7b72012-08-25 01:24:33 +080018from lxml import etree
Matthew Treinisha83a16e2012-12-07 13:44:02 -050019
Vincent Hou6b8a7b72012-08-25 01:24:33 +080020from tempest.common.rest_client import RestClientXML
Matthew Treinish684d8992014-01-30 16:27:40 +000021from tempest import config
Matthew Treinisha83a16e2012-12-07 13:44:02 -050022from tempest import exceptions
dwallecke62b9f02012-10-10 23:34:42 -050023from tempest.services.compute.xml.common import Document
24from tempest.services.compute.xml.common import Element
dwallecke62b9f02012-10-10 23:34:42 -050025from tempest.services.compute.xml.common import xml_to_json
Matthew Treinisha83a16e2012-12-07 13:44:02 -050026
Matthew Treinish684d8992014-01-30 16:27:40 +000027CONF = config.CONF
Vincent Hou6b8a7b72012-08-25 01:24:33 +080028
29XMLNS = "http://docs.openstack.org/identity/api/v2.0"
30
31
Attila Fazekas407b6db2013-01-19 12:48:36 +010032class IdentityClientXML(RestClientXML):
Vincent Hou6b8a7b72012-08-25 01:24:33 +080033
Andrea Frittoli8bbdb162014-01-06 11:06:13 +000034 def __init__(self, auth_provider):
35 super(IdentityClientXML, self).__init__(auth_provider)
Matthew Treinish684d8992014-01-30 16:27:40 +000036 self.service = CONF.identity.catalog_type
Vincent Hou6b8a7b72012-08-25 01:24:33 +080037 self.endpoint_url = 'adminURL'
38
39 def _parse_array(self, node):
40 array = []
41 for child in node.getchildren():
42 array.append(xml_to_json(child))
43 return array
44
45 def _parse_body(self, body):
Attila Fazekas7b487be2013-02-12 11:14:41 +010046 data = xml_to_json(body)
47 return data
Vincent Hou6b8a7b72012-08-25 01:24:33 +080048
49 def has_admin_extensions(self):
50 """
51 Returns True if the KSADM Admin Extensions are supported
52 False otherwise
53 """
54 if hasattr(self, '_has_admin_extensions'):
55 return self._has_admin_extensions
56 resp, body = self.list_roles()
57 self._has_admin_extensions = ('status' in resp and resp.status != 503)
58 return self._has_admin_extensions
59
60 def create_role(self, name):
Sean Daguef237ccb2013-01-04 15:19:14 -050061 """Create a role."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +080062 create_role = Element("role", xmlns=XMLNS, name=name)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080063 resp, body = self.post('OS-KSADM/roles', str(Document(create_role)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +080064 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080065 body = self._parse_body(etree.fromstring(body))
66 return resp, body
67
68 def create_tenant(self, name, **kwargs):
69 """
70 Create a tenant
71 name (required): New tenant name
72 description: Description of new tenant (default is none)
73 enabled <true|false>: Initial tenant status (default is true)
74 """
75 en = kwargs.get('enabled', 'true')
76 create_tenant = Element("tenant",
77 xmlns=XMLNS,
78 name=name,
79 description=kwargs.get('description', ''),
80 enabled=str(en).lower())
81 resp, body = self.post('tenants', str(Document(create_tenant)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +080082 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080083 body = self._parse_body(etree.fromstring(body))
84 return resp, body
85
86 def delete_role(self, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -050087 """Delete a role."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +080088 resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id),
89 self.headers)
90 return resp, body
91
92 def list_user_roles(self, tenant_id, user_id):
Sean Daguef237ccb2013-01-04 15:19:14 -050093 """Returns a list of roles assigned to a user for a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +080094 url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
95 resp, body = self.get(url, self.headers)
96 body = self._parse_array(etree.fromstring(body))
97 return resp, body
98
99 def assign_user_role(self, tenant_id, user_id, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500100 """Add roles to a user on a tenant."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800101 resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
102 (tenant_id, user_id, role_id), '', self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800103 body = self._parse_body(etree.fromstring(body))
104 return resp, body
105
106 def remove_user_role(self, tenant_id, user_id, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500107 """Removes a role assignment for a user on a tenant."""
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800108 return self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
109 (tenant_id, user_id, role_id), self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800110
111 def delete_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500112 """Delete a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800113 resp, body = self.delete('tenants/%s' % str(tenant_id), self.headers)
114 return resp, body
115
116 def get_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500117 """Get tenant details."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800118 resp, body = self.get('tenants/%s' % str(tenant_id), self.headers)
119 body = self._parse_body(etree.fromstring(body))
120 return resp, body
121
122 def list_roles(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500123 """Returns roles."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800124 resp, body = self.get('OS-KSADM/roles', self.headers)
125 body = self._parse_array(etree.fromstring(body))
126 return resp, body
127
128 def list_tenants(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500129 """Returns tenants."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800130 resp, body = self.get('tenants', self.headers)
131 body = self._parse_array(etree.fromstring(body))
132 return resp, body
133
Matthew Treinishbf41e102013-01-08 15:56:28 -0500134 def get_tenant_by_name(self, tenant_name):
135 resp, tenants = self.list_tenants()
136 for tenant in tenants:
137 if tenant['name'] == tenant_name:
138 return tenant
139 raise exceptions.NotFound('No such tenant')
140
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800141 def update_tenant(self, tenant_id, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500142 """Updates a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800143 resp, body = self.get_tenant(tenant_id)
144 name = kwargs.get('name', body['name'])
145 desc = kwargs.get('description', body['description'])
146 en = kwargs.get('enabled', body['enabled'])
147 update_tenant = Element("tenant",
148 xmlns=XMLNS,
149 id=tenant_id,
150 name=name,
151 description=desc,
152 enabled=str(en).lower())
153
154 resp, body = self.post('tenants/%s' % tenant_id,
155 str(Document(update_tenant)),
156 self.headers)
157 body = self._parse_body(etree.fromstring(body))
158 return resp, body
159
huangtianhuafc8db4f2013-10-08 12:05:58 +0800160 def create_user(self, name, password, tenant_id, email, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500161 """Create a user."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800162 create_user = Element("user",
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800163 xmlns=XMLNS,
164 name=name,
165 password=password,
166 tenantId=tenant_id,
167 email=email)
huangtianhuafc8db4f2013-10-08 12:05:58 +0800168 if 'enabled' in kwargs:
169 create_user.add_attr('enabled', str(kwargs['enabled']).lower())
170
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800171 resp, body = self.post('users', str(Document(create_user)),
172 self.headers)
173 body = self._parse_body(etree.fromstring(body))
174 return resp, body
175
Chang Bo Guob36b2f12013-09-13 04:52:00 -0700176 def update_user(self, user_id, **kwargs):
177 """Updates a user."""
178 if 'enabled' in kwargs:
179 kwargs['enabled'] = str(kwargs['enabled']).lower()
180 update_user = Element("user", xmlns=XMLNS, **kwargs)
181
182 resp, body = self.put('users/%s' % user_id,
183 str(Document(update_user)),
184 self.headers)
185 body = self._parse_body(etree.fromstring(body))
186 return resp, body
187
rajalakshmi-ganesan7312bb52013-01-29 20:03:42 +0530188 def get_user(self, user_id):
189 """GET a user."""
190 resp, body = self.get("users/%s" % user_id, self.headers)
191 body = self._parse_body(etree.fromstring(body))
192 return resp, body
193
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800194 def delete_user(self, user_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500195 """Delete a user."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800196 resp, body = self.delete("users/%s" % user_id, self.headers)
197 return resp, body
198
199 def get_users(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500200 """Get the list of users."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800201 resp, body = self.get("users", self.headers)
202 body = self._parse_array(etree.fromstring(body))
203 return resp, body
204
205 def enable_disable_user(self, user_id, enabled):
Sean Daguef237ccb2013-01-04 15:19:14 -0500206 """Enables or disables a user."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800207 enable_user = Element("user", enabled=str(enabled).lower())
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800208 resp, body = self.put('users/%s/enabled' % user_id,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800209 str(Document(enable_user)), self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800210 body = self._parse_array(etree.fromstring(body))
211 return resp, body
212
213 def delete_token(self, token_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500214 """Delete a token."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800215 resp, body = self.delete("tokens/%s" % token_id, self.headers)
216 return resp, body
217
218 def list_users_for_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500219 """List users for a Tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800220 resp, body = self.get('/tenants/%s/users' % tenant_id, self.headers)
221 body = self._parse_array(etree.fromstring(body))
222 return resp, body
223
Matthew Treinishbf41e102013-01-08 15:56:28 -0500224 def get_user_by_username(self, tenant_id, username):
225 resp, users = self.list_users_for_tenant(tenant_id)
226 for user in users:
227 if user['name'] == username:
228 return user
229 raise exceptions.NotFound('No such user')
230
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800231 def create_service(self, name, type, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500232 """Create a service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800233 OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
234 create_service = Element("service",
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800235 xmlns=OS_KSADM,
236 name=name,
237 type=type,
238 description=kwargs.get('description'))
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800239 resp, body = self.post('OS-KSADM/services',
240 str(Document(create_service)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800241 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800242 body = self._parse_body(etree.fromstring(body))
243 return resp, body
244
umamohanb51ad002013-01-24 18:13:15 +0000245 def list_services(self):
246 """Returns services."""
247 resp, body = self.get('OS-KSADM/services', self.headers)
248 body = self._parse_array(etree.fromstring(body))
249 return resp, body
250
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800251 def get_service(self, service_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500252 """Get Service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800253 url = '/OS-KSADM/services/%s' % service_id
254 resp, body = self.get(url, self.headers)
255 body = self._parse_body(etree.fromstring(body))
256 return resp, body
257
258 def delete_service(self, service_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500259 """Delete Service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800260 url = '/OS-KSADM/services/%s' % service_id
261 return self.delete(url, self.headers)
262
263
264class TokenClientXML(RestClientXML):
265
Matthew Treinish684d8992014-01-30 16:27:40 +0000266 def __init__(self):
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000267 super(TokenClientXML, self).__init__(None)
Matthew Treinish684d8992014-01-30 16:27:40 +0000268 auth_url = CONF.identity.uri
Jay Pipes7c88eb22013-01-16 21:32:43 -0500269
Jay Pipes7c88eb22013-01-16 21:32:43 -0500270 # Normalize URI to ensure /tokens is in it.
271 if 'tokens' not in auth_url:
272 auth_url = auth_url.rstrip('/') + '/tokens'
273
274 self.auth_url = auth_url
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800275
276 def auth(self, user, password, tenant):
277 passwordCreds = Element("passwordCredentials",
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800278 username=user,
279 password=password)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800280 auth = Element("auth", tenantName=tenant)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800281 auth.append(passwordCreds)
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000282 resp, body = self.post(self.auth_url, headers=self.headers,
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800283 body=str(Document(auth)))
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000284 return resp, body['access']
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800285
286 def request(self, method, url, headers=None, body=None):
287 """A simple HTTP request interface."""
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800288 if headers is None:
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800289 headers = {}
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000290 # Send XML, accept JSON. XML response is not easily
291 # converted to the corresponding JSON one
292 headers['Accept'] = 'application/json'
Attila Fazekas7b487be2013-02-12 11:14:41 +0100293 self._log_request(method, url, headers, body)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800294 resp, resp_body = self.http_obj.request(url, method,
295 headers=headers, body=body)
Attila Fazekas7b487be2013-02-12 11:14:41 +0100296 self._log_response(resp, resp_body)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800297
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000298 if resp.status in [401, 403]:
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800299 resp_body = json.loads(resp_body)
300 raise exceptions.Unauthorized(resp_body['error']['message'])
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000301 elif resp.status not in [200, 201]:
302 raise exceptions.IdentityError(
303 'Unexpected status code {0}'.format(resp.status))
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800304
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000305 return resp, json.loads(resp_body)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800306
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000307 def get_token(self, user, password, tenant, auth_data=False):
308 """
309 Returns (token id, token data) for supplied credentials
310 """
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800311 resp, body = self.auth(user, password, tenant)
Andrea Frittoli8bbdb162014-01-06 11:06:13 +0000312
313 if auth_data:
314 return body['token']['id'], body
315 else:
316 return body['token']['id']