blob: e6a7188a966b452fd526ea50fb636e49ef955e88 [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
Mate Lakat23a58a32013-08-23 02:06:22 +010020from tempest.common import http
Vincent Hou6b8a7b72012-08-25 01:24:33 +080021from tempest.common.rest_client import RestClientXML
Matthew Treinish684d8992014-01-30 16:27:40 +000022from tempest import config
Matthew Treinisha83a16e2012-12-07 13:44:02 -050023from tempest import exceptions
dwallecke62b9f02012-10-10 23:34:42 -050024from tempest.services.compute.xml.common import Document
25from tempest.services.compute.xml.common import Element
dwallecke62b9f02012-10-10 23:34:42 -050026from tempest.services.compute.xml.common import xml_to_json
Matthew Treinisha83a16e2012-12-07 13:44:02 -050027
Matthew Treinish684d8992014-01-30 16:27:40 +000028CONF = config.CONF
Vincent Hou6b8a7b72012-08-25 01:24:33 +080029
30XMLNS = "http://docs.openstack.org/identity/api/v2.0"
31
32
Attila Fazekas407b6db2013-01-19 12:48:36 +010033class IdentityClientXML(RestClientXML):
Vincent Hou6b8a7b72012-08-25 01:24:33 +080034
Matthew Treinish684d8992014-01-30 16:27:40 +000035 def __init__(self, username, password, auth_url, tenant_name=None):
36 super(IdentityClientXML, self).__init__(username, password,
Attila Fazekas407b6db2013-01-19 12:48:36 +010037 auth_url, tenant_name)
Matthew Treinish684d8992014-01-30 16:27:40 +000038 self.service = CONF.identity.catalog_type
Vincent Hou6b8a7b72012-08-25 01:24:33 +080039 self.endpoint_url = 'adminURL'
40
41 def _parse_array(self, node):
42 array = []
43 for child in node.getchildren():
44 array.append(xml_to_json(child))
45 return array
46
47 def _parse_body(self, body):
Attila Fazekas7b487be2013-02-12 11:14:41 +010048 data = xml_to_json(body)
49 return data
Vincent Hou6b8a7b72012-08-25 01:24:33 +080050
51 def has_admin_extensions(self):
52 """
53 Returns True if the KSADM Admin Extensions are supported
54 False otherwise
55 """
56 if hasattr(self, '_has_admin_extensions'):
57 return self._has_admin_extensions
58 resp, body = self.list_roles()
59 self._has_admin_extensions = ('status' in resp and resp.status != 503)
60 return self._has_admin_extensions
61
62 def create_role(self, name):
Sean Daguef237ccb2013-01-04 15:19:14 -050063 """Create a role."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +080064 create_role = Element("role", xmlns=XMLNS, name=name)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080065 resp, body = self.post('OS-KSADM/roles', str(Document(create_role)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +080066 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080067 body = self._parse_body(etree.fromstring(body))
68 return resp, body
69
70 def create_tenant(self, name, **kwargs):
71 """
72 Create a tenant
73 name (required): New tenant name
74 description: Description of new tenant (default is none)
75 enabled <true|false>: Initial tenant status (default is true)
76 """
77 en = kwargs.get('enabled', 'true')
78 create_tenant = Element("tenant",
79 xmlns=XMLNS,
80 name=name,
81 description=kwargs.get('description', ''),
82 enabled=str(en).lower())
83 resp, body = self.post('tenants', str(Document(create_tenant)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +080084 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +080085 body = self._parse_body(etree.fromstring(body))
86 return resp, body
87
88 def delete_role(self, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -050089 """Delete a role."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +080090 resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id),
91 self.headers)
92 return resp, body
93
94 def list_user_roles(self, tenant_id, user_id):
Sean Daguef237ccb2013-01-04 15:19:14 -050095 """Returns a list of roles assigned to a user for a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +080096 url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
97 resp, body = self.get(url, self.headers)
98 body = self._parse_array(etree.fromstring(body))
99 return resp, body
100
101 def assign_user_role(self, tenant_id, user_id, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500102 """Add roles to a user on a tenant."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800103 resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
104 (tenant_id, user_id, role_id), '', self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800105 body = self._parse_body(etree.fromstring(body))
106 return resp, body
107
108 def remove_user_role(self, tenant_id, user_id, role_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500109 """Removes a role assignment for a user on a tenant."""
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800110 return self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
111 (tenant_id, user_id, role_id), self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800112
113 def delete_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500114 """Delete a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800115 resp, body = self.delete('tenants/%s' % str(tenant_id), self.headers)
116 return resp, body
117
118 def get_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500119 """Get tenant details."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800120 resp, body = self.get('tenants/%s' % str(tenant_id), self.headers)
121 body = self._parse_body(etree.fromstring(body))
122 return resp, body
123
124 def list_roles(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500125 """Returns roles."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800126 resp, body = self.get('OS-KSADM/roles', self.headers)
127 body = self._parse_array(etree.fromstring(body))
128 return resp, body
129
130 def list_tenants(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500131 """Returns tenants."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800132 resp, body = self.get('tenants', self.headers)
133 body = self._parse_array(etree.fromstring(body))
134 return resp, body
135
Matthew Treinishbf41e102013-01-08 15:56:28 -0500136 def get_tenant_by_name(self, tenant_name):
137 resp, tenants = self.list_tenants()
138 for tenant in tenants:
139 if tenant['name'] == tenant_name:
140 return tenant
141 raise exceptions.NotFound('No such tenant')
142
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800143 def update_tenant(self, tenant_id, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500144 """Updates a tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800145 resp, body = self.get_tenant(tenant_id)
146 name = kwargs.get('name', body['name'])
147 desc = kwargs.get('description', body['description'])
148 en = kwargs.get('enabled', body['enabled'])
149 update_tenant = Element("tenant",
150 xmlns=XMLNS,
151 id=tenant_id,
152 name=name,
153 description=desc,
154 enabled=str(en).lower())
155
156 resp, body = self.post('tenants/%s' % tenant_id,
157 str(Document(update_tenant)),
158 self.headers)
159 body = self._parse_body(etree.fromstring(body))
160 return resp, body
161
huangtianhuafc8db4f2013-10-08 12:05:58 +0800162 def create_user(self, name, password, tenant_id, email, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500163 """Create a user."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800164 create_user = Element("user",
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800165 xmlns=XMLNS,
166 name=name,
167 password=password,
168 tenantId=tenant_id,
169 email=email)
huangtianhuafc8db4f2013-10-08 12:05:58 +0800170 if 'enabled' in kwargs:
171 create_user.add_attr('enabled', str(kwargs['enabled']).lower())
172
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
Chang Bo Guob36b2f12013-09-13 04:52:00 -0700178 def update_user(self, user_id, **kwargs):
179 """Updates a user."""
180 if 'enabled' in kwargs:
181 kwargs['enabled'] = str(kwargs['enabled']).lower()
182 update_user = Element("user", xmlns=XMLNS, **kwargs)
183
184 resp, body = self.put('users/%s' % user_id,
185 str(Document(update_user)),
186 self.headers)
187 body = self._parse_body(etree.fromstring(body))
188 return resp, body
189
rajalakshmi-ganesan7312bb52013-01-29 20:03:42 +0530190 def get_user(self, user_id):
191 """GET a user."""
192 resp, body = self.get("users/%s" % user_id, self.headers)
193 body = self._parse_body(etree.fromstring(body))
194 return resp, body
195
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800196 def delete_user(self, user_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500197 """Delete a user."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800198 resp, body = self.delete("users/%s" % user_id, self.headers)
199 return resp, body
200
201 def get_users(self):
Sean Daguef237ccb2013-01-04 15:19:14 -0500202 """Get the list of users."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800203 resp, body = self.get("users", self.headers)
204 body = self._parse_array(etree.fromstring(body))
205 return resp, body
206
207 def enable_disable_user(self, user_id, enabled):
Sean Daguef237ccb2013-01-04 15:19:14 -0500208 """Enables or disables a user."""
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800209 enable_user = Element("user", enabled=str(enabled).lower())
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800210 resp, body = self.put('users/%s/enabled' % user_id,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800211 str(Document(enable_user)), self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800212 body = self._parse_array(etree.fromstring(body))
213 return resp, body
214
215 def delete_token(self, token_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500216 """Delete a token."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800217 resp, body = self.delete("tokens/%s" % token_id, self.headers)
218 return resp, body
219
220 def list_users_for_tenant(self, tenant_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500221 """List users for a Tenant."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800222 resp, body = self.get('/tenants/%s/users' % tenant_id, self.headers)
223 body = self._parse_array(etree.fromstring(body))
224 return resp, body
225
Matthew Treinishbf41e102013-01-08 15:56:28 -0500226 def get_user_by_username(self, tenant_id, username):
227 resp, users = self.list_users_for_tenant(tenant_id)
228 for user in users:
229 if user['name'] == username:
230 return user
231 raise exceptions.NotFound('No such user')
232
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800233 def create_service(self, name, type, **kwargs):
Sean Daguef237ccb2013-01-04 15:19:14 -0500234 """Create a service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800235 OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
236 create_service = Element("service",
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800237 xmlns=OS_KSADM,
238 name=name,
239 type=type,
240 description=kwargs.get('description'))
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800241 resp, body = self.post('OS-KSADM/services',
242 str(Document(create_service)),
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800243 self.headers)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800244 body = self._parse_body(etree.fromstring(body))
245 return resp, body
246
umamohanb51ad002013-01-24 18:13:15 +0000247 def list_services(self):
248 """Returns services."""
249 resp, body = self.get('OS-KSADM/services', self.headers)
250 body = self._parse_array(etree.fromstring(body))
251 return resp, body
252
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800253 def get_service(self, service_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500254 """Get Service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800255 url = '/OS-KSADM/services/%s' % service_id
256 resp, body = self.get(url, self.headers)
257 body = self._parse_body(etree.fromstring(body))
258 return resp, body
259
260 def delete_service(self, service_id):
Sean Daguef237ccb2013-01-04 15:19:14 -0500261 """Delete Service."""
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800262 url = '/OS-KSADM/services/%s' % service_id
263 return self.delete(url, self.headers)
264
265
266class TokenClientXML(RestClientXML):
267
Matthew Treinish684d8992014-01-30 16:27:40 +0000268 def __init__(self):
269 auth_url = CONF.identity.uri
Jay Pipes7c88eb22013-01-16 21:32:43 -0500270
271 # TODO(jaypipes) Why is this all repeated code in here?
272 # Normalize URI to ensure /tokens is in it.
273 if 'tokens' not in auth_url:
274 auth_url = auth_url.rstrip('/') + '/tokens'
275
276 self.auth_url = auth_url
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800277
278 def auth(self, user, password, tenant):
279 passwordCreds = Element("passwordCredentials",
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800280 username=user,
281 password=password)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800282 auth = Element("auth", tenantName=tenant)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800283 auth.append(passwordCreds)
284 headers = {'Content-Type': 'application/xml'}
285 resp, body = self.post(self.auth_url, headers=headers,
286 body=str(Document(auth)))
287 return resp, body
288
289 def request(self, method, url, headers=None, body=None):
290 """A simple HTTP request interface."""
Matthew Treinish684d8992014-01-30 16:27:40 +0000291 dscv = CONF.identity.disable_ssl_certificate_validation
Mate Lakat23a58a32013-08-23 02:06:22 +0100292 self.http_obj = http.ClosingHttp(
293 disable_ssl_certificate_validation=dscv)
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800294 if headers is None:
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800295 headers = {}
Attila Fazekas7b487be2013-02-12 11:14:41 +0100296 self._log_request(method, url, headers, body)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800297 resp, resp_body = self.http_obj.request(url, method,
298 headers=headers, body=body)
Attila Fazekas7b487be2013-02-12 11:14:41 +0100299 self._log_response(resp, resp_body)
Vincent Hou6b8a7b72012-08-25 01:24:33 +0800300
301 if resp.status in (401, 403):
302 resp_body = json.loads(resp_body)
303 raise exceptions.Unauthorized(resp_body['error']['message'])
304
305 return resp, resp_body
306
307 def get_token(self, user, password, tenant):
308 resp, body = self.auth(user, password, tenant)
309 if resp['status'] != '202':
310 body = json.loads(body)
311 access = body['access']
312 token = access['token']
313 return token['id']