2  * ============LICENSE_START=======================================================
 
   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
 
  13  *      http://www.apache.org/licenses/LICENSE-2.0
 
  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.
 
  21  * ============LICENSE_END=========================================================
 
  24 package org.onap.appc.adapter.iaas.impl;
 
  26 import com.att.cdp.exceptions.ContextConnectionException;
 
  27 import com.att.cdp.exceptions.ZoneException;
 
  28 import com.att.cdp.zones.Context;
 
  29 import com.att.cdp.zones.ContextFactory;
 
  30 import com.att.cdp.zones.Provider;
 
  31 import com.att.eelf.configuration.EELFLogger;
 
  32 import com.att.eelf.configuration.EELFManager;
 
  33 import java.io.IOException;
 
  34 import java.util.HashMap;
 
  36 import java.util.Properties;
 
  37 import org.onap.appc.Constants;
 
  38 import org.onap.appc.configuration.Configuration;
 
  39 import org.onap.appc.configuration.ConfigurationFactory;
 
  40 import org.onap.appc.i18n.Msg;
 
  41 import org.onap.appc.pool.Allocator;
 
  42 import org.onap.appc.pool.Destructor;
 
  43 import org.onap.appc.pool.Pool;
 
  44 import org.onap.appc.pool.PoolSpecificationException;
 
  47  * This class maintains a cache of tenants within a specific provider.
 
  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.
 
  55 public class TenantCache implements Allocator<Context>, Destructor<Context> {
 
  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";
 
  61      * The domain to use to authenticate
 
  63     private String domain;
 
  66      * The provider we are part of
 
  68     private ProviderCache provider;
 
  71      * The password used to authenticate
 
  73     private String password;
 
  76      * The context pools by region used to access this tenant
 
  78     private Map<String /* region */, Pool<Context>> pools = new HashMap<>();
 
  83     private String tenantId;
 
  88     private String tenantName;
 
  91      * The user id used to authenticate
 
  93     private String userid;
 
  96      * The configuration of this adapter
 
  98     private Configuration configuration;
 
 101      * The service catalog for this provider
 
 103     private ServiceCatalog catalog;
 
 106      * Set to true when the cache has been initialized
 
 108     private boolean initialized;
 
 113     private EELFLogger logger;
 
 116      * Construct the cache of tenants for the specified provider
 
 118      * @param provider The provider
 
 120     public TenantCache(ProviderCache provider) {
 
 121         configuration = ConfigurationFactory.getConfiguration();
 
 122         logger = EELFManager.getInstance().getLogger(getClass());
 
 123         this.provider = provider;
 
 124         configuration = ConfigurationFactory.getConfiguration();
 
 128      * @return True when the cache has been initialized. A tenant cache is initialized when the service catalog for the
 
 129      *         tenant on the specified provider has been loaded and processed.
 
 131     public boolean isInitialized() {
 
 136      * Initializes the tenant cache.
 
 138      * This method authenticates to the provider and obtains the service catalog. For the service catalog we can
 
 139      * determine all supported regions for this provider, as well as all published services and their endpoints. We will
 
 140      * cache and maintain a copy of the service catalog for later queries.
 
 143      * Once the catalog has been obtained, we create a context pool for each region defined. The context allows access
 
 144      * to services of a single region only, so we need a separate context by region. It is possible to operate on
 
 145      * resources that span regions, but to do so will require acquiring a context for each region of interest.
 
 148      * The context pool maintains the reusable context objects and allocates them as needed. This class is registered as
 
 149      * the allocator and destructor for the pool, so that we can create a new context when needed, and close it when no
 
 153     public void initialize() {
 
 154         logger.debug("Initializing TenantCache");
 
 156         int min = configuration.getIntegerProperty(Constants.PROPERTY_MIN_POOL_SIZE);
 
 157         int max = configuration.getIntegerProperty(Constants.PROPERTY_MAX_POOL_SIZE);
 
 158         int delay = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_DELAY);
 
 159         int limit = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_LIMIT);
 
 161         String url = provider.getIdentityURL();
 
 162         String tenant = tenantName == null ? tenantId : tenantName;
 
 163         Properties properties = configuration.getProperties();
 
 164         catalog = ServiceCatalogFactory.getServiceCatalog(url, tenant, userid, password, domain, properties);
 
 166         if (catalog == null) {
 
 167             logger.error(Msg.IAAS_UNSUPPORTED_IDENTITY_SERVICE, url);
 
 172         while (attempt <= limit) {
 
 175                 tenantId = catalog.getProjectId();
 
 176                 tenantName = catalog.getProjectName();
 
 177                 createPools(min, max, url, properties);
 
 180             } catch (ContextConnectionException e) {
 
 181                 if (++attempt <= limit) {
 
 182                     logger.error(Msg.CONNECTION_FAILED_RETRY, provider.getProviderName(), url, tenantName, tenantId,
 
 183                             e.getMessage(), Integer.toString(delay), Integer.toString(attempt),
 
 184                             Integer.toString(limit));
 
 187             } catch (ZoneException e) {
 
 188                 logger.error("An error occurred when initializing cache", e);
 
 194             logger.error(Msg.CONNECTION_FAILED, provider.getProviderName(), url);
 
 198     private void createPools(int min, int max, String url, Properties properties) {
 
 199         for (String region : catalog.getRegions()) {
 
 201                 Pool<Context> pool = new Pool<>(min, max);
 
 202                 pool.setProperty(ContextFactory.PROPERTY_IDENTITY_URL, url);
 
 203                 pool.setProperty(ContextFactory.PROPERTY_TENANT, tenantName);
 
 204                 pool.setProperty(ContextFactory.PROPERTY_CLIENT_CONNECTOR_CLASS, CLIENT_CONNECTOR_CLASS);
 
 205                 pool.setProperty(ContextFactory.PROPERTY_RETRY_DELAY,
 
 206                         configuration.getProperty(Constants.PROPERTY_RETRY_DELAY));
 
 207                 pool.setProperty(ContextFactory.PROPERTY_RETRY_LIMIT,
 
 208                         configuration.getProperty(Constants.PROPERTY_RETRY_LIMIT));
 
 209                 pool.setProperty(ContextFactory.PROPERTY_REGION, region);
 
 210                 if (properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS) != null) {
 
 211                     pool.setProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS,
 
 212                             properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS));
 
 214                 pool.setAllocator(this);
 
 215                 pool.setDestructor(this);
 
 216                 pools.put(region, pool);
 
 217                 logger.debug(String.format("Put pool for region %s", region));
 
 218             } catch (PoolSpecificationException e) {
 
 219                 logger.error("Error creating pool", e);
 
 224     private void sleep(int delay) {
 
 226             Thread.sleep(delay * 1000L);
 
 227         } catch (InterruptedException ie) {
 
 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.
 
 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.
 
 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));
 
 247      * @return the value of the domain
 
 249     public String getDomain() {
 
 254      * @param domain the value for domain
 
 256     public void setDomain(String domain) {
 
 257         this.domain = domain;
 
 261      * @return the value of provider
 
 263     public ProviderCache getProvider() {
 
 268      * @param provider the value for provider
 
 270     public void setProvider(ProviderCache provider) {
 
 271         this.provider = provider;
 
 275      * @return the value of password
 
 277     public String getPassword() {
 
 282      * @param password the value for password
 
 284     public void setPassword(String password) {
 
 285         this.password = password;
 
 289      * @return the value of tenantId
 
 291     public String getTenantId() {
 
 296      * @param tenantId the value for tenantId
 
 298     public void setTenantId(String tenantId) {
 
 299         this.tenantId = tenantId;
 
 303      * @return the value of tenantName
 
 305     public String getTenantName() {
 
 310      * @param tenantName the value for tenantName
 
 312     public void setTenantName(String tenantName) {
 
 313         this.tenantName = tenantName;
 
 317      * @return the value of userid
 
 319     public String getUserid() {
 
 324      * @param userid the value for userid
 
 326     public void setUserid(String userid) {
 
 327         this.userid = userid;
 
 331      * @return the value of pools
 
 333     public Map<String, Pool<Context>> getPools() {
 
 338      * @see org.onap.appc.pool.Allocator#allocate(org.onap.appc.pool.Pool)
 
 340     @SuppressWarnings("unchecked")
 
 342     public Context allocate(Pool<Context> pool) {
 
 343         logger.debug("Allocationg context for pool");
 
 344         Class<? extends Provider> providerClass;
 
 346             providerClass = (Class<? extends Provider>) Class.forName("com.att.cdp.openstack.OpenStackProvider");
 
 347             Context context = ContextFactory.getContext(providerClass, pool.getProperties());
 
 348             context.login(userid, password);
 
 350         } catch (IllegalStateException | IllegalArgumentException | ZoneException | ClassNotFoundException e) {
 
 351             logger.debug("Failed to allocate context for pool", e);
 
 357      * @see org.onap.appc.pool.Destructor#destroy(java.lang.Object, org.onap.appc.pool.Pool)
 
 360     public void destroy(Context context, Pool<Context> pool) {
 
 363         } catch (IOException e) {
 
 364             logger.error("An error occurred when destroying cache", e);
 
 369      * @return the service catalog for this provider
 
 371     public ServiceCatalog getServiceCatalog() {