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