/* * ============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.cps.ncmp.dmi.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.apache.groovy.parser.antlr4.util.StringUtils; import org.onap.cps.ncmp.dmi.config.DmiPluginConfig.DmiPluginProperties; import org.onap.cps.ncmp.dmi.exception.CmHandleRegistrationException; import org.onap.cps.ncmp.dmi.exception.DmiException; import org.onap.cps.ncmp.dmi.exception.ModuleResourceNotFoundException; import org.onap.cps.ncmp.dmi.exception.ModulesNotFoundException; import org.onap.cps.ncmp.dmi.exception.ResourceDataNotFound; import org.onap.cps.ncmp.dmi.model.ModuleSet; import org.onap.cps.ncmp.dmi.model.ModuleSetSchemas; import org.onap.cps.ncmp.dmi.model.YangResource; import org.onap.cps.ncmp.dmi.model.YangResources; import org.onap.cps.ncmp.dmi.service.client.NcmpRestClient; import org.onap.cps.ncmp.dmi.service.model.CmHandleOperation; import org.onap.cps.ncmp.dmi.service.model.CreatedCmHandle; import org.onap.cps.ncmp.dmi.service.model.ModuleReference; import org.onap.cps.ncmp.dmi.service.model.ModuleSchemaProperties; import org.onap.cps.ncmp.dmi.service.model.ModuleSchemas; import org.onap.cps.ncmp.dmi.service.operation.SdncOperations; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @Service @Slf4j public class DmiServiceImpl implements DmiService { private SdncOperations sdncOperations; private NcmpRestClient ncmpRestClient; private ObjectMapper objectMapper; private DmiPluginProperties dmiPluginProperties; private static final String RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM = "content=all"; private static final String REST_CONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM = "content=config"; private static final String RESPONSE_CODE = "response code : "; private static final String MESSAGE = " message : "; private static final String IETF_NETCONF_MONITORING_OUTPUT = "ietf-netconf-monitoring:output"; /** * Constructor. * * @param dmiPluginProperties dmiPluginProperties * @param ncmpRestClient ncmpRestClient * @param sdncOperations sdncOperations * @param objectMapper objectMapper */ public DmiServiceImpl(final DmiPluginProperties dmiPluginProperties, final NcmpRestClient ncmpRestClient, final SdncOperations sdncOperations, final ObjectMapper objectMapper) { this.dmiPluginProperties = dmiPluginProperties; this.ncmpRestClient = ncmpRestClient; this.objectMapper = objectMapper; this.sdncOperations = sdncOperations; } @Override public ModuleSet getModulesForCmHandle(final String cmHandle) throws DmiException { final ResponseEntity responseEntity = sdncOperations.getModulesFromNode(cmHandle); if (responseEntity.getStatusCode() == HttpStatus.OK) { final String responseBody = responseEntity.getBody(); if (StringUtils.isEmpty(responseBody)) { throw new ModulesNotFoundException(cmHandle, "SDNC returned no modules for given cm-handle."); } return createModuleSchema(responseBody); } else { throw new DmiException("SDNC is not able to process request.", RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } @Override public YangResources getModuleResources(final String cmHandle, final List moduleReferences) { final YangResources yangResources = new YangResources(); for (final var moduleReference : moduleReferences) { final var moduleRequest = createModuleRequest(moduleReference); final ResponseEntity responseEntity = sdncOperations.getModuleResource(cmHandle, moduleRequest); if (responseEntity.getStatusCode() == HttpStatus.OK) { yangResources.add(toYangResource(moduleReference, responseEntity)); } else if (responseEntity.getStatusCode() == HttpStatus.NOT_FOUND) { log.error("SDNC did not return a module resource for the given cmHandle {}", cmHandle); throw new ModuleResourceNotFoundException(cmHandle, "SDNC did not return a module resource for the given cmHandle."); } else { log.error("Error occurred when getting module resources from SDNC for the given cmHandle {}", cmHandle); throw new DmiException(cmHandle, RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } return yangResources; } @Override public void registerCmHandles(final List cmHandles) { final CmHandleOperation cmHandleOperation = new CmHandleOperation(); cmHandleOperation.setDmiPlugin(dmiPluginProperties.getDmiServiceUrl()); final List createdCmHandleList = new ArrayList<>(); for (final String cmHandle : cmHandles) { final CreatedCmHandle createdCmHandle = new CreatedCmHandle(); createdCmHandle.setCmHandle(cmHandle); createdCmHandleList.add(createdCmHandle); } cmHandleOperation.setCreatedCmHandles(createdCmHandleList); final String cmHandlesJson; try { cmHandlesJson = objectMapper.writeValueAsString(cmHandleOperation); } catch (final JsonProcessingException e) { log.error("Parsing error occurred while converting cm-handles to JSON {}", cmHandles); throw new DmiException("Internal Server Error.", "Parsing error occurred while converting given cm-handles object list to JSON "); } final ResponseEntity responseEntity = ncmpRestClient.registerCmHandlesWithNcmp(cmHandlesJson); if ((responseEntity.getStatusCode() != HttpStatus.CREATED)) { throw new CmHandleRegistrationException(responseEntity.getBody()); } } private ModuleSet createModuleSchema(final String responseBody) { final var moduleSchemas = convertModulesToNodeSchema(responseBody); final List moduleSetSchemas = new ArrayList<>(); for (final ModuleSchemaProperties schemaProperties : moduleSchemas.getSchemas().getSchema()) { moduleSetSchemas.add(convertModulesToModuleSchemas(schemaProperties)); } final var moduleSet = new ModuleSet(); moduleSet.setSchemas(moduleSetSchemas); return moduleSet; } private ModuleSetSchemas convertModulesToModuleSchemas(final ModuleSchemaProperties moduleSchemaProperties) { final var moduleSetSchemas = new ModuleSetSchemas(); moduleSetSchemas.setModuleName(moduleSchemaProperties.getIdentifier()); moduleSetSchemas.setNamespace(moduleSchemaProperties.getNamespace()); moduleSetSchemas.setRevision(moduleSchemaProperties.getVersion()); return moduleSetSchemas; } private ModuleSchemas convertModulesToNodeSchema(final String modulesListAsJson) { try { return objectMapper.readValue(modulesListAsJson, ModuleSchemas.class); } catch (final JsonProcessingException e) { log.error("JSON exception occurred when converting the following modules to node schema " + "{}", modulesListAsJson); throw new DmiException("Unable to process JSON.", "JSON exception occurred when mapping modules.", e); } } @Override public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle, final @NotNull String resourceIdentifier, final String acceptParamInHeader, final String optionsParamInQuery, final Map cmHandlePropertyMap) { // not using cmHandlePropertyMap of onap dmi impl , other dmi impl might use this. final ResponseEntity responseEntity = sdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceIdentifier, optionsParamInQuery, acceptParamInHeader, RESTCONF_CONTENT_PASSTHROUGH_OPERATIONAL_QUERY_PARAM); return prepareAndSendResponse(responseEntity, cmHandle); } @Override public Object getResourceDataPassThroughRunningForCmHandle(final @NotNull String cmHandle, final @NotNull String resourceIdentifier, final String acceptParamInHeader, final String optionsParamInQuery, final Map cmHandlePropertyMap) { // not using cmHandlePropertyMap of onap dmi impl , other dmi impl might use this. final ResponseEntity responseEntity = sdncOperations.getResouceDataForOperationalAndRunning(cmHandle, resourceIdentifier, optionsParamInQuery, acceptParamInHeader, REST_CONF_CONTENT_PASSTHROUGH_RUNNING_QUERY_PARAM); return prepareAndSendResponse(responseEntity, cmHandle); } @Override public String writeResourceDataPassthroughForCmHandle(final String cmHandle, final String resourceIdentifier, final String dataType, final String data) { final ResponseEntity responseEntity = sdncOperations.writeResourceDataPassthroughRunning(cmHandle, resourceIdentifier, dataType, data); if (responseEntity.getStatusCode().is2xxSuccessful()) { return responseEntity.getBody(); } else { throw new DmiException(cmHandle, RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } private String prepareAndSendResponse(final ResponseEntity responseEntity, final String cmHandle) { if (responseEntity.getStatusCode() == HttpStatus.OK) { return responseEntity.getBody(); } else { throw new ResourceDataNotFound(cmHandle, RESPONSE_CODE + responseEntity.getStatusCode() + MESSAGE + responseEntity.getBody()); } } private String createModuleRequest(final ModuleReference moduleReference) { final var ietfNetconfModuleReferences = new LinkedHashMap<>(); ietfNetconfModuleReferences.put("ietf-netconf-monitoring:identifier", moduleReference.getName()); ietfNetconfModuleReferences.put("ietf-netconf-monitoring:version", moduleReference.getRevision()); final var writer = objectMapper.writer().withRootName("ietf-netconf-monitoring:input"); final String moduleRequest; try { moduleRequest = writer.writeValueAsString(ietfNetconfModuleReferences); } catch (final JsonProcessingException e) { log.error("JSON exception occurred when creating the module request for the given module reference {}", moduleReference.getName()); throw new DmiException("Unable to process JSON.", "JSON exception occurred when creating the module request.", e); } return moduleRequest; } private YangResource toYangResource(final ModuleReference moduleReference, final ResponseEntity response) { final YangResource yangResource = new YangResource(); yangResource.setModuleName(moduleReference.getName()); yangResource.setRevision(moduleReference.getRevision()); yangResource.setYangSource(extractYangSourceFromBody(response)); return yangResource; } private String extractYangSourceFromBody(final ResponseEntity responseEntity) { final var responseBodyAsJsonObject = new Gson().fromJson(responseEntity.getBody(), JsonObject.class); if (responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) == null || responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT) .getAsJsonPrimitive("data") == null) { log.error("Error occurred when trying to parse the response body from sdnc {}", responseEntity.getBody()); throw new ModuleResourceNotFoundException(responseEntity.getBody(), "Error occurred when trying to parse the response body from sdnc."); } return responseBodyAsJsonObject.getAsJsonObject(IETF_NETCONF_MONITORING_OUTPUT).getAsJsonPrimitive("data") .toString(); } }