summaryrefslogtreecommitdiffstats
path: root/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl
diff options
context:
space:
mode:
Diffstat (limited to 'appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl')
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/IdentityURL.java146
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderAdapterImpl.java359
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderCache.java165
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestContext.java249
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestFailedException.java255
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalog.java310
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogFactory.java70
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV2.java380
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV3.java402
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/TenantCache.java382
-rw-r--r--appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/VMURL.java175
11 files changed, 2893 insertions, 0 deletions
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/IdentityURL.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/IdentityURL.java
new file mode 100644
index 000000000..c98edc410
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/IdentityURL.java
@@ -0,0 +1,146 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to parse the VM URL returned from OpenStack and extract all of the constituent parts.
+ */
+public class IdentityURL {
+ /**
+ * The regular expression pattern used to parse the URL. Capturing groups are used to identify and extract the
+ * various component parts of the URL.
+ */
+ private static Pattern pattern = Pattern.compile("(\\p{Alnum}+)://([^/:]+)(?::([0-9]+))?(/.*)?/(v[0-9\\.]+)/?");
+
+ /**
+ * The URL scheme or protocol, such as HTTP or HTTPS
+ */
+ private String scheme;
+
+ /**
+ * The host name or ip address
+ */
+ private String host;
+
+ /**
+ * The path of the service, or null if no path is defined
+ */
+ private String path;
+
+ /**
+ * The port number, or null if no port is defined
+ */
+ private String port;
+
+ /**
+ * The version of the service
+ */
+ private String version;
+
+ /**
+ * A private default constructor prevents instantiation by any method other than the factory method
+ *
+ * @see #parseURL(String)
+ */
+ private IdentityURL() {
+
+ }
+
+ /**
+ * This static method is used to parse the provided server URL string and return a parse results object (VMURL)
+ * which represents the state of the parse.
+ *
+ * @param serverUrl The server URL to be parsed
+ * @return The VMURL parse results object, or null if the URL was not valid or null.
+ */
+ public static IdentityURL parseURL(String identUrl) {
+ IdentityURL obj = null;
+ if (identUrl != null) {
+ Matcher matcher = pattern.matcher(identUrl.trim()); // http://msb.onap.org:80/api/multicloud/v0/cloudowner_region/identity/v3
+ if (matcher.matches()) { // (\\p{Alnum}+)://([^/:]+)(?::([0-9]+))?(/.*)?/(v[0-9\\.]+)/?"
+ obj = new IdentityURL();
+ obj.scheme = matcher.group(1);
+ obj.host = matcher.group(2);
+ obj.port = matcher.group(3);
+ obj.path = matcher.group(4);
+ obj.version = matcher.group(5);
+ }
+ }
+
+ return obj;
+ }
+
+ /**
+ * @return The URL scheme
+ */
+ public String getScheme() {
+ return scheme;
+ }
+
+ /**
+ * @return The URL host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * @return The URL path, or null if no path was defined
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return The URL port, or null if no port was defined
+ */
+ public String getPort() {
+ return port;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+
+ str.append(scheme + "://" + host);
+ if (port != null) {
+ str.append(":" + port);
+ }
+ if (path != null) {
+ str.append(path);
+ }
+ str.append("/" + version);
+
+ return str.toString();
+ }
+
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderAdapterImpl.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderAdapterImpl.java
new file mode 100644
index 000000000..ba6c8c678
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderAdapterImpl.java
@@ -0,0 +1,359 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.appc.adapter.iaas.impl;
+
+import org.onap.appc.Constants;
+import org.onap.appc.adapter.iaas.ProviderAdapter;
+import org.onap.appc.adapter.iaas.provider.operation.api.IProviderOperation;
+import org.onap.appc.adapter.iaas.provider.operation.api.ProviderOperationFactory;
+import org.onap.appc.adapter.iaas.provider.operation.common.constants.Property;
+import org.onap.appc.adapter.iaas.provider.operation.common.enums.Operation;
+import org.onap.appc.adapter.iaas.provider.operation.impl.EvacuateServer;
+import org.onap.appc.configuration.Configuration;
+import org.onap.appc.configuration.ConfigurationFactory;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.util.StructuredPropertyHelper;
+import org.onap.appc.util.StructuredPropertyHelper.Node;
+import com.att.cdp.zones.model.Image;
+import com.att.cdp.zones.model.Server;
+import com.att.cdp.zones.model.Stack;
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * This class implements the {@link ProviderAdapter} interface. This interface defines the behaviors that our service
+ * provides.
+ *
+ * @since Aug 12, 2015
+ * @version $Id$
+ */
+@SuppressWarnings("javadoc")
+public class ProviderAdapterImpl implements ProviderAdapter {
+ /**
+ * The default domain name for authentication
+ */
+ public static final String DEFAULT_DOMAIN_NAME = "Default";
+
+ /**
+ * The logger to be used
+ */
+ private static final EELFLogger logger = EELFManager.getInstance().getLogger(ProviderAdapterImpl.class);
+ /**
+ * A reference to the adapter configuration object.
+ */
+ private Configuration configuration;
+
+ /**
+ * reference to operation factory
+ */
+ ProviderOperationFactory factory = ProviderOperationFactory.getInstance();
+
+ /**
+ * A cache of providers that are predefined.
+ */
+ private Map<String /* provider name */, ProviderCache> providerCache;
+
+ /**
+ * The username, password, and domain to use for dynamically created connections
+ */
+ private static String DEFAULT_USER;
+ private static String DEFAULT_PASS;
+ private static String DEFAULT_DOMAIN;
+
+
+ /**
+ * This default constructor is used as a work around because the activator wasnt getting called
+ */
+ @SuppressWarnings("all")
+ public ProviderAdapterImpl() {
+ initialize();
+
+ }
+
+ /**
+ * This constructor is used primarily in the test cases to bypass initialization of the adapter for isolated,
+ * disconnected testing
+ *
+ * @param initialize True if the adapter is to be initialized, can false if not
+ */
+ @SuppressWarnings("all")
+ public ProviderAdapterImpl(boolean initialize) {
+ configuration = ConfigurationFactory.getConfiguration();
+ if (initialize) {
+ initialize();
+ }
+ }
+
+ /**
+ * @param props not used
+ */
+ public ProviderAdapterImpl(@SuppressWarnings("unused") Properties props) {
+ initialize();
+
+ }
+
+ @Override
+ public Server restartServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.RESTART_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server stopServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.STOP_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server startServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.START_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server rebuildServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.REBUILD_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server terminateServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.TERMINATE_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server evacuateServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.EVACUATE_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ // pass this object's reference to EvacuateServer to allow rebuild after evacuate
+ ((EvacuateServer) op).setProvideAdapterRef(this);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server migrateServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.MIGRATE_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server vmStatuschecker(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.VMSTATUSCHECK_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Stack terminateStack(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.TERMINATE_STACK);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Stack) op.doOperation(params, context);
+ }
+
+ @Override
+ public Stack snapshotStack(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.SNAPSHOT_STACK);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Stack) op.doOperation(params, context);
+ }
+
+ @Override
+ public Stack restoreStack(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.RESTORE_STACK);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Stack) op.doOperation(params, context);
+ }
+
+ @Override
+ public Server lookupServer(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.LOOKUP_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Server) op.doOperation(params, context);
+ }
+
+ @Override
+ public Image createSnapshot(Map<String, String> params, SvcLogicContext context) throws APPCException {
+
+ IProviderOperation op = factory.getOperationObject(Operation.SNAPSHOT_SERVICE);
+ op.setProviderCache(this.providerCache);
+ op.setDefaultPass(DEFAULT_PASS);
+ op.setDefaultUser(DEFAULT_USER);
+ op.setDefaultDomain(DEFAULT_DOMAIN);
+ return (Image) op.doOperation(params, context);
+ }
+
+ /**
+ * Returns the symbolic name of the adapter
+ *
+ * @return The adapter name
+ * @see org.onap.appc.adapter.iaas.ProviderAdapter#getAdapterName()
+ */
+ @Override
+ public String getAdapterName() {
+ return configuration.getProperty(Constants.PROPERTY_ADAPTER_NAME);
+ }
+
+
+ /**
+ * initialize the provider adapter by building the context cache
+ */
+ private void initialize() {
+ configuration = ConfigurationFactory.getConfiguration();
+
+ /*
+ * Initialize the provider cache for all defined providers. The definition of the providers uses a structured
+ * property set, where the names form a hierarchical name space (dotted notation, such as one.two.three). Each
+ * name in the name space can also be serialized by appending a sequence number. All nodes at the same level
+ * with the same serial number are grouped together in the namespace hierarchy. This allows a hierarchical
+ * multi-valued property to be defined, which can then be used to setup the provider and tenant caches. <p> For
+ * example, the following definitions show how the namespace hierarchy is defined for two providers, with two
+ * tenants on the first provider and a single tenant for the second provider. <pre>
+ * provider1.type=OpenStackProvider provider1.name=ILAB provider1.identity=http://provider1:5000/v2.0
+ * provider1.tenant1.name=CDP-ONAP-APPC provider1.tenant1.userid=testUser
+ * provider1.tenant1.password=testPassword provider1.tenant2.name=TEST-TENANT provider1.tenant2.userid=testUser
+ * provider1.tenant2.password=testPassword provider2.type=OpenStackProvider provider2.name=PDK1
+ * provider2.identity=http://provider2:5000/v2.0 provider2.tenant1.name=someName
+ * provider2.tenant1.userid=someUser provider2.tenant1.password=somePassword </pre> </p>
+ */
+ providerCache = new HashMap<>();
+ Properties properties = configuration.getProperties();
+ List<Node> providers = StructuredPropertyHelper.getStructuredProperties(properties, Property.PROVIDER);
+
+ for (Node provider : providers) {
+ ProviderCache cache = new ProviderCache();
+ List<Node> providerNodes = provider.getChildren();
+ for (Node node : providerNodes) {
+ if (node.getName().equals(Property.PROVIDER_TYPE)) {
+ cache.setProviderType(node.getValue());
+ } else if (node.getName().equals(Property.PROVIDER_IDENTITY)) {
+ cache.setIdentityURL(node.getValue());
+ cache.setProviderName(node.getValue());
+ } else if (node.getName().startsWith(Property.PROVIDER_TENANT)) {
+ String tenantName = null;
+ String userId = null;
+ String password = null;
+ // domain is not required so set a default
+ String domain = DEFAULT_DOMAIN_NAME;
+ for (Node node2 : node.getChildren()) {
+ switch (node2.getName()) {
+ case Property.PROVIDER_TENANT_NAME:
+ tenantName = node2.getValue();
+ break;
+ case Property.PROVIDER_TENANT_USERID:
+ userId = node2.getValue();
+ DEFAULT_USER = node2.getValue();
+ break;
+ case Property.PROVIDER_TENANT_PASSWORD:
+ password = node2.getValue();
+ DEFAULT_PASS = node2.getValue();
+ break;
+ case Property.PROVIDER_TENANT_DOMAIN:
+ domain = node2.getValue();
+ DEFAULT_DOMAIN = node2.getValue();
+ break;
+ }
+ }
+
+ cache.addTenant(null, tenantName, userId, password, domain);
+ }
+ }
+
+ /*
+ * Add the provider to the set of providers cached
+ */
+ if (cache.getIdentityURL() != null && cache.getProviderType() != null) {
+ providerCache.put(null, cache);
+ providerCache.put(cache.getIdentityURL(), cache);
+ }
+
+ /*
+ * Now, initialize the cache for the loaded provider
+ */
+ cache.initialize();
+ }
+ }
+
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderCache.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderCache.java
new file mode 100644
index 000000000..5453a9ec1
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ProviderCache.java
@@ -0,0 +1,165 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class maintains a cache of information by provider, where a provider is identified by both a type and an
+ * identity URL used to connect to that provider.
+ * <p>
+ * Providers may be multi-tenant, such as OpenStack, where the available services and resources vary from one tenant to
+ * another. Therefore, the provider cache maintains a cache of tenants and the service catalogs for each, as well as the
+ * credentials used to access the tenants, and a pool of Context objects for each tenant. The context pool allows use of
+ * the CDP abstraction layer to access the services of the provider within the specific tenant.
+ * </p>
+ */
+public class ProviderCache {
+
+ /**
+ * The type of provider (e.g., OpenStackProvider) used to setup the CDP abstraction layer and load the appropriate
+ * support
+ */
+ private String providerType;
+
+ /**
+ * The URL of the provider's identity service or whatever service is used to login and authenticate to the provider
+ */
+ private String identityURL;
+
+ /**
+ * A string used to identify the provider instance
+ */
+ private String providerName;
+
+ /**
+ * The map of tenant cache objects by tenant id
+ */
+ private Map<String /* tenant id */, TenantCache> tenants = new HashMap<String, TenantCache>();
+
+ /**
+ * @return the value of providerType
+ */
+ public String getProviderType() {
+ return providerType;
+ }
+
+ /**
+ * This method is called to initialize the provider cache, set up the context pools for each of the tenants,
+ * discover all of the regions supported on the provider, and load all of the service catalogs for each provider.
+ */
+ public void initialize() {
+ for (Map.Entry<String, TenantCache> entry : tenants.entrySet()) {
+ entry.getValue().initialize();
+ }
+ }
+
+ /**
+ * @param providerType the value for providerType
+ */
+ public void setProviderType(String providerType) {
+ this.providerType = providerType;
+ }
+
+ /**
+ * @return the value of identityURL
+ */
+ public String getIdentityURL() {
+ return identityURL;
+ }
+
+ /**
+ * @param identityURL the value for identityURL
+ */
+ public void setIdentityURL(String identityURL) {
+ this.identityURL = identityURL;
+ }
+
+ /**
+ * @return the value of providerName
+ */
+ public String getProviderName() {
+ return providerName;
+ }
+
+ /**
+ * @param providerName the value for providerName
+ */
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ /**
+ * @return the value of tenants
+ */
+ public Map<String, TenantCache> getTenants() {
+ return tenants;
+ }
+
+ /**
+ * This method is a helper to return a specific TenantCache
+ *
+ * @param tenantId
+ * @return
+ */
+ public TenantCache getTenant(String tenantId) {
+ return tenants.get(tenantId);
+ }
+
+ // Previously there was no way to add additional tenants to the tenant cache
+ /**
+ * This method is used to add a tenant to the provider cache
+ *
+ * @param tenantId
+ * @param UserId
+ * @param password
+ * @return the new initialized TenantCache or null if unsuccessful
+ */
+ public TenantCache addTenant(String tenantId, String tenantName, String userId, String password, String domain) {
+ if (tenantId != null || tenantName != null && userId != null && password != null) {
+ TenantCache tenant = new TenantCache(this);
+ if (tenantId != null) {
+ tenant.setTenantId(tenantId);
+ }
+ if (tenantName != null) {
+ tenant.setTenantName(tenantName);
+ }
+ tenant.setUserid(userId);
+ tenant.setPassword(password);
+ tenant.setDomain(domain);
+
+ if (identityURL != null) {
+ tenant.initialize();
+ }
+
+ if (tenant.isInitialized()) {
+ tenants.put(tenant.getTenantId(), tenant);
+ return tenant;
+ }
+ }
+ return null;
+ }
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestContext.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestContext.java
new file mode 100644
index 000000000..ef237f96d
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestContext.java
@@ -0,0 +1,249 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.appc.adapter.iaas.impl;
+
+import org.onap.appc.Constants;
+import org.onap.appc.configuration.Configuration;
+import org.onap.appc.configuration.ConfigurationFactory;
+import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
+
+/**
+ * This class is used to track and maintain recovery and time-to-live information for a request as it is being
+ * processed.
+ */
+public class RequestContext {
+ /**
+ * The number of seconds of wait time between successive attempts to connect to the provider. This is used to
+ * recover from provider outages or failures. It is not used to recover from logical errors, such as an invalid
+ * request, server not found, etc.
+ */
+ private Integer retryDelay;
+
+ /**
+ * The number of times we will attempt to connect to the provider. This is used to recover from provider outages or
+ * failures. It is not used to recover from logical errors, such as an invalid request, server not found, etc.
+ */
+ private Integer retryLimit;
+
+ /**
+ * The total time, in milliseconds, that the provider can have to process this request. If the accumulated time
+ * exceeds the time to live, then the request is failed with a timeout exception, regardless of the state of the
+ * provider. Note that the caller may supply this as a value in seconds, in which case it must be converted to
+ * milliseconds for the request context.
+ */
+ private Long timeToLive;
+
+ /**
+ * The accumulated time, in milliseconds, that has been used so far to process the request. This is compared to the
+ * time to live each time it is updated. If the accumulated time exceeds the time to live, then the request is
+ * failed with a timeout exception, regardless of the state of the provider.
+ */
+ private long accumulatedTime;
+
+ /**
+ * The total number of retries attempted so far
+ */
+ private int attempt;
+
+ /**
+ * The time when the stopwatch was started
+ */
+ private long startTime = -1;
+
+ /**
+ * The service logic (DG) context from the SLI
+ */
+ private SvcLogicContext svcLogicContext;
+
+ /**
+ * The configuration
+ */
+ private Configuration configuration = ConfigurationFactory.getConfiguration();
+
+ /**
+ * Set to true whenever the retry limit has been exceeded, reset to false when reset() is called.
+ */
+ private boolean retryFailed;
+
+ /**
+ * Creates the request context
+ *
+ * @param context The service logic (SLI) context associated with the current DG
+ */
+ public RequestContext(SvcLogicContext context) {
+ setSvcLogicContext(context);
+ }
+
+ /**
+ * @return The retry delay, in seconds. If zero, then no retry is to be performed
+ */
+ public int getRetryDelay() {
+ if (retryDelay == null) {
+ int value = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_DELAY);
+ retryDelay = Integer.valueOf(value);
+ }
+
+ return retryDelay.intValue();
+ }
+
+ /**
+ * This method is a helper that allows the caller to delay for the retry interval time and not have to handle the
+ * thread interruption, timer handling, etc.
+ */
+ public void delay() {
+ long time = getRetryDelay() * 1000L;
+ long future = System.currentTimeMillis() + time;
+ if (time != 0) {
+ while (System.currentTimeMillis() < future && time > 0) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ /*
+ * This is rare, but it can happen if another thread interrupts us while we are sleeping. In that
+ * case, the thread is resumed before the delay time has actually expired, so re-calculate the
+ * amount of delay time needed and reenter the sleep until we get to the future time.
+ */
+ time = future - System.currentTimeMillis();
+ }
+ }
+ }
+ }
+
+ /**
+ * @return The number of retries that are allowed per connection
+ */
+ public int getRetryLimit() {
+ if (retryLimit == null) {
+ int value = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_LIMIT);
+ retryLimit = Integer.valueOf(value);
+ }
+
+ return retryLimit.intValue();
+ }
+
+ /**
+ * Check and count the connection attempt.
+ *
+ * @return True if the connection should be attempted. False indicates that the number of retries has been exhausted
+ * and it should NOT be attempted.
+ */
+ public boolean attempt() {
+ if (retryFailed || attempt >= getRetryLimit()) {
+ retryFailed = true;
+ return false;
+ }
+ attempt++;
+
+ return true;
+ }
+
+ /**
+ * @return The number of retry attempts so far
+ */
+ public int getAttempts() {
+ return attempt;
+ }
+
+ /**
+ * @return True if the retry limit has been exceeded, false otherwise
+ */
+ public boolean isFailed() {
+ return retryFailed;
+ }
+
+ /**
+ * This method both checks the time to live to see if it has been exceeded and accumulates the total time used so
+ * far.
+ * <p>
+ * Each time this method is called it accumulates the total duration since the last time it was called to the total
+ * time accumulator. It then checks the total time to the time to live and if greater, it returns false. As long as
+ * the total time used is less than or equal to the time to live limit, the method returns true. It is important to
+ * call this method at the very beginning of the process so that all parts of the process are tracked.
+ * </p>
+ *
+ * @return True if the total time to live has not been exceeded. False indicates that the total time to live has
+ * been exceeded and no further processing should be performed.
+ */
+ public boolean isAlive() {
+ long now = System.currentTimeMillis();
+ if (startTime == -1) {
+ startTime = now;
+ return true;
+ }
+ accumulatedTime += (now - startTime);
+ startTime = now;
+ if (accumulatedTime > timeToLive) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return The total amount of time used, in milliseconds.
+ */
+ public long getTotalDuration() {
+ return accumulatedTime;
+ }
+
+ /**
+ * This method is called to reset the retry counters. It has no effect on the time to live accumulator.
+ */
+ public void reset() {
+ attempt = 0;
+ }
+
+ /**
+ * Sets the time to live to the value, expressed in seconds
+ *
+ * @param time The time to live, in seconds
+ */
+ public void setTimeToLiveSeconds(int time) {
+ setTimeToLiveMS(time * 1000L);
+ }
+
+ /**
+ * Sets the time to live to the value, expressed in milliseconds
+ *
+ * @param time The time to live, in milliseconds
+ */
+ public void setTimeToLiveMS(long time) {
+ this.timeToLive = time;
+ }
+
+ /**
+ * @return The service logic context associated with this request
+ */
+ public SvcLogicContext getSvcLogicContext() {
+ return svcLogicContext;
+ }
+
+ /**
+ * @param svcLogicContext The service logic context to be associated with this request
+ */
+ public void setSvcLogicContext(SvcLogicContext svcLogicContext) {
+ this.svcLogicContext = svcLogicContext;
+ }
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestFailedException.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestFailedException.java
new file mode 100644
index 000000000..13df3900c
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/RequestFailedException.java
@@ -0,0 +1,255 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import org.glassfish.grizzly.http.util.HttpStatus;
+import com.att.cdp.zones.model.Server;
+import com.att.cdp.zones.model.Stack;
+
+/**
+ * This class is used to capture the exact cause and point of failure for the processing of a request. It is then used
+ * to encode the reason for the failure, status code, and anything else that needs to be captured and reported for
+ * diagnostic purposes.
+ */
+public class RequestFailedException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The operation that was being requested or performed at the time of the failure.
+ */
+ private String operation;
+
+ /**
+ * A message that details the reason for the failure
+ */
+ private String reason;
+
+ /**
+ * The server that was being operated upon
+ */
+ private Server server;
+
+ /**
+ * The stack that was being operated upon
+ */
+ private Stack stack;
+ /**
+ * The id of the server being operated upon if the server object is not available (such as the server was not found)
+ */
+ private String serverId;
+
+ /**
+ * The id of the stack being operated upon if the stack object is not available (such as the stack was not found)
+ */
+ private String stackId;
+ /**
+ * The most appropriate Http Status code that reflects the error
+ */
+ private HttpStatus status;
+
+ /**
+ *
+ */
+ public RequestFailedException() {
+ // intentionally empty
+ }
+
+ /**
+ * @param message The error message
+ */
+ public RequestFailedException(String message) {
+ super(message);
+ }
+
+ /**
+ * Construct the request failed exception with the operation being performed, reason for the failure, http status
+ * code that is most appropriate, and the server we were processing.
+ *
+ * @param operation The operation being performed
+ * @param reason The reason that the operation was failed
+ * @param status The http status code that is most appropriate
+ * @param server The server that we were processing
+ */
+ @SuppressWarnings("nls")
+ public RequestFailedException(String operation, String reason, HttpStatus status, Server server) {
+ super(operation + ":" + reason);
+ this.operation = operation;
+ this.reason = reason;
+ this.status = status;
+ this.server = server;
+ if (server != null) {
+ this.serverId = server.getId();
+ }
+ }
+
+
+ /**
+ * Construct the request failed exception with the operation being performed, reason for the failure, http status
+ * code that is most appropriate, and the stack we were processing.
+ *
+ * @param operation The operation being performed
+ * @param reason The reason that the operation was failed
+ * @param status The http status code that is most appropriate
+ * @param stack The stack that we were processing
+ */
+ @SuppressWarnings("nls")
+ public RequestFailedException(String operation, String reason, HttpStatus status, Stack stack) {
+ super(operation + ":" + reason);
+ this.operation = operation;
+ this.reason = reason;
+ this.status = status;
+ this.stack = stack;
+ if (stack != null) {
+ this.stackId = stack.getId();
+ }
+ }
+
+ /**
+ * Construct the request failed exception with the operation being performed, reason for the failure, http status
+ * code that is most appropriate, and the server we were processing.
+ *
+ * @param ex The exception that we are wrapping
+ * @param operation The operation being performed
+ * @param reason The reason that the operation was failed
+ * @param status The http status code that is most appropriate
+ * @param server The server that we were processing
+ */
+ @SuppressWarnings("nls")
+ public RequestFailedException(Throwable ex, String operation, String reason, HttpStatus status, Server server) {
+ super(operation + ":" + reason, ex);
+ this.operation = operation;
+ this.reason = reason;
+ this.status = status;
+ this.server = server;
+ if (server != null) {
+ this.serverId = server.getId();
+ }
+ }
+
+ /**
+ * @param message The error message
+ * @param cause A nested exception
+ */
+ public RequestFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @param message The error message
+ * @param cause A nested exception
+ * @param enableSuppression whether or not suppression is enabled or disabled
+ * @param writableStackTrace whether or not the stack trace should be writable
+ */
+ public RequestFailedException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ /**
+ * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A null value is
+ * permitted, and indicates that the cause is nonexistent or unknown.)
+ */
+ public RequestFailedException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * @return The operation being performed
+ */
+ public String getOperation() {
+ return operation;
+ }
+
+ /**
+ * @return The reason for the failure
+ */
+ public String getReason() {
+ return reason;
+ }
+
+ /**
+ * @return The server being operated upon
+ */
+ public Server getServer() {
+ return server;
+ }
+
+ /**
+ * @return The id of the server being operated upon
+ */
+ public String getServerId() {
+ return serverId;
+ }
+
+ /**
+ * @return The status code from the operation
+ */
+ public HttpStatus getStatus() {
+ return status;
+ }
+
+ /**
+ * @param operation The operation being performed
+ */
+ public void setOperation(String operation) {
+ this.operation = operation;
+ }
+
+ /**
+ * @param reason The reason for the failure
+ */
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ /**
+ * @param server The server being operated upon
+ */
+ public void setServer(Server server) {
+ this.server = server;
+ if (server != null) {
+ setServerId(server.getId());
+ }
+ }
+
+ /**
+ * @param serverId The id of the server being operated upon
+ */
+ public void setServerId(String serverId) {
+ this.serverId = serverId;
+ }
+
+ /**
+ * @param status The status of the request
+ */
+ public void setStatus(HttpStatus status) {
+ this.status = status;
+ }
+
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalog.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalog.java
new file mode 100644
index 000000000..c078eb7b3
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalog.java
@@ -0,0 +1,310 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import com.att.cdp.exceptions.ZoneException;
+import com.att.cdp.zones.spi.RequestState;
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.cdp.zones.spi.AbstractService.State;
+import com.woorea.openstack.keystone.model.Access;
+import com.woorea.openstack.keystone.model.Access.Service;
+import com.woorea.openstack.keystone.model.Tenant;
+
+/**
+ * This class is used to capture and cache the service catalog for a specific OpenStack provider.
+ * <p>
+ * This is needed because the way the servers are represented in the ECOMP product is as their fully qualified URL's.
+ * This is very problematic, because we cant identify their region from the URL, URL's change, and we cant identify the
+ * versions of the service implementations. In otherwords, the URL does not provide us enough information.
+ * </p>
+ * <p>
+ * The zone abstraction layer is designed to detect the versions of the services dynamically, and step up or down to
+ * match those reported versions. In order to do that, we need to know before hand what region we are accessing (since
+ * the supported versions may be different by regions). We will need to authenticate to the identity service in order to
+ * do this, plus we have to duplicate the code supporting proxies and trusted hosts that exists in the abstraction
+ * layer, but that cant be helped.
+ * </p>
+ * <p>
+ * What we do to circumvent this is connect to the provider using the lowest supported identity api, and read the entire
+ * service catalog into this object. Then, we parse the vm URL to extract the host and port and match that to the
+ * compute services defined in the catalog. When we find a compute service that has the same host name and port,
+ * whatever region that service is supporting is the region for that server.
+ * </p>
+ * <p>
+ * While we really only need to do this for compute nodes, there is no telling what other situations may arise where the
+ * full service catalog may be needed. Also, there is very little additional cost (additional RAM) associated with
+ * caching the full service catalog since there is no way to list only a portion of it.
+ * </p>
+ */
+public abstract class ServiceCatalog {
+ /**
+ * The openstack connector version to use
+ */
+ public static final String CLIENT_CONNECTOR_CLASS = "com.woorea.openstack.connector.JaxRs20Connector";
+
+ /**
+ * The service name for the compute service endpoint
+ */
+ public static final String COMPUTE_SERVICE = "compute"; //$NON-NLS-1$
+
+ /**
+ * The default domain for authentication
+ */
+ public static final String DEFAULT_DOMAIN = "Default";
+ /**
+ * The service name for the identity service endpoint
+ */
+ public static final String IDENTITY_SERVICE = "identity"; //$NON-NLS-1$
+
+ /**
+ * The service name for the compute service endpoint
+ */
+ public static final String IMAGE_SERVICE = "image"; //$NON-NLS-1$
+
+ /**
+ * The service name for the metering service endpoint
+ */
+ public static final String METERING_SERVICE = "metering"; //$NON-NLS-1$
+
+ /**
+ * The service name for the network service endpoint
+ */
+ public static final String NETWORK_SERVICE = "network"; //$NON-NLS-1$
+
+ /**
+ * The service name for the persistent object service endpoint
+ */
+ public static final String OBJECT_SERVICE = "object-store"; //$NON-NLS-1$
+
+ /**
+ * The service name for the orchestration service endpoint
+ */
+ public static final String ORCHESTRATION_SERVICE = "orchestration"; //$NON-NLS-1$
+
+ /**
+ * The service name for the volume service endpoint
+ */
+ public static final String VOLUME_SERVICE = "volume"; //$NON-NLS-1$
+
+ /**
+ * The logger to be used
+ */
+ protected static final EELFLogger logger = EELFManager.getInstance().getLogger(ServiceCatalog.class);
+
+ /**
+ * The password for authentication
+ */
+ protected String credential;
+
+ /**
+ * The domain for authentication
+ */
+ protected String domain;
+ /**
+ * The time (local) that the token expires and we need to re-authenticate
+ */
+ protected long expiresLocal;
+
+ /**
+ * The url of the identity service
+ */
+ protected String identityURL;
+
+ /**
+ * The user id for authentication
+ */
+ protected String principal;
+
+ /**
+ * The project or tenant identifier
+ */
+ protected String projectIdentifier;
+
+ /**
+ * Properties for proxy information
+ */
+ protected Properties properties;
+
+ /**
+ * The set of all regions that have been defined
+ */
+ protected Set<String> regions;
+
+ /**
+ * The read/write lock used to protect the cache contents
+ */
+ protected ReadWriteLock rwLock;
+
+ /**
+ * Create the ServiceCatalog cache
+ *
+ * @param identityURL The identity service URL to connect to
+ * @param tenantIdentifier The name or id of the tenant to authenticate with. If the ID is a UUID format
+ * (32-character hexadecimal string), then the authentication is done using the tenant ID, otherwise it is
+ * done using the name.
+ * @param principal The user id to authenticate to the provider
+ * @param credential The password to authenticate to the provider
+ * @param properties Additional properties used to configure the connection, such as proxy and trusted hosts lists
+ * @throws ZoneException
+ * @throws ClassNotFoundException
+ * @throws IllegalAccessException
+ * @throws InstantiationException
+ */
+ public ServiceCatalog(String identityURL, String projectIdentifier, String principal, String credential,
+ String domain, Properties properties) {
+ this.identityURL = identityURL;
+ this.projectIdentifier = projectIdentifier;
+ this.principal = principal;
+ this.credential = credential;
+ this.domain = domain;
+ this.properties = properties;
+ rwLock = new ReentrantReadWriteLock();
+ regions = new HashSet<>();
+ }
+
+ /**
+ * Returns the list of service endpoints for the published service type
+ *
+ * @param serviceType The service type to obtain the endpoints for
+ * @return The list of endpoints for the service type, or null if none exist
+ */
+ public abstract List<?> getEndpoints(String serviceType);
+
+ /**
+ * @return The project or tenant id
+ */
+ public abstract String getProjectId();
+
+ /**
+ * @return The project or tenant name
+ */
+ public abstract String getProjectName();
+
+ /**
+ * @return The set of all regions that are defined
+ */
+ public Set<String> getRegions() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return regions;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * @return A list of service types that are published
+ */
+ public abstract List<String> getServiceTypes();
+
+ /**
+ * This method accepts a fully qualified compute node URL and uses that to determine which region of the provider
+ * hosts that compute node.
+ *
+ * @param url The parsed URL of the compute node
+ * @return The region name, or null if no region of this tenant hosts that compute node.
+ */
+ public abstract String getVMRegion(VMURL url);
+
+ /**
+ * Returns an indication if the specified service type is published by this provider
+ *
+ * @param serviceType The service type to check for
+ * @return True if a service of that type is published
+ */
+ public abstract boolean isServicePublished(String serviceType);
+
+ /**
+ * Load the Service Catalog from the specified provider
+ *
+ * @throws ZoneException
+ */
+ public abstract void init() throws ZoneException;
+
+ /**
+ * This method is used to provide a diagnostic listing of the service catalog
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public abstract String toString();
+
+ /**
+ * Initializes the request state for the current requested service.
+ * <p>
+ * This method is used to track requests made to the various service implementations and to provide additional
+ * information for diagnostic purposes. The <code>RequestState</code> class stores the state in thread-local storage
+ * and is available to all code on that thread.
+ * </p>
+ * <p>
+ * This method first obtains the stack trace and scans the stack backward for the call to this method. It then backs
+ * up one more call and assumes that method is the request that we are "tracking".
+ * </p>
+ *
+ * @param states A variable argument list of additional state values that the caller wants to add to the request
+ * state thread-local object to track the context.
+ */
+ protected void trackRequest(State... states) {
+ RequestState.clear();
+
+ for (State state : states) {
+ RequestState.put(state.getName(), state.getValue());
+ }
+
+ Thread currentThread = Thread.currentThread();
+ StackTraceElement[] stack = currentThread.getStackTrace();
+ if (stack != null && stack.length > 0) {
+ int index = 0;
+ StackTraceElement element = null;
+ for (; index < stack.length; index++) {
+ element = stack[index];
+ if ("trackRequest".equals(element.getMethodName())) { //$NON-NLS-1$
+ break;
+ }
+ }
+ index++;
+
+ if (index < stack.length) {
+ element = stack[index];
+ RequestState.put(RequestState.METHOD, element.getMethodName());
+ RequestState.put(RequestState.CLASS, element.getClassName());
+ RequestState.put(RequestState.LINE_NUMBER, Integer.toString(element.getLineNumber()));
+ RequestState.put(RequestState.THREAD, currentThread.getName());
+ // RequestState.put(RequestState.PROVIDER, context.getProvider().getName());
+ // RequestState.put(RequestState.TENANT, context.getTenantName());
+ // RequestState.put(RequestState.PRINCIPAL, context.getPrincipal());
+ }
+ }
+ }
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogFactory.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogFactory.java
new file mode 100644
index 000000000..4c759b2c6
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogFactory.java
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import java.util.Properties;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+public class ServiceCatalogFactory {
+
+ private static EELFLogger logger= EELFManager.getInstance().getLogger(org.onap.appc.adapter.iaas.impl.ServiceCatalogFactory.class);
+
+ /**
+ * This method accepts a fully qualified identity service URL and uses that to determine which version of the
+ * serviceCatalog to load.
+ *
+ * @param url The parsed URL of the identity service
+ * @param projectIdentifier The project or tenant to be used to connect to the service
+ * @param principal The principal or user to be used to connect to the service
+ * @param ceredential The credential or password to be used to connect to the service
+ * @param properties Properties object for proxy information
+ * @return The serviceCatalog for identity service version specified in the url, null if not supported.
+ */
+ public static ServiceCatalog getServiceCatalog(String url, String projectIdentifier, String principal,
+ String credential, String domain, Properties properties) {
+ IdentityURL idUrl = IdentityURL.parseURL(url);
+ if(idUrl == null){
+ logger.error("Url " + url + " could not be parsed.");
+ return null;
+ }
+ String version = idUrl.getVersion();
+ if(version == null){
+ logger.error("Invalid Identity URL check configuration");
+ return null;
+ }
+ String prefix = version.split("\\.")[0];
+ if (prefix != null) {
+ switch (prefix) {
+ case "v2":
+ return new ServiceCatalogV2(url, projectIdentifier, principal, credential, properties);
+ case "v3":
+ return new ServiceCatalogV3(url, projectIdentifier, principal, credential, domain, properties);
+ }
+ }
+ return null;
+ }
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV2.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV2.java
new file mode 100644
index 000000000..1e7112cd3
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV2.java
@@ -0,0 +1,380 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import com.att.cdp.exceptions.ContextConnectionException;
+import com.att.cdp.exceptions.ZoneException;
+import com.att.cdp.openstack.util.ExceptionMapper;
+import com.att.cdp.pal.util.Time;
+import com.att.cdp.zones.ContextFactory;
+import com.att.cdp.zones.spi.RequestState;
+import com.woorea.openstack.base.client.OpenStackBaseException;
+import com.woorea.openstack.base.client.OpenStackClientConnector;
+import com.woorea.openstack.base.client.OpenStackSimpleTokenProvider;
+import com.woorea.openstack.keystone.Keystone;
+import com.woorea.openstack.keystone.api.TokensResource;
+import com.woorea.openstack.keystone.model.Access;
+import com.woorea.openstack.keystone.model.Access.Service;
+import com.woorea.openstack.keystone.model.Access.Service.Endpoint;
+import com.woorea.openstack.keystone.model.Authentication;
+import com.woorea.openstack.keystone.model.Tenant;
+import com.woorea.openstack.keystone.model.authentication.UsernamePassword;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to capture and cache the service catalog for a specific OpenStack provider.
+ * <p>
+ * This is needed because the way the servers are represented in the ECOMP product is as their fully qualified URL's.
+ * This is very problematic, because we cant identify their region from the URL, URL's change, and we cant identify the
+ * versions of the service implementations. In otherwords, the URL does not provide us enough information.
+ * </p>
+ * <p>
+ * The zone abstraction layer is designed to detect the versions of the services dynamically, and step up or down to
+ * match those reported versions. In order to do that, we need to know before hand what region we are accessing (since
+ * the supported versions may be different by regions). We will need to authenticate to the identity service in order to
+ * do this, plus we have to duplicate the code supporting proxies and trusted hosts that exists in the abstraction
+ * layer, but that cant be helped.
+ * </p>
+ * <p>
+ * What we do to circumvent this is connect to the provider using the lowest supported identity api, and read the entire
+ * service catalog into this object. Then, we parse the vm URL to extract the host and port and match that to the
+ * compute services defined in the catalog. When we find a compute service that has the same host name and port,
+ * whatever region that service is supporting is the region for that server.
+ * </p>
+ * <p>
+ * While we really only need to do this for compute nodes, there is no telling what other situations may arise where the
+ * full service catalog may be needed. Also, there is very little additional cost (additional RAM) associated with
+ * caching the full service catalog since there is no way to list only a portion of it.
+ * </p>
+ */
+public class ServiceCatalogV2 extends ServiceCatalog {
+
+ /**
+ * The Openstack Access object that manages the authenticated token and access control
+ */
+ private Access access;
+
+ /**
+ * A map of endpoints for each service organized by service type
+ */
+ private Map<String /* Service Type */, List<Service.Endpoint>> serviceEndpoints;
+
+ /**
+ * A map of service types that are published
+ */
+ private Map<String /* Service Type */, Service> serviceTypes;
+
+ /**
+ * The tenant that we are accessing
+ */
+ private Tenant tenant;
+
+ /**
+ * A "token provider" that manages the authentication token that we obtain when logging in
+ */
+ private OpenStackSimpleTokenProvider tokenProvider;
+
+ public ServiceCatalogV2(String identityURL, String tenantIdentifier, String principal, String credential,
+ Properties properties) {
+ super(identityURL, tenantIdentifier, principal, credential, null, properties);
+ }
+
+ @Override
+ public void init() throws ZoneException {
+ serviceTypes = new HashMap<>();
+ serviceEndpoints = new HashMap<>();
+ Class<?> connectorClass;
+ OpenStackClientConnector connector;
+ try {
+ connectorClass = Class.forName(CLIENT_CONNECTOR_CLASS);
+ connector = (OpenStackClientConnector) connectorClass.newInstance();
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ return;
+ }
+ Keystone keystone = new Keystone(identityURL, connector);
+
+ String proxyHost = properties.getProperty(ContextFactory.PROPERTY_PROXY_HOST);
+ String proxyPort = properties.getProperty(ContextFactory.PROPERTY_PROXY_PORT);
+ String trustedHosts = properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS, ""); //$NON-NLS-1$
+ if (proxyHost != null && proxyHost.length() > 0) {
+ keystone.getProperties().setProperty(com.woorea.openstack.common.client.Constants.PROXY_HOST, proxyHost);
+ keystone.getProperties().setProperty(com.woorea.openstack.common.client.Constants.PROXY_PORT, proxyPort);
+ }
+ if (trustedHosts != null) {
+ keystone.getProperties().setProperty(com.woorea.openstack.common.client.Constants.TRUST_HOST_LIST,
+ trustedHosts);
+ }
+
+ Authentication authentication = new UsernamePassword(principal, credential);
+ TokensResource tokens = keystone.tokens();
+ TokensResource.Authenticate authenticate = tokens.authenticate(authentication);
+ if (projectIdentifier.length() == 32 && projectIdentifier.matches("[0-9a-fA-F]+")) { //$NON-NLS-1$
+ authenticate = authenticate.withTenantId(projectIdentifier);
+ } else {
+ authenticate = authenticate.withTenantName(projectIdentifier);
+ }
+
+ /*
+ * We have to set up the TrackRequest TLS collection for the ExceptionMapper
+ */
+ trackRequest();
+ RequestState.put(RequestState.PROVIDER, "OpenStackProvider");
+ RequestState.put(RequestState.TENANT, projectIdentifier);
+ RequestState.put(RequestState.PRINCIPAL, principal);
+
+ try {
+ access = authenticate.execute();
+ expiresLocal = getLocalExpiration(access);
+ tenant = access.getToken().getTenant();
+ tokenProvider = new OpenStackSimpleTokenProvider(access.getToken().getId());
+ keystone.setTokenProvider(tokenProvider);
+ parseServiceCatalog(access.getServiceCatalog());
+ } catch (OpenStackBaseException e) {
+ ExceptionMapper.mapException(e);
+ } catch (Exception ex) {
+ throw new ContextConnectionException(ex.getMessage());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Service.Endpoint> getEndpoints(String serviceType) {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return serviceEndpoints.get(serviceType);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getProjectId() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return tenant.getId();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getProjectName() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return tenant.getName();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<String> getRegions() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return regions;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<String> getServiceTypes() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ ArrayList<String> result = new ArrayList<>();
+ result.addAll(serviceTypes.keySet());
+ return result;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getVMRegion(VMURL url) {
+ String region = null;
+ Pattern urlPattern = Pattern.compile("[^:]+://([^:/]+)(?::([0-9]+)).*");
+
+ if (url != null) {
+ for (Endpoint endpoint : getEndpoints(ServiceCatalog.COMPUTE_SERVICE)) {
+ String endpointUrl = endpoint.getPublicURL();
+ Matcher matcher = urlPattern.matcher(endpointUrl);
+ if (matcher.matches()) {
+ if (url.getHost().equals(matcher.group(1))) {
+ if (url.getPort() != null) {
+ if (!url.getPort().equals(matcher.group(2))) {
+ continue;
+ }
+ }
+
+ region = endpoint.getRegion();
+ break;
+ }
+ }
+ }
+ }
+ return region;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isServicePublished(String serviceType) {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return serviceTypes.containsKey(serviceType);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+
+ StringBuilder builder = new StringBuilder();
+ Lock lock = rwLock.readLock();
+ lock.lock();
+ try {
+ builder.append(String.format("Service Catalog: tenant %s, id[%s], description[%s]\n", tenant.getName(), //$NON-NLS-1$
+ tenant.getId(), tenant.getDescription()));
+ if (regions != null && !regions.isEmpty()) {
+ builder.append(String.format("%d regions:\n", regions.size())); //$NON-NLS-1$
+ for (String region : regions) {
+ builder.append("\t" + region + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ builder.append(String.format("%d services:\n", serviceEndpoints.size())); //$NON-NLS-1$
+ for (String serviceType : serviceEndpoints.keySet()) {
+ List<Service.Endpoint> endpoints = serviceEndpoints.get(serviceType);
+ Service service = serviceTypes.get(serviceType);
+
+ builder.append(String.format("\t%s [%s] - %d endpoints\n", service.getType(), service.getName(), //$NON-NLS-1$
+ endpoints.size()));
+ for (Service.Endpoint endpoint : endpoints) {
+ builder.append(String.format("\t\tRegion [%s], public URL [%s]\n", endpoint.getRegion(), //$NON-NLS-1$
+ endpoint.getPublicURL()));
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Parses the service catalog and caches the results
+ *
+ * @param services The list of services published by this provider
+ */
+ private void parseServiceCatalog(List<Service> services) {
+ Lock lock = rwLock.writeLock();
+ lock.lock();
+ try {
+ serviceTypes.clear();
+ serviceEndpoints.clear();
+ regions.clear();
+
+ for (Service service : services) {
+ String type = service.getType();
+ serviceTypes.put(type, service);
+
+ List<Service.Endpoint> endpoints = service.getEndpoints();
+ for (Service.Endpoint endpoint : endpoints) {
+ List<Service.Endpoint> endpointList = serviceEndpoints.get(type);
+ if (endpointList == null) {
+ endpointList = new ArrayList<>();
+ serviceEndpoints.put(type, endpointList);
+ }
+ endpointList.add(endpoint);
+
+ String region = endpoint.getRegion();
+ if (!regions.contains(region)) {
+ regions.add(region);
+ }
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Computes the local time when the access token will expire, after which we will need to re-login to access the
+ * provider.
+ *
+ * @param accessKey The access key used to access the provider
+ * @return The local time the key expires
+ */
+ private static long getLocalExpiration(Access accessKey) {
+ Date now = Time.getCurrentUTCDate();
+ if (accessKey != null && accessKey.getToken() != null) {
+ Calendar issued = accessKey.getToken().getIssued_at();
+ Calendar expires = accessKey.getToken().getExpires();
+ if (issued != null && expires != null) {
+ long tokenLife = expires.getTimeInMillis() - issued.getTimeInMillis();
+ return now.getTime() + tokenLife;
+ }
+ }
+ return now.getTime();
+ }
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV3.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV3.java
new file mode 100644
index 000000000..b1c47e5d5
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/ServiceCatalogV3.java
@@ -0,0 +1,402 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import com.att.cdp.exceptions.ContextConnectionException;
+import com.att.cdp.exceptions.ZoneException;
+import com.att.cdp.openstack.util.ExceptionMapper;
+import com.att.cdp.pal.util.Time;
+import com.att.cdp.zones.ContextFactory;
+import com.att.cdp.zones.spi.RequestState;
+import com.woorea.openstack.base.client.OpenStackBaseException;
+import com.woorea.openstack.base.client.OpenStackClientConnector;
+import com.woorea.openstack.base.client.OpenStackSimpleTokenProvider;
+import com.woorea.openstack.keystone.v3.Keystone;
+import com.woorea.openstack.keystone.v3.api.TokensResource;
+import com.woorea.openstack.keystone.v3.model.Authentication;
+import com.woorea.openstack.keystone.v3.model.Authentication.Identity;
+import com.woorea.openstack.keystone.v3.model.Authentication.Scope;
+import com.woorea.openstack.keystone.v3.model.Token;
+import com.woorea.openstack.keystone.v3.model.Token.Project;
+import com.woorea.openstack.keystone.v3.model.Token.Service;
+import com.woorea.openstack.keystone.v3.model.Token.Service.Endpoint;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to capture and cache the service catalog for a specific OpenStack provider.
+ * <p>
+ * This is needed because the way the servers are represented in the ECOMP product is as their fully qualified URL's.
+ * This is very problematic, because we cant identify their region from the URL, URL's change, and we cant identify the
+ * versions of the service implementations. In otherwords, the URL does not provide us enough information.
+ * </p>
+ * <p>
+ * The zone abstraction layer is designed to detect the versions of the services dynamically, and step up or down to
+ * match those reported versions. In order to do that, we need to know before hand what region we are accessing (since
+ * the supported versions may be different by regions). We will need to authenticate to the identity service in order to
+ * do this, plus we have to duplicate the code supporting proxies and trusted hosts that exists in the abstraction
+ * layer, but that cant be helped.
+ * </p>
+ * <p>
+ * What we do to circumvent this is connect to the provider using the lowest supported identity api, and read the entire
+ * service catalog into this object. Then, we parse the vm URL to extract the host and port and match that to the
+ * compute services defined in the catalog. When we find a compute service that has the same host name and port,
+ * whatever region that service is supporting is the region for that server.
+ * </p>
+ * <p>
+ * While we really only need to do this for compute nodes, there is no telling what other situations may arise where the
+ * full service catalog may be needed. Also, there is very little additional cost (additional RAM) associated with
+ * caching the full service catalog since there is no way to list only a portion of it.
+ * </p>
+ */
+public class ServiceCatalogV3 extends ServiceCatalog {
+
+ /**
+ * The project that we are accessing
+ */
+ private Project project;
+
+ /**
+ * A map of endpoints for each service organized by service type
+ */
+ private Map<String /* Service Type */, List<Service.Endpoint>> serviceEndpoints;
+
+ /**
+ * A map of service types that are published
+ */
+ private Map<String /* Service Type */, Service> serviceTypes;
+
+ /**
+ * The Openstack Access object that manages the authenticated token and access control
+ */
+ private Token token;
+
+ /**
+ * A "token provider" that manages the authentication token that we obtain when logging in
+ */
+ private OpenStackSimpleTokenProvider tokenProvider;
+
+ /**
+ * {@inheritDoc}
+ */
+ public ServiceCatalogV3(String identityURL, String projectIdentifier, String principal, String credential,
+ String domain, Properties properties) {
+ super(identityURL, projectIdentifier, principal, credential, domain, properties);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void init() throws ZoneException {
+ rwLock = new ReentrantReadWriteLock();
+ serviceTypes = new HashMap<>();
+ serviceEndpoints = new HashMap<>();
+ regions = new HashSet<>();
+ Class<?> connectorClass;
+ OpenStackClientConnector connector;
+ try {
+ connectorClass = Class.forName(CLIENT_CONNECTOR_CLASS);
+ connector = (OpenStackClientConnector) connectorClass.newInstance();
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ return;
+ }
+ Keystone keystone = new Keystone(identityURL, connector);
+
+ String proxyHost = properties.getProperty(ContextFactory.PROPERTY_PROXY_HOST);
+ String proxyPort = properties.getProperty(ContextFactory.PROPERTY_PROXY_PORT);
+ String trustedHosts = properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS, ""); //$NON-NLS-1$
+ if (proxyHost != null && proxyHost.length() > 0) {
+ keystone.getProperties().setProperty(com.woorea.openstack.common.client.Constants.PROXY_HOST, proxyHost);
+ keystone.getProperties().setProperty(com.woorea.openstack.common.client.Constants.PROXY_PORT, proxyPort);
+ }
+ if (trustedHosts != null) {
+ keystone.getProperties().setProperty(com.woorea.openstack.common.client.Constants.TRUST_HOST_LIST,
+ trustedHosts);
+ }
+
+ // create identity
+ Identity identity = Identity.password(domain, principal, credential);
+
+ // create scope
+ Scope scope = null;
+ if (projectIdentifier.length() == 32 && projectIdentifier.matches("[0-9a-fA-F]+")) { //$NON-NLS-1$
+ // authenticate = authenticate.withTenantId(projectIdentifier);
+ scope = Scope.project(projectIdentifier);
+ } else {
+ // authenticate = authenticate.withTenantName(projectIdentifier);
+ scope = Scope.project(domain, projectIdentifier);
+ }
+
+ Authentication authentication = new Authentication();
+ authentication.setIdentity(identity);
+ authentication.setScope(scope);
+
+ TokensResource tokens = keystone.tokens();
+ TokensResource.Authenticate authenticate = tokens.authenticate(authentication);
+
+ /*
+ * We have to set up the TrackRequest TLS collection for the ExceptionMapper
+ */
+ trackRequest();
+ RequestState.put(RequestState.PROVIDER, "OpenStackProvider");
+ RequestState.put(RequestState.TENANT, projectIdentifier);
+ RequestState.put(RequestState.PRINCIPAL, principal);
+
+ try {
+ token = authenticate.execute();
+ expiresLocal = getLocalExpiration(token);
+ project = token.getProject();
+ tokenProvider = new OpenStackSimpleTokenProvider(token.getId());
+ keystone.setTokenProvider(tokenProvider);
+ parseServiceCatalog(token.getCatalog());
+ } catch (OpenStackBaseException e) {
+ ExceptionMapper.mapException(e);
+ } catch (Exception ex) {
+ throw new ContextConnectionException(ex.getMessage());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Service.Endpoint> getEndpoints(String serviceType) {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return serviceEndpoints.get(serviceType);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getProjectId() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return project.getId();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getProjectName() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return project.getName();
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<String> getRegions() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return regions;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<String> getServiceTypes() {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ ArrayList<String> result = new ArrayList<>();
+ result.addAll(serviceTypes.keySet());
+ return result;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getVMRegion(VMURL url) {
+ String region = null;
+ Pattern urlPattern = Pattern.compile("[^:]+://([^:/]+)(?::([0-9]+)).*");
+
+ if (url != null) {
+ for (Endpoint endpoint : getEndpoints(ServiceCatalog.COMPUTE_SERVICE)) {
+ String endpointUrl = endpoint.getUrl();
+ Matcher matcher = urlPattern.matcher(endpointUrl);
+ if (matcher.matches()) {
+ if (url.getHost().equals(matcher.group(1))) {
+ if (url.getPort() != null) {
+ if (!url.getPort().equals(matcher.group(2))) {
+ continue;
+ }
+ }
+
+ region = endpoint.getRegion();
+ break;
+ }
+ }
+ }
+ }
+ return region;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isServicePublished(String serviceType) {
+ Lock readLock = rwLock.readLock();
+ readLock.lock();
+ try {
+ return serviceTypes.containsKey(serviceType);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+
+ StringBuilder builder = new StringBuilder();
+ Lock lock = rwLock.readLock();
+ lock.lock();
+ try {
+ builder.append(String.format("Service Catalog: tenant %s, id[%s]\n", project.getName(), //$NON-NLS-1$
+ project.getId()));
+ if (regions != null && !regions.isEmpty()) {
+ builder.append(String.format("%d regions:\n", regions.size())); //$NON-NLS-1$
+ for (String region : regions) {
+ builder.append("\t" + region + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ builder.append(String.format("%d services:\n", serviceEndpoints.size())); //$NON-NLS-1$
+ for (String serviceType : serviceEndpoints.keySet()) {
+ List<Service.Endpoint> endpoints = serviceEndpoints.get(serviceType);
+ Service service = serviceTypes.get(serviceType);
+
+ builder.append(String.format("\t%s - %d endpoints\n", service.getType(), //$NON-NLS-1$
+ endpoints.size()));
+ for (Service.Endpoint endpoint : endpoints) {
+ builder.append(String.format("\t\tRegion [%s], public URL [%s]\n", endpoint.getRegion(), //$NON-NLS-1$
+ endpoint.getUrl()));
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Parses the service catalog and caches the results
+ *
+ * @param services The list of services published by this provider
+ */
+ private void parseServiceCatalog(List<Service> services) {
+ Lock lock = rwLock.writeLock();
+ lock.lock();
+ try {
+ serviceTypes.clear();
+ serviceEndpoints.clear();
+ regions.clear();
+
+ for (Service service : services) {
+ String type = service.getType();
+ serviceTypes.put(type, service);
+
+ List<Service.Endpoint> endpoints = service.getEndpoints();
+ for (Service.Endpoint endpoint : endpoints) {
+ List<Service.Endpoint> endpointList = serviceEndpoints.get(type);
+ if (endpointList == null) {
+ endpointList = new ArrayList<>();
+ serviceEndpoints.put(type, endpointList);
+ }
+ endpointList.add(endpoint);
+
+ String region = endpoint.getRegion();
+ if (!regions.contains(region)) {
+ regions.add(region);
+ }
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Computes the local time when the access token will expire, after which we will need to re-login to access the
+ * provider.
+ *
+ * @param accessKey The access key used to access the provider
+ * @return The local time the key expires
+ */
+ private static long getLocalExpiration(Token accessToken) {
+ Date now = Time.getCurrentUTCDate();
+ if (accessToken != null) {
+ Calendar issued = accessToken.getIssuedAt();
+ Calendar expires = accessToken.getExpiresAt();
+ if (issued != null && expires != null) {
+ long tokenLife = expires.getTimeInMillis() - issued.getTimeInMillis();
+ return now.getTime() + tokenLife;
+ }
+ }
+ return now.getTime();
+ }
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/TenantCache.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/TenantCache.java
new file mode 100644
index 000000000..659202d1c
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/TenantCache.java
@@ -0,0 +1,382 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.onap.appc.Constants;
+import org.onap.appc.configuration.Configuration;
+import org.onap.appc.configuration.ConfigurationFactory;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.pool.Allocator;
+import org.onap.appc.pool.Destructor;
+import org.onap.appc.pool.Pool;
+import org.onap.appc.pool.PoolSpecificationException;
+import com.att.cdp.exceptions.ContextConnectionException;
+import com.att.cdp.exceptions.ZoneException;
+import com.att.cdp.zones.Context;
+import com.att.cdp.zones.ContextFactory;
+import com.att.cdp.zones.Provider;
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.woorea.openstack.connector.JaxRs20Connector;
+// import com.sun.jersey.api.client.ClientHandlerException;
+import com.woorea.openstack.keystone.model.Access.Service.Endpoint;
+
+/**
+ * This class maintains a cache of tenants within a specific provider.
+ * <p>
+ * Providers may be multi-tenant, such as OpenStack, where the available services and resources vary from one tenant to
+ * another. Therefore, the provider cache maintains a cache of tenants and the service catalogs for each, as well as the
+ * credentials used to access the tenants, and a pool of Context objects for each tenant. The context pool allows use of
+ * the CDP abstraction layer to access the services of the provider within the specific tenant.
+ * </p>
+ */
+public class TenantCache implements Allocator<Context>, Destructor<Context> {
+
+ public static final String POOL_PROVIDER_NAME = "pool.provider.name";
+ public static final String POOL_TENANT_NAME = "pool.tenant.name";
+ // public static final String CLIENT_CONNECTOR_CLASS =
+ // "com.woorea.openstack.connector.JerseyConnector";
+ public static final String CLIENT_CONNECTOR_CLASS = "com.woorea.openstack.connector.JaxRs20Connector";
+ /**
+ * The domain to use to authenticate
+ */
+ private String domain;
+
+ /**
+ * The provider we are part of
+ */
+ private ProviderCache provider;
+
+ /**
+ * The password used to authenticate
+ */
+ private String password;
+
+ /**
+ * The context pools by region used to access this tenant
+ */
+ private Map<String /* region */, Pool<Context>> pools = new HashMap<>();
+
+ /**
+ * The tenant id
+ */
+ private String tenantId;
+
+ /**
+ * The tenant name
+ */
+ private String tenantName;
+
+ /**
+ * The user id used to authenticate
+ */
+ private String userid;
+
+ /**
+ * The configuration of this adapter
+ */
+ private Configuration configuration;
+
+ /**
+ * The service catalog for this provider
+ */
+ private ServiceCatalog catalog;
+
+ /**
+ * Set to true when the cache has been initialized
+ */
+ private boolean initialized;
+
+ /**
+ * The logger to use
+ */
+ private EELFLogger logger;
+
+ /**
+ * Construct the cache of tenants for the specified provider
+ *
+ * @param provider The provider
+ */
+ public TenantCache(ProviderCache provider) {
+ configuration = ConfigurationFactory.getConfiguration();
+ logger = EELFManager.getInstance().getLogger(getClass());
+ this.provider = provider;
+ configuration = ConfigurationFactory.getConfiguration();
+ }
+
+ /**
+ * @return True when the cache has been initialized. A tenant cache is initialized when the service catalog for the
+ * tenant on the specified provider has been loaded and processed.
+ */
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ /**
+ * Initializes the tenant cache.
+ * <p>
+ * This method authenticates to the provider and obtains the service catalog. For the service catalog we can
+ * determine all supported regions for this provider, as well as all published services and their endpoints. We will
+ * cache and maintain a copy of the service catalog for later queries.
+ * </p>
+ * <p>
+ * Once the catalog has been obtained, we create a context pool for each region defined. The context allows access
+ * to services of a single region only, so we need a separate context by region. It is possible to operate on
+ * resources that span regions, but to do so will require acquiring a context for each region of interest.
+ * </p>
+ * <p>
+ * The context pool maintains the reusable context objects and allocates them as needed. This class is registered as
+ * the allocator and destructor for the pool, so that we can create a new context when needed, and close it when no
+ * longer used.
+ * </p>
+ */
+ public void initialize() {
+ logger.debug("Initializing TenantCache");
+
+ int min = configuration.getIntegerProperty(Constants.PROPERTY_MIN_POOL_SIZE);
+ int max = configuration.getIntegerProperty(Constants.PROPERTY_MAX_POOL_SIZE);
+ int delay = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_DELAY);
+ int limit = configuration.getIntegerProperty(Constants.PROPERTY_RETRY_LIMIT);
+
+ String url = provider.getIdentityURL();
+ String tenant = tenantName == null ? tenantId : tenantName;
+ Properties properties = configuration.getProperties();
+ catalog = ServiceCatalogFactory.getServiceCatalog(url, tenant, userid, password, domain, properties);
+
+ if (catalog == null) {
+ logger.error(Msg.IAAS_UNSUPPORTED_IDENTITY_SERVICE, url);
+ return;
+ }
+
+ int attempt = 1;
+ while (attempt <= limit) {
+ try {
+ catalog.init();
+ tenantId = catalog.getProjectId();
+ tenantName = catalog.getProjectName();
+
+ for (String region : catalog.getRegions()) {
+ try {
+ Pool<Context> pool = new Pool<>(min, max);
+ pool.setProperty(ContextFactory.PROPERTY_IDENTITY_URL, url);
+ pool.setProperty(ContextFactory.PROPERTY_TENANT, tenantName);
+ pool.setProperty(ContextFactory.PROPERTY_CLIENT_CONNECTOR_CLASS, CLIENT_CONNECTOR_CLASS);
+ pool.setProperty(ContextFactory.PROPERTY_RETRY_DELAY,
+ configuration.getProperty(Constants.PROPERTY_RETRY_DELAY));
+ pool.setProperty(ContextFactory.PROPERTY_RETRY_LIMIT,
+ configuration.getProperty(Constants.PROPERTY_RETRY_LIMIT));
+ pool.setProperty(ContextFactory.PROPERTY_REGION, region);
+ if (properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS) != null) {
+ pool.setProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS,
+ properties.getProperty(ContextFactory.PROPERTY_TRUSTED_HOSTS));
+ }
+ pool.setAllocator(this);
+ pool.setDestructor(this);
+ pools.put(region, pool);
+ logger.debug(String.format("Put pool for region %s", region));
+ } catch (PoolSpecificationException e) {
+ logger.error("Error creating pool", e);
+ e.printStackTrace();
+ }
+ }
+ initialized = true;
+ break;
+ } catch (ContextConnectionException e) {
+ attempt++;
+ if (attempt <= limit) {
+ logger.error(Msg.CONNECTION_FAILED_RETRY, provider.getProviderName(), url, tenantName, tenantId,
+ e.getMessage(), Integer.toString(delay), Integer.toString(attempt),
+ Integer.toString(limit));
+
+ try {
+ Thread.sleep(delay * 1000L);
+ } catch (InterruptedException ie) {
+ // ignore
+ }
+ }
+ } catch (ZoneException e) {
+ logger.error(e.getMessage());
+ break;
+ }
+ }
+
+ if (!initialized) {
+ logger.error(Msg.CONNECTION_FAILED, provider.getProviderName(), url);
+ }
+ }
+
+ /**
+ * This method accepts a fully qualified compute node URL and uses that to determine which region of the provider
+ * hosts that compute node.
+ *
+ * @param url The parsed URL of the compute node
+ * @return The region name, or null if no region of this tenant hosts that compute node.
+ */
+ public String determineRegion(VMURL url) {
+ logger.debug(String.format("Attempting to determine VM region for %s", url));
+ String region = catalog.getVMRegion(url);
+ logger.debug(String.format("Region for %s is %s", url, region));
+ return region;
+ }
+
+ /**
+ * @return the value of the domain
+ */
+ public String getDomain() {
+ return domain;
+ }
+
+ /**
+ * @param domain the value for domain
+ */
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+
+ /**
+ * @return the value of provider
+ */
+ public ProviderCache getProvider() {
+ return provider;
+ }
+
+ /**
+ * @param provider the value for provider
+ */
+ public void setProvider(ProviderCache provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * @return the value of password
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * @param password the value for password
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * @return the value of tenantId
+ */
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ /**
+ * @param tenantId the value for tenantId
+ */
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+
+ /**
+ * @return the value of tenantName
+ */
+ public String getTenantName() {
+ return tenantName;
+ }
+
+ /**
+ * @param tenantName the value for tenantName
+ */
+ public void setTenantName(String tenantName) {
+ this.tenantName = tenantName;
+ }
+
+ /**
+ * @return the value of userid
+ */
+ public String getUserid() {
+ return userid;
+ }
+
+ /**
+ * @param userid the value for userid
+ */
+ public void setUserid(String userid) {
+ this.userid = userid;
+ }
+
+ /**
+ * @return the value of pools
+ */
+ public Map<String, Pool<Context>> getPools() {
+ return pools;
+ }
+
+ /**
+ * @see org.onap.appc.pool.Allocator#allocate(org.onap.appc.pool.Pool)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Context allocate(Pool<Context> pool) {
+ logger.debug("Allocationg context for pool");
+ Class<? extends Provider> providerClass;
+ try {
+ providerClass = (Class<? extends Provider>) Class.forName("com.att.cdp.openstack.OpenStackProvider");
+ // String providerType = provider.getProviderType();
+
+ // Context context = ContextFactory.getContext(providerType, pool.getProperties());
+ Context context = ContextFactory.getContext(providerClass, pool.getProperties());
+ context.login(userid, password);
+ return context;
+ } catch (IllegalStateException | IllegalArgumentException | ZoneException | ClassNotFoundException e) {
+ logger.debug("Failed to allocate context for pool", e);
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * @see org.onap.appc.pool.Destructor#destroy(java.lang.Object, org.onap.appc.pool.Pool)
+ */
+ @Override
+ public void destroy(Context context, Pool<Context> pool) {
+ try {
+ context.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @return the service catalog for this provider
+ */
+ public ServiceCatalog getServiceCatalog() {
+ return catalog;
+ }
+}
diff --git a/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/VMURL.java b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/VMURL.java
new file mode 100644
index 000000000..777327cad
--- /dev/null
+++ b/appc-adapters/appc-iaas-adapter/appc-iaas-adapter-bundle/src/main/java/org/onap/appc/adapter/iaas/impl/VMURL.java
@@ -0,0 +1,175 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.adapter.iaas.impl;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to parse the VM URL returned from OpenStack and extract all of the constituent parts.
+ */
+public class VMURL {
+
+ /**
+ * The regular expression pattern used to parse the URL. Capturing groups are used to identify and extract the
+ * various component parts of the URL.
+ */
+ private static Pattern pattern =
+ Pattern.compile("(\\p{Alnum}+)://([^/:]+)(?::([0-9]+))?(/.*)?/(v[0-9\\.]+)/([^/]+)/servers/([^/]+)");
+
+ /**
+ * The URL scheme or protocol, such as HTTP or HTTPS
+ */
+ private String scheme;
+
+ /**
+ * The host name or ip address
+ */
+ private String host;
+
+ /**
+ * The path, or null if no path is defined
+ */
+ private String path;
+
+ /**
+ * The port number, or null if no port is defined
+ */
+ private String port;
+
+ /**
+ * The tenant UUID
+ */
+ private String tenantId;
+
+ /**
+ * The server UUID
+ */
+ private String serverId;
+
+ /**
+ * The version of the service
+ */
+ private String version;
+
+ /**
+ * A private default constructor prevents instantiation by any method other than the factory method
+ *
+ * @see #parseURL(String)
+ */
+ private VMURL() {
+
+ }
+
+ /**
+ * This static method is used to parse the provided server URL string and return a parse results object (VMURL)
+ * which represents the state of the parse.
+ *
+ * @param serverUrl The server URL to be parsed
+ * @return The VMURL parse results object, or null if the URL was not valid or null.
+ */
+ public static VMURL parseURL(String serverUrl) {
+ VMURL obj = null;
+ if (serverUrl != null) {
+ Matcher matcher = pattern.matcher(serverUrl.trim());
+ if (matcher.matches()) {
+ obj = new VMURL();
+ obj.scheme = matcher.group(1);
+ obj.host = matcher.group(2);
+ obj.port = matcher.group(3);
+ obj.path = matcher.group(4);
+ obj.version = matcher.group(5);
+ obj.tenantId = matcher.group(6);
+ obj.serverId = matcher.group(7);
+ }
+ }
+
+ return obj;
+ }
+
+ /**
+ * @return The URL scheme
+ */
+ public String getScheme() {
+ return scheme;
+ }
+
+ /**
+ * @return The URL host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * @return THe URL path, or null if no path was defined
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return The URL port, or null if no port was defined
+ */
+ public String getPort() {
+ return port;
+ }
+
+ /**
+ * @return The tenant id
+ */
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ /**
+ * @return The server ID
+ */
+ public String getServerId() {
+ return serverId;
+ }
+
+ /**
+ * @return The version of the service
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ str.append(scheme + "://" + host);
+ if (port != null) {
+ str.append(":" + port);
+ }
+ if (path != null) {
+ str.append(path);
+ }
+ str.append("/" + version + "/" + tenantId + "/servers/" + serverId);
+ return str.toString();
+ }
+
+}