From 20c7487f77d0728d270b2bed34c2c798d17cc12d Mon Sep 17 00:00:00 2001 From: liamfallon Date: Wed, 26 May 2021 11:02:07 +0100 Subject: Transfer tosca-poc onto master This is the third if three commits, this commit makes the content of the master branch the same as the tosca-poc branch. This commit adds the "src" files for TOSCA control loops to the modules in master. Difference between this commit and the current master: ------------------------------------------------------ diff -qr clamp clamp-master |diff_filter.sh Only in clamp/common: src Only in clamp/models: src Files clamp/participant/participant-impl/participant-impl-dcae/pom.xml and clamp-master/participant/participant-impl/participant-impl-dcae/pom.xml differ Only in clamp/participant/participant-impl/participant-impl-dcae: src Only in clamp/participant/participant-impl/participant-impl-policy: src Only in clamp/participant/participant-impl/participant-impl-simulator: src Files clamp/participant/participant-impl/pom.xml and clamp-master/participant/participant-impl/pom.xml differ Only in clamp/participant/participant-intermediary: src Files clamp/participant/pom.xml and clamp-master/participant/pom.xml differ Files clamp/pom.xml and clamp-master/pom.xml differ Files clamp/runtime/pom.xml and clamp-master/runtime/pom.xml differ Only in clamp: runtime-controlloop Difference between this commit and the current tosca-poc branch: ---------------------------------------------------------------- diff -qr clamp clamp-tp |diff_filter.sh Files clamp/INFO.yaml and clamp-tp/INFO.yaml differ Only in clamp/releases: 6.0.1-container.yaml Only in clamp/releases: 6.0.1.yaml Only in clamp/releases: 6.0.2-container.yaml Only in clamp/releases: 6.0.2.yaml Only in clamp/releases: 6.1.0-container.yaml Only in clamp/releases: 6.1.0.yaml Only in clamp/releases: 6.1.1-container.yaml Only in clamp/releases: 6.1.1.yaml Files clamp/runtime/src/main/resources/META-INF/resources/swagger.html and clamp-tp/runtime/src/main/resources/META-INF/resources/swagger.html differ Issue-ID: POLICY-3215 Change-Id: Ica1aa3fe5d6110df2396ea856703102e800fa770 Signed-off-by: liamfallon --- .../commissioning/CommissioningHandler.java | 81 ++++ .../commissioning/CommissioningProvider.java | 208 ++++++++++ .../rest/CommissioningController.java | 360 +++++++++++++++++ .../ControlLoopInstantiationProvider.java | 276 +++++++++++++ .../instantiation/InstantiationHandler.java | 82 ++++ .../rest/InstantiationController.java | 416 +++++++++++++++++++ .../main/parameters/ClRuntimeParameterGroup.java | 55 +++ .../main/parameters/ClRuntimeParameterHandler.java | 77 ++++ .../main/parameters/ParticipantParameters.java | 59 +++ .../ParticipantStateChangeParameters.java | 53 +++ .../parameters/ParticipantUpdateParameters.java | 54 +++ .../runtime/main/rest/ControlLoopAafFilter.java | 38 ++ .../runtime/main/rest/RestController.java | 115 ++++++ .../runtime/main/startstop/ClRuntimeActivator.java | 186 +++++++++ .../startstop/ClRuntimeCommandLineArguments.java | 151 +++++++ .../controlloop/runtime/main/startstop/Main.java | 156 +++++++ .../runtime/monitoring/MonitoringHandler.java | 84 ++++ .../runtime/monitoring/MonitoringProvider.java | 273 +++++++++++++ .../monitoring/rest/MonitoringQueryController.java | 371 +++++++++++++++++ .../runtime/supervision/SupervisionHandler.java | 450 +++++++++++++++++++++ .../runtime/supervision/SupervisionScanner.java | 116 ++++++ ...ParticipantControlLoopStateChangePublisher.java | 75 ++++ .../ParticipantControlLoopUpdatePublisher.java | 75 ++++ .../comm/ParticipantStateChangePublisher.java | 74 ++++ .../comm/ParticipantStatusListener.java | 53 +++ .../src/main/resources/META-INF/persistence.xml | 121 ++++++ runtime-controlloop/src/main/resources/version.txt | 4 + 27 files changed, 4063 insertions(+) create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java create mode 100644 runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java create mode 100644 runtime-controlloop/src/main/resources/META-INF/persistence.xml create mode 100644 runtime-controlloop/src/main/resources/version.txt (limited to 'runtime-controlloop/src/main') diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java new file mode 100644 index 000000000..88e8b1df9 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java @@ -0,0 +1,81 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.commissioning; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import javax.ws.rs.core.Response; +import lombok.Getter; +import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler; +import org.onap.policy.clamp.controlloop.runtime.commissioning.rest.CommissioningController; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher; +import org.onap.policy.common.utils.services.Registry; +import org.onap.policy.models.base.PfModelRuntimeException; + +/** + * This class handles commissioning of control loop definitions. + */ +public final class CommissioningHandler extends ControlLoopHandler { + + @Getter + private CommissioningProvider provider; + + /** + * Gets the CommissioningHandler. + * + * @return CommissioningHandler + */ + public static CommissioningHandler getInstance() { + return Registry.get(CommissioningHandler.class.getName()); + } + + /** + * Create a handler. + * + * @param controlLoopParameters the parameters for access to the database + */ + public CommissioningHandler(ClRuntimeParameterGroup controlLoopParameters) { + super(controlLoopParameters.getDatabaseProviderParameters()); + } + + @Override + public Set> getProviderClasses() { + return Set.of(CommissioningController.class); + } + + @Override + public void startProviders() { + provider = new CommissioningProvider(getDatabaseProviderParameters()); + } + + @Override + public void stopProviders() { + try { + provider.close(); + } catch (IOException e) { + throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, + "an error has occured while stopping commissioning providers", e); + } + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java new file mode 100644 index 000000000..50f6787b9 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java @@ -0,0 +1,208 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.commissioning; + +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; +import org.onap.policy.clamp.controlloop.models.messages.rest.commissioning.CommissioningResponse; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.base.PfModelRuntimeException; +import org.onap.policy.models.provider.PolicyModelsProvider; +import org.onap.policy.models.provider.PolicyModelsProviderFactory; +import org.onap.policy.models.provider.PolicyModelsProviderParameters; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplates; +import org.onap.policy.models.tosca.authorative.concepts.ToscaTypedEntityFilter; + +/** + * This class provides the create, read and delete actions on Commissioning of Control Loop concepts in the database to + * the callers. + */ +public class CommissioningProvider implements Closeable { + public static final String CONTROL_LOOP_NODE_TYPE = "org.onap.policy.clamp.controlloop.ControlLoop"; + + private final PolicyModelsProvider modelsProvider; + private final ControlLoopProvider clProvider; + + private static final Object lockit = new Object(); + + /** + * Create a commissioning provider. + * + * @throws ControlLoopRuntimeException on errors creating the provider + */ + public CommissioningProvider(PolicyModelsProviderParameters databaseProviderParameters) + throws ControlLoopRuntimeException { + try { + modelsProvider = new PolicyModelsProviderFactory() + .createPolicyModelsProvider(databaseProviderParameters); + } catch (PfModelException e) { + throw new PfModelRuntimeException(e); + } + + try { + clProvider = new ControlLoopProvider(databaseProviderParameters); + } catch (PfModelException e) { + throw new PfModelRuntimeException(e); + } + } + + @Override + public void close() throws IOException { + try { + modelsProvider.close(); + } catch (PfModelException e) { + throw new IOException("error closing modelsProvider", e); + } + } + + /** + * Create control loops from a service template. + * + * @param serviceTemplate the service template + * @return the result of the commissioning operation + * @throws PfModelException on creation errors + */ + public CommissioningResponse createControlLoopDefinitions(ToscaServiceTemplate serviceTemplate) + throws PfModelException { + synchronized (lockit) { + modelsProvider.createServiceTemplate(serviceTemplate); + } + + CommissioningResponse response = new CommissioningResponse(); + // @formatter:off + response.setAffectedControlLoopDefinitions(serviceTemplate.getToscaTopologyTemplate().getNodeTemplates() + .values() + .stream() + .map(template -> template.getKey().asIdentifier()) + .collect(Collectors.toList())); + // @formatter:on + + return response; + } + + /** + * Delete the control loop definition with the given name and version. + * + * @param name the name of the control loop definition to delete + * @param version the version of the control loop to delete + * @return the result of the deletion + * @throws PfModelException on deletion errors + */ + public CommissioningResponse deleteControlLoopDefinition(String name, String version) throws PfModelException { + synchronized (lockit) { + modelsProvider.deleteServiceTemplate(name, version); + } + + CommissioningResponse response = new CommissioningResponse(); + response.setAffectedControlLoopDefinitions( + Collections.singletonList(new ToscaConceptIdentifier(name, version))); + + return response; + } + + /** + * Get control loop node templates. + * + * @param clName the name of the control loop, null for all + * @param clVersion the version of the control loop, null for all + * @return list of control loop node templates + * @throws PfModelException on errors getting control loop definitions + */ + public List getControlLoopDefinitions(String clName, String clVersion) throws PfModelException { + + // @formatter:off + ToscaTypedEntityFilter nodeTemplateFilter = ToscaTypedEntityFilter + .builder() + .name(clName) + .version(clVersion) + .type(CONTROL_LOOP_NODE_TYPE) + .build(); + // @formatter:on + + return clProvider.getFilteredNodeTemplates(nodeTemplateFilter); + } + + /** + * Get the control loop elements from a control loop node template. + * + * @param controlLoopNodeTemplate the control loop node template + * @return a list of the control loop element node templates in a control loop node template + * @throws PfModelException on errors get control loop element node templates + */ + public List getControlLoopElementDefinitions(ToscaNodeTemplate controlLoopNodeTemplate) + throws PfModelException { + if (!CONTROL_LOOP_NODE_TYPE.equals(controlLoopNodeTemplate.getType())) { + return Collections.emptyList(); + } + + if (MapUtils.isEmpty(controlLoopNodeTemplate.getProperties())) { + return Collections.emptyList(); + } + + @SuppressWarnings("unchecked") + List> controlLoopElements = + (List>) controlLoopNodeTemplate.getProperties().get("elements"); + + if (CollectionUtils.isEmpty(controlLoopElements)) { + return Collections.emptyList(); + } + + List controlLoopElementList = new ArrayList<>(); + // @formatter:off + controlLoopElementList.addAll( + controlLoopElements + .stream() + .map(elementMap -> clProvider.getNodeTemplates(elementMap.get("name"), + elementMap.get("version"))) + .flatMap(List::stream) + .collect(Collectors.toList()) + ); + // @formatter:on + + return controlLoopElementList; + } + + /** + * Get the requested control loop definitions. + * + * @param name the name of the definition to get, null for all definitions + * @param version the version of the definition to get, null for all definitions + * @return the control loop definitions + * @throws PfModelException on errors getting control loop definitions + */ + public ToscaServiceTemplate getToscaServiceTemplate(String name, String version) throws PfModelException { + ToscaServiceTemplates serviceTemplates = new ToscaServiceTemplates(); + serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version)); + return serviceTemplates.getServiceTemplates().get(0); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java new file mode 100644 index 000000000..18e1f7787 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java @@ -0,0 +1,360 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.commissioning.rest; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import io.swagger.annotations.ResponseHeader; +import java.util.List; +import java.util.UUID; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.onap.policy.clamp.controlloop.models.messages.rest.commissioning.CommissioningResponse; +import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningHandler; +import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider; +import org.onap.policy.clamp.controlloop.runtime.main.rest.RestController; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.base.PfModelRuntimeException; +import org.onap.policy.models.errors.concepts.ErrorResponse; +import org.onap.policy.models.errors.concepts.ErrorResponseInfo; +import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class to provide REST end points for creating, deleting, querying commissioned control loops. + */ +public class CommissioningController extends RestController { + + private static final Logger LOGGER = LoggerFactory.getLogger(CommissioningController.class); + + private final CommissioningProvider provider; + + /** + * create Commissioning Controller. + */ + public CommissioningController() { + this.provider = CommissioningHandler.getInstance().getProvider(); + } + + /** + * Creates a control loop definition. + * + * @param requestId request ID used in ONAP logging + * @param body the body of control loop following TOSCA definition + * @return a response + */ + // @formatter:off + @POST + @Path("/commission") + @ApiOperation( + value = "Commissions control loop definitions", + notes = "Commissions control loop definitions, returning the commissioned control loop definition IDs", + response = CommissioningResponse.class, + tags = { + "Control Loop Commissioning API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, + description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_PATCH_NAME, + description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_LATEST_NAME, + description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = REQUEST_ID_NAME, + description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class) + }, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response create( + @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Entity Body of Control Loop", required = true) ToscaServiceTemplate body) { + + try { + CommissioningResponse response = provider.createControlLoopDefinitions(body); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId) + .entity(response).build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("Commissioning of the control loops failed", e); + CommissioningResponse resp = new CommissioningResponse(); + resp.setErrorDetails(e.getErrorResponse().getErrorMessage()); + return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp); + } + + } + + /** + * Deletes a control loop definition. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop definition to delete + * @param version the version of the control loop definition to delete + * @return a response + */ + // @formatter:off + @DELETE + @Path("/commission") + @ApiOperation(value = "Delete a commissioned control loop", + notes = "Deletes a Commissioned Control Loop, returning optional error details", + response = CommissioningResponse.class, + tags = { + "Clamp Control Loop Commissioning API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, + description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_PATCH_NAME, + description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_LATEST_NAME, + description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = REQUEST_ID_NAME, + description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses(value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response delete( + @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop definition name", required = true) @QueryParam("name") String name, + @ApiParam(value = "Control Loop definition version", required = true) + @QueryParam("version") String version) { + + try { + CommissioningResponse response = provider.deleteControlLoopDefinition(name, version); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId) + .entity(response).build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("Decommisssioning of control loop failed", e); + CommissioningResponse resp = new CommissioningResponse(); + resp.setErrorDetails(e.getErrorResponse().getErrorMessage()); + return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp); + } + + } + + /** + * Queries details of all or specific control loop definitions. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop definition to get, null for all definitions + * @param version the version of the control loop definition to get, null for all definitions + * @return the control loop definitions + */ + // @formatter:off + @GET + @Path("/commission") + @ApiOperation(value = "Query details of the requested commissioned control loop definitions", + notes = "Queries details of the requested commissioned control loop definitions, " + + "returning all control loop details", + response = ToscaNodeTemplate.class, + tags = { + "Clamp Control Loop Commissioning API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response query(@HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop definition name", required = true) + @QueryParam("name") String name, + @ApiParam(value = "Control Loop definition version", required = true) + @QueryParam("version") String version) { + + try { + List response = provider.getControlLoopDefinitions(name, version); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("Get of control loop definitions failed", e); + CommissioningResponse resp = new CommissioningResponse(); + resp.setErrorDetails(e.getErrorResponse().getErrorMessage()); + return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp); + } + + } + + /** + * Queries the elements of a specific control loop. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop definition to get + * @param version the version of the control loop definition to get + * @return the control loop element definitions + */ + // @formatter:off + @GET + @Path("/commission/elements") + @ApiOperation(value = "Query details of the requested commissioned control loop element definitions", + notes = "Queries details of the requested commissioned control loop element definitions, " + + "returning all control loop elements' details", + response = ToscaNodeTemplate.class, + tags = { + "Clamp Control Loop Commissioning API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response queryElements(@HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop definition name", required = true) + @QueryParam("name") String name, + @ApiParam(value = "Control Loop definition version", required = true) + @QueryParam("version") String version) throws Exception { + + try { + List nodeTemplate = provider.getControlLoopDefinitions(name, version); + //Prevent ambiguous queries with multiple returns + if (nodeTemplate.size() > 1) { + CommissioningResponse resp = new CommissioningResponse(); + resp.setErrorDetails("Multiple ControlLoops are not supported"); + return returnResponse(Response.Status.NOT_ACCEPTABLE, requestId, resp); + } + + List response = provider.getControlLoopElementDefinitions(nodeTemplate.get(0)); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("Get of control loop element definitions failed", e); + CommissioningResponse resp = new CommissioningResponse(); + resp.setErrorDetails(e.getErrorResponse().getErrorMessage()); + return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp); + } + + } + + private Response returnResponse(Response.Status status, UUID requestId, CommissioningResponse resp) { + return addLoggingHeaders(addVersionControlHeaders(Response.status(status)), + requestId).entity(resp).build(); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java new file mode 100644 index 000000000..eb72d9219 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java @@ -0,0 +1,276 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.instantiation; + +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; +import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationCommand; +import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationResponse; +import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider; +import org.onap.policy.clamp.controlloop.runtime.supervision.SupervisionHandler; +import org.onap.policy.common.parameters.BeanValidationResult; +import org.onap.policy.common.parameters.ObjectValidationResult; +import org.onap.policy.common.parameters.ValidationResult; +import org.onap.policy.common.parameters.ValidationStatus; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.base.PfModelRuntimeException; +import org.onap.policy.models.provider.PolicyModelsProviderParameters; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; + +/** + * This class is dedicated to the Instantiation of Commissioned control loop. + */ +public class ControlLoopInstantiationProvider implements Closeable { + private final ControlLoopProvider controlLoopProvider; + private final CommissioningProvider commissioningProvider; + + private static final Object lockit = new Object(); + + /** + * Create a instantiation provider. + * + * @param databaseProviderParameters the parameters for database access + */ + public ControlLoopInstantiationProvider(PolicyModelsProviderParameters databaseProviderParameters) { + try { + controlLoopProvider = new ControlLoopProvider(databaseProviderParameters); + commissioningProvider = new CommissioningProvider(databaseProviderParameters); + } catch (PfModelException e) { + throw new PfModelRuntimeException(e); + } + } + + @Override + public void close() throws IOException { + controlLoopProvider.close(); + } + + /** + * Create control loops. + * + * @param controlLoops the control loop + * @return the result of the instantiation operation + * @throws PfModelException on creation errors + */ + public InstantiationResponse createControlLoops(ControlLoops controlLoops) throws PfModelException { + + synchronized (lockit) { + for (ControlLoop controlLoop : controlLoops.getControlLoopList()) { + ControlLoop checkControlLoop = controlLoopProvider.getControlLoop(controlLoop.getKey().asIdentifier()); + if (checkControlLoop != null) { + throw new PfModelException(Response.Status.BAD_REQUEST, + controlLoop.getKey().asIdentifier() + " already defined"); + } + } + BeanValidationResult validationResult = validateControlLoops(controlLoops); + if (!validationResult.isValid()) { + throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult()); + } + controlLoopProvider.createControlLoops(controlLoops.getControlLoopList()); + } + + InstantiationResponse response = new InstantiationResponse(); + response.setAffectedControlLoops(controlLoops.getControlLoopList().stream() + .map(cl -> cl.getKey().asIdentifier()).collect(Collectors.toList())); + + return response; + } + + /** + * Update control loops. + * + * @param controlLoops the control loop + * @return the result of the instantiation operation + * @throws PfModelException on update errors + */ + public InstantiationResponse updateControlLoops(ControlLoops controlLoops) throws PfModelException { + synchronized (lockit) { + BeanValidationResult validationResult = validateControlLoops(controlLoops); + if (!validationResult.isValid()) { + throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult()); + } + controlLoopProvider.updateControlLoops(controlLoops.getControlLoopList()); + } + + InstantiationResponse response = new InstantiationResponse(); + response.setAffectedControlLoops(controlLoops.getControlLoopList().stream() + .map(cl -> cl.getKey().asIdentifier()).collect(Collectors.toList())); + + return response; + } + + /** + * Validate ControlLoops. + * + * @param controlLoops ControlLoops to validate + * @result the result of validation + * @throws PfModelException if controlLoops is not valid + */ + private BeanValidationResult validateControlLoops(ControlLoops controlLoops) throws PfModelException { + + BeanValidationResult result = new BeanValidationResult("ControlLoops", controlLoops); + + for (ControlLoop controlLoop : controlLoops.getControlLoopList()) { + BeanValidationResult subResult = new BeanValidationResult( + "entry " + controlLoop.getDefinition().getName(), controlLoop); + + List toscaNodeTemplates = commissioningProvider.getControlLoopDefinitions( + controlLoop.getDefinition().getName(), controlLoop.getDefinition().getVersion()); + + if (toscaNodeTemplates.isEmpty()) { + subResult + .addResult(new ObjectValidationResult("ControlLoop", controlLoop.getDefinition().getName(), + ValidationStatus.INVALID, "Commissioned control loop definition not FOUND")); + } else if (toscaNodeTemplates.size() > 1) { + subResult + .addResult(new ObjectValidationResult("ControlLoop", controlLoop.getDefinition().getName(), + ValidationStatus.INVALID, "Commissioned control loop definition not VALID")); + } else { + + List clElementDefinitions = + commissioningProvider.getControlLoopElementDefinitions(toscaNodeTemplates.get(0)); + + // @formatter:off + Map definitions = clElementDefinitions + .stream() + .map(nodeTemplate -> nodeTemplate.getKey().asIdentifier()) + .collect(Collectors.toMap(ToscaConceptIdentifier::getName, UnaryOperator.identity())); + // @formatter:on + + for (ControlLoopElement element : controlLoop.getElements().values()) { + subResult.addResult(validateDefinition(definitions, element.getDefinition())); + } + } + result.addResult(subResult); + } + return result; + } + + /** + * Validate ToscaConceptIdentifier, checking if exist in ToscaConceptIdentifiers map. + * + * @param definitions map of all ToscaConceptIdentifiers + * @param definition ToscaConceptIdentifier to validate + * @result result the validation result + */ + private ValidationResult validateDefinition(Map definitions, + ToscaConceptIdentifier definition) { + BeanValidationResult result = new BeanValidationResult("entry " + definition.getName(), definition); + ToscaConceptIdentifier identifier = definitions.get(definition.getName()); + if (identifier == null) { + result.setResult(ValidationStatus.INVALID, "Not FOUND"); + } else if (!identifier.equals(definition)) { + result.setResult(ValidationStatus.INVALID, "Version not matching"); + } + return (result.isClean() ? null : result); + } + + /** + * Delete the control loop with the given name and version. + * + * @param name the name of the control loop to delete + * @param version the version of the control loop to delete + * @return the result of the deletion + * @throws PfModelException on deletion errors + */ + public InstantiationResponse deleteControlLoop(String name, String version) throws PfModelException { + InstantiationResponse response = new InstantiationResponse(); + synchronized (lockit) { + List controlLoops = controlLoopProvider.getControlLoops(name, version); + if (controlLoops.isEmpty()) { + throw new PfModelException(Response.Status.NOT_FOUND, "Control Loop not found"); + } + for (ControlLoop controlLoop : controlLoops) { + if (!ControlLoopState.UNINITIALISED.equals(controlLoop.getState())) { + throw new PfModelException(Response.Status.BAD_REQUEST, + "Control Loop State is still " + controlLoop.getState()); + } + } + + response.setAffectedControlLoops(Collections + .singletonList(controlLoopProvider.deleteControlLoop(name, version).getKey().asIdentifier())); + } + return response; + } + + /** + * Get the requested control loops. + * + * @param name the name of the control loop to get, null for all control loops + * @param version the version of the control loop to get, null for all control loops + * @return the control loops + * @throws PfModelException on errors getting control loops + */ + public ControlLoops getControlLoops(String name, String version) throws PfModelException { + ControlLoops controlLoops = new ControlLoops(); + controlLoops.setControlLoopList(controlLoopProvider.getControlLoops(name, version)); + + return controlLoops; + } + + /** + * Issue a command to control loops, setting their ordered state. + * + * @param command the command to issue to control loops + * @return the result of the initiation command + * @throws PfModelException on errors setting the ordered state on the control loops + * @throws ControlLoopException on ordered state invalid + */ + public InstantiationResponse issueControlLoopCommand(InstantiationCommand command) + throws ControlLoopException, PfModelException { + + if (command.getOrderedState() == null) { + throw new ControlLoopException(Status.BAD_REQUEST, "ordered state invalid or not specified on command"); + } + + synchronized (lockit) { + List controlLoops = new ArrayList<>(command.getControlLoopIdentifierList().size()); + for (ToscaConceptIdentifier id : command.getControlLoopIdentifierList()) { + ControlLoop controlLoop = controlLoopProvider.getControlLoop(id); + controlLoop.setCascadedOrderedState(command.getOrderedState()); + controlLoops.add(controlLoop); + } + controlLoopProvider.updateControlLoops(controlLoops); + } + + SupervisionHandler supervisionHandler = SupervisionHandler.getInstance(); + supervisionHandler.triggerControlLoopSupervision(command.getControlLoopIdentifierList()); + InstantiationResponse response = new InstantiationResponse(); + response.setAffectedControlLoops(command.getControlLoopIdentifierList()); + + return response; + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java new file mode 100644 index 000000000..d81e54ccf --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java @@ -0,0 +1,82 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.instantiation; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import javax.ws.rs.core.Response; +import lombok.Getter; +import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler; +import org.onap.policy.clamp.controlloop.runtime.instantiation.rest.InstantiationController; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher; +import org.onap.policy.common.utils.services.Registry; +import org.onap.policy.models.base.PfModelRuntimeException; + +/** + * This class handles instantiation of control loop instances. + * + *

It is effectively a singleton that is started at system start + */ +public final class InstantiationHandler extends ControlLoopHandler { + + @Getter + private ControlLoopInstantiationProvider controlLoopInstantiationProvider; + + /** + * Gets the InstantiationHandler. + * + * @return InstantiationHandler + */ + public static InstantiationHandler getInstance() { + return Registry.get(InstantiationHandler.class.getName()); + } + + /** + * Create a handler. + * + * @param controlLoopParameters the parameters for access to the database + */ + public InstantiationHandler(ClRuntimeParameterGroup controlLoopParameters) { + super(controlLoopParameters.getDatabaseProviderParameters()); + } + + @Override + public Set> getProviderClasses() { + return Set.of(InstantiationController.class); + } + + @Override + public void startProviders() { + controlLoopInstantiationProvider = new ControlLoopInstantiationProvider(getDatabaseProviderParameters()); + } + + @Override + public void stopProviders() { + try { + controlLoopInstantiationProvider.close(); + } catch (IOException e) { + throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); + } + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java new file mode 100644 index 000000000..7581aaf74 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java @@ -0,0 +1,416 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.instantiation.rest; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import io.swagger.annotations.ResponseHeader; +import java.util.UUID; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops; +import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationCommand; +import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationResponse; +import org.onap.policy.clamp.controlloop.runtime.instantiation.ControlLoopInstantiationProvider; +import org.onap.policy.clamp.controlloop.runtime.instantiation.InstantiationHandler; +import org.onap.policy.clamp.controlloop.runtime.main.rest.RestController; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.base.PfModelRuntimeException; +import org.onap.policy.models.errors.concepts.ErrorResponseInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class to provide REST end points for creating, deleting, query and commanding a control loop definition. + */ +public class InstantiationController extends RestController { + + private static final Logger LOGGER = LoggerFactory.getLogger(InstantiationController.class); + + // The CL provider for instantiation requests + private final ControlLoopInstantiationProvider provider; + + /** + * create Instantiation Controller. + */ + public InstantiationController() { + this.provider = InstantiationHandler.getInstance().getControlLoopInstantiationProvider(); + } + + /** + * Creates a control loop. + * + * @param requestId request ID used in ONAP logging + * @param controlLoops the control loops + * @return a response + */ + // @formatter:off + @POST + @Path("/instantiation") + @ApiOperation( + value = "Commissions control loop definitions", + notes = "Commissions control loop definitions, returning the control loop IDs", + response = InstantiationResponse.class, + tags = { + "Control Loop Instantiation API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, + description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_PATCH_NAME, + description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_LATEST_NAME, + description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = REQUEST_ID_NAME, + description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class) + }, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response create( + @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Entity Body of Control Loop", required = true) ControlLoops controlLoops) { + + try { + InstantiationResponse response = provider.createControlLoops(controlLoops); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("creation of control loop failed", e); + return createInstantiationErrorResponse(e, requestId); + } + } + + /** + * Queries details of all control loops. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop to get, null for all control loops + * @param version the version of the control loop to get, null for all control loops + * @return the control loops + */ + // @formatter:off + @GET + @Path("/instantiation") + @ApiOperation(value = "Query details of the requested control loops", + notes = "Queries details of the requested control loops, returning all control loop details", + response = ControlLoops.class, + tags = { + "Clamp control loop Instantiation API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response query( + @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop definition name", required = true) @QueryParam("name") String name, + @ApiParam(value = "Control Loop definition version", + required = true) @QueryParam("version") String version) { + + try { + ControlLoops response = provider.getControlLoops(name, version); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("commisssioning of control loop failed", e); + return createInstantiationErrorResponse(e, requestId); + } + + } + + /** + * Updates a control loop. + * + * @param requestId request ID used in ONAP logging + * @param controlLoops the control loops + * @return a response + */ + // @formatter:off + @PUT + @Path("/instantiation") + @ApiOperation( + value = "Updates control loop definitions", + notes = "Updates control loop definitions, returning the updated control loop definition IDs", + response = InstantiationResponse.class, + tags = { + "Control Loop Instantiation API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, + description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_PATCH_NAME, + description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_LATEST_NAME, + description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = REQUEST_ID_NAME, + description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class) + }, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response update( + @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Entity Body of Control Loop", required = true) ControlLoops controlLoops) { + + try { + InstantiationResponse response = provider.updateControlLoops(controlLoops); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("update of control loops failed", e); + return createInstantiationErrorResponse(e, requestId); + } + } + + /** + * Deletes a control loop definition. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop to delete + * @param version the version of the control loop to delete + * @return a response + */ + // @formatter:off + @DELETE + @Path("/instantiation") + @ApiOperation(value = "Delete a control loop", + notes = "Deletes a control loop, returning optional error details", + response = InstantiationResponse.class, + tags = { + "Clamp Control Loop Instantiation API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, + description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_PATCH_NAME, + description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = VERSION_LATEST_NAME, + description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader( + name = REQUEST_ID_NAME, + description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses(value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + + public Response delete( + @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop definition name", required = true) @QueryParam("name") String name, + @ApiParam(value = "Control Loop definition version", required = true) + @QueryParam("version") String version) { + + try { + InstantiationResponse response = provider.deleteControlLoop(name, version); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("delete of control loop failed", e); + return createInstantiationErrorResponse(e, requestId); + } + } + + /** + * Issues control loop commands to control loops. + * + * @param requestId request ID used in ONAP logging + * @param command the command to issue to control loops + * @return the control loop definitions + */ + // @formatter:off + @PUT + @Path("/instantiation/command") + @ApiOperation(value = "Issue a command to the requested control loops", + notes = "Issues a command to a control loop, ordering a state change on the control loop", + response = InstantiationResponse.class, + tags = { + "Clamp Control Loop Instantiation API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response issueControlLoopCommand( + @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Entity Body of control loop command", required = true) InstantiationCommand command) { + + try { + InstantiationResponse response = provider.issueControlLoopCommand(command); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.ACCEPTED)), requestId) + .entity(response).build(); + + } catch (PfModelRuntimeException | PfModelException | ControlLoopException e) { + LOGGER.warn("creation of control loop failed", e); + return createInstantiationErrorResponse(e, requestId); + } + } + + /** + * create a Instantiation Response from an exception. + * @param e the error + * @param requestId request ID used in ONAP logging + * @return the Instantiation Response + */ + private Response createInstantiationErrorResponse(ErrorResponseInfo e, UUID requestId) { + InstantiationResponse resp = new InstantiationResponse(); + resp.setErrorDetails(e.getErrorResponse().getErrorMessage()); + return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())), + requestId).entity(resp).build(); + } +} + diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java new file mode 100644 index 000000000..4c99b8e57 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.main.parameters; + +import java.util.List; +import javax.validation.constraints.NotBlank; +import lombok.Getter; +import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams; +import org.onap.policy.common.endpoints.parameters.RestServerParameters; +import org.onap.policy.common.endpoints.parameters.TopicParameterGroup; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.NotNull; +import org.onap.policy.models.provider.PolicyModelsProviderParameters; + +/** + * Class to hold all parameters needed for the Control Loop runtime component. + * + */ +@NotNull +@NotBlank +@Getter +public class ClRuntimeParameterGroup extends ParameterGroupImpl { + private RestServerParameters restServerParameters; + private PolicyModelsProviderParameters databaseProviderParameters; + private ParticipantParameters participantParameters; + private TopicParameterGroup topicParameterGroup; + private List healthCheckRestClientParameters; + + /** + * Create the Control Loop parameter group. + * + * @param name the parameter group name + */ + public ClRuntimeParameterGroup(final String name) { + super(name); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java new file mode 100644 index 000000000..a463ad171 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java @@ -0,0 +1,77 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.main.parameters; + +import java.io.File; +import javax.ws.rs.core.Response; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.runtime.main.startstop.ClRuntimeCommandLineArguments; +import org.onap.policy.common.parameters.ValidationResult; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; + +/** + * This class handles reading, parsing and validating of control loop runtime parameters from JSON files. + */ +public class ClRuntimeParameterHandler { + + private static final Coder CODER = new StandardCoder(); + + /** + * Read the parameters from the parameter file. + * + * @param arguments the arguments passed to control loop runtime + * @return the parameters read from the configuration file + * @throws ControlLoopException on parameter exceptions + */ + public ClRuntimeParameterGroup getParameters(final ClRuntimeCommandLineArguments arguments) + throws ControlLoopException { + ClRuntimeParameterGroup clRuntimeParameterGroup = null; + + // Read the parameters + try { + // Read the parameters from JSON + File file = new File(arguments.getFullConfigurationFilePath()); + clRuntimeParameterGroup = CODER.decode(file, ClRuntimeParameterGroup.class); + } catch (final CoderException e) { + throw new ControlLoopException( + Response.Status.NOT_ACCEPTABLE, "error reading parameters from \"" + + arguments.getConfigurationFilePath() + "\"\n" + "(" + e.getClass().getSimpleName() + ")", + e); + } + + // The JSON processing returns null if there is an empty file + if (clRuntimeParameterGroup == null) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + "no parameters found in \"" + arguments.getConfigurationFilePath() + "\""); + } + + // validate the parameters + final ValidationResult validationResult = clRuntimeParameterGroup.validate(); + if (!validationResult.isValid()) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, "validation error(s) on parameters from \"" + + arguments.getConfigurationFilePath() + "\"\n" + validationResult.getResult()); + } + + return clRuntimeParameterGroup; + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java new file mode 100644 index 000000000..dfc1b2806 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.main.parameters; + +import java.util.concurrent.TimeUnit; +import lombok.Getter; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; + +/** + * Parameters for communicating with participants. + */ +@NotNull +@NotBlank +@Getter +public class ParticipantParameters extends ParameterGroupImpl { + + /** + * Default maximum message age, in milliseconds, that should be examined. Any message + * older than this is discarded. + */ + public static final long DEFAULT_MAX_AGE_MS = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES); + + + @Min(1) + private long heartBeatMs; + + @Min(1) + private long maxMessageAgeMs = DEFAULT_MAX_AGE_MS; + + private ParticipantUpdateParameters updateParameters; + private ParticipantStateChangeParameters stateChangeParameters; + + + /** + * Constructs the object. + */ + public ParticipantParameters() { + super(ParticipantParameters.class.getSimpleName()); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java new file mode 100644 index 000000000..2eea4ab51 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.main.parameters; + +import lombok.Getter; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; + +/** + * Parameters for Participant STATE-CHANGE requests. + */ +@NotNull +@NotBlank +@Getter +public class ParticipantStateChangeParameters extends ParameterGroupImpl { + + /** + * Maximum number of times to re-send a request to a PDP. + */ + @Min(value = 0) + private int maxRetryCount; + + /** + * Maximum time to wait, in milliseconds, for a PDP response. + */ + @Min(value = 0) + private long maxWaitMs; + + /** + * Constructs the object. + */ + public ParticipantStateChangeParameters() { + super(ParticipantStateChangeParameters.class.getSimpleName()); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java new file mode 100644 index 000000000..2af5be534 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java @@ -0,0 +1,54 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.main.parameters; + +import lombok.Getter; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; + +/** + * Parameters for Participant UPDATE requests. + */ +@NotNull +@NotBlank +@Getter +public class ParticipantUpdateParameters extends ParameterGroupImpl { + + /** + * Maximum number of times to re-send a request to a PDP. + */ + @Min(value = 0) + private int maxRetryCount; + + /** + * Maximum time to wait, in milliseconds, for a PDP response. + */ + @Min(value = 0) + private long maxWaitMs; + + /** + * Constructs the object. + */ + public ParticipantUpdateParameters() { + super(ParticipantUpdateParameters.class.getSimpleName()); + } +} + diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java new file mode 100644 index 000000000..f166de5d6 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.main.rest; + +import org.onap.policy.common.endpoints.http.server.aaf.AafGranularAuthFilter; +import org.onap.policy.common.utils.resources.MessageConstants; + +/** + * Class to manage AAF filters for the control loop runtime component. + */ +public class ControlLoopAafFilter extends AafGranularAuthFilter { + + public static final String AAF_NODETYPE = MessageConstants.POLICY_CLAMP; + public static final String AAF_ROOT_PERMISSION = DEFAULT_NAMESPACE + "." + AAF_NODETYPE; + + @Override + public String getPermissionTypeRoot() { + return AAF_ROOT_PERMISSION; + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java new file mode 100644 index 000000000..dd3fa30fc --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java @@ -0,0 +1,115 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.main.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.BasicAuthDefinition; +import io.swagger.annotations.Info; +import io.swagger.annotations.SecurityDefinition; +import io.swagger.annotations.SwaggerDefinition; +import io.swagger.annotations.Tag; +import java.net.HttpURLConnection; +import java.util.UUID; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response.ResponseBuilder; + +/** + * Common superclass to provide REST endpoints for the control loop service. + */ +// @formatter:off +@Path("/onap/controlloop/v2") +@Api(value = "Control Loop API") +@Produces({MediaType.APPLICATION_JSON, RestController.APPLICATION_YAML}) +@SwaggerDefinition( + info = @Info(description = + "Control Loop Service", version = "v1.0", + title = "Control Loop"), + consumes = {MediaType.APPLICATION_JSON, RestController.APPLICATION_YAML}, + produces = {MediaType.APPLICATION_JSON, RestController.APPLICATION_YAML}, + schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}, + tags = {@Tag(name = "controlloop", description = "Control Loop Service")}, + securityDefinition = @SecurityDefinition(basicAuthDefinitions = {@BasicAuthDefinition(key = "basicAuth")})) +// @formatter:on +public class RestController { + public static final String APPLICATION_YAML = "application/yaml"; + + public static final String EXTENSION_NAME = "interface info"; + + public static final String API_VERSION_NAME = "api-version"; + public static final String API_VERSION = "1.0.0"; + + public static final String LAST_MOD_NAME = "last-mod-release"; + public static final String LAST_MOD_RELEASE = "Dublin"; + + public static final String VERSION_MINOR_NAME = "X-MinorVersion"; + public static final String VERSION_MINOR_DESCRIPTION = + "Used to request or communicate a MINOR version back from the client" + + " to the server, and from the server back to the client"; + + public static final String VERSION_PATCH_NAME = "X-PatchVersion"; + public static final String VERSION_PATCH_DESCRIPTION = "Used only to communicate a PATCH version in a response for" + + " troubleshooting purposes only, and will not be provided by" + " the client on request"; + + public static final String VERSION_LATEST_NAME = "X-LatestVersion"; + public static final String VERSION_LATEST_DESCRIPTION = "Used only to communicate an API's latest version"; + + public static final String REQUEST_ID_NAME = "X-ONAP-RequestID"; + public static final String REQUEST_ID_HDR_DESCRIPTION = "Used to track REST transactions for logging purpose"; + public static final String REQUEST_ID_PARAM_DESCRIPTION = "RequestID for http transaction"; + + public static final String AUTHORIZATION_TYPE = "basicAuth"; + + public static final int AUTHENTICATION_ERROR_CODE = HttpURLConnection.HTTP_UNAUTHORIZED; + public static final int AUTHORIZATION_ERROR_CODE = HttpURLConnection.HTTP_FORBIDDEN; + public static final int SERVER_ERROR_CODE = HttpURLConnection.HTTP_INTERNAL_ERROR; + + public static final String AUTHENTICATION_ERROR_MESSAGE = "Authentication Error"; + public static final String AUTHORIZATION_ERROR_MESSAGE = "Authorization Error"; + public static final String SERVER_ERROR_MESSAGE = "Internal Server Error"; + + /** + * Adds version headers to the response. + * + * @param respBuilder response builder + * @return the response builder, with version headers + */ + public ResponseBuilder addVersionControlHeaders(ResponseBuilder respBuilder) { + return respBuilder.header(VERSION_MINOR_NAME, "0").header(VERSION_PATCH_NAME, "0").header(VERSION_LATEST_NAME, + API_VERSION); + } + + /** + * Adds logging headers to the response. + * + * @param respBuilder response builder + * @return the response builder, with version logging + */ + public ResponseBuilder addLoggingHeaders(ResponseBuilder respBuilder, UUID requestId) { + if (requestId == null) { + // Generate a random uuid if client does not embed requestId in rest request + return respBuilder.header(REQUEST_ID_NAME, UUID.randomUUID()); + } + + return respBuilder.header(REQUEST_ID_NAME, requestId); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java new file mode 100644 index 000000000..a4238a9c4 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java @@ -0,0 +1,186 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.main.startstop; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import javax.ws.rs.core.Response.Status; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler; +import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningHandler; +import org.onap.policy.clamp.controlloop.runtime.instantiation.InstantiationHandler; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.clamp.controlloop.runtime.main.rest.ControlLoopAafFilter; +import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringHandler; +import org.onap.policy.clamp.controlloop.runtime.supervision.SupervisionHandler; +import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.common.endpoints.http.server.RestServer; +import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher; +import org.onap.policy.common.parameters.ParameterService; +import org.onap.policy.common.utils.services.ServiceManagerContainer; + +/** + * This class activates the control loop runtime component as a complete service together with all its controllers, + * listeners & handlers. + */ +public class ClRuntimeActivator extends ServiceManagerContainer { + // Name of the message type for messages on topics + private static final String[] MSG_TYPE_NAMES = {"messageType"}; + + private final ClRuntimeParameterGroup clRuntimeParameterGroup; + + // Topics from which the application receives and to which the application sends messages + private List topicSinks; + private List topicSources; + + /** + * Listens for messages on the topic, decodes them into a message, and then dispatches them. + */ + private final MessageTypeDispatcher msgDispatcher; + + /** + * Instantiate the activator for the control loop runtime as a complete service. + * + * @param clRuntimeParameterGroup the parameters for the control loop runtime service + */ + public ClRuntimeActivator(final ClRuntimeParameterGroup clRuntimeParameterGroup) { + + if (clRuntimeParameterGroup == null || !clRuntimeParameterGroup.isValid()) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, "ParameterGroup not valid"); + } + + this.clRuntimeParameterGroup = clRuntimeParameterGroup; + + topicSinks = TopicEndpointManager.getManager() + .addTopicSinks(clRuntimeParameterGroup.getTopicParameterGroup().getTopicSinks()); + + topicSources = TopicEndpointManager.getManager() + .addTopicSources(clRuntimeParameterGroup.getTopicParameterGroup().getTopicSources()); + + try { + msgDispatcher = new MessageTypeDispatcher(MSG_TYPE_NAMES); + } catch (final RuntimeException e) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, + "topic message dispatcher failed to start", e); + } + + final AtomicReference commissioningHandler = new AtomicReference<>(); + final AtomicReference instantiationHandler = new AtomicReference<>(); + final AtomicReference supervisionHandler = new AtomicReference<>(); + final AtomicReference monitoringHandler = new AtomicReference<>(); + final AtomicReference restServer = new AtomicReference<>(); + + // @formatter:off + addAction("Control loop runtime parameters", + () -> ParameterService.register(clRuntimeParameterGroup), + () -> ParameterService.deregister(clRuntimeParameterGroup.getName())); + + addAction("Topic endpoint management", + () -> TopicEndpointManager.getManager().start(), + () -> TopicEndpointManager.getManager().shutdown()); + + addAction("Commissioning Handler", + () -> commissioningHandler.set(new CommissioningHandler(clRuntimeParameterGroup)), + () -> commissioningHandler.get().close()); + + addAction("Instantiation Handler", + () -> instantiationHandler.set(new InstantiationHandler(clRuntimeParameterGroup)), + () -> instantiationHandler.get().close()); + + addAction("Supervision Handler", + () -> supervisionHandler.set(new SupervisionHandler(clRuntimeParameterGroup)), + () -> supervisionHandler.get().close()); + + addAction("Monitoring Handler", + () -> monitoringHandler.set(new MonitoringHandler(clRuntimeParameterGroup)), + () -> monitoringHandler.get().close()); + + addHandlerActions("Commissioning", commissioningHandler); + addHandlerActions("Instantiation", instantiationHandler); + addHandlerActions("Supervision", supervisionHandler); + addHandlerActions("Monitoring", monitoringHandler); + + addAction("Topic Message Dispatcher", this::registerMsgDispatcher, this::unregisterMsgDispatcher); + + clRuntimeParameterGroup.getRestServerParameters().setName(clRuntimeParameterGroup.getName()); + + addAction("REST server", + () -> { + Set> providerClasses = new HashSet<>(); + providerClasses.addAll(commissioningHandler.get().getProviderClasses()); + providerClasses.addAll(instantiationHandler.get().getProviderClasses()); + providerClasses.addAll(supervisionHandler.get().getProviderClasses()); + providerClasses.addAll(monitoringHandler.get().getProviderClasses()); + + RestServer server = new RestServer(clRuntimeParameterGroup.getRestServerParameters(), + ControlLoopAafFilter.class, + providerClasses.toArray(new Class[providerClasses.size()])); + + restServer.set(server); + restServer.get().start(); + }, + () -> restServer.get().stop()); + // @formatter:on + } + + private void addHandlerActions(final String name, final AtomicReference handler) { + addAction(name + " Providers", + () -> handler.get().startProviders(), + () -> handler.get().stopProviders()); + addAction(name + " Listeners", + () -> handler.get().startAndRegisterListeners(msgDispatcher), + () -> handler.get().stopAndUnregisterListeners(msgDispatcher)); + addAction(name + " Publishers", + () -> handler.get().startAndRegisterPublishers(topicSinks), + () -> handler.get().stopAndUnregisterPublishers()); + } + + /** + * Registers the dispatcher with the topic source(s). + */ + private void registerMsgDispatcher() { + for (final TopicSource source : topicSources) { + source.register(msgDispatcher); + } + } + + /** + * Unregisters the dispatcher from the topic source(s). + */ + private void unregisterMsgDispatcher() { + for (final TopicSource source : topicSources) { + source.unregister(msgDispatcher); + } + } + + /** + * Get the parameters used by the activator. + * + * @return the parameters of the activator + */ + public ClRuntimeParameterGroup getParameterGroup() { + return clRuntimeParameterGroup; + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java new file mode 100644 index 000000000..f36bb858b --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java @@ -0,0 +1,151 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.main.startstop; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URL; +import java.util.Arrays; +import javax.ws.rs.core.Response; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.common.startstop.CommonCommandLineArguments; +import org.onap.policy.common.utils.resources.ResourceUtils; + +/** + * This class reads and handles command line parameters for the control loop runtime service. + */ +public class ClRuntimeCommandLineArguments { + private static final String FILE_MESSAGE_PREAMBLE = " file \""; + private static final int HELP_LINE_LENGTH = 120; + + private final Options options; + private final CommonCommandLineArguments commonCommandLineArguments; + + @Getter() + @Setter() + private String configurationFilePath = null; + + /** + * Construct the options for the control loop runtime component. + */ + public ClRuntimeCommandLineArguments() { + options = new Options(); + commonCommandLineArguments = new CommonCommandLineArguments(options); + } + + /** + * Construct the options for the CLI editor and parse in the given arguments. + * + * @param args The command line arguments + */ + public ClRuntimeCommandLineArguments(final String[] args) { + // Set up the options with the default constructor + this(); + + // Parse the arguments + try { + parse(args); + } catch (final ControlLoopException e) { + throw new ControlLoopRuntimeException(Response.Status.NOT_ACCEPTABLE, + "parse error on control loop runtime parameters", e); + } + } + + /** + * Parse the command line options. + * + * @param args The command line arguments + * @return a string with a message for help and version, or null if there is no message + * @throws ControlLoopException on command argument errors + */ + public String parse(final String[] args) throws ControlLoopException { + // Clear all our arguments + setConfigurationFilePath(null); + CommandLine commandLine = null; + try { + commandLine = new DefaultParser().parse(options, args); + } catch (final ParseException e) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + "invalid command line arguments specified : " + e.getMessage()); + } + + // Arguments left over after Commons CLI does its stuff + final String[] remainingArgs = commandLine.getArgs(); + + if (remainingArgs.length > 0) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + "too many command line arguments specified : " + Arrays.toString(args)); + } + + if (commandLine.hasOption('h')) { + return commonCommandLineArguments.help(Main.class.getName(), options); + } + + if (commandLine.hasOption('v')) { + return commonCommandLineArguments.version(); + } + + if (commandLine.hasOption('c')) { + setConfigurationFilePath(commandLine.getOptionValue('c')); + } + + return null; + } + + /** + * Validate the command line options. + * + * @throws ControlLoopException on command argument validation errors + */ + public void validate() throws ControlLoopException { + commonCommandLineArguments.validate(configurationFilePath); + } + + /** + * Gets the full expanded configuration file path. + * + * @return the configuration file path + */ + public String getFullConfigurationFilePath() { + return ResourceUtils.getFilePath4Resource(getConfigurationFilePath()); + } + + /** + * Sets the configuration file path. + * + * @param configurationFilePath the configuration file path + */ + public void setConfigurationFilePath(final String configurationFilePath) { + this.configurationFilePath = configurationFilePath; + + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java new file mode 100644 index 000000000..8e60d68cf --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java @@ -0,0 +1,156 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.main.startstop; + +import java.util.Arrays; +import javax.ws.rs.core.Response; +import org.onap.policy.clamp.controlloop.common.ControlLoopConstants; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterHandler; +import org.onap.policy.common.utils.resources.MessageConstants; +import org.onap.policy.common.utils.services.Registry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class initiates ONAP Policy Framework Control Loop runtime component. + */ +public class Main { + + private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); + + private ClRuntimeActivator activator; + private ClRuntimeParameterGroup parameterGroup; + + /** + * Instantiates the control loop runtime service. + * + * @param args the command line arguments + */ + public Main(final String[] args) { + final String argumentString = Arrays.toString(args); + LOGGER.info("Starting the control loop runtime service with arguments - {}", argumentString); + + // Check the arguments + final ClRuntimeCommandLineArguments arguments = new ClRuntimeCommandLineArguments(); + try { + // The arguments return a string if there is a message to print and we should exit + final String argumentMessage = arguments.parse(args); + if (argumentMessage != null) { + LOGGER.info(argumentMessage); + return; + } + // Validate that the arguments are sane + arguments.validate(); + + // Read the parameters + parameterGroup = new ClRuntimeParameterHandler().getParameters(arguments); + + // Now, create the activator for the service + activator = new ClRuntimeActivator(parameterGroup); + Registry.register(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR, activator); + + // Start the activator + activator.start(); + } catch (Exception exp) { + if (null != activator) { + Registry.unregister(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR); + } + throw new ControlLoopRuntimeException(Response.Status.BAD_REQUEST, + String.format(MessageConstants.START_FAILURE_MSG, MessageConstants.POLICY_CLAMP), exp); + } + + // Add a shutdown hook to shut everything down in an orderly manner + Runtime.getRuntime().addShutdownHook(new ClRuntimeShutdownHookClass()); + String successMsg = String.format(MessageConstants.START_SUCCESS_MSG, MessageConstants.POLICY_CLAMP); + LOGGER.info(successMsg); + } + + /** + * Check if main is running. + */ + public boolean isRunning() { + return activator != null && activator.isAlive(); + } + + /** + * Get the parameters specified in JSON. + * + * @return the parameters + */ + public ClRuntimeParameterGroup getParameters() { + return parameterGroup; + } + + /** + * Shut down Execution. + * + * @throws ControlLoopException on shutdown errors + */ + public void shutdown() throws ControlLoopException { + // clear the parameterGroup variable + parameterGroup = null; + + // clear the cl runtime activator + if (activator != null) { + activator.stop(); + } + } + + /** + * The Class ClRuntimeShutdownHookClass terminates the control loop runtime service when its run method is called. + */ + private class ClRuntimeShutdownHookClass extends Thread { + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + if (!activator.isAlive()) { + return; + } + + try { + // Shutdown the control loop runtime service and wait for everything to stop + activator.stop(); + } catch (final RuntimeException e) { + LOGGER.warn("error occured during shut down of the control loop runtime service", e); + } + } + } + + /** + * The main method. + * + * @param args the arguments + */ + public static void main(final String[] args) { // NOSONAR + /* + * NOTE: arguments are validated by the constructor, thus sonar is disabled. + */ + + new Main(args); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java new file mode 100644 index 000000000..a7ad9180a --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java @@ -0,0 +1,84 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.monitoring; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import javax.ws.rs.core.Response; +import lombok.Getter; +import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.clamp.controlloop.runtime.monitoring.rest.MonitoringQueryController; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher; +import org.onap.policy.common.utils.services.Registry; +import org.onap.policy.models.base.PfModelRuntimeException; + +/** + * This class handles monitoring of control loop definitions, + * so only one object of this type should be built at a time. + * + *

+ * It is effectively a singleton that is started at system start. + */ +public class MonitoringHandler extends ControlLoopHandler { + + @Getter + private MonitoringProvider monitoringProvider; + + /** + * Gets the Monitoring Handler. + * + * @return MonitoringHandler + */ + public static MonitoringHandler getInstance() { + return Registry.get(MonitoringHandler.class.getName()); + } + + /** + * Create a handler. + * + * @param controlLoopParameters the parameters for access to the database + */ + public MonitoringHandler(ClRuntimeParameterGroup controlLoopParameters) { + super(controlLoopParameters.getDatabaseProviderParameters()); + } + + @Override + public Set> getProviderClasses() { + return Set.of(MonitoringQueryController.class); + } + + @Override + public void startProviders() { + monitoringProvider = new MonitoringProvider(getDatabaseProviderParameters()); + } + + @Override + public void stopProviders() { + try { + monitoringProvider.close(); + } catch (IOException e) { + throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, "Cannot stop provider", e); + } + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java new file mode 100644 index 000000000..193f8d557 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java @@ -0,0 +1,273 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.monitoring; + +import java.io.Closeable; +import java.io.IOException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.NonNull; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatisticsList; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatistics; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatisticsList; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ClElementStatisticsProvider; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantStatisticsProvider; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.base.PfModelRuntimeException; +import org.onap.policy.models.provider.PolicyModelsProviderParameters; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +/** + * This class provides information about statistics data of CL elements and CL Participants in database to callers. + */ +public class MonitoringProvider implements Closeable { + + private static final String DESC_ORDER = "DESC"; + private final ParticipantStatisticsProvider participantStatisticsProvider; + private final ClElementStatisticsProvider clElementStatisticsProvider; + private final ControlLoopProvider controlLoopProvider; + + /** + * Create a Monitoring provider. + * + */ + public MonitoringProvider(PolicyModelsProviderParameters parameters) { + + try { + participantStatisticsProvider = new ParticipantStatisticsProvider(parameters); + clElementStatisticsProvider = new ClElementStatisticsProvider(parameters); + controlLoopProvider = new ControlLoopProvider(parameters); + } catch (PfModelException e) { + throw new PfModelRuntimeException(e); + } + } + + @Override + public void close() throws IOException { + controlLoopProvider.close(); + clElementStatisticsProvider.close(); + participantStatisticsProvider.close(); + } + + /** + * Create participant statistics. + * + * @param participantStatistics the participant statistics + * @return the result of create operation + * @throws PfModelException on creation errors + */ + public ParticipantStatisticsList createParticipantStatistics(List participantStatistics) + throws PfModelException { + ParticipantStatisticsList participantStatisticsList = new ParticipantStatisticsList(); + participantStatisticsList.setStatisticsList(participantStatisticsProvider + .createParticipantStatistics(participantStatistics)); + + return participantStatisticsList; + } + + /** + * Create clElement statistics. + * + * @param clElementStatisticsList the clElement statistics + * @return the result of create operation + * @throws PfModelException on creation errors + */ + public ClElementStatisticsList createClElementStatistics(List clElementStatisticsList) + throws PfModelException { + ClElementStatisticsList elementStatisticsList = new ClElementStatisticsList(); + elementStatisticsList.setClElementStatistics(clElementStatisticsProvider + .createClElementStatistics(clElementStatisticsList)); + + return elementStatisticsList; + } + + /** + * Get participant statistics based on specific filters. + * + * @param name the name of the participant statistics to get, null to get all statistics + * @param version the version of the participant statistics to get, null to get all statistics + * @param recordCount number of records to be fetched. + * @param startTime start of the timestamp, from statistics to be filtered + * @param endTime end of the timestamp up to which statistics to be filtered + * @return the participant found + */ + public ParticipantStatisticsList fetchFilteredParticipantStatistics(@NonNull final String name, + final String version, int recordCount, + Instant startTime, Instant endTime) { + ParticipantStatisticsList participantStatisticsList = new ParticipantStatisticsList(); + + //Additional parameters can be added in filterMap for filtering data. + Map filterMap = null; + participantStatisticsList.setStatisticsList(participantStatisticsProvider.getFilteredParticipantStatistics( + name, version, startTime, endTime, filterMap, DESC_ORDER, recordCount)); + + return participantStatisticsList; + } + + /** + * Get all participant statistics records found for a specific control loop. * + * + * @param controlLoopName name of the control loop + * @param controlLoopVersion version of the control loop + * @return All the participant statistics found + * @throws PfModelException on errors getting participant statistics + */ + public ParticipantStatisticsList fetchParticipantStatsPerControlLoop(@NonNull final String controlLoopName, + @NonNull final String controlLoopVersion) + throws PfModelException { + ParticipantStatisticsList statisticsList = new ParticipantStatisticsList(); + List participantStatistics = new ArrayList<>(); + try { + //Fetch all participantIds for a specific control loop + List participantIds = getAllParticipantIdsPerControlLoop(controlLoopName, + controlLoopVersion); + for (ToscaConceptIdentifier id: participantIds) { + participantStatistics.addAll(participantStatisticsProvider.getFilteredParticipantStatistics( + id.getName(), id.getVersion(), null, null, null, DESC_ORDER, 0)); + } + statisticsList.setStatisticsList(participantStatistics); + } catch (PfModelException e) { + throw new PfModelRuntimeException(e); + } + return statisticsList; + } + + + + /** + * Get clElement statistics based on specific filters. + * + * @param name the name of the clElement statistics to get, null to get all statistics + * @param version the version of the clElement statistics to get, null to get all statistics + * @param id UUID of the control loop element + * @param startTime start of the timestamp, from statistics to be filtered + * @param endTime end of the timestamp up to which statistics to be filtered + * @param recordCount number of records to be fetched. + * @return the participant found + * @throws PfModelException on errors getting control loop statistics + */ + public ClElementStatisticsList fetchFilteredClElementStatistics(@NonNull final String name, final String version, + final String id, Instant startTime, Instant endTime, + int recordCount) throws PfModelException { + ClElementStatisticsList clElementStatisticsList = new ClElementStatisticsList(); + Map filterMap = new HashMap<>(); + //Adding UUID in filter if present + if (id != null) { + filterMap.put("localName", id); + } + clElementStatisticsList.setClElementStatistics(clElementStatisticsProvider.getFilteredClElementStatistics( + name, version, startTime, endTime, filterMap, DESC_ORDER, recordCount)); + + return clElementStatisticsList; + } + + + /** + * Get clElement statistics per control loop. + * + * @param name the name of the control loop + * @param version the version of the control loop + * @return the clElement statistics found + * @throws PfModelException on errors getting control loop statistics + */ + public ClElementStatisticsList fetchClElementStatsPerControlLoop(@NonNull final String name, + @NonNull final String version) + throws PfModelException { + ClElementStatisticsList clElementStatisticsList = new ClElementStatisticsList(); + List clElementStats = new ArrayList<>(); + try { + List clElements = new ArrayList<>(); + //Fetch all control loop elements for the control loop + ControlLoop controlLoop = controlLoopProvider.getControlLoop(new ToscaConceptIdentifier(name, + version)); + if (controlLoop != null) { + clElements.addAll(controlLoop.getElements().values()); + //Collect control loop element statistics for each cl element. + for (ControlLoopElement clElement : clElements) { + clElementStats.addAll(fetchFilteredClElementStatistics(clElement.getParticipantId().getName(), + clElement.getParticipantId().getVersion(), clElement.getId().toString(), null, + null, 0).getClElementStatistics()); + } + } + clElementStatisticsList.setClElementStatistics(clElementStats); + } catch (PfModelException e) { + throw new PfModelRuntimeException(e); + } + return clElementStatisticsList; + } + + /** + * If required, REST end point can be defined for this method to fetch associated participant Ids + * for a control loop. + * + * @param name the name of the control loop + * @param version the version of the control loop + * @return List of participant Id + * @throws PfModelException on errors + */ + public List getAllParticipantIdsPerControlLoop(String name, String version) + throws PfModelException { + List participantIds = new ArrayList<>(); + ControlLoop controlLoop = controlLoopProvider.getControlLoop(new ToscaConceptIdentifier(name, version)); + if (controlLoop != null) { + for (ControlLoopElement clElement : controlLoop.getElements().values()) { + participantIds.add(clElement.getParticipantId()); + } + } + return participantIds; + } + + /** + * If required, REST end point can be defined for this method to fetch associated control loop element Ids + * for a control loop. + * + * @param name the name of the control loop + * @param version the version of the control loop + * @return Map of control loop Id and participant details + * @throws PfModelException on errors + */ + public Map getAllClElementsIdPerControlLoop(String name, String version) + throws PfModelException { + Map clElementId = new HashMap<>(); + ControlLoop controlLoop = controlLoopProvider.getControlLoop(new ToscaConceptIdentifier(name, version)); + if (controlLoop != null) { + for (ControlLoopElement clElement : controlLoop.getElements().values()) { + clElementId.put(clElement.getId().toString(), clElement.getParticipantId()); + } + } + return clElementId; + } + + public void updateClElementStatistics(List clElementStatistics) { + // TODO Auto-generated method stub + } + + public void updateParticipantStatistics(List statisticsList) { + // TODO Auto-generated method stub + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java new file mode 100644 index 000000000..2e19ffe3a --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java @@ -0,0 +1,371 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.monitoring.rest; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import io.swagger.annotations.ResponseHeader; +import java.time.Instant; +import java.util.UUID; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatisticsList; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatisticsList; +import org.onap.policy.clamp.controlloop.runtime.main.rest.RestController; +import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringHandler; +import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringProvider; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.base.PfModelRuntimeException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class handles REST endpoints for CL Statistics monitoring. + */ +public class MonitoringQueryController extends RestController { + + private static final Logger LOGGER = LoggerFactory.getLogger(MonitoringQueryController.class); + private final MonitoringProvider provider; + + /** + * Create Monitoring Controller. + */ + public MonitoringQueryController() { + this.provider = MonitoringHandler.getInstance().getMonitoringProvider(); + } + + + /** + * Queries details of control loop participants statistics. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the participant to get, null for all participants statistics + * @param recordCount the record count to be fetched + * @return the participant statistics + */ + // @formatter:off + @GET + @Path("/monitoring/participant") + @ApiOperation(value = "Query details of the requested participant stats", + notes = "Queries details of the requested participant stats, returning all participant stats", + response = ParticipantStatisticsList.class, + tags = { + "Clamp control loop Monitoring API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + } + ) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response queryParticipantStatistics(@HeaderParam(REQUEST_ID_NAME) + @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop participant name", required = true) + @QueryParam("name") final String name, + @ApiParam(value = "Control Loop participant version", required = true) + @QueryParam("version") final String version, + @ApiParam(value = "Record count", required = false) @DefaultValue("0") + @QueryParam("recordCount") final int recordCount, + @ApiParam(value = "start time", required = false) + @QueryParam("startTime") final String startTime, + @ApiParam(value = "end time", required = false) + @QueryParam("endTime") final String endTime) { + + try { + Instant startTimestamp = null; + Instant endTimestamp = null; + + if (startTime != null) { + startTimestamp = Instant.parse(startTime); + } + if (endTime != null) { + endTimestamp = Instant.parse(endTime); + } + ParticipantStatisticsList response = provider.fetchFilteredParticipantStatistics(name, version, recordCount, + startTimestamp, endTimestamp); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId) + .entity(response) + .build(); + + } catch (PfModelRuntimeException e) { + LOGGER.warn("Monitoring of participants statistics failed", e); + return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())), + requestId).build(); + } + + } + + /** + * Queries details of all participant statistics per control loop. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop + * @param version version of the control loop + * @return the control loop element statistics + */ + // @formatter:off + @GET + @Path("/monitoring/participants/controlloop") + @ApiOperation(value = "Query details of all the participant stats in a control loop", + notes = "Queries details of the participant stats, returning all participant stats", + response = ClElementStatisticsList.class, + tags = { + "Clamp control loop Monitoring API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + }) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response queryParticipantStatisticsPerControlLoop(@HeaderParam(REQUEST_ID_NAME) + @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop name", required = true) + @QueryParam("name") final String name, + @ApiParam(value = "Control Loop version", required = true) + @QueryParam("version") final String version) { + + try { + ParticipantStatisticsList response = provider.fetchParticipantStatsPerControlLoop(name, version); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId) + .entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("Monitoring of Cl participant statistics failed", e); + return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())), + requestId).build(); + } + + } + + + + /** + * Queries details of all control loop element statistics per control loop. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop + * @param version version of the control loop + * @return the control loop element statistics + */ + // @formatter:off + @GET + @Path("/monitoring/clelements/controlloop") + @ApiOperation(value = "Query details of the requested cl element stats in a control loop", + notes = "Queries details of the requested cl element stats, returning all clElement stats", + response = ClElementStatisticsList.class, + tags = { + "Clamp control loop Monitoring API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + }) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response queryElementStatisticsPerControlLoop(@HeaderParam(REQUEST_ID_NAME) + @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Control Loop name", required = true) + @QueryParam("name") final String name, + @ApiParam(value = "Control Loop version", required = true) + @QueryParam("version") final String version) { + + try { + ClElementStatisticsList response = provider.fetchClElementStatsPerControlLoop(name, version); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId) + .entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("Monitoring of Cl Element statistics failed", e); + return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())), + requestId).build(); + } + + } + + + + + /** + * Queries details of all control loop element statistics per control loop. + * + * @param requestId request ID used in ONAP logging + * @param name the name of the control loop + * @param version version of the control loop + * @param id Id of the control loop element + * @param recordCount the record count to be fetched + * @return the control loop element statistics + */ + // @formatter:off + @GET + @Path("/monitoring/clelement") + @ApiOperation(value = "Query details of the requested cl element stats", + notes = "Queries details of the requested cl element stats, returning all clElement stats", + response = ClElementStatisticsList.class, + tags = { + "Clamp control loop Monitoring API" + }, + authorizations = @Authorization(value = AUTHORIZATION_TYPE), + responseHeaders = { + @ResponseHeader( + name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, + response = String.class), + @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, + response = String.class), + @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, + response = UUID.class)}, + extensions = { + @Extension( + name = EXTENSION_NAME, + properties = { + @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), + @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE) + } + ) + }) + @ApiResponses( + value = { + @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), + @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), + @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE) + } + ) + // @formatter:on + public Response queryElementStatistics(@HeaderParam(REQUEST_ID_NAME) + @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, + @ApiParam(value = "Participant name", required = true) + @QueryParam("name") final String name, + @ApiParam(value = "Participant version", required = true) + @QueryParam("version") final String version, + @ApiParam(value = "Record count", required = false) + @DefaultValue("0") @QueryParam("recordCount") final int recordCount, + @ApiParam(value = "Control Loop element id", required = false) + @QueryParam("id") final String id, + @ApiParam(value = "start time", required = false) + @QueryParam("startTime") final String startTime, + @ApiParam(value = "end time", required = false) + @QueryParam("endTime") final String endTime) { + + try { + Instant startTimestamp = null; + Instant endTimestamp = null; + + if (startTime != null) { + startTimestamp = Instant.parse(startTime); + } + if (endTime != null) { + endTimestamp = Instant.parse(endTime); + } + ClElementStatisticsList response = provider.fetchFilteredClElementStatistics(name, version, id, + startTimestamp, endTimestamp, recordCount); + return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId) + .entity(response) + .build(); + + } catch (PfModelRuntimeException | PfModelException e) { + LOGGER.warn("Monitoring of Cl Element statistics failed", e); + return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())), + requestId).build(); + } + + } + +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java new file mode 100644 index 000000000..63bff00fc --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java @@ -0,0 +1,450 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.supervision; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.commons.collections4.CollectionUtils; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopStateChange; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopUpdate; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageType; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; +import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningHandler; +import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringHandler; +import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringProvider; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantControlLoopStateChangePublisher; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantControlLoopUpdatePublisher; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStateChangePublisher; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStatusListener; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher; +import org.onap.policy.common.utils.services.Registry; +import org.onap.policy.common.utils.services.ServiceManager; +import org.onap.policy.common.utils.services.ServiceManagerException; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.base.PfModelRuntimeException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class handles supervision of control loop instances, so only one object of this type should be built at a time. + * + *

It is effectively a singleton that is started at system start. + */ +public class SupervisionHandler extends ControlLoopHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionHandler.class); + + private static final String CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE = "Control loop can't transition from state "; + private static final String CONTROL_LOOP_IS_ALREADY_IN_STATE = "Control loop is already in state "; + private static final String TO_STATE = " to state "; + private static final String AND_TRANSITIONING_TO_STATE = " and transitioning to state "; + + private ControlLoopProvider controlLoopProvider; + private ParticipantProvider participantProvider; + private CommissioningProvider commissioningProvider; + private MonitoringProvider monitoringProvider; + + // Publishers for participant communication + private ParticipantStateChangePublisher stateChangePublisher; + private ParticipantControlLoopUpdatePublisher controlLoopUpdatePublisher; + private ParticipantControlLoopStateChangePublisher controlLoopStateChangePublisher; + + // Database scanner + private SupervisionScanner scanner; + + /** + * Used to manage the services. + */ + private ServiceManager manager; + private ServiceManager publisherManager; + + /** + * Gets the SupervisionHandler. + * + * @return SupervisionHandler + */ + public static SupervisionHandler getInstance() { + return Registry.get(SupervisionHandler.class.getName()); + } + + /** + * Create a handler. + * + * @param clRuntimeParameterGroup the parameters for the control loop runtime + */ + public SupervisionHandler(ClRuntimeParameterGroup clRuntimeParameterGroup) { + super(clRuntimeParameterGroup.getDatabaseProviderParameters()); + // @formatter:off + this.manager = new ServiceManager() + .addAction("ControlLoop Provider", + () -> controlLoopProvider = new ControlLoopProvider(getDatabaseProviderParameters()), + () -> controlLoopProvider = null) + .addAction("Participant Provider", + () -> participantProvider = new ParticipantProvider(getDatabaseProviderParameters()), + () -> participantProvider = null); + // @formatter:on + } + + /** + * Supervision trigger called when a command is issued on control loops. + * + *

Causes supervision to start or continue supervision on the control loops in question. + * + * @param controlLoopIdentifierList the control loops for which the supervision command has been issued + * @throws ControlLoopException on supervision triggering exceptions + */ + public void triggerControlLoopSupervision(List controlLoopIdentifierList) + throws ControlLoopException { + + LOGGER.debug("triggering control loop supervision on control loops {}", controlLoopIdentifierList); + + if (CollectionUtils.isEmpty(controlLoopIdentifierList)) { + // This is just to force throwing of the exception in certain circumstances. + exceptionOccured(Response.Status.NOT_ACCEPTABLE, + "The list of control loops for supervision is empty"); + } + + for (ToscaConceptIdentifier controlLoopId : controlLoopIdentifierList) { + try { + ControlLoop controlLoop = controlLoopProvider.getControlLoop(controlLoopId); + + superviseControlLoop(controlLoop); + + controlLoopProvider.updateControlLoop(controlLoop); + } catch (PfModelException pfme) { + throw new ControlLoopException(pfme.getErrorResponse().getResponseCode(), pfme.getMessage(), pfme); + } + } + } + + @Override + public void startAndRegisterListeners(MessageTypeDispatcher msgDispatcher) { + msgDispatcher.register(ParticipantMessageType.PARTICIPANT_STATUS.name(), new ParticipantStatusListener()); + } + + @Override + public void startAndRegisterPublishers(List topicSinks) { + // TODO: Use a parameter for the timeout + // @formatter:off + this.publisherManager = new ServiceManager() + .addAction("Supervision scanner", + () -> scanner = new SupervisionScanner(controlLoopProvider, 10000), + () -> scanner = null) + .addAction("ControlLoopUpdate publisher", + () -> controlLoopUpdatePublisher = new ParticipantControlLoopUpdatePublisher(topicSinks, -1), + () -> controlLoopUpdatePublisher.terminate()) + .addAction("StateChange Publisher", + () -> stateChangePublisher = new ParticipantStateChangePublisher(topicSinks, 10000), + () -> stateChangePublisher.terminate()) + .addAction("ControlLoopStateChange Publisher", + () -> controlLoopStateChangePublisher = + new ParticipantControlLoopStateChangePublisher(topicSinks, -1), + () -> controlLoopStateChangePublisher.terminate()); + // @formatter:on + try { + publisherManager.start(); + } catch (final ServiceManagerException exp) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, + "Supervision handler start of publishers or scanner failed", exp); + } + } + + @Override + public void stopAndUnregisterPublishers() { + try { + publisherManager.stop(); + } catch (final ServiceManagerException exp) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, + "Supervision handler stop of publishers or scanner failed", exp); + } + } + + @Override + public void stopAndUnregisterListeners(MessageTypeDispatcher msgDispatcher) { + msgDispatcher.unregister(ParticipantMessageType.PARTICIPANT_STATUS.name()); + } + + /** + * Handle a ParticipantStatus message from a participant. + * + * @param participantStatusMessage the ParticipantStatus message received from a participant + */ + public void handleParticipantStatusMessage(ParticipantStatus participantStatusMessage) { + LOGGER.debug("Participant Status received {}", participantStatusMessage); + + try { + superviseParticipant(participantStatusMessage); + } catch (PfModelException | ControlLoopException svExc) { + LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc); + return; + } + + try { + superviseControlLoops(participantStatusMessage); + } catch (PfModelException | ControlLoopException svExc) { + LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc); + } + } + + /** + * Supervise a control loop, performing whatever actions need to be performed on the control loop. + * + * @param controlLoop the control loop to supervises + * @throws ControlLoopException on supervision errors + */ + private void superviseControlLoop(ControlLoop controlLoop) throws ControlLoopException, PfModelException { + switch (controlLoop.getOrderedState()) { + case UNINITIALISED: + superviseControlLoopUninitialization(controlLoop); + break; + + case PASSIVE: + superviseControlLoopPassivation(controlLoop); + break; + + case RUNNING: + superviseControlLoopActivation(controlLoop); + break; + + default: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, + "A control loop cannot be commanded to go into state " + controlLoop.getOrderedState().name()); + } + } + + /** + * Supervise a control loop uninitialisation, performing whatever actions need to be performed on the control loop, + * control loop ordered state is UNINITIALIZED. + * + * @param controlLoop the control loop to supervises + * @throws ControlLoopException on supervision errors + */ + private void superviseControlLoopUninitialization(ControlLoop controlLoop) throws ControlLoopException { + switch (controlLoop.getState()) { + case UNINITIALISED: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, + CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name()); + break; + + case UNINITIALISED2PASSIVE: + case PASSIVE: + controlLoop.setState(ControlLoopState.PASSIVE2UNINITIALISED); + sendControlLoopStateChange(controlLoop); + break; + + case PASSIVE2UNINITIALISED: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE + + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState()); + break; + + default: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE + + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState()); + break; + } + } + + private void superviseControlLoopPassivation(ControlLoop controlLoop) + throws ControlLoopException, PfModelException { + switch (controlLoop.getState()) { + case PASSIVE: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, + CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name()); + break; + case UNINITIALISED: + controlLoop.setState(ControlLoopState.UNINITIALISED2PASSIVE); + sendControlLoopUpdate(controlLoop); + break; + + case UNINITIALISED2PASSIVE: + case RUNNING2PASSIVE: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE + + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState()); + break; + + case RUNNING: + controlLoop.setState(ControlLoopState.RUNNING2PASSIVE); + sendControlLoopStateChange(controlLoop); + break; + + default: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE + + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState()); + break; + } + } + + private void superviseControlLoopActivation(ControlLoop controlLoop) throws ControlLoopException { + switch (controlLoop.getState()) { + case RUNNING: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, + CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name()); + break; + + case PASSIVE2RUNNING: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE + + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState()); + break; + + case PASSIVE: + controlLoop.setState(ControlLoopState.PASSIVE2RUNNING); + sendControlLoopStateChange(controlLoop); + break; + + default: + exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE + + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState()); + break; + } + } + + private void sendControlLoopUpdate(ControlLoop controlLoop) throws PfModelException { + ParticipantControlLoopUpdate pclu = new ParticipantControlLoopUpdate(); + pclu.setControlLoopId(controlLoop.getKey().asIdentifier()); + pclu.setControlLoop(controlLoop); + // TODO: We should look up the correct TOSCA node template here for the control loop + // Tiny hack implemented to return the tosca service template entry from the database and be passed onto dmaap + commissioningProvider = CommissioningHandler.getInstance().getProvider(); + pclu.setControlLoopDefinition(commissioningProvider.getToscaServiceTemplate(null, null)); + controlLoopUpdatePublisher.send(pclu); + } + + private void sendControlLoopStateChange(ControlLoop controlLoop) { + ParticipantControlLoopStateChange clsc = new ParticipantControlLoopStateChange(); + clsc.setControlLoopId(controlLoop.getKey().asIdentifier()); + clsc.setMessageId(UUID.randomUUID()); + clsc.setOrderedState(controlLoop.getOrderedState()); + + controlLoopStateChangePublisher.send(clsc); + } + + private void superviseParticipant(ParticipantStatus participantStatusMessage) + throws PfModelException, ControlLoopException { + if (participantStatusMessage.getParticipantId() == null) { + exceptionOccured(Response.Status.NOT_FOUND, + "Participant ID on PARTICIPANT_STATUS message is null"); + } + + List participantList = + participantProvider.getParticipants(participantStatusMessage.getParticipantId().getName(), + participantStatusMessage.getParticipantId().getVersion()); + + if (CollectionUtils.isEmpty(participantList)) { + Participant participant = new Participant(); + participant.setName(participantStatusMessage.getParticipantId().getName()); + participant.setVersion(participantStatusMessage.getParticipantId().getVersion()); + participant.setDefinition(new ToscaConceptIdentifier("unknown", "0.0.0")); + participant.setParticipantState(participantStatusMessage.getState()); + participant.setHealthStatus(participantStatusMessage.getHealthStatus()); + + participantList.add(participant); + participantProvider.createParticipants(participantList); + } else { + for (Participant participant : participantList) { + participant.setParticipantState(participantStatusMessage.getState()); + participant.setHealthStatus(participantStatusMessage.getHealthStatus()); + } + participantProvider.updateParticipants(participantList); + } + + monitoringProvider = MonitoringHandler.getInstance().getMonitoringProvider(); + monitoringProvider.createParticipantStatistics( + List.of(participantStatusMessage.getParticipantStatistics())); + } + + private void superviseControlLoops(ParticipantStatus participantStatusMessage) + throws PfModelException, ControlLoopException { + if (CollectionUtils.isEmpty(participantStatusMessage.getControlLoops().getControlLoopList())) { + return; + } + + for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) { + if (controlLoop == null) { + exceptionOccured(Response.Status.NOT_FOUND, + "PARTICIPANT_STATUS message references unknown control loop: " + controlLoop); + } + + ControlLoop dbControlLoop = controlLoopProvider + .getControlLoop(new ToscaConceptIdentifier(controlLoop.getName(), controlLoop.getVersion())); + if (dbControlLoop == null) { + exceptionOccured(Response.Status.NOT_FOUND, + "PARTICIPANT_STATUS control loop not found in database: " + controlLoop); + } + + for (ControlLoopElement element : controlLoop.getElements().values()) { + ControlLoopElement dbElement = dbControlLoop.getElements().get(element.getId()); + + if (dbElement == null) { + exceptionOccured(Response.Status.NOT_FOUND, + "PARTICIPANT_STATUS message references unknown control loop element: " + element); + } + + // Replace element entry in the database + dbControlLoop.getElements().put(element.getId(), element); + } + controlLoopProvider.updateControlLoop(dbControlLoop); + } + + monitoringProvider = MonitoringHandler.getInstance().getMonitoringProvider(); + for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) { + monitoringProvider.createClElementStatistics(controlLoop.getControlLoopElementStatisticsList(controlLoop)); + } + } + + @Override + public void startProviders() { + try { + manager.start(); + } catch (final ServiceManagerException exp) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, + "Supervision handler start of providers failed", exp); + } + } + + @Override + public void stopProviders() { + try { + manager.stop(); + } catch (final ServiceManagerException exp) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, + "Supervision handler stop of providers failed", exp); + } + } + + private void exceptionOccured(Response.Status status, String reason) throws ControlLoopException { + throw new ControlLoopException(status, reason); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java new file mode 100644 index 000000000..0ccfddff3 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java @@ -0,0 +1,116 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.supervision; + +import java.io.Closeable; +import java.util.Collection; +import java.util.List; +import java.util.TimerTask; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; +import org.onap.policy.models.base.PfModelException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used to scan the control loops in the database and check if they are in the correct state. + */ +public class SupervisionScanner implements Runnable, Closeable { + private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionScanner.class); + + private ControlLoopProvider controlLoopProvider; + private ScheduledExecutorService timerPool; + + /** + * Constructor for instantiating SupervisionScanner. + * + * @param controlLoopProvider the provider to use to read control loops from the database + * @param interval time interval to perform scans + */ + public SupervisionScanner(final ControlLoopProvider controlLoopProvider, final long interval) { + this.controlLoopProvider = controlLoopProvider; + + // Kick off the timer + timerPool = makeTimerPool(); + timerPool.scheduleAtFixedRate(this, 0, interval, TimeUnit.SECONDS); + } + + @Override + public void run() { + LOGGER.debug("Scanning control loops in the database . . ."); + + try { + for (ControlLoop controlLoop : controlLoopProvider.getControlLoops(null, null)) { + scanControlLoop(controlLoop); + } + } catch (PfModelException pfme) { + LOGGER.warn("error reading control loops from database", pfme); + } + + LOGGER.debug("Control loop scan complete . . ."); + } + + @Override + public void close() { + timerPool.shutdown(); + } + + private void scanControlLoop(final ControlLoop controlLoop) throws PfModelException { + LOGGER.debug("scanning control loop {} . . .", controlLoop.getKey().asIdentifier()); + + if (controlLoop.getState().equals(controlLoop.getOrderedState().asState())) { + LOGGER.debug("control loop {} scanned, OK", controlLoop.getKey().asIdentifier()); + return; + } + + for (ControlLoopElement element : controlLoop.getElements().values()) { + if (!element.getState().equals(element.getOrderedState().asState())) { + LOGGER.debug("control loop scan: transitioning from state {} to {}", controlLoop.getState(), + controlLoop.getOrderedState()); + return; + } + } + + LOGGER.debug("control loop scan: transition from state {} to {} completed", controlLoop.getState(), + controlLoop.getOrderedState()); + + controlLoop.setState(controlLoop.getOrderedState().asState()); + controlLoopProvider.updateControlLoop(controlLoop); + } + + /** + * Makes a new timer pool. + * + * @return a new timer pool + */ + protected ScheduledExecutorService makeTimerPool() { + return Executors.newScheduledThreadPool(1); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java new file mode 100644 index 000000000..c9c8ab851 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java @@ -0,0 +1,75 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.supervision.comm; + +import java.util.List; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopStateChange; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used to send ParticipantControlLoopStateChangePublisher messages to participants on DMaaP. + */ +public class ParticipantControlLoopStateChangePublisher { + private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantControlLoopStateChangePublisher.class); + + private TopicSinkClient topicSinkClient; + + /** + * Constructor for instantiating ParticipantControlLoopStateChangePublisherPublisher. + * + * @param topicSinks the topic sinks + * @param interval time interval to send ParticipantControlLoopStateChangePublisher messages + */ + public ParticipantControlLoopStateChangePublisher(final List topicSinks, final long interval) { + // TODO: Should not be dependent on the order of topic sinks in the config + this.topicSinkClient = new TopicSinkClient(topicSinks.get(0)); + } + + /** + * Terminates the current timer. + */ + public void terminate() { + // This is a user initiated message and doesn't need a timer. + } + + /** + * Get the current time interval used by the timer task. + * + * @return interval the current time interval + */ + public long getInterval() { + // This is a user initiated message and doesn't need a timer. + return -1; + } + + /** + * Method to send ParticipantControlLoopStateChangePublisher status message to participants on demand. + * + * @param controlLoopStateChange the ParticipantControlLoopStateChangePublisher message + */ + public void send(final ParticipantControlLoopStateChange controlLoopStateChange) { + topicSinkClient.send(controlLoopStateChange); + LOGGER.debug("Sent ParticipantControlLoopStateChange to Participants - {}", controlLoopStateChange); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java new file mode 100644 index 000000000..3c5d230c5 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java @@ -0,0 +1,75 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.supervision.comm; + +import java.util.List; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopUpdate; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used to send ParticipantControlLoopUpdate messages to participants on DMaaP. + */ +public class ParticipantControlLoopUpdatePublisher { + private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantControlLoopUpdatePublisher.class); + + private TopicSinkClient topicSinkClient; + + /** + * Constructor for instantiating ParticipantUpdatePublisher. + * + * @param topicSinks the topic sinks + * @param interval time interval to send ParticipantControlLoopUpdate messages + */ + public ParticipantControlLoopUpdatePublisher(final List topicSinks, final long interval) { + // TODO: Should not be dependent on the order of topic sinks in the config + this.topicSinkClient = new TopicSinkClient(topicSinks.get(0)); + } + + /** + * Terminates the current timer. + */ + public void terminate() { + // This is a user initiated message and doesn't need a timer. + } + + /** + * Get the current time interval used by the timer task. + * + * @return interval the current time interval + */ + public long getInterval() { + // This is a user initiated message and doesn't need a timer. + return -1; + } + + /** + * Method to send ParticipantControlLoopUpdate status message to participants on demand. + * + * @param participantControlLoopUpdate the ParticipantControlLoopUpdate message + */ + public void send(final ParticipantControlLoopUpdate participantControlLoopUpdate) { + topicSinkClient.send(participantControlLoopUpdate); + LOGGER.debug("Sent ParticipantControlLoopUpdate to Participants - {}", participantControlLoopUpdate); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java new file mode 100644 index 000000000..099039115 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java @@ -0,0 +1,74 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.supervision.comm; + +import java.util.List; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStateChange; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used to send ParticipantStateChange messages to participants on DMaaP. + */ +public class ParticipantStateChangePublisher { + private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantStateChangePublisher.class); + + private TopicSinkClient topicSinkClient; + + /** + * Constructor for instantiating ParticipantStateChangePublisher. + * + * @param topicSinks the topic sinks + * @param interval time interval to send ParticipantStateChange messages + */ + public ParticipantStateChangePublisher(final List topicSinks, final long interval) { + // TODO: Should not be dependent on the order of topic sinks in the config + this.topicSinkClient = new TopicSinkClient(topicSinks.get(0)); + } + + /** + * Terminates the current timer. + */ + public void terminate() { + // Nothing to terminate, this publisher does not have a timer + } + + /** + * Get the current time interval used by the timer task. + * + * @return interval the current time interval + */ + public long getInterval() { + return -1; + } + + /** + * Method to send ParticipantStateChange status message to participants on demand. + * + * @param participantStateChange the ParticipantStateChange message + */ + public void send(final ParticipantStateChange participantStateChange) { + topicSinkClient.send(participantStateChange); + LOGGER.debug("Sent ParticipantStateChange to Participants - {}", participantStateChange); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java new file mode 100644 index 000000000..a05f4aa20 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.clamp.controlloop.runtime.supervision.comm; + +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; +import org.onap.policy.clamp.controlloop.runtime.supervision.SupervisionHandler; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.endpoints.listeners.ScoListener; +import org.onap.policy.common.utils.coder.StandardCoderObject; +import org.onap.policy.common.utils.services.Registry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Listener for ParticipantStatus messages sent by participants. + */ +public class ParticipantStatusListener extends ScoListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantStatusListener.class); + + private final SupervisionHandler supervisionHandler = Registry.get(SupervisionHandler.class.getName()); + + /** + * Constructs the object. + */ + public ParticipantStatusListener() { + super(ParticipantStatus.class); + } + + @Override + public void onTopicEvent(final CommInfrastructure infra, final String topic, final StandardCoderObject sco, + final ParticipantStatus participantStatusMessage) { + LOGGER.debug("ParticipantStatus message received from participant - {}", participantStatusMessage); + supervisionHandler.handleParticipantStatusMessage(participantStatusMessage); + } +} diff --git a/runtime-controlloop/src/main/resources/META-INF/persistence.xml b/runtime-controlloop/src/main/resources/META-INF/persistence.xml new file mode 100644 index 000000000..e5d2cab11 --- /dev/null +++ b/runtime-controlloop/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,121 @@ + + + + + org.eclipse.persistence.jpa.PersistenceProvider + + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityAssignment + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityAssignments + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityType + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaDataType + org.onap.policy.models.tosca.simple.concepts.JpaToscaDataTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeTemplate + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeTemplates + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeType + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaParameter + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicies + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicy + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyType + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaProperty + org.onap.policy.models.tosca.simple.concepts.JpaToscaRelationshipType + org.onap.policy.models.tosca.simple.concepts.JpaToscaRelationshipTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaRequirement + org.onap.policy.models.tosca.simple.concepts.JpaToscaRequirements + org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate + org.onap.policy.models.tosca.simple.concepts.JpaToscaTopologyTemplate + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaControlLoop + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaControlLoopElement + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaParticipant + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaParticipantStatistics + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaClElementStatistics + + + + + + + + + NONE + + + + org.eclipse.persistence.jpa.PersistenceProvider + + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityAssignment + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityAssignments + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityType + org.onap.policy.models.tosca.simple.concepts.JpaToscaCapabilityTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaDataType + org.onap.policy.models.tosca.simple.concepts.JpaToscaDataTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeTemplate + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeTemplates + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeType + org.onap.policy.models.tosca.simple.concepts.JpaToscaNodeTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaParameter + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicies + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicy + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyType + org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaProperty + org.onap.policy.models.tosca.simple.concepts.JpaToscaRelationshipType + org.onap.policy.models.tosca.simple.concepts.JpaToscaRelationshipTypes + org.onap.policy.models.tosca.simple.concepts.JpaToscaRequirement + org.onap.policy.models.tosca.simple.concepts.JpaToscaRequirements + org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate + org.onap.policy.models.tosca.simple.concepts.JpaToscaTopologyTemplate + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaControlLoop + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaControlLoopElement + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaParticipant + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaParticipantStatistics + org.onap.policy.clamp.controlloop.models.controlloop.persistence.concepts.JpaClElementStatistics + + + + + + + + NONE + + + diff --git a/runtime-controlloop/src/main/resources/version.txt b/runtime-controlloop/src/main/resources/version.txt new file mode 100644 index 000000000..e11449e5b --- /dev/null +++ b/runtime-controlloop/src/main/resources/version.txt @@ -0,0 +1,4 @@ +ONAP Tosca defined control loop +Version: ${project.version} +Built (UTC): ${maven.build.timestamp} +ONAP https://wiki.onap.org -- cgit 1.2.3-korg