diff options
Diffstat (limited to 'cps-ncmp-service/src/main')
8 files changed, 261 insertions, 43 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index d0e17d4c08..d46d634104 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -75,21 +75,33 @@ public class DmiDataOperations extends DmiOperations { final DataStoreEnum dataStore, final String requestId, final String topicParamInQuery) { - CpsValidator.validateNameCharacters(cmHandleId); - final YangModelCmHandle yangModelCmHandle = - inventoryPersistence.getYangModelCmHandle(cmHandleId); + final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); + final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); + final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, resourceId, optionsParamInQuery, dataStore, + topicParamInQuery, yangModelCmHandle); + final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); + isCmHandleStateReady(yangModelCmHandle, cmHandleState); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ); + } + + /** + * This method fetches all the resource data from operational data store for given cm handle + * identifier using dmi client. + * + * @param cmHandleId network resource identifier + * @param dataStore data store enum + * @param requestId requestId for async responses + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId, + final DataStoreEnum dataStore, + final String requestId) { + final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); + final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); + final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, "/", null, dataStore, + null, yangModelCmHandle); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); isCmHandleStateReady(yangModelCmHandle, cmHandleState); - final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() - .operation(READ) - .requestId(requestId) - .build(); - dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties()); - final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody); - final String dmiResourceDataUrl = dmiServiceUrlBuilder.getDmiDatastoreUrl( - dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, - topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables( - yangModelCmHandle, cmHandleId, dataStore)); return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ); } @@ -109,23 +121,42 @@ public class DmiDataOperations extends DmiOperations { final OperationEnum operation, final String requestData, final String dataType) { - CpsValidator.validateNameCharacters(cmHandleId); - final YangModelCmHandle yangModelCmHandle = - inventoryPersistence.getYangModelCmHandle(cmHandleId); + final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); + final String jsonBody = getDmiRequestBody(operation, null, requestData, dataType, yangModelCmHandle); + final String dmiUrl = getDmiRequestUrl(cmHandleId, resourceId, null, PASSTHROUGH_RUNNING, + null, yangModelCmHandle); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); isCmHandleStateReady(yangModelCmHandle, cmHandleState); + return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation); + } + + private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { + CpsValidator.validateNameCharacters(cmHandleId); + return inventoryPersistence.getYangModelCmHandle(cmHandleId); + } + + private String getDmiRequestBody(final OperationEnum operation, final String requestId, final String requestData, + final String dataType, final YangModelCmHandle yangModelCmHandle) { final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() - .operation(operation) - .data(requestData) - .dataType(dataType) - .build(); + .operation(operation) + .requestId(requestId) + .data(requestData) + .dataType(dataType) + .build(); dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties()); - final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody); - final String dmiUrl = - dmiServiceUrlBuilder.getDmiDatastoreUrl(dmiServiceUrlBuilder.populateQueryParams(resourceId, - null, null), - dmiServiceUrlBuilder.populateUriVariables(yangModelCmHandle, cmHandleId, PASSTHROUGH_RUNNING)); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation); + return jsonObjectMapper.asJsonString(dmiRequestBody); + } + + private String getDmiRequestUrl(final String cmHandleId, + final String resourceId, + final String optionsParamInQuery, + final DataStoreEnum dataStore, + final String topicParamInQuery, + final YangModelCmHandle yangModelCmHandle) { + return dmiServiceUrlBuilder.getDmiDatastoreUrl( + dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, + topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables( + yangModelCmHandle, cmHandleId, dataStore)); } private void isCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, final CmHandleState cmHandleState) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java index eeaa4cd0be..df303b5daa 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java @@ -57,7 +57,8 @@ public class CompositeState { /** * Date and Time in the format of yyyy-MM-dd'T'HH:mm:ss.SSSZ */ - public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter + .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); /** @@ -93,7 +94,7 @@ public class CompositeState { public static class Operational { @JsonProperty("sync-state") - private String syncState; + private SyncState syncState; @JsonProperty("last-sync-time") private String lastSyncTime; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java index 4ab0cecbf5..f4d96389de 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java @@ -98,7 +98,7 @@ public class CompositeStateBuilder { * @param lastSyncTime for the locked state * @return CompositeStateBuilder */ - public CompositeStateBuilder withOperationalDataStores(final String syncState, final String lastSyncTime) { + public CompositeStateBuilder withOperationalDataStores(final SyncState syncState, final String lastSyncTime) { this.datastores = DataStores.builder().operationalDataStore( Operational.builder().syncState(syncState).lastSyncTime(lastSyncTime).build()).build(); return this; @@ -111,20 +111,20 @@ public class CompositeStateBuilder { * @return CompositeState */ public CompositeStateBuilder fromDataNode(final DataNode dataNode) { - this.cmHandleState = CmHandleState.valueOf((String) dataNode.getLeaves() - .get("cm-handle-state")); + this.cmHandleState = CmHandleState.valueOf((String) dataNode.getLeaves() + .get("cm-handle-state")); for (final DataNode stateChildNode : dataNode.getChildDataNodes()) { if (stateChildNode.getXpath().endsWith("/lock-reason")) { this.lockReason = new LockReason(LockReasonCategory.valueOf( - (String) stateChildNode.getLeaves().get("reason")), - (String) stateChildNode.getLeaves().get("details")); + (String) stateChildNode.getLeaves().get("reason")), + (String) stateChildNode.getLeaves().get("details")); } if (stateChildNode.getXpath().endsWith("/datastores")) { for (final DataNode dataStoreNodes : stateChildNode.getChildDataNodes()) { Operational operationalDataStore = null; if (dataStoreNodes.getXpath().contains("/operational")) { operationalDataStore = Operational.builder() - .syncState((String) dataStoreNodes.getLeaves().get("sync-state")) + .syncState(SyncState.valueOf((String) dataStoreNodes.getLeaves().get("sync-state"))) .lastSyncTime((String) dataStoreNodes.getLeaves().get("last-sync-time")) .build(); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java index ce34154b9b..1985bd959a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java @@ -93,14 +93,39 @@ public class InventoryPersistence { } /** - * Method to return cm handles from the cps path. + * Method to return data nodes representing the cm handles. * * @param cpsPath cps path for which the cmHandle is requested - * @return a list of cm handles + * @return a list of data nodes representing the cm handles. */ - public List<DataNode> getCmHandlesByCpsPath(final String cpsPath) { + public List<DataNode> getCmHandleDataNodesByCpsPath(final String cpsPath) { return cpsDataPersistenceService.queryDataNodes( - NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath, FetchDescendantsOption.OMIT_DESCENDANTS); + NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath, FetchDescendantsOption.OMIT_DESCENDANTS); + } + + /** + * Method which returns cm handles by the cm handle id and state. + * @param cmHandleId cm handle id + * @param cmHandleState cm handle state + * @return a list of cm handles + */ + public List<DataNode> getCmHandlesByIdAndState(final String cmHandleId, final CmHandleState cmHandleState) { + return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, + NCMP_DMI_REGISTRY_ANCHOR, "//cm-handles[@id='" + cmHandleId + "']/state[@cm-handle-state=\"" + + cmHandleState + "\"]/ancestor::cm-handles", + FetchDescendantsOption.OMIT_DESCENDANTS); + } + + /** + * Method which returns cm handles by the operational sync state of cm handle. + * @param syncState sync state + * @return a list of cm handles + */ + public List<DataNode> getCmHandlesByOperationalSyncState(final SyncState syncState) { + return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, + NCMP_DMI_REGISTRY_ANCHOR, "//state/datastores" + + "/operational[@sync-state=\"" + syncState + "\"]/ancestor::cm-handles", + FetchDescendantsOption.OMIT_DESCENDANTS); } /** diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/SyncState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/SyncState.java new file mode 100644 index 0000000000..9c7a476183 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/SyncState.java @@ -0,0 +1,25 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.inventory; + +public enum SyncState { + SYNCHRONIZED, UNSYNCHRONIZED, NONE_REQUESTED +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/DataSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/DataSyncWatchdog.java new file mode 100644 index 0000000000..553db65dd0 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/DataSyncWatchdog.java @@ -0,0 +1,74 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.inventory.sync; + +import java.time.OffsetDateTime; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.api.CpsDataService; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.inventory.CompositeState; +import org.onap.cps.ncmp.api.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.inventory.SyncState; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Slf4j +@RequiredArgsConstructor +@Service +public class DataSyncWatchdog { + + private final InventoryPersistence inventoryPersistence; + + private final CpsDataService cpsDataService; + + private final SyncUtils syncUtils; + + /** + * Execute Cm Handle poll which queries the cm handle state in 'READY' and Operational Datastore Sync State in + * 'UNSYNCHRONIZED'. + */ + @Scheduled(fixedDelayString = "${timers.cm-handle-data-sync.sleep-time-ms:30000}") + public void executeUnSynchronizedReadyCmHandlePoll() { + YangModelCmHandle unSynchronizedReadyCmHandle = syncUtils.getAnUnSynchronizedReadyCmHandle(); + while (unSynchronizedReadyCmHandle != null) { + final String cmHandleId = unSynchronizedReadyCmHandle.getId(); + log.debug("Cm-Handles found in READY and UNSYNCHRONIZED state: {}", cmHandleId); + final CompositeState compositeState = inventoryPersistence + .getCmHandleState(cmHandleId); + final String resourceData = syncUtils.getResourceData(cmHandleId); + if (resourceData == null) { + log.debug("Error accessing the node for Cm-Handle: {}", cmHandleId); + } else { + cpsDataService.saveData("NFP-Operational", cmHandleId, + resourceData, OffsetDateTime.now()); + } + compositeState.setLastUpdateTimeNow(); + compositeState.getDataStores() + .setOperationalDataStore(CompositeState.Operational.builder() + .syncState(SyncState.SYNCHRONIZED) + .lastSyncTime(CompositeState.nowInSyncTimeFormat()).build()); + inventoryPersistence.saveCmHandleState(cmHandleId, compositeState); + unSynchronizedReadyCmHandle = syncUtils.getAnUnSynchronizedReadyCmHandle(); + } + log.debug("No Cm-Handles currently found in an READY State and Operational Sync State is UNSYNCHRONIZED"); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java index d6aaa32bef..9cfa0a05c8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java @@ -86,7 +86,7 @@ public class ModuleSyncWatchdog { */ @Scheduled(fixedDelayString = "${timers.locked-modules-sync.sleep-time-ms:300000}") public void executeLockedMisbehavingCmHandlePoll() { - final List<YangModelCmHandle> lockedMisbehavingCmHandles = syncUtils.getLockedMisbehavingCmHandles(); + final List<YangModelCmHandle> lockedMisbehavingCmHandles = syncUtils.getLockedMisbehavingYangModelCmHandles(); for (final YangModelCmHandle lockedMisbehavingModelCmHandle: lockedMisbehavingCmHandles) { final CompositeState updatedCompositeState = lockedMisbehavingModelCmHandle.getCompositeState(); updatedCompositeState.setCmHandleState(CmHandleState.ADVISED); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java index 22eeabb0df..b5456ab149 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java @@ -21,24 +21,35 @@ package org.onap.cps.ncmp.api.inventory.sync; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableMap; import java.security.SecureRandom; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; +import org.onap.cps.ncmp.api.impl.operations.DmiOperations; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.CmHandleState; import org.onap.cps.ncmp.api.inventory.CompositeState; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.ncmp.api.inventory.LockReasonCategory; +import org.onap.cps.ncmp.api.inventory.SyncState; import org.onap.cps.spi.model.DataNode; -import org.springframework.stereotype.Component; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; @Slf4j -@Component +@Service @RequiredArgsConstructor public class SyncUtils { @@ -46,6 +57,10 @@ public class SyncUtils { private final InventoryPersistence inventoryPersistence; + private final DmiDataOperations dmiDataOperations; + + private final JsonObjectMapper jsonObjectMapper; + private static final Pattern retryAttemptPattern = Pattern.compile("^Attempt #(\\d+) failed:"); /** @@ -64,14 +79,38 @@ public class SyncUtils { return inventoryPersistence.getYangModelCmHandle(cmHandleId); } + /** + * First query data nodes for cm handles with CM Handle Operational Sync State in "UNSYNCHRONIZED" and + * randomly select a CM Handle and query the data nodes for CM Handle State in "READY". + * + * @return a random yang model cm handle with State in READY and Operation Sync State in "UNSYNCHRONIZED", + * return null if not found + */ + public YangModelCmHandle getAnUnSynchronizedReadyCmHandle() { + final List<DataNode> unSynchronizedCmHandles = inventoryPersistence + .getCmHandlesByOperationalSyncState(SyncState.UNSYNCHRONIZED); + if (unSynchronizedCmHandles.isEmpty()) { + return null; + } + Collections.shuffle(unSynchronizedCmHandles); + for (final DataNode cmHandle : unSynchronizedCmHandles) { + final String cmHandleId = cmHandle.getLeaves().get("id").toString(); + final List<DataNode> readyCmHandles = inventoryPersistence + .getCmHandlesByIdAndState(cmHandleId, CmHandleState.READY); + if (!readyCmHandles.isEmpty()) { + return inventoryPersistence.getYangModelCmHandle(cmHandleId); + } + } + return null; + } /** * Query data nodes for cm handles with an "LOCKED" cm handle state with reason LOCKED_MISBEHAVING". * * @return a random yang model cm handle with an ADVISED state, return null if not found */ - public List<YangModelCmHandle> getLockedMisbehavingCmHandles() { - final List<DataNode> lockedCmHandleAsDataNodeList = inventoryPersistence.getCmHandlesByCpsPath( + public List<YangModelCmHandle> getLockedMisbehavingYangModelCmHandles() { + final List<DataNode> lockedCmHandleAsDataNodeList = inventoryPersistence.getCmHandleDataNodesByCpsPath( "//lock-reason[@reason=\"LOCKED_MISBEHAVING\"]/ancestor::cm-handles"); return lockedCmHandleAsDataNodeList.stream() .map(cmHandle -> YangDataConverter.convertCmHandleToYangModel(cmHandle, @@ -99,4 +138,27 @@ public class SyncUtils { .lockReasonCategory(lockReasonCategory).build()); } + /** + * Get the Resourece Data from Node through DMI Passthrough service. + * + * @param cmHandleId cm handle id + * @return optional string containing the resource data + */ + public String getResourceData(final String cmHandleId) { + final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi( + cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, + UUID.randomUUID().toString()); + if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) { + return getFirstResource(resourceDataResponseEntity.getBody()); + } + return null; + } + + private String getFirstResource(final Object responseBody) { + final String jsonObjectAsString = jsonObjectMapper.asJsonString(responseBody); + final JsonNode overallJsonNode = jsonObjectMapper.convertToJsonNode(jsonObjectAsString); + final Iterator<Map.Entry<String, JsonNode>> overallJsonTreeMap = overallJsonNode.fields(); + final Map.Entry<String, JsonNode> firstElement = overallJsonTreeMap.next(); + return jsonObjectMapper.asJsonString(ImmutableMap.of(firstElement.getKey(), firstElement.getValue())); + } } |