/*- * ============LICENSE_START======================================================= * ONAP * ================================================================================ * Copyright (C) 2021 AT&T Intellectual Property. All rights reserved. * Modifications Copyright (C) 2022 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= */ package org.onap.policy.pap.main.notification; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.BiPredicate; import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.Getter; import org.onap.policy.models.pap.concepts.PolicyNotification; import org.onap.policy.models.pdp.concepts.PdpPolicyStatus; import org.onap.policy.models.pdp.concepts.PdpPolicyStatus.State; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; import org.onap.policy.pap.main.notification.StatusAction.Action; import org.onap.policy.pap.main.service.PolicyStatusService; /** * Collection of Policy Deployment Status records. The sequence of method invocations * should be as follows: *
    *
  1. {@link #loadByGroup(String)}
  2. *
  3. various other methods
  4. *
  5. repeat the previous steps as appropriate
  6. *
  7. {@link #flush(PolicyNotification)}
  8. *
*/ public class DeploymentStatus { /** * Tracks the groups that have been loaded. */ private final Set pdpGroupLoaded = new HashSet<>(); /** * Records, mapped by PDP/Policy pair. */ @Getter(AccessLevel.PROTECTED) private final Map recordMap = new HashMap<>(); /** * Records the policy status so that notifications can be generated. When * {@link #loadByGroup(String)} is invoked, records are added to this. Other than * that, this is not updated until {@link #addNotifications(PolicyNotification)} is * invoked. */ private DeploymentTracker tracker = new DeploymentTracker(); private PolicyStatusService policyStatusService; /** * Constructs the object. * * @param policyStatusService the policyStatusService */ public DeploymentStatus(PolicyStatusService policyStatusService) { this.policyStatusService = policyStatusService; } /** * Adds new policy status to a notification. * * @param notif notification to which to add policy status */ protected void addNotifications(PolicyNotification notif) { var newTracker = new DeploymentTracker(); recordMap.values().forEach(newTracker::add); tracker.addNotifications(notif, newTracker); tracker = newTracker; } /** * Loads policy deployment status associated with the given PDP group. * * @param pdpGroup group whose records are to be loaded */ public void loadByGroup(String pdpGroup) { if (pdpGroupLoaded.contains(pdpGroup)) { return; } pdpGroupLoaded.add(pdpGroup); for (PdpPolicyStatus status : policyStatusService.getGroupPolicyStatus(pdpGroup)) { var status2 = new StatusAction(Action.UNCHANGED, status); recordMap.put(new StatusKey(status), status2); tracker.add(status2); } } /** * Flushes changes to the DB, adding policy status to the notification. * * @param notif notification to which to add policy status */ public void flush(PolicyNotification notif) { // must add notifications BEFORE deleting undeployments addNotifications(notif); deleteUndeployments(); flush(); } /** * Flushes changes to the DB. */ protected void flush() { // categorize the records List created = new ArrayList<>(); List updated = new ArrayList<>(); List deleted = new ArrayList<>(); for (StatusAction status : recordMap.values()) { switch (status.getAction()) { case CREATED: created.add(status.getStatus()); break; case UPDATED: updated.add(status.getStatus()); break; case DELETED: deleted.add(status.getStatus()); break; default: break; } } policyStatusService.cudPolicyStatus(created, updated, deleted); /* * update the records to indicate everything is now unchanged (i.e., matches what * is in the DB) */ Iterator iter = recordMap.values().iterator(); while (iter.hasNext()) { StatusAction status = iter.next(); if (status.getAction() == Action.DELETED) { iter.remove(); } else { status.setAction(Action.UNCHANGED); } } } /** * Deletes records for any policies that have been completely undeployed. */ protected void deleteUndeployments() { // identify the incomplete policies // @formatter:off Set incomplete = recordMap.values().stream() .filter(status -> status.getAction() != Action.DELETED) .map(StatusAction::getStatus) .filter(status -> status.getState() == State.WAITING) .map(PdpPolicyStatus::getPolicy) .collect(Collectors.toSet()); // @formatter:on // delete if UNDEPLOYED and not incomplete deleteDeployment((key, status) -> !status.getStatus().isDeploy() && !incomplete.contains(key.getPolicy())); } /** * Delete deployment records for a PDP. * * @param pdpId PDP whose records are to be deleted */ public void deleteDeployment(String pdpId) { deleteDeployment((key, status) -> key.getPdpId().equals(pdpId)); } /** * Delete deployment records for a policy. * * @param policy policy whose records are to be deleted * @param deploy {@code true} to delete deployment records, {@code false} to delete * undeployment records */ public void deleteDeployment(ToscaConceptIdentifier policy, boolean deploy) { deleteDeployment((key, status) -> status.getStatus().isDeploy() == deploy && key.getPolicy().equals(policy)); } /** * Delete deployment records for a policy. * * @param filter filter to identify records to be deleted */ private void deleteDeployment(BiPredicate filter) { Iterator> iter = recordMap.entrySet().iterator(); while (iter.hasNext()) { Entry entry = iter.next(); StatusKey key = entry.getKey(); StatusAction value = entry.getValue(); if (filter.test(key, value)) { if (value.getAction() == Action.CREATED) { // it's a new record - just remove it iter.remove(); } else { // it's an existing record - mark it for deletion value.setAction(Action.DELETED); } } } } /** * Deploys/undeploys a policy to a PDP. Assumes that * {@link #deleteDeployment(ToscaConceptIdentifier, boolean)} has already been invoked * to delete any records having the wrong "deploy" value. * * @param pdpId PDP to which the policy is to be deployed * @param policy policy to be deployed * @param policyType policy's type * @param pdpGroup PdpGroup containing the PDP of interest * @param pdpType PDP type (i.e., PdpSubGroup) containing the PDP of interest * @param deploy {@code true} if the policy is being deployed, {@code false} if * undeployed */ public void deploy(String pdpId, ToscaConceptIdentifier policy, ToscaConceptIdentifier policyType, String pdpGroup, String pdpType, boolean deploy) { recordMap.compute(new StatusKey(pdpId, policy), (key, status) -> { if (status == null) { // no record yet - create one // @formatter:off return new StatusAction(Action.CREATED, PdpPolicyStatus.builder() .pdpGroup(pdpGroup) .pdpId(pdpId) .pdpType(pdpType) .policy(policy) .policyType(policyType) .deploy(deploy) .state(State.WAITING) .build()); // @formatter:on } PdpPolicyStatus status2 = status.getStatus(); // record already exists - see if the deployment flag should be changed if (status2.isDeploy() != deploy) { // deployment flag has changed status.setChanged(); status2.setDeploy(deploy); status2.setState(State.WAITING); } else if (status.getAction() == Action.DELETED) { // deployment flag is unchanged status.setAction(Action.UPDATED); } return status; }); } /** * Indicates the deployment/undeployment of a set of policies to a PDP has completed. * * @param pdpId PDP of interest * @param expectedPolicies policies that we expected to be deployed to the PDP * @param actualPolicies policies that were actually deployed to the PDP */ public void completeDeploy(String pdpId, Set expectedPolicies, Set actualPolicies) { for (StatusAction status : recordMap.values()) { PdpPolicyStatus status2 = status.getStatus(); if (!status.getStatus().getPdpId().equals(pdpId) || expectedPolicies.contains(status2.getPolicy()) != status2.isDeploy()) { /* * The policy is "expected" to be deployed, but the record is not marked * for deployment (or vice versa), which means the expected policy is out * of date with the DB, thus we'll ignore this policy for now. */ continue; } State state; if (actualPolicies.contains(status2.getPolicy())) { state = (status.getStatus().isDeploy() ? State.SUCCESS : State.FAILURE); } else { state = (status.getStatus().isDeploy() ? State.FAILURE : State.SUCCESS); } if (status2.getState() != state) { status.setChanged(); status2.setState(state); } } } }