blocker sonar fix
[so.git] / adapters / mso-adapter-utils / src / main / java / org / onap / so / openstack / utils / MsoKeystoneUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (c) 2019 Samsung
8  * ================================================================================
9  * Modifications Copyright (c) 2020 Nokia
10  * ================================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  * 
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  * 
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.so.openstack.utils;
26
27
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Optional;
31 import org.onap.so.db.catalog.beans.CloudIdentity;
32 import org.onap.so.db.catalog.beans.CloudSite;
33 import org.onap.logging.filter.base.ErrorCode;
34 import org.onap.so.logger.MessageEnum;
35 import org.onap.so.openstack.beans.MsoTenant;
36 import org.onap.so.openstack.exceptions.MsoAdapterException;
37 import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
38 import org.onap.so.openstack.exceptions.MsoException;
39 import org.onap.so.openstack.exceptions.MsoOpenstackException;
40 import org.onap.so.openstack.exceptions.MsoTenantAlreadyExists;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.springframework.stereotype.Component;
44 import com.woorea.openstack.base.client.OpenStackBaseException;
45 import com.woorea.openstack.base.client.OpenStackConnectException;
46 import com.woorea.openstack.base.client.OpenStackRequest;
47 import com.woorea.openstack.base.client.OpenStackResponseException;
48 import com.woorea.openstack.keystone.Keystone;
49 import com.woorea.openstack.keystone.model.Access;
50 import com.woorea.openstack.keystone.model.Authentication;
51 import com.woorea.openstack.keystone.model.Metadata;
52 import com.woorea.openstack.keystone.model.Role;
53 import com.woorea.openstack.keystone.model.Roles;
54 import com.woorea.openstack.keystone.model.Tenant;
55 import com.woorea.openstack.keystone.model.User;
56 import com.woorea.openstack.keystone.utils.KeystoneUtils;
57
58 @Component
59 public class MsoKeystoneUtils extends MsoTenantUtils {
60
61     public static final String DELETE_TENANT = "Delete Tenant";
62     private static final Logger LOGGER = LoggerFactory.getLogger(MsoKeystoneUtils.class);
63
64     /**
65      * Create a tenant with the specified name in the given cloud. If the tenant already exists, an Exception will be
66      * thrown. The MSO User will also be added to the "member" list of the new tenant to perform subsequent Nova/Heat
67      * commands in the tenant. If the MSO User association fails, the entire transaction will be rolled back.
68      * <p>
69      * For the AIC Cloud (DCP/LCP): it is not clear that cloudId is needed, as all admin requests go to the centralized
70      * identity service in DCP. However, if some artifact must exist in each local LCP instance as well, then it will be
71      * needed to access the correct region.
72      * <p>
73      *
74      * @param tenantName The tenant name to create
75      * @param cloudSiteId The cloud identifier (may be a region) in which to create the tenant.
76      * @return the tenant ID of the newly created tenant
77      * @throws MsoTenantAlreadyExists Thrown if the requested tenant already exists
78      * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception
79      */
80     public String createTenant(String tenantName, String cloudSiteId, Map<String, String> metadata, boolean backout)
81             throws MsoException {
82         // Obtain the cloud site information where we will create the tenant
83         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
84         if (!cloudSiteOpt.isPresent()) {
85             LOGGER.error("{} MSOCloudSite {} not found {} ", MessageEnum.RA_CREATE_TENANT_ERR, cloudSiteId,
86                     ErrorCode.DataError.getValue());
87             throw new MsoCloudSiteNotFound(cloudSiteId);
88         }
89         Keystone keystoneAdminClient = getKeystoneAdminClient(cloudSiteOpt.get());
90         Tenant tenant = null;
91         try {
92             // Check if the tenant already exists
93             tenant = findTenantByName(keystoneAdminClient, tenantName);
94
95             if (tenant != null) {
96                 // Tenant already exists. Throw an exception
97                 LOGGER.error("{} Tenant name {} already exists on Cloud site id {}, {}",
98                         MessageEnum.RA_TENANT_ALREADY_EXIST, tenantName, cloudSiteId, ErrorCode.DataError.getValue());
99                 throw new MsoTenantAlreadyExists(tenantName, cloudSiteId);
100             }
101
102             // Does not exist, create a new one
103             tenant = new Tenant();
104             tenant.setName(tenantName);
105             tenant.setDescription("SDN Tenant (via MSO)");
106             tenant.setEnabled(true);
107
108             OpenStackRequest<Tenant> request = keystoneAdminClient.tenants().create(tenant);
109             tenant = executeAndRecordOpenstackRequest(request);
110         } catch (OpenStackBaseException e) {
111             // Convert Keystone OpenStackResponseException to MsoOpenstackException
112             throw keystoneErrorToMsoException(e, "CreateTenant");
113         } catch (RuntimeException e) {
114             // Catch-all
115             throw runtimeExceptionToMsoException(e, "CreateTenant");
116         }
117
118         // Add MSO User to the tenant as a member and
119         // apply tenant metadata if supported by the cloud site
120         try {
121             CloudIdentity cloudIdentity = cloudSiteOpt.get().getIdentityService();
122
123             User msoUser = findUserByNameOrId(keystoneAdminClient, cloudIdentity.getMsoId());
124             Role memberRole = findRoleByNameOrId(keystoneAdminClient, cloudIdentity.getMemberRole());
125
126             if (msoUser != null && memberRole != null) {
127                 OpenStackRequest<Void> request =
128                         keystoneAdminClient.tenants().addUser(tenant.getId(), msoUser.getId(), memberRole.getId());
129                 executeAndRecordOpenstackRequest(request);
130             }
131
132             if (cloudIdentity.getTenantMetadata() && metadata != null && !metadata.isEmpty()) {
133                 Metadata tenantMetadata = new Metadata();
134                 tenantMetadata.setMetadata(metadata);
135
136                 OpenStackRequest<Metadata> metaRequest =
137                         keystoneAdminClient.tenants().createOrUpdateMetadata(tenant.getId(), tenantMetadata);
138                 executeAndRecordOpenstackRequest(metaRequest);
139             }
140         } catch (Exception e) {
141             // Failed to attach MSO User to the new tenant. Can't operate without access,
142             // so roll back the tenant.
143             if (!backout) {
144                 LOGGER.warn("{} Create Tenant errored, Tenant deletion suppressed {} ",
145                         MessageEnum.RA_CREATE_TENANT_ERR, ErrorCode.DataError.getValue());
146             } else {
147                 try {
148                     OpenStackRequest<Void> request = keystoneAdminClient.tenants().delete(tenant.getId());
149                     executeAndRecordOpenstackRequest(request);
150                 } catch (Exception e2) {
151                     // Just log this one. We will report the original exception.
152                     LOGGER.error("{} Nested exception rolling back tenant {} ", MessageEnum.RA_CREATE_TENANT_ERR,
153                             ErrorCode.DataError.getValue(), e2);
154                 }
155             }
156
157
158             // Propagate the original exception on user/role/tenant mapping
159             if (e instanceof OpenStackBaseException) {
160                 // Convert Keystone Exception to MsoOpenstackException
161                 throw keystoneErrorToMsoException((OpenStackBaseException) e, "CreateTenantUser");
162             } else {
163                 MsoAdapterException me = new MsoAdapterException(e.getMessage(), e);
164                 me.addContext("CreateTenantUser");
165                 throw me;
166             }
167         }
168         return tenant.getId();
169     }
170
171     /**
172      * Query for a tenant by ID in the given cloud. If the tenant exists, return an MsoTenant object. If not, return
173      * null.
174      * <p>
175      * For the AIC Cloud (DCP/LCP): it is not clear that cloudId is needed, as all admin requests go to the centralized
176      * identity service in DCP. However, if some artifact must exist in each local LCP instance as well, then it will be
177      * needed to access the correct region.
178      * <p>
179      *
180      * @param tenantId The Openstack ID of the tenant to query
181      * @param cloudSiteId The cloud identifier (may be a region) in which to query the tenant.
182      * @return the tenant properties of the queried tenant, or null if not found
183      * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception
184      */
185     public MsoTenant queryTenant(String tenantId, String cloudSiteId) throws MsoException {
186         // Obtain the cloud site information where we will query the tenant
187         CloudSite cloudSite =
188                 cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId));
189
190         Keystone keystoneAdminClient = getKeystoneAdminClient(cloudSite);
191
192         // Check if the tenant exists and return its Tenant Id
193         try {
194             Tenant tenant = findTenantById(keystoneAdminClient, tenantId);
195             if (tenant == null) {
196                 return null;
197             }
198
199             Map<String, String> metadata = new HashMap<>();
200             if (cloudSite.getIdentityService().getTenantMetadata()) {
201                 OpenStackRequest<Metadata> request = keystoneAdminClient.tenants().showMetadata(tenant.getId());
202                 Metadata tenantMetadata = executeAndRecordOpenstackRequest(request);
203                 if (tenantMetadata != null) {
204                     metadata = tenantMetadata.getMetadata();
205                 }
206             }
207             return new MsoTenant(tenant.getId(), tenant.getName(), metadata);
208         } catch (OpenStackBaseException e) {
209             // Convert Keystone OpenStackResponseException to MsoOpenstackException
210             throw keystoneErrorToMsoException(e, "QueryTenant");
211         } catch (RuntimeException e) {
212             // Catch-all
213             throw runtimeExceptionToMsoException(e, "QueryTenant");
214         }
215     }
216
217     /**
218      * Query for a tenant with the specified name in the given cloud. If the tenant exists, return an MsoTenant object.
219      * If not, return null. This query is useful if the client knows it has the tenant name, skipping an initial lookup
220      * by ID that would always fail.
221      * <p>
222      * For the AIC Cloud (DCP/LCP): it is not clear that cloudId is needed, as all admin requests go to the centralized
223      * identity service in DCP. However, if some artifact must exist in each local LCP instance as well, then it will be
224      * needed to access the correct region.
225      * <p>
226      *
227      * @param tenantName The name of the tenant to query
228      * @param cloudSiteId The cloud identifier (may be a region) in which to query the tenant.
229      * @return the tenant properties of the queried tenant, or null if not found
230      * @throws MsoOpenstackException Thrown if the Openstack API call returns an exception
231      */
232     public MsoTenant queryTenantByName(String tenantName, String cloudSiteId) throws MsoException {
233         // Obtain the cloud site information where we will query the tenant
234         CloudSite cloudSite =
235                 cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId));
236         Keystone keystoneAdminClient = getKeystoneAdminClient(cloudSite);
237
238         try {
239             Tenant tenant = findTenantByName(keystoneAdminClient, tenantName);
240             if (tenant == null) {
241                 return null;
242             }
243
244             Map<String, String> metadata = new HashMap<>();
245             if (cloudSite.getIdentityService().getTenantMetadata()) {
246                 OpenStackRequest<Metadata> request = keystoneAdminClient.tenants().showMetadata(tenant.getId());
247                 Metadata tenantMetadata = executeAndRecordOpenstackRequest(request);
248                 if (tenantMetadata != null) {
249                     metadata = tenantMetadata.getMetadata();
250                 }
251             }
252             return new MsoTenant(tenant.getId(), tenant.getName(), metadata);
253         } catch (OpenStackBaseException e) {
254             // Convert Keystone OpenStackResponseException to MsoOpenstackException
255             throw keystoneErrorToMsoException(e, "QueryTenantName");
256         } catch (RuntimeException e) {
257             // Catch-all
258             throw runtimeExceptionToMsoException(e, "QueryTenantName");
259         }
260     }
261
262     /**
263      * Delete the specified Tenant (by ID) in the given cloud. This method returns true or false, depending on whether
264      * the tenant existed and was successfully deleted, or if the tenant already did not exist. Both cases are treated
265      * as success (no Exceptions).
266      * <p>
267      * Note for the AIC Cloud (DCP/LCP): all admin requests go to the centralized identity service in DCP. So deleting a
268      * tenant from one cloudSiteId will remove it from all sites managed by that identity service.
269      * <p>
270      *
271      * @param tenantId The Openstack ID of the tenant to delete
272      * @param cloudSiteId The cloud identifier from which to delete the tenant.
273      * @return true if the tenant was deleted, false if the tenant did not exist.
274      * @throws MsoOpenstackException If the Openstack API call returns an exception.
275      */
276     public boolean deleteTenant(String tenantId, String cloudSiteId) throws MsoException {
277         // Obtain the cloud site information where we will query the tenant
278         CloudSite cloudSite =
279                 cloudConfig.getCloudSite(cloudSiteId).orElseThrow(() -> new MsoCloudSiteNotFound(cloudSiteId));
280         Keystone keystoneAdminClient = getKeystoneAdminClient(cloudSite);
281
282         try {
283             // Check that the tenant exists. Also, need the ID to delete
284             Tenant tenant = findTenantById(keystoneAdminClient, tenantId);
285             if (tenant == null) {
286                 LOGGER.error("{} Tenant id {} not found on cloud site id {}, {}", MessageEnum.RA_TENANT_NOT_FOUND,
287                         tenantId, cloudSiteId, ErrorCode.DataError.getValue());
288                 return false;
289             }
290
291             OpenStackRequest<Void> request = keystoneAdminClient.tenants().delete(tenant.getId());
292             executeAndRecordOpenstackRequest(request);
293             LOGGER.debug("Deleted Tenant {} ({})", tenant.getId(), tenant.getName());
294         } catch (OpenStackBaseException e) {
295             // Convert Keystone OpenStackResponseException to MsoOpenstackException
296             throw keystoneErrorToMsoException(e, DELETE_TENANT);
297         } catch (RuntimeException e) {
298             // Catch-all
299             throw runtimeExceptionToMsoException(e, DELETE_TENANT);
300         }
301
302         return true;
303     }
304
305     /**
306      * Delete the specified Tenant (by Name) in the given cloud. This method returns true or false, depending on whether
307      * the tenant existed and was successfully deleted, or if the tenant already did not exist. Both cases are treated
308      * as success (no Exceptions).
309      * <p>
310      * Note for the AIC Cloud (DCP/LCP): all admin requests go to the centralized identity service in DCP. So deleting a
311      * tenant from one cloudSiteId will remove it from all sites managed by that identity service.
312      * <p>
313      *
314      * @param tenantName The name of the tenant to delete
315      * @param cloudSiteId The cloud identifier from which to delete the tenant.
316      * @return true if the tenant was deleted, false if the tenant did not exist.
317      * @throws MsoOpenstackException If the Openstack API call returns an exception.
318      */
319     public boolean deleteTenantByName(String tenantName, String cloudSiteId) throws MsoException {
320         // Obtain the cloud site information where we will query the tenant
321         Optional<CloudSite> cloudSite = cloudConfig.getCloudSite(cloudSiteId);
322         if (!cloudSite.isPresent()) {
323             throw new MsoCloudSiteNotFound(cloudSiteId);
324         }
325         Keystone keystoneAdminClient = getKeystoneAdminClient(cloudSite.get());
326
327         try {
328             // Need the Tenant ID to delete (can't directly delete by name)
329             Tenant tenant = findTenantByName(keystoneAdminClient, tenantName);
330             if (tenant == null) {
331                 // OK if tenant already doesn't exist.
332                 LOGGER.error("{} Tenant {} not found on Cloud site id {}, {}", MessageEnum.RA_TENANT_NOT_FOUND,
333                         tenantName, cloudSiteId, ErrorCode.DataError.getValue());
334                 return false;
335             }
336
337             // Execute the Delete. It has no return value.
338             OpenStackRequest<Void> request = keystoneAdminClient.tenants().delete(tenant.getId());
339             executeAndRecordOpenstackRequest(request);
340
341             LOGGER.debug("Deleted Tenant {} ({})", tenant.getId(), tenant.getName());
342
343         } catch (OpenStackBaseException e) {
344             // Note: It doesn't seem to matter if tenant doesn't exist, no exception is thrown.
345             // Convert Keystone OpenStackResponseException to MsoOpenstackException
346             throw keystoneErrorToMsoException(e, DELETE_TENANT);
347         } catch (RuntimeException e) {
348             // Catch-all
349             throw runtimeExceptionToMsoException(e, DELETE_TENANT);
350         }
351
352         return true;
353     }
354
355     // -------------------------------------------------------------------
356     // PRIVATE UTILITY FUNCTIONS FOR USE WITHIN THIS CLASS
357
358     /*
359      * Get a Keystone Admin client for the Openstack Identity service. This requires an 'admin'-level userId + password
360      * along with an 'admin' tenant in the target cloud. These values will be retrieved from properties based on the
361      * specified cloud ID. <p> On successful authentication, the Keystone object will be cached for the cloudId so that
362      * it can be reused without going back to Openstack every time.
363      *
364      * @param cloudId
365      *
366      * @return an authenticated Keystone object
367      */
368     public Keystone getKeystoneAdminClient(CloudSite cloudSite) throws MsoException {
369         CloudIdentity cloudIdentity = cloudSite.getIdentityService();
370
371         String adminTenantName = cloudIdentity.getAdminTenant();
372         String region = cloudSite.getRegionId();
373
374         MsoTenantUtils tenantUtils =
375                 tenantUtilsFactory.getTenantUtilsByServerType(cloudIdentity.getIdentityServerType());
376         final String keystoneUrl = tenantUtils.getKeystoneUrl(region, cloudIdentity);
377         Keystone keystone = new Keystone(keystoneUrl);
378
379         // Must authenticate against the 'admin' tenant to get the services endpoints
380         Access access = null;
381         String token = null;
382         try {
383             Authentication credentials = authenticationMethodFactory.getAuthenticationFor(cloudIdentity);
384             OpenStackRequest<Access> request =
385                     keystone.tokens().authenticate(credentials).withTenantName(adminTenantName);
386             access = executeAndRecordOpenstackRequest(request);
387             token = access.getToken().getId();
388         } catch (OpenStackResponseException e) {
389             if (e.getStatus() == 401) {
390                 // Authentication error. Can't access admin tenant - something is mis-configured
391                 String error = "MSO Authentication Failed for " + cloudIdentity.getId();
392
393                 throw new MsoAdapterException(error);
394             } else {
395                 throw keystoneErrorToMsoException(e, "TokenAuth");
396             }
397         } catch (OpenStackConnectException e) {
398             // Connection to Openstack failed
399             throw keystoneErrorToMsoException(e, "TokenAuth");
400         }
401
402         // Get the Identity service URL. Throws runtime exception if not found per region.
403         String adminUrl = null;
404         try {
405             adminUrl = KeystoneUtils.findEndpointURL(access.getServiceCatalog(), "identity", region, "public");
406             adminUrl = adminUrl.replaceFirst("5000", "35357");
407         } catch (RuntimeException e) {
408             String error = "Identity service not found: region=" + region + ",cloud=" + cloudIdentity.getId();
409
410             LOGGER.error("{} Region: {} Cloud identity {} {} Exception in findEndpointURL ",
411                     MessageEnum.IDENTITY_SERVICE_NOT_FOUND, region, cloudIdentity.getId(),
412                     ErrorCode.DataError.getValue(), e);
413             throw new MsoAdapterException(error, e);
414         }
415
416         // A new Keystone object is required for the new URL. Use the auth token from above.
417         // Note: this doesn't go back to Openstack, it's just a local object.
418         keystone = new Keystone(adminUrl);
419         keystone.token(token);
420         return keystone;
421     }
422
423     /*
424      * Find a tenant (or query its existance) by its Name or Id. Check first against the ID. If that fails, then try by
425      * name.
426      *
427      * @param adminClient an authenticated Keystone object
428      *
429      * @param tenantName the tenant name or ID to query
430      *
431      * @return a Tenant object or null if not found
432      */
433     public Tenant findTenantByNameOrId(Keystone adminClient, String tenantNameOrId) {
434         if (tenantNameOrId == null) {
435             return null;
436         }
437
438         Tenant tenant = findTenantById(adminClient, tenantNameOrId);
439         if (tenant == null) {
440             tenant = findTenantByName(adminClient, tenantNameOrId);
441         }
442
443         return tenant;
444     }
445
446     /*
447      * Find a tenant (or query its existance) by its Id.
448      *
449      * @param adminClient an authenticated Keystone object
450      *
451      * @param tenantName the tenant ID to query
452      *
453      * @return a Tenant object or null if not found
454      */
455     private Tenant findTenantById(Keystone adminClient, String tenantId) {
456         if (tenantId == null) {
457             return null;
458         }
459
460         try {
461             OpenStackRequest<Tenant> request = adminClient.tenants().show(tenantId);
462             return executeAndRecordOpenstackRequest(request);
463         } catch (OpenStackResponseException e) {
464             if (e.getStatus() == 404) {
465                 return null;
466             } else {
467                 LOGGER.error("{} {} Openstack Error, GET Tenant by Id ({}): ", MessageEnum.RA_CONNECTION_EXCEPTION,
468                         ErrorCode.DataError.getValue(), tenantId, e);
469                 throw e;
470             }
471         }
472     }
473
474     /*
475      * Find a tenant (or query its existance) by its Name. This method avoids an initial lookup by ID when it's known
476      * that we have the tenant Name.
477      *
478      * @param adminClient an authenticated Keystone object
479      *
480      * @param tenantName the tenant name to query
481      *
482      * @return a Tenant object or null if not found
483      */
484     public Tenant findTenantByName(Keystone adminClient, String tenantName) {
485         if (tenantName == null) {
486             return null;
487         }
488
489         try {
490             OpenStackRequest<Tenant> request = adminClient.tenants().show("").queryParam("name", tenantName);
491             return executeAndRecordOpenstackRequest(request);
492         } catch (OpenStackResponseException e) {
493             if (e.getStatus() == 404) {
494                 return null;
495             } else {
496                 LOGGER.error("{} {} Openstack Error, GET Tenant By Name ({}) ", MessageEnum.RA_CONNECTION_EXCEPTION,
497                         ErrorCode.DataError.getValue(), tenantName, e);
498                 throw e;
499             }
500         }
501     }
502
503     /*
504      * Look up an Openstack User by Name or Openstack ID. Check the ID first, and if that fails, try the Name.
505      *
506      * @param adminClient an authenticated Keystone object
507      *
508      * @param userName the user name or ID to query
509      *
510      * @return a User object or null if not found
511      */
512     private User findUserByNameOrId(Keystone adminClient, String userNameOrId) {
513         if (userNameOrId == null) {
514             return null;
515         }
516
517         try {
518             OpenStackRequest<User> request = adminClient.users().show(userNameOrId);
519             return executeAndRecordOpenstackRequest(request);
520         } catch (OpenStackResponseException e) {
521             if (e.getStatus() == 404) {
522                 // Not found by ID. Search for name
523                 return findUserByName(adminClient, userNameOrId);
524             } else {
525                 LOGGER.error("{} {} Openstack Error, GET User ({}) ", MessageEnum.RA_CONNECTION_EXCEPTION,
526                         ErrorCode.DataError.getValue(), userNameOrId, e);
527                 throw e;
528             }
529         }
530     }
531
532     /*
533      * Look up an Openstack User by Name. This avoids initial Openstack query by ID if we know we have the User Name.
534      *
535      * @param adminClient an authenticated Keystone object
536      *
537      * @param userName the user name to query
538      *
539      * @return a User object or null if not found
540      */
541     public User findUserByName(Keystone adminClient, String userName) {
542         if (userName == null) {
543             return null;
544         }
545
546         try {
547             OpenStackRequest<User> request = adminClient.users().show("").queryParam("name", userName);
548             return executeAndRecordOpenstackRequest(request);
549         } catch (OpenStackResponseException e) {
550             if (e.getStatus() == 404) {
551                 return null;
552             } else {
553                 LOGGER.error("{} {} Openstack Error, GET User By Name ({}): ", MessageEnum.RA_CONNECTION_EXCEPTION,
554                         ErrorCode.DataError.getValue(), userName, e);
555                 throw e;
556             }
557         }
558     }
559
560     /*
561      * Look up an Openstack Role by Name or Id. There is no direct query for Roles, so need to retrieve a full list from
562      * Openstack and look for a match. By default, Openstack should have a "_member_" role for normal VM-level
563      * privileges and an "admin" role for expanded privileges (e.g. administer tenants, users, and roles). <p>
564      *
565      * @param adminClient an authenticated Keystone object
566      *
567      * @param roleNameOrId the Role name or ID to look up
568      *
569      * @return a Role object
570      */
571     private Role findRoleByNameOrId(Keystone adminClient, String roleNameOrId) {
572         if (roleNameOrId == null) {
573             return null;
574         }
575
576         // Search by name or ID. Must search in list
577         OpenStackRequest<Roles> request = adminClient.roles().list();
578         Roles roles = executeAndRecordOpenstackRequest(request);
579
580         for (Role role : roles) {
581             if (roleNameOrId.equals(role.getName()) || roleNameOrId.equals(role.getId())) {
582                 return role;
583             }
584         }
585
586         return null;
587     }
588
589     @Override
590     public String getKeystoneUrl(String regionId, CloudIdentity cloudIdentity) throws MsoException {
591         return cloudIdentity.getIdentityUrl();
592     }
593 }