Applying license changes to all files
[appc.git] / appc-adapters / appc-iaas-adapter / appc-iaas-adapter-bundle / src / main / java / org / openecomp / appc / adapter / iaas / impl / TenantCache.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.io.IOException;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Properties;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 import org.openecomp.appc.Constants;
35 import org.openecomp.appc.configuration.Configuration;
36 import org.openecomp.appc.configuration.ConfigurationFactory;
37 import org.openecomp.appc.i18n.Msg;
38 import org.openecomp.appc.pool.Allocator;
39 import org.openecomp.appc.pool.Destructor;
40 import org.openecomp.appc.pool.Pool;
41 import org.openecomp.appc.pool.PoolSpecificationException;
42 import com.att.cdp.exceptions.ContextConnectionException;
43 import com.att.cdp.exceptions.ZoneException;
44 import com.att.cdp.zones.Context;
45 import com.att.cdp.zones.ContextFactory;
46 import com.att.cdp.zones.Provider;
47 import com.att.eelf.configuration.EELFLogger;
48 import com.att.eelf.configuration.EELFManager;
49 import com.woorea.openstack.connector.JaxRs20Connector;
50 //import com.sun.jersey.api.client.ClientHandlerException;
51 import com.woorea.openstack.keystone.model.Access.Service.Endpoint;
52
53 /**
54  * This class maintains a cache of tenants within a specific provider.
55  * <p>
56  * Providers may be multi-tenant, such as OpenStack, where the available services and resources vary from one tenant to
57  * another. Therefore, the provider cache maintains a cache of tenants and the service catalogs for each, as well as the
58  * credentials used to access the tenants, and a pool of Context objects for each tenant. The context pool allows use of
59  * the CDP abstraction layer to access the services of the provider within the specific tenant.
60  * </p>
61  */
62 public class TenantCache implements Allocator<Context>, Destructor<Context> {
63
64     public static final String POOL_PROVIDER_NAME = "pool.provider.name";
65     public static final String POOL_TENANT_NAME = "pool.tenant.name";
66     //public static final String CLIENT_CONNECTOR_CLASS = "com.woorea.openstack.connector.JerseyConnector";
67     public static final String CLIENT_CONNECTOR_CLASS = "com.woorea.openstack.connector.JaxRs20Connector";
68     /**
69      * The provider we are part of
70      */
71     private ProviderCache provider;
72
73     /**
74      * The password used to authenticate
75      */
76     private String password;
77
78     /**
79      * The context pools by region used to access this tenant
80      */
81     private Map<String /* region */, Pool<Context>> pools = new HashMap<>();
82
83     /**
84      * The tenant id
85      */
86     private String tenantId;
87
88     /**
89      * The tenant name
90      */
91     private String tenantName;
92
93     /**
94      * The user id used to authenticate
95      */
96     private String userid;
97
98     /**
99      * The configuration of this adapter
100      */
101     private Configuration configuration;
102
103     /**
104      * The service catalog for this provider
105      */
106     private ServiceCatalog catalog;
107
108     /**
109      * Set to true when the cache has been initialized
110      */
111     private boolean initialized;
112
113     /**
114      * The logger to use
115      */
116     private EELFLogger logger;
117
118     /**
119      * Construct the cache of tenants for the specified provider
120      *
121      * @param provider
122      *            The provider
123      */
124     public TenantCache(ProviderCache provider) {
125         configuration = ConfigurationFactory.getConfiguration();
126         logger = EELFManager.getInstance().getLogger(getClass());
127         this.provider = provider;
128         configuration = ConfigurationFactory.getConfiguration();
129     }
130
131     /**
132      * @return True when the cache has been initialized. A tenant cache is initialized when the service catalog for the
133      *         tenant on the specified provider has been loaded and processed.
134      */
135     public boolean isInitialized() {
136         return initialized;
137     }
138
139     /**
140      * Initializes the tenant cache.
141      * <p>
142      * This method authenticates to the provider and obtains the service catalog. For the service catalog we can
143      * determine all supported regions for this provider, as well as all published services and their endpoints. We will
144      * cache and maintain a copy of the service catalog for later queries.
145      * </p>
146      * <p>
147      * Once the catalog has been obtained, we create a context pool for each region defined. The context allows access
148      * to services of a single region only, so we need a separate context by region. It is possible to operate on
149      * resources that span regions, but to do so will require acquiring a context for each region of interest.
150      * </p>
151      * <p>
152      * The context pool maintains the reusable context objects and allocates them as needed. This class is registered as
153      * the allocator and destructor for the pool, so that we can create a new context when needed, and close it when no
154      * longer used.
155      * </p>
156      */
157     public void initialize() {
158         logger.debug("Initializing TenantCache");
159
160         int min = configuration.getIntegerProperty(Constants.PROPERTY_MIN_POOL_SIZE);
161         int max = configuration.getIntegerProperty(Constants.PROPERTY_MAX_POOL_SIZE);
162         int delay = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_DELAY);
163         int limit = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_LIMIT);
164
165         String url = provider.getIdentityURL();
166         String tenant = tenantName == null ? tenantId : tenantName;
167         Properties properties = configuration.getProperties();
168
169         int attempt = 1;
170         while (attempt <= limit) {
171             try {
172                 catalog = new ServiceCatalog(url, tenant, userid, password, properties);
173                 tenantId = catalog.getTenantId();
174                 tenantName = catalog.getTenantName();
175
176                 for (String region : catalog.getRegions()) {
177                     try {
178                         Pool<Context> pool = new Pool<>(min, max);
179                         pool.setProperty(ContextFactory.PROPERTY_IDENTITY_URL, url);
180                         pool.setProperty(ContextFactory.PROPERTY_TENANT, tenantName);
181                         pool.setProperty(ContextFactory.PROPERTY_CLIENT_CONNECTOR_CLASS, CLIENT_CONNECTOR_CLASS);
182                         pool.setProperty(ContextFactory.PROPERTY_RETRY_DELAY,
183                             configuration.getProperty(Constants.PROPERTY_RETRY_DELAY));
184                         pool.setProperty(ContextFactory.PROPERTY_RETRY_LIMIT,
185                             configuration.getProperty(Constants.PROPERTY_RETRY_LIMIT));
186                         pool.setProperty(ContextFactory.PROPERTY_REGION, region);
187                         if (properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS) != null) {
188                             pool.setProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS,
189                                 properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS));
190                         }
191                         pool.setAllocator(this);
192                         pool.setDestructor(this);
193                         pools.put(region, pool);
194                         logger.debug(String.format("Put pool for region %s", region));
195                     } catch (PoolSpecificationException e) {
196                         logger.error("Error creating pool", e);
197                         e.printStackTrace();
198                     }
199                 }
200                 initialized = true;
201                 break;
202             } catch (ContextConnectionException e) {
203                 attempt++;
204                 if (attempt <= limit) {
205                     logger.error(Msg.CONNECTION_FAILED_RETRY, provider.getProviderName(), url, tenantName, tenantId, e.getMessage(), Integer.toString(delay), Integer.toString(attempt),
206                             Integer.toString(limit));
207
208                     try {
209                         Thread.sleep(delay * 1000L);
210                     } catch (InterruptedException ie) {
211                         // ignore
212                     }
213                 }
214             } catch ( ZoneException e) {
215                 logger.error(e.getMessage());
216                 break;
217             }
218         }
219
220         if (!initialized) {
221             logger.error(Msg.CONNECTION_FAILED, provider.getProviderName(), url);
222         }
223     }
224
225     /**
226      * This method accepts a fully qualified compute node URL and uses that to determine which region of the provider
227      * hosts that compute node.
228      *
229      * @param url
230      *            The parsed URL of the compute node
231      * @return The region name, or null if no region of this tenant hosts that compute node.
232      */
233     public String determineRegion(VMURL url) {
234         logger.debug(String.format("Attempting to determine VM region for %s", url));
235         String region = null;
236         Pattern urlPattern = Pattern.compile("[^:]+://([^:/]+)(?::([0-9]+)).*");
237
238         if (url != null) {
239             for (Endpoint endpoint : catalog.getEndpoints(ServiceCatalog.COMPUTE_SERVICE)) {
240                 String endpointUrl = endpoint.getPublicURL();
241                 Matcher matcher = urlPattern.matcher(endpointUrl);
242                 if (matcher.matches()) {
243                     if (url.getHost().equals(matcher.group(1))) {
244                         if (url.getPort() != null) {
245                             if (!url.getPort().equals(matcher.group(2))) {
246                                 continue;
247                             }
248                         }
249
250                         region = endpoint.getRegion();
251                         break;
252                     }
253                 }
254             }
255         }
256         logger.debug(String.format("Region for %s is %s", url, region));
257         return region;
258     }
259
260     /**
261      * @return the value of provider
262      */
263     public ProviderCache getProvider() {
264         return provider;
265     }
266
267     /**
268      * @param provider
269      *            the value for provider
270      */
271     public void setProvider(ProviderCache provider) {
272         this.provider = provider;
273     }
274
275     /**
276      * @return the value of password
277      */
278     public String getPassword() {
279         return password;
280     }
281
282     /**
283      * @param password
284      *            the value for password
285      */
286     public void setPassword(String password) {
287         this.password = password;
288     }
289
290     /**
291      * @return the value of tenantId
292      */
293     public String getTenantId() {
294         return tenantId;
295     }
296
297     /**
298      * @param tenantId
299      *            the value for tenantId
300      */
301     public void setTenantId(String tenantId) {
302         this.tenantId = tenantId;
303     }
304
305     /**
306      * @return the value of tenantName
307      */
308     public String getTenantName() {
309         return tenantName;
310     }
311
312     /**
313      * @param tenantName
314      *            the value for tenantName
315      */
316     public void setTenantName(String tenantName) {
317         this.tenantName = tenantName;
318     }
319
320     /**
321      * @return the value of userid
322      */
323     public String getUserid() {
324         return userid;
325     }
326
327     /**
328      * @param userid
329      *            the value for userid
330      */
331     public void setUserid(String userid) {
332         this.userid = userid;
333     }
334
335     /**
336      * @return the value of pools
337      */
338     public Map<String, Pool<Context>> getPools() {
339         return pools;
340     }
341
342     /**
343      * @see org.openecomp.appc.pool.Allocator#allocate(org.openecomp.appc.pool.Pool)
344      */
345     @SuppressWarnings("unchecked")
346     @Override
347     public Context allocate(Pool<Context> pool) {
348         logger.debug("Allocationg context for pool");
349         Class<? extends Provider> providerClass;
350         try {
351             providerClass = (Class<? extends Provider>) Class.forName("com.att.cdp.openstack.OpenStackProvider");
352             // String providerType = provider.getProviderType();
353
354             // Context context = ContextFactory.getContext(providerType, pool.getProperties());
355             Context context = ContextFactory.getContext(providerClass, pool.getProperties());
356             context.login(userid, password);
357             return context;
358         } catch (IllegalStateException | IllegalArgumentException | ZoneException | ClassNotFoundException e) {
359             logger.debug("Failed to allocate context for pool", e);
360             e.printStackTrace();
361         }
362         return null;
363     }
364
365     /**
366      * @see org.openecomp.appc.pool.Destructor#destroy(java.lang.Object, org.openecomp.appc.pool.Pool)
367      */
368     @Override
369     public void destroy(Context context, Pool<Context> pool) {
370         try {
371             context.close();
372         } catch (IOException e) {
373             e.printStackTrace();
374         }
375     }
376
377     /**
378      * @return the service catalog for this provider
379      */
380     public ServiceCatalog getServiceCatalog() {
381         return catalog;
382     }
383 }