From 3d170c6e14976549cd2edc405c5c242110bff2ff Mon Sep 17 00:00:00 2001 From: Sirisha_Manchikanti Date: Tue, 14 Dec 2021 18:12:07 +0000 Subject: [PATCH] Add ControlLoop distribution to runtime This commit unpacks the csar received either from SDC or for local verification from file-system, prepares the toscaservicetemplate with needed node-types and data-types, forwards the template to controlloop runtime components for commisioning of controlloop. Issue-ID: POLICY-3808 Signed-off-by: Sirisha_Manchikanti Change-Id: Ib3600542aca7b32ae19242c2f924bdaf2ab870a8 --- .../resources/parameters/TestConfigParams.json | 64 ++ .../src/main/resources/etc/defaultConfig.json | 37 +- .../src/main/resources/etc/s3pConfig.json | 37 +- .../api/LifecycleApiControlLoopForwarder.java | 121 +++ ...LifecycleApiControlLoopForwarderParameters.java | 47 + ...cycleApiControlLoopForwarderParametersTest.java | 88 ++ .../api/LifecycleApiControlLoopForwarderTest.java | 115 +++ .../LifecycleApiControlLoopSimulatorEndpoint.java | 54 ++ .../LifecycleApiControlLoopSimulatorMain.java | 67 ++ ...LifecycleApiControlLoopForwarderParameters.json | 10 + ...leApiControlLoopForwarderParametersInvalid.json | 10 + .../resources/parameters/sample_control_loop.json | 1022 ++++++++++++++++++++ .../policy/file/ControlLoopDecoderFileInCsar.java | 117 +++ ...ControlLoopDecoderFileInCsarParameterGroup.java | 44 + .../file/PolicyDecoderFileInCsarToPolicy.java | 80 +- .../distribution/reception/util/ReceptionUtil.java | 92 ++ ...rolLoopDecoderFileInCsarParameterGroupTest.java | 67 ++ .../file/ControlLoopDecoderFileInCsarTest.java | 96 ++ .../FileInCsarControlLoopDecoderParameters.json | 3 + ...eInCsarControlLoopDecoderParametersInvalid.json | 3 + .../service-Sampleservice-controlloop.csar | Bin 0 -> 79322 bytes .../setup/distribution/etc/defaultConfig.json | 37 +- 22 files changed, 2130 insertions(+), 81 deletions(-) create mode 100644 main/src/test/resources/parameters/TestConfigParams.json create mode 100644 plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarder.java create mode 100644 plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParameters.java create mode 100644 plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParametersTest.java create mode 100644 plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderTest.java create mode 100644 plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorEndpoint.java create mode 100644 plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorMain.java create mode 100644 plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParameters.json create mode 100644 plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParametersInvalid.json create mode 100644 plugins/forwarding-plugins/src/test/resources/parameters/sample_control_loop.json create mode 100644 plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsar.java create mode 100644 plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroup.java create mode 100644 plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java create mode 100644 plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroupTest.java create mode 100644 plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarTest.java create mode 100644 plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParameters.json create mode 100644 plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParametersInvalid.json create mode 100644 plugins/reception-plugins/src/test/resources/service-Sampleservice-controlloop.csar diff --git a/main/src/test/resources/parameters/TestConfigParams.json b/main/src/test/resources/parameters/TestConfigParams.json new file mode 100644 index 00000000..0bb50d65 --- /dev/null +++ b/main/src/test/resources/parameters/TestConfigParams.json @@ -0,0 +1,64 @@ +{ + "name":"SDCDistributionGroup", + "restServerParameters":{ + "host":"0.0.0.0", + "port":54290, + "userName":"healthcheck", + "password":"zb!XztG34" + }, + "receptionHandlerParameters":{ + "DummyReceptionHandler":{ + "receptionHandlerType":"DummyReceptionHandler", + "receptionHandlerClassName":"org.onap.policy.distribution.main.testclasses.DummyReceptionHandler", + "receptionHandlerConfigurationName":"dummyReceptionHandlerConfiguration", + "pluginHandlerParameters":{ + "policyDecoders":{ + "DummyDecoder":{ + "decoderType":"DummyDecoder", + "decoderClassName":"org.onap.policy.distribution.main.testclasses.DummyDecoder", + "decoderConfigurationParameters": "dummyDecoderConfiguration" + } + }, + "policyForwarders":{ + "DummyForwarder":{ + "forwarderType":"DummyForwarder", + "forwarderClassName":"org.onap.policy.distribution.main.testclasses.DummyPolicyForwarder", + "forwarderConfigurationParameters": "dummyConfiguration" + } + } + } + } + }, + "receptionHandlerConfigurationParameters":{ + "dummyReceptionHandlerConfiguration":{ + "parameterClassName":"org.onap.policy.distribution.main.testclasses.DummyReceptionHandlerParameterGroup", + "parameters":{ + "myStringParameter": "stringValue", + "myIntegerParameter":20, + "myBooleanParameter": true + } + } + }, + "policyForwarderConfigurationParameters":{ + "dummyConfiguration":{ + "parameterClassName":"org.onap.policy.distribution.main.testclasses.DummyPolicyForwarderParameterGroup", + "parameters":{ + "useHttps": false, + "hostname": "192.168.99.100", + "port": 8081, + "userName": "user", + "password": "pw123", + "isManaged": true + } + } + }, + "policyDecoderConfigurationParameters":{ + "dummyDecoderConfiguration":{ + "parameterClassName":"org.onap.policy.distribution.main.testclasses.DummyPolicyDecoderParameterGroup", + "parameters":{ + "policyName": "SamplePolicy", + "policyType": "DUMMY" + } + } + } +} diff --git a/packages/policy-distribution-tarball/src/main/resources/etc/defaultConfig.json b/packages/policy-distribution-tarball/src/main/resources/etc/defaultConfig.json index bda3b11f..4c14f75b 100644 --- a/packages/policy-distribution-tarball/src/main/resources/etc/defaultConfig.json +++ b/packages/policy-distribution-tarball/src/main/resources/etc/defaultConfig.json @@ -19,13 +19,23 @@ "decoderType": "ToscaPolicyDecoder", "decoderClassName": "org.onap.policy.distribution.reception.decoding.policy.file.PolicyDecoderFileInCsarToPolicy", "decoderConfigurationName": "toscaPolicyDecoderConfiguration" + }, + "ToscaControlLoopDecoder": { + "decoderType": "ToscaControlLoopDecoder", + "decoderClassName": "org.onap.policy.distribution.reception.decoding.policy.file.ControlLoopDecoderFileInCsar", + "decoderConfigurationName": "toscaControlLoopDecoderConfiguration" } }, "policyForwarders": { - "LifeCycleApiForwarder": { - "forwarderType": "LifeCycleAPI", + "LifeCycleApiPolicyForwarder": { + "forwarderType": "LifeCyclePolicyAPI", "forwarderClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiPolicyForwarder", - "forwarderConfigurationName": "lifecycleApiConfiguration" + "forwarderConfigurationName": "lifecycleApiPolicyConfiguration" + }, + "LifeCycleApiControlLoopForwarder": { + "forwarderType": "LifeCycleControlLoopAPI", + "forwarderClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiControlLoopForwarder", + "forwarderConfigurationName": "lifecycleApiControlLoopConfiguration" } } } @@ -65,10 +75,16 @@ "policyFileName": "tosca_policy", "policyTypeFileName": "tosca_policy_type" } + }, + "toscaControlLoopDecoderConfiguration": { + "parameterClassName": "org.onap.policy.distribution.reception.decoding.policy.file.ControlLoopDecoderFileInCsarParameterGroup", + "parameters": { + "controlLoopType": "controlloop" + } } }, "policyForwarderConfigurationParameters": { - "lifecycleApiConfiguration": { + "lifecycleApiPolicyConfiguration": { "parameterClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiForwarderParameters", "parameters": { "apiParameters": { @@ -89,6 +105,19 @@ }, "deployPolicies": true } + }, + "lifecycleApiControlLoopConfiguration": { + "parameterClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiControlLoopForwarderParameters", + "parameters": { + "controlLoopRuntimeParameters": { + "clientName": "policy-clamp-cl-runtime", + "hostname": "policy-clamp-cl-runtime", + "port": 6969, + "useHttps": true, + "userName": "policyadmin", + "password": "zb!XztG34" + } + } } } } diff --git a/packages/policy-distribution-tarball/src/main/resources/etc/s3pConfig.json b/packages/policy-distribution-tarball/src/main/resources/etc/s3pConfig.json index 7059f5b4..cea48547 100644 --- a/packages/policy-distribution-tarball/src/main/resources/etc/s3pConfig.json +++ b/packages/policy-distribution-tarball/src/main/resources/etc/s3pConfig.json @@ -17,13 +17,23 @@ "decoderType": "ToscaPolicyDecoder", "decoderClassName": "org.onap.policy.distribution.reception.decoding.policy.file.PolicyDecoderFileInCsarToPolicy", "decoderConfigurationName": "toscaPolicyDecoderConfiguration" + }, + "ToscaControlLoopDecoder": { + "decoderType": "ToscaControlLoopDecoder", + "decoderClassName": "org.onap.policy.distribution.reception.decoding.policy.file.ControlLoopDecoderFileInCsar", + "decoderConfigurationName": "toscaControlLoopDecoderConfiguration" } }, "policyForwarders": { - "LifeCycleApiForwarder": { - "forwarderType": "LifeCycleAPI", + "LifeCycleApiPolicyForwarder": { + "forwarderType": "LifeCyclePolicyAPI", "forwarderClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiPolicyForwarder", - "forwarderConfigurationName": "lifecycleApiConfiguration" + "forwarderConfigurationName": "lifecycleApiPolicyConfiguration" + }, + "LifeCycleApiControlLoopForwarder": { + "forwarderType": "LifeCycleControlLoopAPI", + "forwarderClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiControlLoopForwarder", + "forwarderConfigurationName": "lifecycleApiControlLoopConfiguration" } } } @@ -44,10 +54,16 @@ "policyFileName": "tosca_policy", "policyTypeFileName": "tosca_policy_type" } + }, + "toscaControlLoopDecoderConfiguration": { + "parameterClassName": "org.onap.policy.distribution.reception.decoding.policy.file.ControlLoopDecoderFileInCsarParameterGroup", + "parameters": { + "controlLoopType": "controlloop" + } } }, "policyForwarderConfigurationParameters": { - "lifecycleApiConfiguration": { + "lifecycleApiPolicyConfiguration": { "parameterClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiForwarderParameters", "parameters": { "apiParameters": { @@ -68,6 +84,19 @@ }, "deployPolicies": true } + }, + "lifecycleApiControlLoopConfiguration": { + "parameterClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiControlLoopForwarderParameters", + "parameters": { + "controlLoopRuntimeParameters": { + "clientName": "policy-clamp-cl-runtime", + "hostname": "policy-clamp-cl-runtime", + "port": 6969, + "useHttps": true, + "userName": "policyadmin", + "password": "zb!XztG34" + } + } } } } diff --git a/plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarder.java b/plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarder.java new file mode 100644 index 00000000..605c68cd --- /dev/null +++ b/plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarder.java @@ -0,0 +1,121 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.forwarding.lifecycle.api; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.onap.policy.common.endpoints.http.client.HttpClient; +import org.onap.policy.common.endpoints.http.client.HttpClientConfigException; +import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance; +import org.onap.policy.common.parameters.ParameterService; +import org.onap.policy.distribution.forwarding.PolicyForwarder; +import org.onap.policy.distribution.forwarding.PolicyForwardingException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provides an implementation of {@link PolicyForwarder} interface for forwarding the + * controlloop design template to the life cycle api's of controlloop components. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +public class LifecycleApiControlLoopForwarder implements PolicyForwarder { + + private static final String COMMISSION_CONTROLLOOP_URI = "/onap/controlloop/v2/commission"; + private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleApiControlLoopForwarder.class); + + private LifecycleApiControlLoopForwarderParameters forwarderParameters; + private HttpClient controlLoopClient; + + /** + * {@inheritDoc}. + */ + @Override + public void configure(final String parameterGroupName) throws HttpClientConfigException { + forwarderParameters = ParameterService.get(parameterGroupName); + + controlLoopClient = HttpClientFactoryInstance.getClientFactory().build( + forwarderParameters.getControlLoopRuntimeParameters()); + } + + /** + * {@inheritDoc}. + */ + @Override + public void forward(final Collection entities) throws PolicyForwardingException { + final List failedEntities = new ArrayList<>(); + for (final ToscaEntity entity : entities) { + forwardSingleEntity(failedEntities, entity); + } + if (!failedEntities.isEmpty()) { + throw new PolicyForwardingException( + "Failed forwarding the following entities: " + Arrays.toString(failedEntities.toArray())); + } + } + + private void forwardSingleEntity(final List failedEntities, final ToscaEntity entity) { + try { + if (entity instanceof ToscaServiceTemplate) { + final var toscaServiceTemplate = (ToscaServiceTemplate) entity; + if (null != toscaServiceTemplate.getToscaTopologyTemplate() + && null != toscaServiceTemplate.getNodeTypes() + && null != toscaServiceTemplate.getDataTypes()) { + commissionControlLoop(toscaServiceTemplate); + } + } else { + throw new PolicyForwardingException("The entity is not of type ToscaServiceTemplate - " + entity); + } + } catch (final Exception exp) { + LOGGER.error(exp.getMessage(), exp); + failedEntities.add(entity); + } + } + + private Response commissionControlLoop(final ToscaServiceTemplate toscaServiceTemplate) + throws PolicyForwardingException { + return invokeHttpClient(Entity.entity(toscaServiceTemplate, MediaType.APPLICATION_JSON), + COMMISSION_CONTROLLOOP_URI); + } + + private Response invokeHttpClient(final Entity entity, final String path) + throws PolicyForwardingException { + var response = controlLoopClient.post(path, entity, Map.of(HttpHeaders.ACCEPT, + MediaType.APPLICATION_JSON, HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)); + if (response.getStatus() / 100 != 2) { + LOGGER.error( + "Invocation of path {} failed for entity {}. Response status: {}, Response status info: {}", + path, entity, response.getStatus(), response.getStatusInfo()); + throw new PolicyForwardingException("Failed creating the entity - " + entity); + } + return response; + } +} + diff --git a/plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParameters.java b/plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParameters.java new file mode 100644 index 00000000..d1abce71 --- /dev/null +++ b/plugins/forwarding-plugins/src/main/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParameters.java @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.forwarding.lifecycle.api; + +import lombok.Getter; +import org.onap.policy.common.endpoints.parameters.RestClientParameters; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; +import org.onap.policy.common.parameters.annotations.Valid; +import org.onap.policy.distribution.main.parameters.PolicyForwarderConfigurationParameterGroup; + +/** + * Holds the parameters for the {@link LifecycleApiControlLoopForwarder}. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +@Getter +@NotNull +@NotBlank +public class LifecycleApiControlLoopForwarderParameters extends PolicyForwarderConfigurationParameterGroup { + public static final String CONTROLLOOP_FORWARDER_PLUGIN_CLASS = LifecycleApiControlLoopForwarder.class.getName(); + + private @Valid RestClientParameters controlLoopRuntimeParameters; + + public LifecycleApiControlLoopForwarderParameters() { + super(LifecycleApiControlLoopForwarderParameters.class.getSimpleName()); + } +} diff --git a/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParametersTest.java b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParametersTest.java new file mode 100644 index 00000000..ba8d9bfb --- /dev/null +++ b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderParametersTest.java @@ -0,0 +1,88 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.forwarding.lifecycle.api; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.onap.policy.common.parameters.ValidationStatus; +import org.onap.policy.distribution.forwarding.testclasses.CommonTestData; + +/** + * Class to perform unit test of {@link LifecycleApiControlLoopForwarderParameters}. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +public class LifecycleApiControlLoopForwarderParametersTest { + + private static final String CONTROLLOOP_RUNTIME_HOST_NAME = "0.0.0.0"; + private static final int CONTROLLOOP_RUNTIME_PORT = 6969; + private static final String CONTROLLOOP_RUNTIME_USER = "policyadmin"; + private static final String CONTROLLOOP_RUNTIME_PASSWORD = "zb!XztG34"; + + + @Test + public void testValidParameters() { + final LifecycleApiControlLoopForwarderParameters configurationParameters = + CommonTestData.getPolicyForwarderParameters( + "src/test/resources/parameters/LifecycleApiControlLoopForwarderParameters.json", + LifecycleApiControlLoopForwarderParameters.class); + + assertEquals(LifecycleApiControlLoopForwarderParameters.class.getSimpleName(), + configurationParameters.getName()); + + assertEquals(CONTROLLOOP_RUNTIME_HOST_NAME, + configurationParameters.getControlLoopRuntimeParameters().getHostname()); + assertEquals(CONTROLLOOP_RUNTIME_PORT, + configurationParameters.getControlLoopRuntimeParameters().getPort()); + assertFalse(configurationParameters.getControlLoopRuntimeParameters().isUseHttps()); + assertEquals(CONTROLLOOP_RUNTIME_USER, + configurationParameters.getControlLoopRuntimeParameters().getUserName()); + assertEquals(CONTROLLOOP_RUNTIME_PASSWORD, + configurationParameters.getControlLoopRuntimeParameters().getPassword()); + + assertThat(configurationParameters.validate().getResult()).isNull(); + assertEquals(ValidationStatus.CLEAN, configurationParameters.validate().getStatus()); + } + + @Test + public void testInvalidParameters() { + final LifecycleApiForwarderParameters configurationParameters = + CommonTestData.getPolicyForwarderParameters( + "src/test/resources/parameters/LifecycleApiPolicyForwarderParametersInvalid.json", + LifecycleApiForwarderParameters.class); + + assertEquals(ValidationStatus.INVALID, configurationParameters.validate().getStatus()); + } + + @Test + public void testEmptyParameters() { + final LifecycleApiForwarderParameters configurationParameters = + CommonTestData.getPolicyForwarderParameters("src/test/resources/parameters/EmptyParameters.json", + LifecycleApiForwarderParameters.class); + + assertEquals(ValidationStatus.INVALID, configurationParameters.validate().getStatus()); + } +} diff --git a/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderTest.java b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderTest.java new file mode 100644 index 00000000..047482ef --- /dev/null +++ b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/lifecycle/api/LifecycleApiControlLoopForwarderTest.java @@ -0,0 +1,115 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.forwarding.lifecycle.api; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.ArrayList; +import java.util.Collection; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.policy.common.parameters.ParameterGroup; +import org.onap.policy.common.parameters.ParameterService; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.network.NetworkUtil; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.distribution.forwarding.PolicyForwardingException; +import org.onap.policy.distribution.forwarding.testclasses.CommonTestData; +import org.onap.policy.distribution.forwarding.testclasses.LifecycleApiControlLoopSimulatorMain; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; + +/** + * Class to perform unit test of {@link LifecycleApiControlLoopForwarder}. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +public class LifecycleApiControlLoopForwarderTest { + + private static final String CONTROL_LOOP = "src/test/resources/parameters/sample_control_loop.json"; + private final StandardCoder standardCoder = new StandardCoder(); + private static final LifecycleApiControlLoopSimulatorMain simulator = new LifecycleApiControlLoopSimulatorMain(); + + /** + * Set up. + * + * @throws CoderException if any error occurs + * @throws PolicyForwardingException if any error occurs + * @throws InterruptedException if any error occurs + */ + @BeforeClass + public static void setUp() throws PolicyForwardingException, CoderException, InterruptedException { + final ParameterGroup parameterGroup = CommonTestData.getPolicyForwarderParameters( + "src/test/resources/parameters/LifecycleApiControlLoopForwarderParameters.json", + LifecycleApiControlLoopForwarderParameters.class); + ParameterService.register(parameterGroup); + simulator.startLifecycycleApiSimulator(); + if (!NetworkUtil.isTcpPortOpen("0.0.0.0", 6969, 50, 200L)) { + throw new IllegalStateException("cannot connect to port 6969"); + } + } + + /** + * Tear down. + */ + @AfterClass + public static void tearDown() { + ParameterService.deregister(LifecycleApiControlLoopForwarderParameters.class.getSimpleName()); + simulator.stopLifecycycleApiSimulator(); + } + + @Test + public void testForwardControlLoopUsingSimulator() throws Exception { + assertThatCode(() -> { + final ToscaServiceTemplate toscaServiceTemplate = + standardCoder.decode(ResourceUtils.getResourceAsString(CONTROL_LOOP), ToscaServiceTemplate.class); + + final LifecycleApiControlLoopForwarder forwarder = new LifecycleApiControlLoopForwarder(); + forwarder.configure(LifecycleApiControlLoopForwarderParameters.class.getSimpleName()); + + final Collection controlLoopList = new ArrayList<>(); + controlLoopList.add(toscaServiceTemplate); + + forwarder.forward(controlLoopList); + + }).doesNotThrowAnyException(); + } + + @Test + public void testForwardControlLoopFailureUsingSimulator() throws Exception { + + final ToscaEntity toscaEntity = new ToscaEntity(); + toscaEntity.setName("FailureCase"); + + final LifecycleApiControlLoopForwarder forwarder = new LifecycleApiControlLoopForwarder(); + forwarder.configure(LifecycleApiControlLoopForwarderParameters.class.getSimpleName()); + + final Collection controlLoopList = new ArrayList<>(); + controlLoopList.add(toscaEntity); + + assertThatThrownBy(() -> forwarder.forward(controlLoopList)).isInstanceOf(PolicyForwardingException.class) + .hasMessageContaining("Failed forwarding the following entities:"); + } +} diff --git a/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorEndpoint.java b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorEndpoint.java new file mode 100644 index 00000000..d361d166 --- /dev/null +++ b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorEndpoint.java @@ -0,0 +1,54 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.forwarding.testclasses; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; + +/** + * Class to provide rest end points for LifecycleApiControlLoopSimulator. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +@Path("/onap") +@Produces(MediaType.APPLICATION_JSON) +public class LifecycleApiControlLoopSimulatorEndpoint { + + /** + * ControlLoop commissioning end-point. + * + * @param body the post body + * @return the response object + */ + @POST + @Path("/controlloop/v2/commission") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response commissionControlLoop(final ToscaServiceTemplate body) { + return Response.status(Response.Status.OK).entity(body).build(); + } +} diff --git a/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorMain.java b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorMain.java new file mode 100644 index 00000000..a60fda1d --- /dev/null +++ b/plugins/forwarding-plugins/src/test/java/org/onap/policy/distribution/forwarding/testclasses/LifecycleApiControlLoopSimulatorMain.java @@ -0,0 +1,67 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.forwarding.testclasses; + +import org.onap.policy.common.endpoints.http.server.RestServer; +import org.onap.policy.common.endpoints.parameters.RestServerParameters; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.distribution.forwarding.PolicyForwardingException; +import org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiControlLoopForwarder; +import org.onap.policy.distribution.main.rest.aaf.AafDistributionFilter; + +/** + * The class for starting/stopping simulator for testing {@link LifecycleApiControlLoopForwarder} . + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +public class LifecycleApiControlLoopSimulatorMain { + private RestServer restServer; + + /** + * Starts the simulator. + * + * @throws PolicyForwardingException if error occurs + * @throws CoderException if error occurs + */ + public void startLifecycycleApiSimulator() throws PolicyForwardingException, CoderException { + final StandardCoder standardCoder = new StandardCoder(); + final RestServerParameters restServerParameters = standardCoder.decode( + ResourceUtils.getResourceAsString("src/test/resources/parameters/RestServerParameters.json"), + RestServerParameters.class); + restServer = new RestServer(restServerParameters, AafDistributionFilter.class, + LifecycleApiControlLoopSimulatorEndpoint.class); + if (!restServer.start()) { + throw new PolicyForwardingException("Failed to start rest simulator. Check log for more details..."); + } + } + + /** + * Shut down Execution. + */ + public void stopLifecycycleApiSimulator() { + if (restServer != null) { + restServer.stop(); + } + } +} diff --git a/plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParameters.json b/plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParameters.json new file mode 100644 index 00000000..1eef9773 --- /dev/null +++ b/plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParameters.json @@ -0,0 +1,10 @@ +{ + "controlLoopRuntimeParameters": { + "clientName": "policy-clamp-cl-runtime", + "hostname": "0.0.0.0", + "port": 6969, + "useHttps": false, + "userName": "policyadmin", + "password": "zb!XztG34" + } +} diff --git a/plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParametersInvalid.json b/plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParametersInvalid.json new file mode 100644 index 00000000..5ac0c4ee --- /dev/null +++ b/plugins/forwarding-plugins/src/test/resources/parameters/LifecycleApiControlLoopForwarderParametersInvalid.json @@ -0,0 +1,10 @@ +{ + "controlLoopRuntimeParameters": { + "clientName": "policy-clamp-cl-runtime", + "hostname": "", + "port": 6969, + "useHttps": false, + "userName": "policyadmin", + "password": "zb!XztG34" + } +} diff --git a/plugins/forwarding-plugins/src/test/resources/parameters/sample_control_loop.json b/plugins/forwarding-plugins/src/test/resources/parameters/sample_control_loop.json new file mode 100644 index 00000000..20dedc50 --- /dev/null +++ b/plugins/forwarding-plugins/src/test/resources/parameters/sample_control_loop.json @@ -0,0 +1,1022 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_3", + "data_types": { + "onap.datatypes.ToscaConceptIdentifier": { + "derived_from": "tosca.datatypes.Root", + "properties": { + "name": { + "type": "string", + "required": true + }, + "version": { + "type": "string", + "required": true + } + } + }, + "onap.datatype.controlloop.Target": { + "derived_from": "tosca.datatypes.Root", + "description": "Definition for a entity in A&AI to perform a control loop operation on", + "properties": { + "targetType": { + "type": "string", + "description": "Category for the target type", + "required": true, + "constraints": [ + { + "valid_values": [ + "VNF", + "VM", + "VFMODULE", + "PNF" + ] + } + ] + }, + "entityIds": { + "type": "map", + "description": "Map of values that identify the resource. If none are provided, it is assumed that the\nentity that generated the ONSET event will be the target.\n", + "required": false, + "metadata": { + "clamp_possible_values": "ClampExecution:CSAR_RESOURCES" + }, + "entry_schema": { + "type": "string" + } + } + } + }, + "onap.datatype.controlloop.Actor": { + "derived_from": "tosca.datatypes.Root", + "description": "An actor/operation/target definition", + "properties": { + "actor": { + "type": "string", + "description": "The actor performing the operation.", + "required": true, + "metadata": { + "clamp_possible_values": "Dictionary:DefaultActors,ClampExecution:CDS/actor" + } + }, + "operation": { + "type": "string", + "description": "The operation the actor is performing.", + "metadata": { + "clamp_possible_values": "Dictionary:DefaultOperations,ClampExecution:CDS/operation" + }, + "required": true + }, + "target": { + "type": "onap.datatype.controlloop.Target", + "description": "The resource the operation should be performed on.", + "required": true + }, + "payload": { + "type": "map", + "description": "Name/value pairs of payload information passed by Policy to the actor", + "required": false, + "metadata": { + "clamp_possible_values": "ClampExecution:CDS/payload" + }, + "entry_schema": { + "type": "string" + } + } + } + }, + "onap.datatype.controlloop.Operation": { + "derived_from": "tosca.datatypes.Root", + "description": "An operation supported by an actor", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the operation", + "required": true + }, + "description": { + "type": "string", + "description": "A user-friendly description of the intent for the operation", + "required": false + }, + "operation": { + "type": "onap.datatype.controlloop.Actor", + "description": "The definition of the operation to be performed.", + "required": true + }, + "timeout": { + "type": "integer", + "description": "The amount of time for the actor to perform the operation.", + "required": true + }, + "retries": { + "type": "integer", + "description": "The number of retries the actor should attempt to perform the operation.", + "required": true, + "default": 0 + }, + "success": { + "type": "string", + "description": "Points to the operation to invoke on success. A value of \"final_success\" indicates and end to the operation.", + "required": false, + "default": "final_success" + }, + "failure": { + "type": "string", + "description": "Points to the operation to invoke on Actor operation failure.", + "required": false, + "default": "final_failure" + }, + "failure_timeout": { + "type": "string", + "description": "Points to the operation to invoke when the time out for the operation occurs.", + "required": false, + "default": "final_failure_timeout" + }, + "failure_retries": { + "type": "string", + "description": "Points to the operation to invoke when the current operation has exceeded its max retries.", + "required": false, + "default": "final_failure_retries" + }, + "failure_exception": { + "type": "string", + "description": "Points to the operation to invoke when the current operation causes an exception.", + "required": false, + "default": "final_failure_exception" + }, + "failure_guard": { + "type": "string", + "description": "Points to the operation to invoke when the current operation is blocked due to guard policy enforcement.", + "required": false, + "default": "final_failure_guard" + } + } + }, + "onap.datatypes.monitoring.managedObjectDNsBasic": { + "constraints": [], + "properties": { + "DN": { + "name": "DN", + "type": "string", + "typeVersion": "0.0.0", + "description": "Managed object distinguished name", + "required": true, + "constraints": [], + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.managedObjectDNsBasic", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "onap.datatypes.monitoring.managedObjectDNsBasics": { + "constraints": [], + "properties": { + "managedObjectDNsBasic": { + "name": "managedObjectDNsBasic", + "type": "map", + "typeVersion": "0.0.0", + "description": "Managed object distinguished name object", + "required": true, + "constraints": [], + "entry_schema": { + "type": "onap.datatypes.monitoring.managedObjectDNsBasic", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.managedObjectDNsBasics", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "onap.datatypes.monitoring.measurementGroup": { + "constraints": [], + "properties": { + "measurementTypes": { + "name": "measurementTypes", + "type": "list", + "typeVersion": "0.0.0", + "description": "List of measurement types", + "required": true, + "constraints": [], + "entry_schema": { + "type": "onap.datatypes.monitoring.measurementTypes", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + }, + "managedObjectDNsBasic": { + "name": "managedObjectDNsBasic", + "type": "list", + "typeVersion": "0.0.0", + "description": "List of managed object distinguished names", + "required": true, + "constraints": [], + "entry_schema": { + "type": "onap.datatypes.monitoring.managedObjectDNsBasics", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.measurementGroup", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "onap.datatypes.monitoring.measurementGroups": { + "constraints": [], + "properties": { + "measurementGroup": { + "name": "measurementGroup", + "type": "map", + "typeVersion": "0.0.0", + "description": "Measurement Group", + "required": true, + "constraints": [], + "entry_schema": { + "type": "onap.datatypes.monitoring.measurementGroup", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.measurementGroups", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "onap.datatypes.monitoring.measurementType": { + "constraints": [], + "properties": { + "measurementType": { + "name": "measurementType", + "type": "string", + "typeVersion": "0.0.0", + "description": "Measurement type", + "required": true, + "constraints": [], + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.measurementType", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "onap.datatypes.monitoring.measurementTypes": { + "constraints": [], + "properties": { + "measurementType": { + "name": "measurementType", + "type": "map", + "typeVersion": "0.0.0", + "description": "Measurement type object", + "required": true, + "constraints": [], + "entry_schema": { + "type": "onap.datatypes.monitoring.measurementType", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.measurementTypes", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "onap.datatypes.monitoring.nfFilter": { + "constraints": [], + "properties": { + "modelNames": { + "name": "modelNames", + "type": "list", + "typeVersion": "0.0.0", + "description": "List of model names", + "required": true, + "constraints": [], + "entry_schema": { + "type": "string", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + }, + "modelInvariantIDs": { + "name": "modelInvariantIDs", + "type": "list", + "typeVersion": "0.0.0", + "description": "List of model invariant IDs", + "required": true, + "constraints": [], + "entry_schema": { + "type": "string", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + }, + "modelVersionIDs": { + "name": "modelVersionIDs", + "type": "list", + "typeVersion": "0.0.0", + "description": "List of model version IDs", + "required": true, + "constraints": [], + "entry_schema": { + "type": "string", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + }, + "nfNames": { + "name": "nfNames", + "type": "list", + "typeVersion": "0.0.0", + "description": "List of network functions", + "required": true, + "constraints": [], + "entry_schema": { + "type": "string", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.nfFilter", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "onap.datatypes.monitoring.subscription": { + "constraints": [], + "properties": { + "measurementGroups": { + "name": "measurementGroups", + "type": "list", + "typeVersion": "0.0.0", + "description": "Measurement Groups", + "required": true, + "constraints": [], + "entry_schema": { + "type": "onap.datatypes.monitoring.measurementGroups", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + }, + "fileBasedGP": { + "name": "fileBasedGP", + "type": "integer", + "typeVersion": "0.0.0", + "description": "File based granularity period", + "required": true, + "constraints": [], + "metadata": {} + }, + "fileLocation": { + "name": "fileLocation", + "type": "string", + "typeVersion": "0.0.0", + "description": "ROP file location", + "required": true, + "constraints": [], + "metadata": {} + }, + "subscriptionName": { + "name": "subscriptionName", + "type": "string", + "typeVersion": "0.0.0", + "description": "Name of the subscription", + "required": true, + "constraints": [], + "metadata": {} + }, + "administrativeState": { + "name": "administrativeState", + "type": "string", + "typeVersion": "0.0.0", + "description": "State of the subscription", + "required": true, + "constraints": [ + { + "valid_values": [ + "LOCKED", + "UNLOCKED" + ] + } + ], + "metadata": {} + }, + "nfFilter": { + "name": "nfFilter", + "type": "map", + "typeVersion": "0.0.0", + "description": "Network function filter", + "required": true, + "constraints": [], + "entry_schema": { + "type": "onap.datatypes.monitoring.nfFilter", + "typeVersion": "0.0.0", + "constraints": [] + }, + "metadata": {} + } + }, + "name": "onap.datatypes.monitoring.subscription", + "version": "0.0.0", + "derived_from": "tosca.datatypes.Root", + "metadata": {} + }, + "org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.RestRequest": { + "version": "1.0.0", + "derived_from": "tosca.datatypes.Root", + "properties": { + "restRequestId": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "typeVersion": "1.0.0", + "required": true, + "description": "The name and version of a REST request to be sent to a REST endpoint" + }, + "httpMethod": { + "type": "string", + "required": true, + "constraints": [ + { + "valid_values": [ + "POST", + "PUT", + "GET", + "DELETE" + ] + } + ], + "description": "The REST method to use" + }, + "path": { + "type": "string", + "required": true, + "description": "The path of the REST request relative to the base URL" + }, + "body": { + "type": "string", + "required": false, + "description": "The body of the REST request for PUT and POST requests" + }, + "expectedResponse": { + "type": "integer", + "required": true, + "constraints": [], + "description": "THe expected HTTP status code for the REST request" + } + }, + "org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.ConfigurationEntity": { + "version": "1.0.0", + "derived_from": "tosca.datatypes.Root", + "properties": { + "configurationEntityId": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "typeVersion": "1.0.0", + "required": true, + "description": "The name and version of a Configuration Entity to be handled by the HTTP Control Loop Element" + }, + "restSequence": { + "type": "list", + "entry_schema": { + "type": "org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.RestRequest", + "typeVersion": "1.0.0" + }, + "description": "A sequence of REST commands to send to the REST endpoint" + } + } + } + } + }, + "node_types": { + "org.onap.policy.clamp.controlloop.Participant": { + "version": "1.0.1", + "derived_from": "tosca.nodetypes.Root", + "properties": { + "provider": { + "type": "string", + "requred": false + } + } + }, + "org.onap.policy.clamp.controlloop.ControlLoopElement": { + "version": "1.0.1", + "derived_from": "tosca.nodetypes.Root", + "properties": { + "provider": { + "type": "string", + "required": false, + "metadata": { + "common": true + }, + "description": "Specifies the organization that provides the control loop element" + }, + "participant_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": true, + "metadata": { + "common": true + } + }, + "participantType": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "required": true, + "metadata": { + "common": true + }, + "description": "The identity of the participant type that hosts this type of Control Loop Element" + }, + "startPhase": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "metadata": { + "common": true + }, + "description": "A value indicating the start phase in which this control loop element will be started, the first start phase is zero. Control Loop Elements are started in their start_phase order and stopped in reverse start phase order. Control Loop Elements with the same start phase are started and stopped simultaneously" + }, + "uninitializedToPassiveTimeout": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "default": 60, + "metadata": { + "common": true + }, + "description": "The maximum time in seconds to wait for a state chage from uninitialized to passive" + }, + "passiveToRunningTimeout": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "default": 60, + "metadata": { + "common": true + }, + "description": "The maximum time in seconds to wait for a state chage from passive to running" + }, + "runningToPassiveTimeout": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "default": 60, + "metadata": { + "common": true + }, + "description": "The maximum time in seconds to wait for a state chage from running to passive" + }, + "passiveToUninitializedTimeout": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "default": 60, + "metadata": { + "common": true + }, + "description": "The maximum time in seconds to wait for a state chage from passive to uninitialized" + } + } + }, + "org.onap.policy.clamp.controlloop.ControlLoop": { + "version": "1.0.1", + "derived_from": "tosca.nodetypes.Root", + "properties": { + "provider": { + "type": "string", + "required": false, + "metadata": { + "common": true + }, + "description": "Specifies the organization that provides the control loop element" + }, + "elements": { + "type": "list", + "required": true, + "metadata": { + "common": true + }, + "entry_schema": { + "type": "onap.datatypes.ToscaConceptIdentifier" + }, + "description": "Specifies a list of control loop element definitions that make up this control loop definition" + } + } + }, + "org.onap.policy.clamp.controlloop.PolicyControlLoopElement": { + "version": "1.0.1", + "derived_from": "org.onap.policy.clamp.controlloop.ControlLoopElement", + "properties": { + "policy_type_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": true + }, + "policy_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": false + } + } + }, + "org.onap.policy.clamp.controlloop.DerivedPolicyControlLoopElement": { + "version": "1.0.1", + "derived_from": "org.onap.policy.clamp.controlloop.PolicyControlLoopElement", + "properties": { + "policy_type_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": true + }, + "policy_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": false + } + } + }, + "org.onap.policy.clamp.controlloop.DerivedDerivedPolicyControlLoopElement": { + "version": "1.0.1", + "derived_from": "org.onap.policy.clamp.controlloop.DerivedPolicyControlLoopElement", + "properties": { + "policy_type_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": true + }, + "policy_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": false + } + } + }, + "org.onap.policy.clamp.controlloop.CDSControlLoopElement": { + "version": "1.0.1", + "derived_from": "org.onap.policy.clamp.controlloop.ControlLoopElement", + "properties": { + "cds_blueprint_id": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "requred": true + } + } + }, + "org.onap.policy.clamp.controlloop.K8SMicroserviceControlLoopElement": { + "version": "1.0.1", + "derived_from": "org.onap.policy.clamp.controlloop.ControlLoopElement", + "properties": { + "chart": { + "type": "string", + "required": true + }, + "configs": { + "type": "list", + "required": false + }, + "requirements": { + "type": "string", + "requred": false + }, + "templates": { + "type": "list", + "required": false, + "entry_schema": null + }, + "values": { + "type": "string", + "requred": true + } + } + }, + "org.onap.policy.clamp.controlloop.HttpControlLoopElement": { + "version": "1.0.1", + "derived_from": "org.onap.policy.clamp.controlloop.ControlLoopElement", + "properties": { + "baseUrl": { + "type": "string", + "required": true, + "description": "The base URL to be prepended to each path, identifies the host for the REST endpoints." + }, + "httpHeaders": { + "type": "map", + "required": false, + "entry_schema": { + "type": "string" + }, + "description": "HTTP headers to send on REST requests" + }, + "configurationEntities": { + "type": "map", + "required": true, + "entry_schema": { + "type": "org.onap.datatypes.policy.clamp.controlloop.httpControlLoopElement.ConfigurationEntity", + "typeVersion": "1.0.0" + }, + "description": "The connfiguration entities the Control Loop Element is managing and their associated REST requests" + } + } + } + }, + "topology_template": { + "inputs": { + "pmsh_monitoring_policy": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "description": "The ID of the PMSH monitoring policy to use", + "default": { + "name": "MICROSERVICE_vLoadBalancerMS_v1_0_dcae-pm-subscription-handler_1_0_0test", + "version": "1.0.0" + } + }, + "pmsh_operational_policy": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "description": "The ID of the PMSH operational policy to use", + "default": { + "name": "operational.apex.pmcontrol", + "version": "1.0.0" + } + } + }, + "node_templates": { + "org.onap.policy.controlloop.PolicyControlLoopParticipant": { + "version": "2.3.1", + "type": "org.onap.policy.clamp.controlloop.Participant", + "type_version": "1.0.1", + "description": "Participant for DCAE microservices", + "properties": { + "provider": "ONAP" + } + }, + "org.onap.domain.pmsh.PMSH_MonitoringPolicyControlLoopElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.PolicyControlLoopElement", + "type_version": "1.0.0", + "description": "Control loop element for the monitoring policy for Performance Management Subscription Handling", + "properties": { + "provider": "Ericsson", + "participant_id": { + "name": "org.onap.PM_Policy", + "version": "1.0.0" + }, + "participantType": { + "name": "org.onap.policy.controlloop.PolicyControlLoopParticipant", + "version": "2.3.1" + }, + "policy_type_id": { + "name": "onap.policies.monitoring.pm-subscription-handler", + "version": "1.0.0" + }, + "policy_id": { + "get_input": "pmsh_monitoring_policy" + } + } + }, + "org.onap.domain.pmsh.PMSH_OperationalPolicyControlLoopElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.PolicyControlLoopElement", + "type_version": "1.0.0", + "description": "Control loop element for the operational policy for Performance Management Subscription Handling", + "properties": { + "provider": "Ericsson", + "participant_id": { + "name": "org.onap.PM_Policy", + "version": "1.0.0" + }, + "participantType": { + "name": "org.onap.policy.controlloop.PolicyControlLoopParticipant", + "version": "2.3.1" + }, + "policy_type_id": { + "name": "onap.policies.operational.pm-subscription-handler", + "version": "1.0.0" + }, + "policy_id": { + "get_input": "pmsh_operational_policy" + } + } + }, + "org.onap.domain.pmsh.DerivedPolicyControlLoopElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.DerivedPolicyControlLoopElement", + "type_version": "1.0.0", + "description": "Control loop for Performance Management Subscription Handling", + "properties": { + "provider": "Ericsson", + "participantType": { + "name": "org.onap.policy.controlloop.PolicyControlLoopParticipant", + "version": "2.3.1" + }, + "participant_id": { + "name": "org.onap.PM_Policy", + "version": "1.0.0" + } + } + }, + "org.onap.domain.pmsh.DerivedDerivedPolicyControlLoopElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.DerivedDerivedPolicyControlLoopElement", + "type_version": "1.0.0", + "description": "Control loop for Performance Management Subscription Handling", + "properties": { + "provider": "Ericsson", + "participantType": { + "name": "org.onap.policy.controlloop.PolicyControlLoopParticipant", + "version": "2.3.1" + }, + "participant_id": { + "name": "org.onap.PM_Policy", + "version": "1.0.0" + } + } + }, + "org.onap.k8s.controlloop.K8SControlLoopParticipant": { + "version": "2.3.4", + "type": "org.onap.policy.clamp.controlloop.Participant", + "type_version": "1.0.1", + "description": "Participant for K8S", + "properties": { + "provider": "ONAP" + } + }, + "org.onap.domain.database.PMSH_K8SMicroserviceControlLoopElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.K8SMicroserviceControlLoopElement", + "type_version": "1.0.0", + "description": "Control loop element for the K8S microservice for PMSH", + "properties": { + "provider": "ONAP", + "participant_id": { + "name": "K8sParticipant0", + "version": "1.0.0" + }, + "participantType": { + "name": "org.onap.k8s.controlloop.K8SControlLoopParticipant", + "version": "2.3.4" + }, + "chart": { + "chartId": { + "name": "dcae-pmsh", + "version": "8.0.0" + }, + "namespace": "onap", + "releaseName": "pmshms", + "repository": { + "repoName": "chartmuseum", + "protocol": "http", + "address": "10.152.183.120", + "port": 80, + "userName": "onapinitializer", + "password": "demo123456!" + }, + "overrideParams": { + "global.masterPassword": "test" + } + } + } + }, + "org.onap.domain.database.Local_K8SMicroserviceControlLoopElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.K8SMicroserviceControlLoopElement", + "type_version": "1.0.0", + "description": "Control loop element for the K8S microservice for local chart", + "properties": { + "provider": "ONAP", + "participant_id": { + "name": "K8sParticipant0", + "version": "1.0.0" + }, + "participantType": { + "name": "org.onap.k8s.controlloop.K8SControlLoopParticipant", + "version": "2.3.4" + }, + "chart": { + "chartId": { + "name": "nginx-ingress", + "version": "0.9.1" + }, + "releaseName": "nginxms", + "namespace": "test" + } + } + }, + "org.onap.controlloop.HttpControlLoopParticipant": { + "version": "2.3.4", + "type": "org.onap.policy.clamp.controlloop.Participant", + "type_version": "1.0.1", + "description": "Participant for Http requests", + "properties": { + "provider": "ONAP" + } + }, + "org.onap.domain.database.Http_PMSHMicroserviceControlLoopElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.HttpControlLoopElement", + "type_version": "1.0.1", + "description": "Control loop element for the http requests of PMSH microservice", + "properties": { + "provider": "ONAP", + "participant_id": { + "name": "HttpParticipant0", + "version": "1.0.0" + }, + "participantType": { + "name": "org.onap.k8s.controlloop.HttpControlLoopParticipant", + "version": "2.3.4" + }, + "uninitializedToPassiveTimeout": 180, + "startPhase": 1, + "baseUrl": "http://10.152.183.51:8500", + "httpHeaders": { + "Content-Type": "application/json" + }, + "configurationEntities": [ + { + "configurationEntityId": { + "name": "entity1", + "version": "1.0.1" + }, + "restSequence": [ + { + "restRequestId": { + "name": "request1", + "version": "1.0.1" + }, + "httpMethod": "PUT", + "path": "v1/kv/dcae-pmsh2", + "body": "{ \"control_loop_name\":\"pmsh-control-loop\", \"operational_policy_name\":\"pmsh-operational-policy\", \"aaf_password\":\"demo123456!\", \"aaf_identity\":\"dcae@dcae.onap.org\", \"cert_path\":\"/opt/app/pmsh/etc/certs/cert.pem\", \"key_path\":\"/opt/app/pmsh/etc/certs/key.pem\", \"ca_cert_path\":\"/opt/app/pmsh/etc/certs/cacert.pem\", \"enable_tls\":\"true\", \"pmsh_policy\":{ \"subscription\":{ \"subscriptionName\":\"ExtraPM-All-gNB-R2B\", \"administrativeState\":\"UNLOCKED\", \"fileBasedGP\":15, \"fileLocation\":\"\\/pm\\/pm.xml\", \"nfFilter\":{ \"nfNames\":[ \"^pnf.*\", \"^vnf.*\" ], \"modelInvariantIDs\":[ ], \"modelVersionIDs\":[ ], \"modelNames\":[ ] }, \"measurementGroups\":[ { \"measurementGroup\":{ \"measurementTypes\":[ { \"measurementType\":\"countera\" }, { \"measurementType\":\"counterb\" } ], \"managedObjectDNsBasic\":[ { \"DN\":\"dna\" }, { \"DN\":\"dnb\" } ] } }, { \"measurementGroup\":{ \"measurementTypes\":[ { \"measurementType\":\"counterc\" }, { \"measurementType\":\"counterd\" } ], \"managedObjectDNsBasic\":[ { \"DN\":\"dnc\" }, { \"DN\":\"dnd\" } ] } } ] } }, \"streams_subscribes\":{ \"aai_subscriber\":{ \"type\":\"message_router\", \"dmaap_info\":{ \"topic_url\":\"https://10.152.183.151:3905/events/AAI_EVENT\", \"client_role\":\"org.onap.dcae.aaiSub\", \"location\":\"san-francisco\", \"client_id\":\"1575976809466\" } }, \"policy_pm_subscriber\":{ \"type\":\"message_router\", \"dmaap_info\":{ \"topic_url\":\"https://10.152.183.151:3905/events/org.onap.dmaap.mr.PM_SUBSCRIPTIONS\", \"client_role\":\"org.onap.dcae.pmSubscriber\", \"location\":\"san-francisco\", \"client_id\":\"1575876809456\" } } }, \"streams_publishes\":{ \"policy_pm_publisher\":{ \"type\":\"message_router\", \"dmaap_info\":{ \"topic_url\":\"https://10.152.183.151:3905/events/org.onap.dmaap.mr.PM_SUBSCRIPTIONS\", \"client_role\":\"org.onap.dcae.pmPublisher\", \"location\":\"san-francisco\", \"client_id\":\"1475976809466\" } }, \"other_publisher\":{ \"type\":\"message_router\", \"dmaap_info\":{ \"topic_url\":\"https://10.152.183.151:3905/events/org.onap.dmaap.mr.SOME_OTHER_TOPIC\", \"client_role\":\"org.onap.dcae.pmControlPub\", \"location\":\"san-francisco\", \"client_id\":\"1875976809466\" } } } }", + "expectedResponse": 200 + } + ] + } + ] + } + }, + "org.onap.domain.sample.GenericK8s_ControlLoopDefinition": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.controlloop.ControlLoop", + "type_version": "1.0.0", + "description": "Control loop for Hello World", + "properties": { + "provider": "ONAP", + "elements": [ + { + "name": "org.onap.domain.database.PMSH_K8SMicroserviceControlLoopElement", + "version": "1.2.3" + }, + { + "name": "org.onap.domain.database.Local_K8SMicroserviceControlLoopElement", + "version": "1.2.3" + }, + { + "name": "org.onap.domain.database.Http_PMSHMicroserviceControlLoopElement", + "version": "1.2.3" + }, + { + "name": "org.onap.domain.pmsh.DerivedPolicyControlLoopElement", + "version": "1.2.3" + }, + { + "name": "org.onap.domain.pmsh.DerivedDerivedPolicyControlLoopElement", + "version": "1.2.3" + }, + { + "name": "org.onap.domain.pmsh.PMSH_MonitoringPolicyControlLoopElement", + "version": "1.2.3" + }, + { + "name": "org.onap.domain.pmsh.PMSH_OperationalPolicyControlLoopElement", + "version": "1.2.3" + } + ] + } + } + } + } +} diff --git a/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsar.java b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsar.java new file mode 100644 index 00000000..96db632c --- /dev/null +++ b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsar.java @@ -0,0 +1,117 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.reception.decoding.policy.file; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.onap.policy.common.parameters.ParameterService; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.distribution.model.Csar; +import org.onap.policy.distribution.model.PolicyInput; +import org.onap.policy.distribution.reception.decoding.PolicyDecoder; +import org.onap.policy.distribution.reception.decoding.PolicyDecodingException; +import org.onap.policy.distribution.reception.util.ReceptionUtil; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; + +/** + * This class extracts controlloop information from a CSAR file. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +public class ControlLoopDecoderFileInCsar implements PolicyDecoder { + + private ControlLoopDecoderFileInCsarParameterGroup decoderParameters; + private static final String NODE_TYPES = "nodes.yml"; + private static final String DATA_TYPES = "data.yml"; + + /** + * {@inheritDoc}. + */ + @Override + public void configure(final String parameterGroupName) { + decoderParameters = ParameterService.get(parameterGroupName); + } + + /** + * {@inheritDoc}. + */ + @Override + public boolean canHandle(final PolicyInput policyInput) { + return policyInput.getClass().isAssignableFrom(Csar.class); + } + + /** + * {@inheritDoc}. + */ + @Override + public Collection decode(final Csar csar) throws PolicyDecodingException { + final Collection controlLoopList = new ArrayList<>(); + ToscaServiceTemplate nodeTypes = null; + ToscaServiceTemplate dataTypes = null; + + try (var zipFile = new ZipFile(csar.getCsarFilePath())) { + final Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + // + // Sonar will flag this as a Security Hotspot + // "Expanding archive files is security-sensitive" + // isZipEntryValid ensures the file being read exists in the archive + // + final ZipEntry entry = entries.nextElement(); // NOSONAR + final String entryName = entry.getName(); + + // Store node_types + if (entryName.contains(NODE_TYPES)) { + nodeTypes = ReceptionUtil.decodeFile(zipFile, entry); + } + + // Store data_types + if (entryName.contains(DATA_TYPES)) { + dataTypes = ReceptionUtil.decodeFile(zipFile, entry); + } + + if (entryName.contains(decoderParameters.getControlLoopType())) { + ReceptionUtil.validateZipEntry(entryName, csar.getCsarFilePath(), entry.getSize()); + final ToscaServiceTemplate controlLoop = ReceptionUtil.decodeFile(zipFile, entry); + if (null != controlLoop.getToscaTopologyTemplate()) { + if (null != nodeTypes) { + controlLoop.setNodeTypes(nodeTypes.getNodeTypes()); + } + if (null != dataTypes) { + controlLoop.setDataTypes(dataTypes.getDataTypes()); + } + controlLoopList.add(controlLoop); + } + } + } + } catch (final IOException | CoderException exp) { + throw new PolicyDecodingException("Failed decoding the controlloop", exp); + } + + return controlLoopList; + } +} diff --git a/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroup.java b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroup.java new file mode 100644 index 00000000..358b1f87 --- /dev/null +++ b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroup.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.reception.decoding.policy.file; + +import lombok.Getter; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; +import org.onap.policy.distribution.reception.parameters.PolicyDecoderConfigurationParameterGroup; + +/** + * Holds the parameters for the{@link ControlLoopDecoderFileInCsar}. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +@Getter +@NotNull +@NotBlank +public class ControlLoopDecoderFileInCsarParameterGroup extends PolicyDecoderConfigurationParameterGroup { + + private String controlLoopType; + + public ControlLoopDecoderFileInCsarParameterGroup() { + super(ControlLoopDecoderFileInCsarParameterGroup.class.getSimpleName()); + } +} diff --git a/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/PolicyDecoderFileInCsarToPolicy.java b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/PolicyDecoderFileInCsarToPolicy.java index 72316f28..c4ba21fe 100644 --- a/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/PolicyDecoderFileInCsarToPolicy.java +++ b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/decoding/policy/file/PolicyDecoderFileInCsarToPolicy.java @@ -1,7 +1,7 @@ /*- * ============LICENSE_START======================================================= * Copyright (C) 2018 Ericsson. All rights reserved. - * Copyright (C) 2019 Nordix Foundation. + * Copyright (C) 2022 Nordix Foundation. * Modifications Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved. * Modifications Copyright (C) 2021 Bell Canada. All rights reserved. * ================================================================================ @@ -24,7 +24,6 @@ package org.onap.policy.distribution.reception.decoding.policy.file; import java.io.IOException; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; @@ -32,12 +31,11 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.onap.policy.common.parameters.ParameterService; import org.onap.policy.common.utils.coder.CoderException; -import org.onap.policy.common.utils.coder.StandardCoder; -import org.onap.policy.common.utils.coder.StandardYamlCoder; import org.onap.policy.distribution.model.Csar; import org.onap.policy.distribution.model.PolicyInput; import org.onap.policy.distribution.reception.decoding.PolicyDecoder; import org.onap.policy.distribution.reception.decoding.PolicyDecodingException; +import org.onap.policy.distribution.reception.util.ReceptionUtil; import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; @@ -49,9 +47,6 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; public class PolicyDecoderFileInCsarToPolicy implements PolicyDecoder { private PolicyDecoderFileInCsarToPolicyParameterGroup decoderParameters; - private StandardCoder coder; - private StandardYamlCoder yamlCoder; - private static final long MAX_FILE_SIZE = 512L * 1024; /** * {@inheritDoc}. @@ -59,8 +54,6 @@ public class PolicyDecoderFileInCsarToPolicy implements PolicyDecoder MAX_FILE_SIZE) { - throw new PolicyDecodingException("Zip entry for " + entryName + " is too large " + entrySize); - } - // - // Now ensure that there is no path injection - // - var path = Path.of(csarPath, entryName).normalize(); - // - // Throw an exception if path is outside the csar - // - if (! path.startsWith(csarPath)) { - throw new PolicyDecodingException("Potential path injection for zip entry " + entryName); - } - return true; - } - - return false; - } - - /** - * Method to decode either a json or yaml file into an object. - * - * @param zipFile the zip file - * @param entry the entry to read in the zip file. - * @return the decoded ToscaServiceTemplate object. - * @throws CoderException IOException if the file decoding fails. - */ - private ToscaServiceTemplate decodeFile(ZipFile zipFile, final ZipEntry entry) throws IOException, CoderException { - ToscaServiceTemplate policy = null; - if (entry.getName().endsWith(".json")) { - policy = coder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class); - } else if (entry.getName().endsWith(".yaml")) { - policy = yamlCoder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class); - } - return policy; - } } diff --git a/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java new file mode 100644 index 00000000..9c0bab43 --- /dev/null +++ b/plugins/reception-plugins/src/main/java/org/onap/policy/distribution/reception/util/ReceptionUtil.java @@ -0,0 +1,92 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.reception.util; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.coder.StandardYamlCoder; +import org.onap.policy.distribution.reception.decoding.PolicyDecodingException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; + +/** + * This class extracts and validates information from a CSAR file. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +public class ReceptionUtil { + + private static StandardCoder coder = new StandardCoder(); + private static StandardYamlCoder yamlCoder = new StandardYamlCoder(); + private static final long MAX_FILE_SIZE = 512L * 1024; + + /** + * Method to ensure validation of entries in the Zipfile. Attempts to solve path + * injection java security issues. + * + * @param entryName name of the ZipEntry to check + * @param csarPath Absolute path to the csar the ZipEntry is in + * @param entrySize size of the ZipEntry + * @throws PolicyDecodingException if the file size is too large + */ + public static void validateZipEntry(String entryName, String csarPath, long entrySize) + throws PolicyDecodingException { + // + // Check file size + // + if (entrySize > MAX_FILE_SIZE) { + throw new PolicyDecodingException("Zip entry for " + entryName + " is too large " + entrySize); + } + // + // Now ensure that there is no path injection + // + var path = Path.of(csarPath, entryName).normalize(); + // + // Throw an exception if path is outside the csar + // + if (! path.startsWith(csarPath)) { + throw new PolicyDecodingException("Potential path injection for zip entry " + entryName); + } + } + + /** + * Method to decode either a json or yaml file into an object. + * + * @param zipFile the zip file + * @param entry the entry to read in the zip file. + * @return the decoded ToscaServiceTemplate object. + * @throws CoderException IOException if the file decoding fails. + */ + public static ToscaServiceTemplate decodeFile(ZipFile zipFile, final ZipEntry entry) + throws IOException, CoderException { + ToscaServiceTemplate toscaServiceTemplate = null; + if (entry.getName().endsWith(".json")) { + toscaServiceTemplate = coder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class); + } else if (entry.getName().endsWith(".yml")) { + toscaServiceTemplate = yamlCoder.decode(zipFile.getInputStream(entry), ToscaServiceTemplate.class); + } + return toscaServiceTemplate; + } +} diff --git a/plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroupTest.java b/plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroupTest.java new file mode 100644 index 00000000..16f80096 --- /dev/null +++ b/plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarParameterGroupTest.java @@ -0,0 +1,67 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.reception.decoding.policy.file; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.onap.policy.common.parameters.ValidationStatus; +import org.onap.policy.distribution.reception.handling.sdc.CommonTestData; + +/** + * Class to perform unit test of {@link ControlLoopDecoderFileInCsarParameterGroup}. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +public class ControlLoopDecoderFileInCsarParameterGroupTest { + + @Test + public void testValidParameters() { + final ControlLoopDecoderFileInCsarParameterGroup configurationParameters = CommonTestData + .getPolicyDecoderParameters("src/test/resources/parameters/FileInCsarControlLoopDecoderParameters.json", + ControlLoopDecoderFileInCsarParameterGroup.class); + + assertEquals(ControlLoopDecoderFileInCsarParameterGroup.class.getSimpleName(), + configurationParameters.getName()); + assertEquals("controlloop", configurationParameters.getControlLoopType()); + assertEquals(ValidationStatus.CLEAN, configurationParameters.validate().getStatus()); + } + + @Test + public void testInvalidParameters() { + final ControlLoopDecoderFileInCsarParameterGroup configurationParameters = + CommonTestData.getPolicyDecoderParameters( + "src/test/resources/parameters/FileInCsarControlLoopDecoderParametersInvalid.json", + ControlLoopDecoderFileInCsarParameterGroup.class); + + assertEquals(ValidationStatus.INVALID, configurationParameters.validate().getStatus()); + } + + @Test + public void testEmptyParameters() { + final ControlLoopDecoderFileInCsarParameterGroup configurationParameters = + CommonTestData.getPolicyDecoderParameters("src/test/resources/parameters/EmptyParameters.json", + ControlLoopDecoderFileInCsarParameterGroup.class); + + assertEquals(ValidationStatus.INVALID, configurationParameters.validate().getStatus()); + } +} diff --git a/plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarTest.java b/plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarTest.java new file mode 100644 index 00000000..e452a7ce --- /dev/null +++ b/plugins/reception-plugins/src/test/java/org/onap/policy/distribution/reception/decoding/policy/file/ControlLoopDecoderFileInCsarTest.java @@ -0,0 +1,96 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.distribution.reception.decoding.policy.file; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.Collection; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.onap.policy.common.parameters.ParameterService; +import org.onap.policy.distribution.model.Csar; +import org.onap.policy.distribution.reception.decoding.PolicyDecodingException; +import org.onap.policy.distribution.reception.handling.sdc.CommonTestData; +import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity; + +/** + * Class to perform unit test of {@link ControlLoopDecoderFileInCsar}. + * + * @author Sirisha Manchikanti (sirisha.manchikanti@est.tech) + */ +@RunWith(MockitoJUnitRunner.class) +public class ControlLoopDecoderFileInCsarTest { + + /** + * Set up. + */ + @BeforeClass + public static void setUp() { + final ControlLoopDecoderFileInCsarParameterGroup configurationParameters = CommonTestData + .getPolicyDecoderParameters("src/test/resources/parameters/FileInCsarControlLoopDecoderParameters.json", + ControlLoopDecoderFileInCsarParameterGroup.class); + configurationParameters.setName(ControlLoopDecoderFileInCsarParameterGroup.class.getSimpleName()); + ParameterService.register(configurationParameters); + } + + /** + * Tear down. + */ + @AfterClass + public static void tearDown() { + ParameterService.deregister(ControlLoopDecoderFileInCsarParameterGroup.class.getSimpleName()); + } + + @Test + public void testDecodeControlLoop() throws PolicyDecodingException { + + final ControlLoopDecoderFileInCsar decoder = new ControlLoopDecoderFileInCsar(); + decoder.configure(ControlLoopDecoderFileInCsarParameterGroup.class.getSimpleName()); + + final File file = new File("src/test/resources/service-Sampleservice-controlloop.csar"); + final Csar csar = new Csar(file.getAbsolutePath()); + + assertTrue(decoder.canHandle(csar)); + final Collection controlLoopHolders = decoder.decode(csar); + assertEquals(1, controlLoopHolders.size()); + } + + @Test + public void testDecodeControlLoopZipError() { + + final ControlLoopDecoderFileInCsar decoder = new ControlLoopDecoderFileInCsar(); + decoder.configure(ControlLoopDecoderFileInCsarParameterGroup.class.getSimpleName()); + + final File file = new File("unknown.csar"); + final Csar csar = new Csar(file.getAbsolutePath()); + + assertTrue(decoder.canHandle(csar)); + assertThatThrownBy(() -> decoder.decode(csar)).isInstanceOf(PolicyDecodingException.class) + .hasMessageContaining("Failed decoding the controlloop"); + } +} diff --git a/plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParameters.json b/plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParameters.json new file mode 100644 index 00000000..ca112388 --- /dev/null +++ b/plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParameters.json @@ -0,0 +1,3 @@ +{ + "controlLoopType": "controlloop" +} diff --git a/plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParametersInvalid.json b/plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParametersInvalid.json new file mode 100644 index 00000000..9d33896e --- /dev/null +++ b/plugins/reception-plugins/src/test/resources/parameters/FileInCsarControlLoopDecoderParametersInvalid.json @@ -0,0 +1,3 @@ +{ + "controlLoop": "" +} diff --git a/plugins/reception-plugins/src/test/resources/service-Sampleservice-controlloop.csar b/plugins/reception-plugins/src/test/resources/service-Sampleservice-controlloop.csar new file mode 100644 index 0000000000000000000000000000000000000000..03e293660a61d7d7314c7a95c21839d7336589da GIT binary patch literal 79322 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 YKXU;ZI*>VlIDsEZVEWDi{s;v6AClj`8~^|S literal 0 HcmV?d00001 diff --git a/testsuites/stability/src/main/resources/setup/distribution/etc/defaultConfig.json b/testsuites/stability/src/main/resources/setup/distribution/etc/defaultConfig.json index a4cf85a8..2b3078ac 100644 --- a/testsuites/stability/src/main/resources/setup/distribution/etc/defaultConfig.json +++ b/testsuites/stability/src/main/resources/setup/distribution/etc/defaultConfig.json @@ -17,13 +17,23 @@ "decoderType":"ToscaPolicyDecoder", "decoderClassName":"org.onap.policy.distribution.reception.decoding.policy.file.PolicyDecoderFileInCsarToPolicy", "decoderConfigurationName": "toscaPolicyDecoderConfiguration" + }, + "ToscaControlLoopDecoder": { + "decoderType": "ToscaControlLoopDecoder", + "decoderClassName": "org.onap.policy.distribution.reception.decoding.policy.file.ControlLoopDecoderFileInCsar", + "decoderConfigurationName": "toscaControlLoopDecoderConfiguration" } }, "policyForwarders": { - "LifeCycleApiForwarder": { - "forwarderType": "LifeCycleAPI", + "LifeCycleApiPolicyForwarder": { + "forwarderType": "LifeCyclePolicyAPI", "forwarderClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiPolicyForwarder", - "forwarderConfigurationName": "lifecycleApiConfiguration" + "forwarderConfigurationName": "lifecycleApiPolicyConfiguration" + }, + "LifeCycleApiControlLoopForwarder": { + "forwarderType": "LifeCycleControlLoopAPI", + "forwarderClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiControlLoopForwarder", + "forwarderConfigurationName": "lifecycleApiControlLoopConfiguration" } } } @@ -45,11 +55,17 @@ "policyFileName": "tosca_policy", "policyTypeFileName": "tosca_policy_type" } + }, + "toscaControlLoopDecoderConfiguration": { + "parameterClassName": "org.onap.policy.distribution.reception.decoding.policy.file.ControlLoopDecoderFileInCsarParameterGroup", + "parameters": { + "controlLoopType": "controlloop" + } } }, "policyForwarderConfigurationParameters": { - "lifecycleApiConfiguration": { + "lifecycleApiPolicyConfiguration": { "parameterClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiForwarderParameters", "parameters": { "apiParameters": { @@ -70,6 +86,19 @@ }, "deployPolicies": true } + }, + "lifecycleApiControlLoopConfiguration": { + "parameterClassName": "org.onap.policy.distribution.forwarding.lifecycle.api.LifecycleApiControlLoopForwarderParameters", + "parameters": { + "controlLoopRuntimeParameters": { + "clientName": "policy-clamp-cl-runtime", + "hostname": "policy-clamp-cl-runtime", + "port": 6969, + "useHttps": true, + "userName": "policyadmin", + "password": "zb!XztG34" + } + } } } } -- 2.16.6