Fix sonar issues for APPC
[appc.git] / appc-adapters / appc-iaas-adapter / appc-iaas-adapter-bundle / src / main / java / org / onap / 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.onap.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 org.onap.appc.Constants;
32 import org.onap.appc.configuration.Configuration;
33 import org.onap.appc.configuration.ConfigurationFactory;
34 import org.onap.appc.i18n.Msg;
35 import org.onap.appc.pool.Allocator;
36 import org.onap.appc.pool.Destructor;
37 import org.onap.appc.pool.Pool;
38 import org.onap.appc.pool.PoolSpecificationException;
39 import com.att.cdp.exceptions.ContextConnectionException;
40 import com.att.cdp.exceptions.ZoneException;
41 import com.att.cdp.zones.Context;
42 import com.att.cdp.zones.ContextFactory;
43 import com.att.cdp.zones.Provider;
44 import com.att.eelf.configuration.EELFLogger;
45 import com.att.eelf.configuration.EELFManager;
46 // import com.sun.jersey.api.client.ClientHandlerException;
47
48 /**
49  * This class maintains a cache of tenants within a specific provider.
50  * <p>
51  * Providers may be multi-tenant, such as OpenStack, where the available services and resources vary from one tenant to
52  * another. Therefore, the provider cache maintains a cache of tenants and the service catalogs for each, as well as the
53  * credentials used to access the tenants, and a pool of Context objects for each tenant. The context pool allows use of
54  * the CDP abstraction layer to access the services of the provider within the specific tenant.
55  * </p>
56  */
57 public class TenantCache implements Allocator<Context>, Destructor<Context> {
58
59     public static final String POOL_PROVIDER_NAME = "pool.provider.name";
60     public static final String POOL_TENANT_NAME = "pool.tenant.name";
61     // public static final String CLIENT_CONNECTOR_CLASS =
62     // "com.woorea.openstack.connector.JerseyConnector";
63     public static final String CLIENT_CONNECTOR_CLASS = "com.woorea.openstack.connector.JaxRs20Connector";
64     /**
65      * The domain to use to authenticate
66      */
67     private String domain;
68
69     /**
70      * The provider we are part of
71      */
72     private ProviderCache provider;
73
74     /**
75      * The password used to authenticate
76      */
77     private String password;
78
79     /**
80      * The context pools by region used to access this tenant
81      */
82     private Map<String /* region */, Pool<Context>> pools = new HashMap<>();
83
84     /**
85      * The tenant id
86      */
87     private String tenantId;
88
89     /**
90      * The tenant name
91      */
92     private String tenantName;
93
94     /**
95      * The user id used to authenticate
96      */
97     private String userid;
98
99     /**
100      * The configuration of this adapter
101      */
102     private Configuration configuration;
103
104     /**
105      * The service catalog for this provider
106      */
107     private ServiceCatalog catalog;
108
109     /**
110      * Set to true when the cache has been initialized
111      */
112     private boolean initialized;
113
114     /**
115      * The logger to use
116      */
117     private EELFLogger logger;
118
119     /**
120      * Construct the cache of tenants for the specified provider
121      *
122      * @param provider 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         catalog = ServiceCatalogFactory.getServiceCatalog(url, tenant, userid, password, domain, properties);
169
170         if (catalog == null) {
171             logger.error(Msg.IAAS_UNSUPPORTED_IDENTITY_SERVICE, url);
172             return;
173         }
174
175         int attempt = 1;
176         while (attempt <= limit) {
177             try {
178                 catalog.init();
179                 tenantId = catalog.getProjectId();
180                 tenantName = catalog.getProjectName();
181
182                 for (String region : catalog.getRegions()) {
183                     try {
184                         Pool<Context> pool = new Pool<>(min, max);
185                         pool.setProperty(ContextFactory.PROPERTY_IDENTITY_URL, url);
186                         pool.setProperty(ContextFactory.PROPERTY_TENANT, tenantName);
187                         pool.setProperty(ContextFactory.PROPERTY_CLIENT_CONNECTOR_CLASS, CLIENT_CONNECTOR_CLASS);
188                         pool.setProperty(ContextFactory.PROPERTY_RETRY_DELAY,
189                                 configuration.getProperty(Constants.PROPERTY_RETRY_DELAY));
190                         pool.setProperty(ContextFactory.PROPERTY_RETRY_LIMIT,
191                                 configuration.getProperty(Constants.PROPERTY_RETRY_LIMIT));
192                         pool.setProperty(ContextFactory.PROPERTY_REGION, region);
193                         if (properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS) != null) {
194                             pool.setProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS,
195                                     properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS));
196                         }
197                         pool.setAllocator(this);
198                         pool.setDestructor(this);
199                         pools.put(region, pool);
200                         logger.debug(String.format("Put pool for region %s", region));
201                     } catch (PoolSpecificationException e) {
202                         logger.error("Error creating pool", e);
203                         e.printStackTrace();
204                     }
205                 }
206                 initialized = true;
207                 break;
208             } catch (ContextConnectionException e) {
209                 attempt++;
210                 if (attempt <= limit) {
211                     logger.error(Msg.CONNECTION_FAILED_RETRY, provider.getProviderName(), url, tenantName, tenantId,
212                             e.getMessage(), Integer.toString(delay), Integer.toString(attempt),
213                             Integer.toString(limit));
214
215                     try {
216                         Thread.sleep(delay * 1000L);
217                     } catch (InterruptedException ie) {
218                         // ignore
219                     }
220                 }
221             } catch (ZoneException e) {
222                 logger.error(e.getMessage());
223                 break;
224             }
225         }
226
227         if (!initialized) {
228             logger.error(Msg.CONNECTION_FAILED, provider.getProviderName(), url);
229         }
230     }
231
232     /**
233      * This method accepts a fully qualified compute node URL and uses that to determine which region of the provider
234      * hosts that compute node.
235      *
236      * @param url The parsed URL of the compute node
237      * @return The region name, or null if no region of this tenant hosts that compute node.
238      */
239     public String determineRegion(VMURL url) {
240         logger.debug(String.format("Attempting to determine VM region for %s", url));
241         String region = catalog.getVMRegion(url);
242         logger.debug(String.format("Region for %s is %s", url, region));
243         return region;
244     }
245
246     /**
247      * @return the value of the domain
248      */
249     public String getDomain() {
250         return domain;
251     }
252
253     /**
254      * @param domain the value for domain
255      */
256     public void setDomain(String domain) {
257         this.domain = domain;
258     }
259
260     /**
261      * @return the value of provider
262      */
263     public ProviderCache getProvider() {
264         return provider;
265     }
266
267     /**
268      * @param provider the value for provider
269      */
270     public void setProvider(ProviderCache provider) {
271         this.provider = provider;
272     }
273
274     /**
275      * @return the value of password
276      */
277     public String getPassword() {
278         return password;
279     }
280
281     /**
282      * @param password the value for password
283      */
284     public void setPassword(String password) {
285         this.password = password;
286     }
287
288     /**
289      * @return the value of tenantId
290      */
291     public String getTenantId() {
292         return tenantId;
293     }
294
295     /**
296      * @param tenantId the value for tenantId
297      */
298     public void setTenantId(String tenantId) {
299         this.tenantId = tenantId;
300     }
301
302     /**
303      * @return the value of tenantName
304      */
305     public String getTenantName() {
306         return tenantName;
307     }
308
309     /**
310      * @param tenantName the value for tenantName
311      */
312     public void setTenantName(String tenantName) {
313         this.tenantName = tenantName;
314     }
315
316     /**
317      * @return the value of userid
318      */
319     public String getUserid() {
320         return userid;
321     }
322
323     /**
324      * @param userid the value for userid
325      */
326     public void setUserid(String userid) {
327         this.userid = userid;
328     }
329
330     /**
331      * @return the value of pools
332      */
333     public Map<String, Pool<Context>> getPools() {
334         return pools;
335     }
336
337     /**
338      * @see org.onap.appc.pool.Allocator#allocate(org.onap.appc.pool.Pool)
339      */
340     @SuppressWarnings("unchecked")
341     @Override
342     public Context allocate(Pool<Context> pool) {
343         logger.debug("Allocationg context for pool");
344         Class<? extends Provider> providerClass;
345         try {
346             providerClass = (Class<? extends Provider>) Class.forName("com.att.cdp.openstack.OpenStackProvider");
347             // String providerType = provider.getProviderType();
348
349             // Context context = ContextFactory.getContext(providerType, pool.getProperties());
350             Context context = ContextFactory.getContext(providerClass, pool.getProperties());
351             context.login(userid, password);
352             return context;
353         } catch (IllegalStateException | IllegalArgumentException | ZoneException | ClassNotFoundException e) {
354             logger.debug("Failed to allocate context for pool", e);
355             e.printStackTrace();
356         }
357         return null;
358     }
359
360     /**
361      * @see org.onap.appc.pool.Destructor#destroy(java.lang.Object, org.onap.appc.pool.Pool)
362      */
363     @Override
364     public void destroy(Context context, Pool<Context> pool) {
365         try {
366             context.close();
367         } catch (IOException e) {
368             e.printStackTrace();
369         }
370     }
371
372     /**
373      * @return the service catalog for this provider
374      */
375     public ServiceCatalog getServiceCatalog() {
376         return catalog;
377     }
378 }