diff options
Diffstat (limited to 'adaptors/chef-adaptor/chef-adaptor-bundle/src/main')
14 files changed, 1678 insertions, 0 deletions
diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/ChefAdaptor.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/ChefAdaptor.java new file mode 100644 index 000000000..33b71a8a7 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/ChefAdaptor.java @@ -0,0 +1,216 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 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. + * + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef; + +import java.util.Map; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; + +/** + * This interface defines the operations that the provider adaptor exposes. + * <p> + * This interface defines static constant property values that can be used to configure the adaptor. These constants are + * prefixed with the name PROPERTY_ to indicate that they are configuration properties. These properties are read from + * the configuration file for the adaptor and are used to define the providers, identity service URLs, and other + * information needed by the adaptor to interface with an IaaS provider. + * </p> + */ +public interface ChefAdaptor extends SvcLogicJavaPlugin { + + /** + * The type of provider to be accessed to locate and operate on a virtual machine instance. This is used to load the + * correct provider support through the CDP IaaS abstraction layer and can be OpenStackProvider, BareMetalProvider, + * or any other supported provider type. + */ + static final String PROPERTY_PROVIDER_TYPE = "org.onap.appc.provider.type"; + + /** + * The adaptor maintains a cache of providers organized by the name of the provider, not its type. This is + * equivalent to the system or installation name. All regions within the same installation are assumed to be the + * same type. + */ + static final String PROPERTY_PROVIDER_NAME = "org.onap.appc.provider.name"; + + /** + * The fully-qualified URL of the instance to be manipulated as it is known to the provider. + */ + static final String PROPERTY_INSTANCE_URL = "org.onap.appc.instance.url"; + + /** + * The fully-qualified URL of the instance to be manipulated as it is known to the provider. + */ + static final String PROPERTY_IDENTITY_URL = "org.onap.appc.identity.url"; + + /** + * This method is used to restart an existing virtual machine given the fully qualified URL of the machine. + * <p> + * This method is invoked from a directed graph as an <code>Executor</code> node. This means that the parameters + * passed to the method are passed as properties in a map. This method expects the following properties to be + * defined: + * <dl> + * <dt>org.onap.appc.provider.type</dt> + * <dd>The appropriate provider type, such as <code>OpenStackProvider</code>. This is used by the CDP IaaS + * abstraction layer to dynamically load and open a connection to the appropriate provider type. All CDP supported + * provider types are legal.</dd> + * <dt>org.onap.appc.instance.url</dt> + * <dd>The fully qualified URL of the instance to be restarted, as it is known to the provider (i.e., the self-link + * URL of the server)</dd> + * </dl> + * </p> + * + * @param properties + * A map of name-value pairs that supply the parameters needed by this method. The properties needed are + * defined above. + * @param context + * The service logic context of the graph being executed. + * @return The <code>Server</code> object that represents the VM being restarted. The returned server object can be + * inspected for the final state of the server once the restart has been completed. The method does not + * return until the restart has either completed or has failed. + * @throws SvcLogicException + * If the server cannot be restarted for some reason + */ + // Server restartServer(Map<String, String> properties, SvcLogicContext context) throws SvcLogicException; + + /** + * This method is used to stop the indicated server + * <p> + * This method is invoked from a directed graph as an <code>Executor</code> node. This means that the parameters + * passed to the method are passed as properties in a map. This method expects the following properties to be + * defined: + * <dl> + * <dt>org.onap.appc.provider.type</dt> + * <dd>The appropriate provider type, such as <code>OpenStackProvider</code>. This is used by the CDP IaaS + * abstraction layer to dynamically load and open a connection to the appropriate provider type. All CDP supported + * provider types are legal.</dd> + * <dt>org.onap.appc.instance.url</dt> + * <dd>The fully qualified URL of the instance to be stopped, as it is known to the provider (i.e., the self-link + * URL of the server)</dd> + * </dl> + * </p> + * + * @param properties + * A map of name-value pairs that supply the parameters needed by this method. The properties needed are + * defined above. + * @param context + * The service logic context of the graph being executed. + * @return The <code>Server</code> object that represents the VM being stopped. The returned server object can be + * inspected for the final state of the server once the stop has been completed. The method does not return + * until the stop has either completed or has failed. + * @throws SvcLogicException + * If the server cannot be stopped for some reason + */ + //Server stopServer(Map<String, String> properties, SvcLogicContext context) throws SvcLogicException; + + /** + * This method is used to start the indicated server + * <p> + * This method is invoked from a directed graph as an <code>Executor</code> node. This means that the parameters + * passed to the method are passed as properties in a map. This method expects the following properties to be + * defined: + * <dl> + * <dt>org.onap.appc.provider.type</dt> + * <dd>The appropriate provider type, such as <code>OpenStackProvider</code>. This is used by the CDP IaaS + * abstraction layer to dynamically load and open a connection to the appropriate provider type. All CDP supported + * provider types are legal.</dd> + * <dt>org.onap.appc.instance.url</dt> + * <dd>The fully qualified URL of the instance to be started, as it is known to the provider (i.e., the self-link + * URL of the server)</dd> + * </dl> + * </p> + * + * @param properties + * A map of name-value pairs that supply the parameters needed by this method. The properties needed are + * defined above. + * @param context + * The service logic context of the graph being executed. + * @return The <code>Server</code> object that represents the VM being started. The returned server object can be + * inspected for the final state of the server once the start has been completed. The method does not return + * until the start has either completed or has failed. + * @throws SvcLogicException + * If the server cannot be started for some reason + */ + // Server startServer(Map<String, String> properties, SvcLogicContext context) throws SvcLogicException; + + /** + * This method is used to rebuild the indicated server + * <p> + * This method is invoked from a directed graph as an <code>Executor</code> node. This means that the parameters + * passed to the method are passed as properties in a map. This method expects the following properties to be + * defined: + * <dl> + * <dt>org.onap.appc.provider.type</dt> + * <dd>The appropriate provider type, such as <code>OpenStackProvider</code>. This is used by the CDP IaaS + * abstraction layer to dynamically load and open a connection to the appropriate provider type. All CDP supported + * provider types are legal.</dd> + * <dt>org.onap.appc.instance.url</dt> + * <dd>The fully qualified URL of the instance to be rebuilt, as it is known to the provider (i.e., the self-link + * URL of the server)</dd> + * </dl> + * </p> + * + * @param properties A map of name-value pairs that supply the parameters needed by this method. The properties needed are + * defined above. + * @param context The service logic context of the graph being executed. + * + * @return The <code>Server</code> object that represents the VM being rebuilt. The returned server object can be + * inspected for the final state of the server once the rebuild has been completed. The method does not + * return until the rebuild has either completed or has failed. + * + * @throws SvcLogicException If the server cannot be rebuilt for some reason + */ + // Server rebuildServer(Map<String, String> properties, SvcLogicContext context) throws SvcLogicException; + + // Server evacuateServer(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + //Server migrateServer(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + void trigger(Map<String, String> params, SvcLogicContext ctx); + + void chefGet(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void chefPut(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void chefPost(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void chefDelete(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void nodeObejctBuilder(Map<String, String> params, SvcLogicContext ctx); + + void checkPushJob(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void pushJob(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void retrieveData(Map<String, String> params, SvcLogicContext ctx); + + void combineStrings(Map<String, String> params, SvcLogicContext ctx); + + void vnfcEnvironment(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void vnfcNodeobjects(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void vnfcPushJob(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + + void fetchResults(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException; + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/ChefApiClientFactory.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/ChefApiClientFactory.java new file mode 100644 index 000000000..07745ed1c --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/ChefApiClientFactory.java @@ -0,0 +1,72 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.collect.ImmutableMap; +import java.io.File; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefApiClient; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefApiClientImpl; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefApiHeaderFactory; + +public class ChefApiClientFactory { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(ChefApiClientFactory.class); + + private HttpClient httpClient = createChefHttpClient(); + private ChefApiHeaderFactory chefApiHeaderFactory = new ChefApiHeaderFactory(); + + private HttpClient createChefHttpClient() { + String trustStoreFileName = "/opt/onap/chef/chefServerSSL.jks"; + char[] trustStoreCreds = "adminadmin".toCharArray(); + try { + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + SSLContexts.custom().loadTrustMaterial(new File(trustStoreFileName), trustStoreCreds).build(), + SSLConnectionSocketFactory.getDefaultHostnameVerifier()); + return HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException + | IOException e) { + logger.error(e.getMessage(), e); + } + return null; + } + + public ChefApiClient create(String endPoint, String organizations, String userId, String pemPath) { + return new ChefApiClientImpl(httpClient, endPoint, organizations, (methodName, requestPath, body) -> chefApiHeaderFactory + .create(methodName, requestPath, body, userId, organizations, pemPath)); + } + + public ChefApiClient create(String endPoint, String organizations) { + return new ChefApiClientImpl(httpClient, endPoint, organizations, (methodName, requestPath, body) -> ImmutableMap.of()); + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/api/ChefApiClient.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/api/ChefApiClient.java new file mode 100644 index 000000000..947888d34 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/api/ChefApiClient.java @@ -0,0 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.api; + +public interface ChefApiClient { + + org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse get(String path); + + org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse delete(String path); + + org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse post(String path, String body); + + org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse put(String path, String body); + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/api/ChefResponse.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/api/ChefResponse.java new file mode 100644 index 000000000..c4615fc77 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/api/ChefResponse.java @@ -0,0 +1,47 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * + * Modifications Copyright © 2018 IBM. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.api; + +public final class ChefResponse { + + private final String body; + private final int status; + + private ChefResponse(int status, String body) { + this.status = status; + this.body = body; + } + + public static ChefResponse create(int status, String body) { + return new ChefResponse(status, body); + } + + public int getStatusCode() { + return status; + } + + public String getBody() { + return body; + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiClientImpl.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiClientImpl.java new file mode 100644 index 000000000..7229b0d10 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiClientImpl.java @@ -0,0 +1,117 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * Modifications Copyright (C) 2019 IBM + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import java.io.IOException; +import java.net.URISyntaxException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.util.EntityUtils; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefApiClient; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefRequestBuilder.OngoingRequestBuilder; + +public class ChefApiClientImpl implements ChefApiClient { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(ChefApiClientImpl.class); + private final HttpClient httpClient; + private final String endpoint; + private final String organization; + private final org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.HttpHeaderFactory httpHeaderFactory; + + public ChefApiClientImpl(HttpClient httpClient, String endpoint, String organization, + org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.HttpHeaderFactory httpHeaderFactory) { + this.httpClient = httpClient; + this.endpoint = endpoint; + this.organization = organization; + this.httpHeaderFactory = httpHeaderFactory; + } + + @Override + public ChefResponse get(String path) { + OngoingRequestBuilder requestBuilder = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefRequestBuilder.newRequestTo(endpoint) + .httpGet() + .withPath(getPath(path)) + .withHeaders(httpHeaderFactory.create("GET", path, "")); + return execute(requestBuilder); + } + + @Override + public ChefResponse delete(String path) { + OngoingRequestBuilder requestBuilder = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefRequestBuilder.newRequestTo(endpoint) + .httpDelete() + .withPath(getPath(path)) + .withHeaders(httpHeaderFactory.create("DELETE", path, "")); + return execute(requestBuilder); + } + + @Override + public ChefResponse post(String path, String body) { + OngoingRequestBuilder requestBuilder = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefRequestBuilder.newRequestTo(endpoint) + .httpPost(body) + .withPath(getPath(path)) + .withHeaders(httpHeaderFactory.create("POST", path, body)); + return execute(requestBuilder); + } + + @Override + public ChefResponse put(String path, String body) { + OngoingRequestBuilder requestBuilder = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.ChefRequestBuilder.newRequestTo(endpoint) + .httpPut(body) + .withPath(getPath(path)) + .withHeaders(httpHeaderFactory.create("PUT", path, body)); + logger.info("request: PATH: " + path + " body: " + body); + return execute(requestBuilder); + } + + private ChefResponse execute(OngoingRequestBuilder chefRequest) { + try { + if (httpClient == null) { + return ChefResponse.create(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Could not create http client for chef"); + } + HttpResponse response = httpClient.execute(chefRequest.build()); + int statusCode = response.getStatusLine().getStatusCode(); + HttpEntity httpEntity = response.getEntity(); + String responseBody = EntityUtils.toString(httpEntity); + return ChefResponse.create(statusCode, responseBody); + } catch (IOException ex) { + logger.error("Error occured while reading response", ex.getMessage(), ex); + return ChefResponse.create(HttpStatus.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + } catch (URISyntaxException ex) { + logger.error("Malformed URL", ex.getMessage(), ex); + return ChefResponse.create(HttpStatus.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + } + } + + private String getPath(String path) { + return "/organizations/" + organization + path; + } + +} + diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiHeaderFactory.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiHeaderFactory.java new file mode 100644 index 000000000..14485e554 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefApiHeaderFactory.java @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * Modification Copyright (C) 2019 IBM + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import java.util.Date; + +public class ChefApiHeaderFactory { + + private org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.FormattedTimestamp formattedTimestamp = new org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.FormattedTimestamp(); + + public ImmutableMap<String, String> create(String methodName, String path, String body, String userId, + String organizations, String pemPath) { + + String hashedBody = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.Utils.sha1AndBase64(body); + String timeStamp = formattedTimestamp.format(new Date()); + + Builder<String, String> builder = ImmutableMap.builder(); + builder + .put("Content-type", "application/json") + .put("Accept", "application/json") + .put("X-Ops-Timestamp", timeStamp) + .put("X-Ops-UserId", userId) + .put("X-Chef-Version", "12.4.1") + .put("X-Ops-Content-Hash", hashedBody) + .put("X-Ops-Sign", "version=1.0") + .build(); + + String hashedPath = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.Utils.sha1AndBase64("/organizations/" + organizations + path); + + StringBuilder sb = new StringBuilder(); + sb.append("Method:").append(methodName).append("\n"); + sb.append("Hashed Path:").append(hashedPath).append("\n"); + sb.append("X-Ops-Content-Hash:").append(hashedBody).append("\n"); + sb.append("X-Ops-Timestamp:").append(timeStamp).append("\n"); + sb.append("X-Ops-UserId:").append(userId); + + String authString = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.Utils.signWithRSA(sb.toString(), pemPath); + String[] authHeaders = org.onap.ccsdk.sli.adaptors.chef.chefclient.impl.Utils.splitAs60(authString); + + for (int i = 0; i < authHeaders.length; i++) { + builder.put("X-Ops-Authorization-" + (i + 1), authHeaders[i]); + } + + return builder.build(); + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefRequestBuilder.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefRequestBuilder.java new file mode 100644 index 000000000..3a79af35f --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/ChefRequestBuilder.java @@ -0,0 +1,115 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import com.google.common.collect.ImmutableMap; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map.Entry; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; + +final class ChefRequestBuilder { + + private ChefRequestBuilder() { + } + + static OngoingRequestBuilder newRequestTo(String endPoint) { + return new OngoingRequestBuilder(endPoint); + } + + static class OngoingRequestBuilder { + + private HttpRequestBase httpRequestBase; + private String endPoint; + private String path; + private ImmutableMap<String, String> headers; + + private OngoingRequestBuilder(String endPoint) { + this.endPoint = endPoint; + } + + public OngoingRequestBuilder withPath(String path) { + this.path = path; + return this; + } + + public OngoingRequestBuilder httpGet() { + httpRequestBase = new HttpGet(); + return this; + } + + public OngoingRequestBuilder httpDelete() { + httpRequestBase = new HttpDelete(); + return this; + } + + public OngoingRequestBuilder httpPost(String body) { + HttpPost httpPost = new HttpPost(); + httpPost.setEntity(toEntity(body)); + httpRequestBase = httpPost; + return this; + } + + public OngoingRequestBuilder httpPut(String body) { + HttpPut httpPut = new HttpPut(); + httpPut.setEntity(toEntity(body)); + httpRequestBase = httpPut; + return this; + } + + private StringEntity toEntity(String body) { + StringEntity stringEntity = new StringEntity(body, "UTF-8"); + stringEntity.setContentType("application/json"); + return stringEntity; + } + + public OngoingRequestBuilder withHeaders(ImmutableMap<String, String> headers) { + this.headers = headers; + return this; + } + + public HttpRequestBase build() throws URISyntaxException { + setRequestUri(); + setRequestHeaders(); + return httpRequestBase; + } + + private void setRequestUri() throws URISyntaxException { + URI fullPath = new URIBuilder(endPoint).setPath(path).build(); + httpRequestBase.setURI(fullPath); + } + + private void setRequestHeaders() { + for (Entry<String, String> entry : headers.entrySet()) { + httpRequestBase.addHeader(entry.getKey(), entry.getValue()); + } + } + + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/FormattedTimestamp.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/FormattedTimestamp.java new file mode 100644 index 000000000..0d86b6e64 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/FormattedTimestamp.java @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +class FormattedTimestamp { + + String format(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String timeStamp = sdf.format(date); + timeStamp = timeStamp.replace(" ", "T"); + timeStamp = timeStamp + "Z"; + return timeStamp; + } + +}
\ No newline at end of file diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/HttpHeaderFactory.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/HttpHeaderFactory.java new file mode 100644 index 000000000..2ba5ee834 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/HttpHeaderFactory.java @@ -0,0 +1,30 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import com.google.common.collect.ImmutableMap; + +@FunctionalInterface +public interface HttpHeaderFactory { + + ImmutableMap<String, String> create(String methodName, String requestPath, String body); + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/Utils.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/Utils.java new file mode 100644 index 000000000..8f12ab13a --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/chefclient/impl/Utils.java @@ -0,0 +1,101 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * Modifications Copyright (C) 2019 IBM + * ============================================================================= + * + * 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. + * + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.chefclient.impl; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.util.encoders.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Utils { + private static final Logger logger = LoggerFactory.getLogger(Utils.class); + + private Utils() { + } + + public static String sha1AndBase64(String inStr) { + MessageDigest md = null; + byte[] outbty = null; + try { + md = MessageDigest.getInstance("SHA-1"); + byte[] digest = md.digest(inStr.getBytes()); + outbty = Base64.encode(digest); + } catch (NoSuchAlgorithmException nsae) { + logger.error(nsae.getMessage()); + } + return new String(outbty); + } + + public static String signWithRSA(String inStr, String pemPath) { + byte[] outStr = null; + try (BufferedReader br = new BufferedReader(new FileReader(pemPath))) { + Security.addProvider(new BouncyCastleProvider()); + PEMParser pemParser = new PEMParser(br); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + Object object = pemParser.readObject(); + KeyPair kp = converter.getKeyPair((PEMKeyPair) object); + PrivateKey privateKey = kp.getPrivate(); + Signature instance = Signature.getInstance("RSA"); + instance.initSign(privateKey); + instance.update(inStr.getBytes()); + byte[] signature = instance.sign(); + outStr = Base64.encode(signature); + } catch (InvalidKeyException | IOException | SignatureException | NoSuchAlgorithmException e) { + logger.error(e.getMessage()); + } + return new String(outStr); + } + + public static String[] splitAs60(String inStr) { + int count = inStr.length() / 60; + String[] out = new String[count + 1]; + for (int i = 0; i < count; i++) { + String tmp = inStr.substring(i * 60, i * 60 + 60); + out[i] = tmp; + } + if (inStr.length() > count * 60) { + String tmp = inStr.substring(count * 60, inStr.length()); + out[count] = tmp; + } + return out; + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorFactory.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorFactory.java new file mode 100644 index 000000000..59851b463 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorFactory.java @@ -0,0 +1,35 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import org.onap.ccsdk.sli.adaptors.chef.ChefAdaptor; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.ChefApiClientFactory; + +public class ChefAdaptorFactory { + + private ChefApiClientFactory chefApiClientFactory = new ChefApiClientFactory(); + private org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker privateKeyChecker = new org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker(); + + public ChefAdaptor create() { + return new org.onap.ccsdk.sli.adaptors.chef.impl.ChefAdaptorImpl(chefApiClientFactory, privateKeyChecker); + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImpl.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImpl.java new file mode 100644 index 000000000..1582e518c --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/ChefAdaptorImpl.java @@ -0,0 +1,674 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * Modifications Copyright (C) 2019 IBM + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.commons.lang.StringUtils; +import org.json.JSONException; +import org.json.JSONObject; +import org.onap.ccsdk.sli.adaptors.chef.ChefAdaptor; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.ChefApiClientFactory; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefApiClient; +import org.onap.ccsdk.sli.adaptors.chef.chefclient.api.ChefResponse; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * This class implements the {@link ChefAdaptor} interface. This interface + * defines the behaviors that our service provides. + */ +public class ChefAdaptorImpl implements ChefAdaptor { + + @SuppressWarnings("nls") + public static final String MDC_ADAPTOR = "adaptor"; + @SuppressWarnings("nls") + public static final String MDC_SERVICE = "service"; + @SuppressWarnings("nls") + public static final String OUTCOME_FAILURE = "failure"; + @SuppressWarnings("nls") + public static final String OUTCOME_SUCCESS = "success"; + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER = "provider"; + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER_IDENTITY = "identity"; + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER_NAME = "name"; + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER_TENANT = "tenant"; + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER_TENANT_NAME = "name"; + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER_TENANT_PASSWORD = "password"; // NOSONAR + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER_TENANT_USERID = "userid"; + @SuppressWarnings("nls") + public static final String PROPERTY_PROVIDER_TYPE = "type"; + private static final EELFLogger logger = EELFManager.getInstance().getLogger(ChefAdaptorImpl.class); + private static final String CANNOT_FIND_PRIVATE_KEY_STR = "Cannot find the private key in the APPC file system, please load the private key to "; + private static final String POSTING_REQUEST_JSON_ERROR_STR = "Error posting request due to invalid JSON block: "; + private static final String POSTING_REQUEST_ERROR_STR = "Error posting request: "; + private static final String CHEF_CLIENT_RESULT_CODE_STR = "chefClientResult.code"; + private static final String CHEF_SERVER_RESULT_CODE_STR = "chefServerResult.code"; + private static final String CHEF_CLIENT_RESULT_MSG_STR = "chefClientResult.message"; + private static final String CHEF_SERVER_RESULT_MSG_STR = "chefServerResult.message"; + private static final String CHEF_ACTION_STR = "chefAction"; + private static final String NODE_LIST_STR = "NodeList"; + private static final Integer STATUS_OK = 200; + private static final Integer STATUS_PUSHJOBCHECK = 201; + private static final Integer PUSHJOBSTATUS = 202; + private static final Integer KEY_NOTFOUND = 500; + private static final Integer NO_ENVIRONMENT = 404; + private static final Integer APPC_ERRORCODE = 401; + private final ChefApiClientFactory chefApiClientFactory; + private final org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker privateKeyChecker; + // chef server Initialize variable + private String username = StringUtils.EMPTY; + private String clientPrivatekey = StringUtils.EMPTY; + private String chefserver = StringUtils.EMPTY; + private String serverAddress = StringUtils.EMPTY; + private String organizations = StringUtils.EMPTY; + + ChefAdaptorImpl(ChefApiClientFactory chefApiClientFactory, org.onap.ccsdk.sli.adaptors.chef.impl.PrivateKeyChecker privateKeyChecker) { + this.chefApiClientFactory = chefApiClientFactory; + this.privateKeyChecker = privateKeyChecker; + logger.info("Initialize Chef Adaptor"); + } + + @SuppressWarnings("nls") + @Override + public void vnfcEnvironment(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + int code; + logger.info("environment of VNF-C"); + chefInfo(params, ctx); + String env = params.get("Environment"); + logger.info("Environmnet" + env); + if (env.equals(StringUtils.EMPTY)) { + chefServerResult(ctx, STATUS_OK, "Skip Environment block "); + } else { + String message; + if (privateKeyChecker.doesExist(clientPrivatekey)) { + try { + JSONObject envJ = new JSONObject(env); + String envName = envJ.getString("name"); + // update the details of an environment on the Chef server. + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, + clientPrivatekey); + ChefResponse chefResponse = chefApiClient.put("/environments/" + envName, env); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + if (code == NO_ENVIRONMENT) { + // need create a new environment + chefResponse = chefApiClient.post("/environments", env); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + logger.info("requestbody {}", chefResponse.getBody()); + } + chefServerResult(ctx, code, message); + } catch (JSONException e) { + code = APPC_ERRORCODE; + logger.error(POSTING_REQUEST_JSON_ERROR_STR, e); + doFailure(ctx, code, POSTING_REQUEST_JSON_ERROR_STR + e.getMessage()); + } catch (Exception e) { + code = APPC_ERRORCODE; + logger.error(POSTING_REQUEST_ERROR_STR + "vnfcEnvironment", e); + doFailure(ctx, code, POSTING_REQUEST_ERROR_STR + "vnfcEnvironment" + e.getMessage()); + } + } else { + code = KEY_NOTFOUND; + message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; + doFailure(ctx, code, message); + } + } + } + + @SuppressWarnings("nls") + @Override + public void vnfcNodeobjects(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + logger.info("update the nodeObjects of VNF-C"); + int code; + final String LOG_ERR_METHOD_STR = "vnfcNodeobjects"; + try { + chefInfo(params, ctx); + String nodeListS = params.get(NODE_LIST_STR); + String nodeS = params.get("Node"); + if (StringUtils.isNotBlank(nodeListS) && StringUtils.isNotBlank(nodeS)) { + nodeListS = nodeListS.replace("[", StringUtils.EMPTY); + nodeListS = nodeListS.replace("]", StringUtils.EMPTY); + nodeListS = nodeListS.replace("\"", StringUtils.EMPTY); + nodeListS = nodeListS.replace(" ", StringUtils.EMPTY); + List<String> nodes = Arrays.asList(nodeListS.split("\\s*,\\s*")); + code = STATUS_OK; + String message = null; + if (privateKeyChecker.doesExist(clientPrivatekey)) { + ChefApiClient cac = chefApiClientFactory.create(chefserver, organizations, username, + clientPrivatekey); + + for (String nodeName : nodes) { + JSONObject nodeJ = new JSONObject(nodeS); + nodeJ.remove("name"); + nodeJ.put("name", nodeName); + String nodeObject = nodeJ.toString(); + logger.info(nodeObject); + ChefResponse chefResponse = cac.put("/nodes/" + nodeName, nodeObject); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + if (code != STATUS_OK) { + break; + } + } + } else { + code = KEY_NOTFOUND; + message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; + doFailure(ctx, code, message); + } + chefServerResult(ctx, code, message); + } else { + throw new SvcLogicException("Missing Mandatory param(s) Node , NodeList "); + } + } catch (JSONException e) { + code = APPC_ERRORCODE; + logger.error(POSTING_REQUEST_JSON_ERROR_STR + LOG_ERR_METHOD_STR, e); + doFailure(ctx, code, POSTING_REQUEST_JSON_ERROR_STR + LOG_ERR_METHOD_STR + e.getMessage()); + } catch (Exception e) { + code = APPC_ERRORCODE; + logger.error(POSTING_REQUEST_ERROR_STR + LOG_ERR_METHOD_STR, e); + doFailure(ctx, code, POSTING_REQUEST_ERROR_STR + LOG_ERR_METHOD_STR + e.getMessage()); + } + } + + @Override + public void vnfcPushJob(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + int code; + try { + chefInfo(params, ctx); + String nodeList = params.get(NODE_LIST_STR); + if (StringUtils.isNotBlank(nodeList)) { + String isCallback = params.get("CallbackCapable"); + String chefAction = "/pushy/jobs"; + // need work on this + String pushRequest; + if ("true".equals(isCallback)) { + String requestId = params.get("RequestId"); + String callbackUrl = params.get("CallbackUrl"); + pushRequest = "{" + "\"command\": \"chef-client\"," + "\"run_timeout\": 300," + "\"nodes\":" + + nodeList + "," + "\"env\": {\"RequestId\": \"" + requestId + "\", \"CallbackUrl\": \"" + + callbackUrl + "\"}," + "\"capture_output\": true" + "}"; + } else { + pushRequest = "{" + "\"command\": \"chef-client\"," + "\"run_timeout\": 300," + "\"nodes\":" + + nodeList + "," + "\"env\": {}," + "\"capture_output\": true" + "}"; + } + ChefApiClient cac = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); + ChefResponse chefResponse = cac.post(chefAction, pushRequest); + code = chefResponse.getStatusCode(); + logger.info("pushRequest:" + pushRequest); + logger.info("requestbody: {}", chefResponse.getBody()); + String message = chefResponse.getBody(); + if (code == STATUS_PUSHJOBCHECK) { + int startIndex = message.indexOf("jobs") + 5; + int endIndex = message.length() - 2; + String jobID = message.substring(startIndex, endIndex); + ctx.setAttribute("jobID", jobID); + logger.info(jobID); + } + chefServerResult(ctx, code, message); + } else { + throw new SvcLogicException("Missing Mandatory param(s) NodeList "); + } + } catch (Exception e) { + code = APPC_ERRORCODE; + logger.error(POSTING_REQUEST_ERROR_STR + "vnfcPushJob", e); + doFailure(ctx, code, POSTING_REQUEST_ERROR_STR + "vnfcPushJob" + e.getMessage()); + } + } + + @SuppressWarnings("nls") + @Override + public void fetchResults(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + int code = STATUS_OK; + final String LOG_STR = "fetchResults"; + try { + chefInfo(params, ctx); + String nodeListS = params.get(NODE_LIST_STR); + if (StringUtils.isNotBlank(nodeListS)) { + nodeListS = nodeListS.replace("[", StringUtils.EMPTY); + nodeListS = nodeListS.replace("]", StringUtils.EMPTY); + nodeListS = nodeListS.replace("\"", StringUtils.EMPTY); + nodeListS = nodeListS.replace(" ", StringUtils.EMPTY); + List<String> nodes = Arrays.asList(nodeListS.split("\\s*,\\s*")); + JSONObject result = new JSONObject(); + String returnMessage = StringUtils.EMPTY; + + for (String node : nodes) { + String chefAction = "/nodes/" + node; + String message; + if (privateKeyChecker.doesExist(clientPrivatekey)) { + ChefResponse chefResponse = getApiMethod(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + } else { + code = KEY_NOTFOUND; + message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; + doFailure(ctx, code, message); + } + if (code == STATUS_OK) { + JSONObject nodeResult = new JSONObject(); + JSONObject allNodeData = new JSONObject(message); + allNodeData = allNodeData.getJSONObject("normal"); + String attribute = "PushJobOutput"; + + String resultData = allNodeData.optString(attribute, null); + if (resultData == null) { + resultData = Optional.ofNullable(allNodeData.optJSONObject(attribute)) + .map(p -> p.toString()).orElse(null); + if (resultData == null) { + resultData = Optional.ofNullable(allNodeData.optJSONArray(attribute)) + .map(p -> p.toString()).orElse(null); + + if (resultData == null) { + code = KEY_NOTFOUND; + returnMessage = "Cannot find " + attribute; + break; + } + } + } + nodeResult.put(attribute, resultData); + result.put(node, nodeResult); + returnMessage = result.toString(); + } else { + code = KEY_NOTFOUND; + returnMessage = message + " Cannot access: " + node; + doFailure(ctx, code, message); + break; + } + } + + chefServerResult(ctx, code, returnMessage); + } else { + throw new SvcLogicException("Missing Mandatory param(s) NodeList "); + } + } catch (JSONException e) { + code = APPC_ERRORCODE; + logger.error(POSTING_REQUEST_JSON_ERROR_STR + LOG_STR, e); + doFailure(ctx, code, POSTING_REQUEST_JSON_ERROR_STR + LOG_STR + e.getMessage()); + } catch (Exception e) { + code = APPC_ERRORCODE; + logger.error(POSTING_REQUEST_ERROR_STR + LOG_STR, e); + doFailure(ctx, code, POSTING_REQUEST_ERROR_STR + LOG_STR + e.getMessage()); + } + } + + private ChefResponse getApiMethod(String chefAction) { + ChefApiClient cac = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); + return cac.get(chefAction); + } + + /** + * build node object + */ + @SuppressWarnings("nls") + @Override + public void nodeObejctBuilder(Map<String, String> params, SvcLogicContext ctx) { + logger.info("nodeObejctBuilder"); + String name = params.get("nodeobject.name"); + String normal = params.get("nodeobject.normal"); + String overrides = params.get("nodeobject.overrides"); + String defaults = params.get("nodeobject.defaults"); + String runList = params.get("nodeobject.run_list"); + String chefEnvironment = params.get("nodeobject.chef_environment"); + String nodeObject = "{\"json_class\":\"Chef::Node\",\"default\":{" + defaults + + "},\"chef_type\":\"node\",\"run_list\":[" + runList + "],\"override\":{" + overrides + + "},\"normal\": {" + normal + "},\"automatic\":{},\"name\":\"" + name + "\",\"chef_environment\":\"" + + chefEnvironment + "\",}"; + logger.info(nodeObject); + ctx.setAttribute("chef.nodeObject", nodeObject); + } + + /** + * send get request to chef server + */ + private void chefInfo(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + + username = params.get("username"); + serverAddress = params.get("serverAddress"); + organizations = params.get("organizations"); + if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(serverAddress) + && StringUtils.isNotBlank(organizations)) { + chefserver = "https://" + serverAddress + "/organizations/" + organizations; + clientPrivatekey = "/opt/onap/appc/chef/" + serverAddress + "/" + organizations + "/" + username + ".pem"; + logger.info(" clientPrivatekey " + clientPrivatekey); + } else { + doFailure(ctx, APPC_ERRORCODE, "Missing mandatory param(s) such as username, serverAddress, organizations"); + } + } + + @SuppressWarnings("nls") + @Override + public void retrieveData(Map<String, String> params, SvcLogicContext ctx) { + String allConfigData = params.get("allConfig"); + String key = params.get("key"); + String dgContext = params.get("dgContext"); + JSONObject jsonConfig = new JSONObject(allConfigData); + String contextData = fetchContextData(key, jsonConfig); + ctx.setAttribute(dgContext, contextData); + } + + private String fetchContextData(String key, JSONObject jsonConfig) { + try { + return jsonConfig.getString(key); + } catch (Exception e) { + logger.error("Failed getting string value corresponding to " + key + ". Trying to fetch nested json object", + e); + try { + return jsonConfig.getJSONObject(key).toString(); + } catch (Exception ex) { + logger.error("Failed getting json object corresponding to " + key + ". Trying to fetch array", ex); + return jsonConfig.getJSONArray(key).toString(); + } + } + } + + @SuppressWarnings("nls") + @Override + public void combineStrings(Map<String, String> params, SvcLogicContext ctx) { + String string1 = params.get("String1"); + String string2 = params.get("String2"); + String dgContext = params.get("dgContext"); + String contextData = string1 + string2; + ctx.setAttribute(dgContext, contextData); + } + + /** + * Send GET request to chef server + */ + @SuppressWarnings("nls") + + @Override + public void chefGet(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + logger.info("chef get method"); + chefInfo(params, ctx); + String chefAction = params.get(CHEF_ACTION_STR); + int code; + String message; + if (privateKeyChecker.doesExist(clientPrivatekey)) { + ChefResponse chefResponse = getApiMethod(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + } else { + code = KEY_NOTFOUND; + message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; + } + chefServerResult(ctx, code, message); + } + + /** + * Send PUT request to chef server + */ + @SuppressWarnings("nls") + + @Override + public void chefPut(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + chefInfo(params, ctx); + String chefAction = params.get(CHEF_ACTION_STR); + String chefNodeStr = params.get("chefRequestBody"); + int code; + String message; + if (privateKeyChecker.doesExist(clientPrivatekey)) { + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, + clientPrivatekey); + ChefResponse chefResponse = chefApiClient.put(chefAction, chefNodeStr); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + } else { + code = KEY_NOTFOUND; + message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; + } + logger.info(code + " " + message); + chefServerResult(ctx, code, message); + } + + /** + * send Post request to chef server + */ + @Override + public void chefPost(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + chefInfo(params, ctx); + logger.info("chef Post method"); + logger.info(username + " " + clientPrivatekey + " " + chefserver + " " + organizations); + String chefNodeStr = params.get("chefRequestBody"); + String chefAction = params.get(CHEF_ACTION_STR); + int code; + String message; + // should load pem from somewhere else + if (privateKeyChecker.doesExist(clientPrivatekey)) { + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, + clientPrivatekey); + // need pass path into it + // "/nodes/testnode" + ChefResponse chefResponse = chefApiClient.post(chefAction, chefNodeStr); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + } else { + code = KEY_NOTFOUND; + message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; + } + logger.info(code + " " + message); + chefServerResult(ctx, code, message); + } + + /** + * send delete request to chef server + */ + @Override + public void chefDelete(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + logger.info("chef delete method"); + chefInfo(params, ctx); + String chefAction = params.get(CHEF_ACTION_STR); + int code; + String message; + if (privateKeyChecker.doesExist(clientPrivatekey)) { + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, + clientPrivatekey); + ChefResponse chefResponse = chefApiClient.delete(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + } else { + code = KEY_NOTFOUND; + message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; + } + logger.info(code + " " + message); + chefServerResult(ctx, code, message); + } + + /** + * Trigger target vm run chef + */ + @Override + public void trigger(Map<String, String> params, SvcLogicContext svcLogicContext) { + logger.info("Run trigger method"); + String tVmIp = params.get("ip"); + try { + ChefResponse chefResponse = chefApiClientFactory.create(tVmIp, organizations).get(""); + chefClientResult(svcLogicContext, chefResponse.getStatusCode(), chefResponse.getBody()); + svcLogicContext.setAttribute("chefAgent.code", STATUS_OK.toString()); + } catch (Exception e) { + logger.error("An error occurred when executing trigger method", e); + svcLogicContext.setAttribute("chefAgent.code", KEY_NOTFOUND.toString()); + svcLogicContext.setAttribute("chefAgent.message", e.toString()); + } + } + + @SuppressWarnings("nls") + @Override + public void checkPushJob(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + int code; + try { + chefInfo(params, ctx); + String jobID = params.get("jobid"); + String retry = params.get("retryTimes"); + String intrva = params.get("retryInterval"); + if (StringUtils.isNotBlank(jobID) && StringUtils.isNotBlank(retry) && StringUtils.isNotBlank(intrva)) { + + int retryTimes = Integer.parseInt(params.get("retryTimes")); + int retryInterval = Integer.parseInt(params.get("retryInterval")); + String chefAction = "/pushy/jobs/" + jobID; + String message = StringUtils.EMPTY; + String status = StringUtils.EMPTY; + for (int i = 0; i < retryTimes; i++) { + sleepFor(retryInterval); + ChefResponse chefResponse = getApiMethod(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + JSONObject obj = new JSONObject(message); + status = obj.getString("status"); + if (!"running".equals(status)) { + logger.info(i + " time " + code + " " + status); + break; + } + } + resolveSvcLogicAttributes(ctx, message, status); + } else { + throw new SvcLogicException("Missing Mandatory param(s) retryTimes , retryInterval "); + } + } catch (Exception e) { + code = APPC_ERRORCODE; + logger.error("An error occurred when executing checkPushJob method", e); + doFailure(ctx, code, e.getMessage()); + } + } + + private void resolveSvcLogicAttributes(SvcLogicContext svcLogic, String message, String status) { + if ("complete".equals(status)) { + if (hasFailedNode(message)) { + String finalMessage = "PushJob Status Complete but check failed nodes in the message :" + message; + svcLogic.setAttribute(CHEF_SERVER_RESULT_CODE_STR, APPC_ERRORCODE.toString()); + svcLogic.setAttribute(CHEF_SERVER_RESULT_MSG_STR, finalMessage); + } else { + svcLogic.setAttribute(CHEF_SERVER_RESULT_CODE_STR, STATUS_OK.toString()); + svcLogic.setAttribute(CHEF_SERVER_RESULT_MSG_STR, message); + } + } else if ("running".equals(status)) { + svcLogic.setAttribute(CHEF_SERVER_RESULT_CODE_STR, PUSHJOBSTATUS.toString()); + svcLogic.setAttribute(CHEF_SERVER_RESULT_MSG_STR, "chef client runtime out"); + } else { + svcLogic.setAttribute(CHEF_SERVER_RESULT_CODE_STR, KEY_NOTFOUND.toString()); + svcLogic.setAttribute(CHEF_SERVER_RESULT_MSG_STR, message); + } + } + + private Boolean hasFailedNode(String message) { + try { + JSONObject messageJson = new JSONObject(message); + JSONObject node = messageJson.getJSONObject("nodes"); + final String failed = "failed"; + if (node == null) { + logger.debug("Status Complete but node details in the message is null : " + message); + return Boolean.TRUE; + } + if (node.has(failed) && !(node.isNull(failed)) && (node.getJSONArray(failed).length() != 0)) { + logger.debug("Status Complete but one or more Failed nodes ....FAILURE " + message); + return Boolean.TRUE; + } + logger.debug("Status Complete and no failed nodes ....SUCCESS " + message); + return Boolean.FALSE; + } catch (JSONException e) { + logger.error("Exception occured in hasFailedNode", e); + throw new JSONException("Exception occured in hasFailedNode" + e.getMessage()); + } + + } + + private void sleepFor(int retryInterval) { + try { + Thread.sleep(retryInterval); // 1000 milliseconds is one second. + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + + @SuppressWarnings("nls") + @Override + public void pushJob(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + int code; + try { + chefInfo(params, ctx); + String pushRequest = params.get("pushRequest"); + String chefAction = "/pushy/jobs"; + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, + clientPrivatekey); + ChefResponse chefResponse = chefApiClient.post(chefAction, pushRequest); + code = chefResponse.getStatusCode(); + String message = chefResponse.getBody(); + if (code == STATUS_PUSHJOBCHECK) { + int startIndex = message.indexOf("jobs") + 6; + int endIndex = message.length() - 2; + String jobID = message.substring(startIndex, endIndex); + ctx.setAttribute("jobID", jobID); + logger.info(jobID); + } + chefServerResult(ctx, code, message); + } catch (Exception e) { + code = APPC_ERRORCODE; + logger.error("An error occurred when executing pushJob method", e); + doFailure(ctx, code, e.getMessage()); + } + } + + @SuppressWarnings("static-method") + private void chefServerResult(SvcLogicContext svcLogicContext, int code, String message) { + initSvcLogic(svcLogicContext, code, message, "server"); + } + + @SuppressWarnings("static-method") + private void chefClientResult(SvcLogicContext svcLogicContext, int code, String message) { + initSvcLogic(svcLogicContext, code, message, "client"); + } + + private void initSvcLogic(SvcLogicContext svcLogicContext, int code, String message, String target) { + + String codeStr = "server".equals(target) ? CHEF_SERVER_RESULT_CODE_STR : CHEF_CLIENT_RESULT_CODE_STR; + String messageStr = "client".equals(target) ? CHEF_CLIENT_RESULT_MSG_STR : CHEF_SERVER_RESULT_MSG_STR; + svcLogicContext.setStatus(OUTCOME_SUCCESS); + svcLogicContext.setAttribute(codeStr, Integer.toString(code)); + svcLogicContext.setAttribute(messageStr, message); + logger.info(codeStr + ": " + svcLogicContext.getAttribute(codeStr)); + logger.info(messageStr + ": " + svcLogicContext.getAttribute(messageStr)); + } + + @SuppressWarnings("static-method") + private void doFailure(SvcLogicContext svcLogic, int code, String message) throws SvcLogicException { + + String cutMessage = message.contains("\n") ? message.substring(message.indexOf('\n')) : message; + svcLogic.setStatus(OUTCOME_FAILURE); + svcLogic.setAttribute(CHEF_SERVER_RESULT_CODE_STR, Integer.toString(code)); + svcLogic.setAttribute(CHEF_SERVER_RESULT_MSG_STR, cutMessage); + throw new SvcLogicException("Chef Adaptor error:" + cutMessage); + } + +} diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/PrivateKeyChecker.java b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/PrivateKeyChecker.java new file mode 100644 index 000000000..d2cafdff6 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/java/org/onap/ccsdk/sli/adaptors/chef/impl/PrivateKeyChecker.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.adaptors.chef.impl; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import java.io.File; + +class PrivateKeyChecker { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(PrivateKeyChecker.class); + + Boolean doesExist(String clientPrivateKeyPath) { + File f = new File(clientPrivateKeyPath); + if (f.exists()) { + logger.info("Key exists"); + return true; + } else { + logger.info("Key doesn't exists"); + return false; + } + } + +}
\ No newline at end of file diff --git a/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/resources/chef-adaptor.properties b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/resources/chef-adaptor.properties new file mode 100644 index 000000000..2a8bbe869 --- /dev/null +++ b/adaptors/chef-adaptor/chef-adaptor-bundle/src/main/resources/chef-adaptor.properties @@ -0,0 +1,89 @@ +### +# ============LICENSE_START======================================================= +# ONAP : APPC +# ================================================================================ +# Copyright (C) 2017-2018 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. +# +# ============LICENSE_END========================================================= +### +# +# Default properties for the APP-C Provider Adaptor +# +# ------------------------------------------------------------------------------------------------- +# +# Define the name and path of any user-provided configuration (bootstrap) file that can be loaded +# to supply configuration options +org.onap.appc.bootstrap.file=appc.properties +org.onap.appc.bootstrap.path=/opt/onap/appc/data/properties,${user.home},. +appc.application.name=APPC +# +# Define the message resource bundle name to be loaded +org.onap.appc.resources=org/onap/appc/i18n/MessageResources +# +# The name of the adaptor. +org.onap.appc.provider.adaptor.name=org.onap.appc.appc_provider_adaptor +# +# Set up the logging environment +# +org.onap.appc.logging.file=org/onap/appc/logback.xml +org.onap.appc.logging.path=${user.home};etc;../etc +org.onap.appc.logger=org.onap.appc +org.onap.appc.security.logger=org.onap.appc.security +# +# The minimum and maximum provider/tenant context pool sizes. Min=1 means that as soon +# as the provider/tenant is referenced a Context is opened and added to the pool. Max=0 +# means that the upper bound on the pool is unbounded. +org.onap.appc.provider.min.pool=1 +org.onap.appc.provider.max.pool=0 +# +# The following properties are used to configure the retry logic for connection to the +# IaaS provider(s). The retry delay property is the amount of time, in seconds, the +# application waits between retry attempts. The retry limit is the number of retries +# that are allowed before the request is failed. +org.onap.appc.provider.retry.delay=30 +org.onap.appc.provider.retry.limit=10 +# +# The trusted hosts list for SSL access when a certificate is not provided. +# +provider.trusted.hosts=* +# +# The amount of time, in seconds, to wait for a server state change (start->stop, stop->start, etc). +# If the server does not change state to a valid state within the alloted time, the operation +# fails. +org.onap.appc.server.state.change.timeout=300 +# +# The amount of time to wait, in seconds, between subsequent polls to the OpenStack provider +# to refresh the status of a resource we are waiting on. +# +org.onap.appc.openstack.poll.interval=20 +# +# The connection information to connect to the provider we are using. These properties +# are "structured" properties, in that the name is a compound name, where the nodes +# of the name can be ordered (1, 2, 3, ...). All of the properties with the same ordinal +# position are defining the same entity. For example, provider1.type and provider1.name +# are defining the same provider, whereas provider2.name and provider2.type are defining +# the values for a different provider. Any number of providers can be defined in this +# way. +# +# Don't change these 2 right now since they are hard coded in the DG +#provider1.type=appc +#provider1.name=appc +#These you can change +#provider1.identity=appc +#provider1.tenant1.name=appc +#provider1.tenant1.userid=appc +#provider1.tenant1.password=appc |