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