Add support for OpenStack V3 Identity Service
[appc.git] / appc-adapters / appc-iaas-adapter / appc-iaas-adapter-bundle / src / main / java / org / openecomp / appc / adapter / iaas / impl / ServiceCatalog.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.openecomp.appc.adapter.iaas.impl;
26
27 import java.net.NoRouteToHostException;
28 import java.net.SocketException;
29 import java.util.ArrayList;
30 import java.util.Calendar;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Properties;
37 import java.util.Set;
38 import java.util.concurrent.locks.Lock;
39 import java.util.concurrent.locks.ReadWriteLock;
40 import java.util.concurrent.locks.ReentrantReadWriteLock;
41 import com.att.cdp.exceptions.ContextConnectionException;
42 import com.att.cdp.exceptions.ZoneException;
43 import com.att.cdp.openstack.util.ExceptionMapper;
44 import com.att.cdp.pal.util.Time;
45 import com.att.cdp.zones.ContextFactory;
46 import com.att.cdp.zones.spi.AbstractService;
47 import com.att.cdp.zones.spi.RequestState;
48 import com.att.eelf.configuration.EELFLogger;
49 import com.att.eelf.configuration.EELFManager;
50 import com.att.cdp.zones.spi.AbstractService.State;
51 import com.woorea.openstack.base.client.OpenStackBaseException;
52 import com.woorea.openstack.base.client.OpenStackClientConnector;
53 import com.woorea.openstack.base.client.OpenStackResponseException;
54 import com.woorea.openstack.base.client.OpenStackSimpleTokenProvider;
55 import com.woorea.openstack.keystone.Keystone;
56 import com.woorea.openstack.keystone.api.TokensResource;
57 import com.woorea.openstack.keystone.model.Access;
58 import com.woorea.openstack.keystone.model.Access.Service;
59 import com.woorea.openstack.keystone.model.Access.Service.Endpoint;
60 import com.woorea.openstack.keystone.model.Authentication;
61 import com.woorea.openstack.keystone.model.Tenant;
62 import com.woorea.openstack.keystone.model.authentication.UsernamePassword;
63
64 /**
65  * This class is used to capture and cache the service catalog for a specific OpenStack provider.
66  * <p>
67  * This is needed because the way the servers are represented in the ECOMP product is as their fully qualified URL's.
68  * This is very problematic, because we cant identify their region from the URL, URL's change, and we cant identify the
69  * versions of the service implementations. In otherwords, the URL does not provide us enough information.
70  * </p>
71  * <p>
72  * The zone abstraction layer is designed to detect the versions of the services dynamically, and step up or down to
73  * match those reported versions. In order to do that, we need to know before hand what region we are accessing (since
74  * the supported versions may be different by regions). We will need to authenticate to the identity service in order to
75  * do this, plus we have to duplicate the code supporting proxies and trusted hosts that exists in the abstraction
76  * layer, but that cant be helped.
77  * </p>
78  * <p>
79  * What we do to circumvent this is connect to the provider using the lowest supported identity api, and read the entire
80  * service catalog into this object. Then, we parse the vm URL to extract the host and port and match that to the
81  * compute services defined in the catalog. When we find a compute service that has the same host name and port,
82  * whatever region that service is supporting is the region for that server.
83  * </p>
84  * <p>
85  * While we really only need to do this for compute nodes, there is no telling what other situations may arise where the
86  * full service catalog may be needed. Also, there is very little additional cost (additional RAM) associated with
87  * caching the full service catalog since there is no way to list only a portion of it.
88  * </p>
89  */
90 public abstract class ServiceCatalog {
91     /**
92      * The openstack connector version to use
93      */
94     public static final String CLIENT_CONNECTOR_CLASS = "com.woorea.openstack.connector.JaxRs20Connector";
95
96     /**
97      * The service name for the compute service endpoint
98      */
99     public static final String COMPUTE_SERVICE = "compute"; //$NON-NLS-1$
100
101     /**
102      * The default domain for authentication
103      */
104     public static final String DEFAULT_DOMAIN = "Default";
105     /**
106      * The service name for the identity service endpoint
107      */
108     public static final String IDENTITY_SERVICE = "identity"; //$NON-NLS-1$
109
110     /**
111      * The service name for the compute service endpoint
112      */
113     public static final String IMAGE_SERVICE = "image"; //$NON-NLS-1$
114
115     /**
116      * The service name for the metering service endpoint
117      */
118     public static final String METERING_SERVICE = "metering"; //$NON-NLS-1$
119
120     /**
121      * The service name for the network service endpoint
122      */
123     public static final String NETWORK_SERVICE = "network"; //$NON-NLS-1$
124
125     /**
126      * The service name for the persistent object service endpoint
127      */
128     public static final String OBJECT_SERVICE = "object-store"; //$NON-NLS-1$
129
130     /**
131      * The service name for the orchestration service endpoint
132      */
133     public static final String ORCHESTRATION_SERVICE = "orchestration"; //$NON-NLS-1$
134
135     /**
136      * The service name for the volume service endpoint
137      */
138     public static final String VOLUME_SERVICE = "volume"; //$NON-NLS-1$
139
140     /**
141      * The logger to be used
142      */
143     protected static final EELFLogger logger = EELFManager.getInstance().getLogger(ServiceCatalog.class);
144
145     /**
146      * The password for authentication
147      */
148     protected String credential;
149
150     /**
151      * The domain for authentication
152      */
153     protected String domain;
154     /**
155      * The time (local) that the token expires and we need to re-authenticate
156      */
157     protected long expiresLocal;
158
159     /**
160      * The url of the identity service
161      */
162     protected String identityURL;
163
164     /**
165      * The user id for authentication
166      */
167     protected String principal;
168
169     /**
170      * The project or tenant identifier
171      */
172     protected String projectIdentifier;
173
174     /**
175      * Properties for proxy information
176      */
177     protected Properties properties;
178
179     /**
180      * The set of all regions that have been defined
181      */
182     protected Set<String> regions;
183
184     /**
185      * The read/write lock used to protect the cache contents
186      */
187     protected ReadWriteLock rwLock;
188
189     /**
190      * Create the ServiceCatalog cache
191      * 
192      * @param identityURL The identity service URL to connect to
193      * @param tenantIdentifier The name or id of the tenant to authenticate with. If the ID is a UUID format
194      *        (32-character hexadecimal string), then the authentication is done using the tenant ID, otherwise it is
195      *        done using the name.
196      * @param principal The user id to authenticate to the provider
197      * @param credential The password to authenticate to the provider
198      * @param properties Additional properties used to configure the connection, such as proxy and trusted hosts lists
199      * @throws ZoneException
200      * @throws ClassNotFoundException
201      * @throws IllegalAccessException
202      * @throws InstantiationException
203      */
204     public ServiceCatalog(String identityURL, String projectIdentifier, String principal, String credential,
205             String domain, Properties properties) {
206         this.identityURL = identityURL;
207         this.projectIdentifier = projectIdentifier;
208         this.principal = principal;
209         this.credential = credential;
210         this.domain = domain;
211         this.properties = properties;
212         rwLock = new ReentrantReadWriteLock();
213         regions = new HashSet<>();
214     }
215
216     /**
217      * Returns the list of service endpoints for the published service type
218      * 
219      * @param serviceType The service type to obtain the endpoints for
220      * @return The list of endpoints for the service type, or null if none exist
221      */
222     public abstract List<?> getEndpoints(String serviceType);
223
224     /**
225      * @return The project or tenant id
226      */
227     public abstract String getProjectId();
228
229     /**
230      * @return The project or tenant name
231      */
232     public abstract String getProjectName();
233
234     /**
235      * @return The set of all regions that are defined
236      */
237     public Set<String> getRegions() {
238         Lock readLock = rwLock.readLock();
239         readLock.lock();
240         try {
241             return regions;
242         } finally {
243             readLock.unlock();
244         }
245     }
246
247     /**
248      * @return A list of service types that are published
249      */
250     public abstract List<String> getServiceTypes();
251
252     /**
253      * This method accepts a fully qualified compute node URL and uses that to determine which region of the provider
254      * hosts that compute node.
255      *
256      * @param url The parsed URL of the compute node
257      * @return The region name, or null if no region of this tenant hosts that compute node.
258      */
259     public abstract String getVMRegion(VMURL url);
260
261     /**
262      * Returns an indication if the specified service type is published by this provider
263      * 
264      * @param serviceType The service type to check for
265      * @return True if a service of that type is published
266      */
267     public abstract boolean isServicePublished(String serviceType);
268
269     /**
270      * Load the Service Catalog from the specified provider
271      * 
272      * @throws ZoneException
273      */
274     public abstract void init() throws ZoneException;
275
276     /**
277      * This method is used to provide a diagnostic listing of the service catalog
278      * 
279      * @see java.lang.Object#toString()
280      */
281     @Override
282     public abstract String toString();
283
284     /**
285      * Initializes the request state for the current requested service.
286      * <p>
287      * This method is used to track requests made to the various service implementations and to provide additional
288      * information for diagnostic purposes. The <code>RequestState</code> class stores the state in thread-local storage
289      * and is available to all code on that thread.
290      * </p>
291      * <p>
292      * This method first obtains the stack trace and scans the stack backward for the call to this method. It then backs
293      * up one more call and assumes that method is the request that we are "tracking".
294      * </p>
295      * 
296      * @param states A variable argument list of additional state values that the caller wants to add to the request
297      *        state thread-local object to track the context.
298      */
299     protected void trackRequest(State... states) {
300         RequestState.clear();
301
302         for (State state : states) {
303             RequestState.put(state.getName(), state.getValue());
304         }
305
306         Thread currentThread = Thread.currentThread();
307         StackTraceElement[] stack = currentThread.getStackTrace();
308         if (stack != null && stack.length > 0) {
309             int index = 0;
310             StackTraceElement element = null;
311             for (; index < stack.length; index++) {
312                 element = stack[index];
313                 if ("trackRequest".equals(element.getMethodName())) { //$NON-NLS-1$
314                     break;
315                 }
316             }
317             index++;
318
319             if (index < stack.length) {
320                 element = stack[index];
321                 RequestState.put(RequestState.METHOD, element.getMethodName());
322                 RequestState.put(RequestState.CLASS, element.getClassName());
323                 RequestState.put(RequestState.LINE_NUMBER, Integer.toString(element.getLineNumber()));
324                 RequestState.put(RequestState.THREAD, currentThread.getName());
325                 // RequestState.put(RequestState.PROVIDER, context.getProvider().getName());
326                 // RequestState.put(RequestState.TENANT, context.getTenantName());
327                 // RequestState.put(RequestState.PRINCIPAL, context.getPrincipal());
328             }
329         }
330     }
331 }