summaryrefslogtreecommitdiffstats
path: root/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java')
-rw-r--r--nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java211
1 files changed, 211 insertions, 0 deletions
diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java
new file mode 100644
index 00000000..142072d5
--- /dev/null
+++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2016-2017, Nokia Corporation
+ *
+ * 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.
+ */
+package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.nokia.cbam.lcm.v32.ApiException;
+import com.nokia.cbam.lcm.v32.api.OperationExecutionsApi;
+import com.nokia.cbam.lcm.v32.api.VnfsApi;
+import com.nokia.cbam.lcm.v32.model.*;
+import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.INotificationSender;
+import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions;
+import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider;
+import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties;
+import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.tryFind;
+import static com.google.common.collect.Sets.newConcurrentHashSet;
+import static com.nokia.cbam.lcm.v32.model.OperationType.INSTANTIATE;
+import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.childElement;
+import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure;
+import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
+import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCN_API_VERSION;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Responsible for handling lifecycle change notifications from CBAM.
+ * The received LCNs are transformed into ONAP LCNs.
+ * The following CBAM LCNs are processed:
+ * - HEAL
+ * - INSTANTIATE
+ * - SCALE
+ * - TERMINATE
+ * The current limitations
+ * - if a LCN can not be be processed due to VNF having been deleted the problem is logged and CBAM is notified that
+ * the LCN has been processed (even if not in reality) because the signaling of failed LCN delivery blocks the delivery
+ * on all LCN deliveries. The consequence of this is that the information known by VF-C / A&AI may be inconsistent with
+ * reality (VNF having been deleted)
+ */
+@Component
+public class LifecycleChangeNotificationManager implements ILifecycleChangeNotificationManager {
+
+ public static final String PROBLEM = "All operations must return the { \"operationResult\" : { \"cbam_pre\" : [<fillMeOut>], \"cbam_post\" : [<fillMeOut>] } } structure";
+ /**
+ * < Separates the VNF id and the resource id within a VNF
+ */
+ private static final Set<OperationStatus> terminalStatus = Sets.newHashSet(OperationStatus.FINISHED, OperationStatus.FAILED);
+ private static Logger logger = getLogger(LifecycleChangeNotificationManager.class);
+
+ private final CbamRestApiProvider restApiProvider;
+ private final DriverProperties driverProperties;
+ private final INotificationSender notificationSender;
+ private Set<ProcessedNotification> processedNotifications = newConcurrentHashSet();
+
+ @Autowired
+ LifecycleChangeNotificationManager(CbamRestApiProvider restApiProvider, DriverProperties driverProperties, INotificationSender notificationSender) {
+ this.notificationSender = notificationSender;
+ this.driverProperties = driverProperties;
+ this.restApiProvider = restApiProvider;
+ }
+
+ @VisibleForTesting
+ static OperationExecution findLastInstantiationBefore(List<OperationExecution> operationExecutions, OperationExecution currentOperation) {
+ for (OperationExecution opExs : filter(NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions), (OperationExecution opex2) -> !opex2.getStartTime().isAfter(currentOperation.getStartTime()))) {
+ if (INSTANTIATE.equals(opExs.getOperationType()) &&
+ (opExs.getStartTime().toLocalDate().isBefore(currentOperation.getStartTime().toLocalDate()) ||
+ opExs.getStartTime().toLocalDate().isEqual(currentOperation.getStartTime().toLocalDate())
+ )) {
+ return opExs;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void handleLcn(VnfLifecycleChangeNotification recievedNotification) {
+ logger.info("Received LCN: " + new Gson().toJson(recievedNotification));
+ VnfsApi cbamLcmApi = restApiProvider.getCbamLcmApi(driverProperties.getVnfmId());
+ try {
+ List<VnfInfo> vnfs = cbamLcmApi.vnfsGet(NOKIA_LCM_API_VERSION);
+ com.google.common.base.Optional<VnfInfo> currentVnf = tryFind(vnfs, vnf -> vnf.getId().equals(recievedNotification.getVnfInstanceId()));
+ if (!currentVnf.isPresent()) {
+ logger.warn("The VNF with " + recievedNotification.getVnfInstanceId() + " disappeared before being able to process the LCN");
+ //swallow LCN
+ return;
+ } else {
+ VnfInfo vnf = cbamLcmApi.vnfsVnfInstanceIdGet(recievedNotification.getVnfInstanceId(), NOKIA_LCN_API_VERSION);
+ com.google.common.base.Optional<VnfProperty> externalVnfmId = tryFind(vnf.getExtensions(), prop -> prop.getName().equals(EXTERNAL_VNFM_ID));
+ if (!externalVnfmId.isPresent()) {
+ logger.warn("The VNF with " + vnf.getId() + " identifier is not a managed VNF");
+ return;
+ }
+ if (!externalVnfmId.get().getValue().equals(driverProperties.getVnfmId())) {
+ logger.warn("The VNF with " + vnf.getId() + " identifier is not a managed by the VNFM with id " + externalVnfmId.get().getValue());
+ return;
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Unable to list VNFs / query VNF", e);
+ throw new RuntimeException("Unable to list VNFs / query VNF", e);
+ }
+ OperationExecutionsApi cbamOperationExecutionApi = restApiProvider.getCbamOperationExecutionApi(driverProperties.getVnfmId());
+ try {
+ List<OperationExecution> operationExecutions = cbamLcmApi.vnfsVnfInstanceIdOperationExecutionsGet(recievedNotification.getVnfInstanceId(), NOKIA_LCM_API_VERSION);
+ OperationExecution operationExecution = cbamOperationExecutionApi.operationExecutionsOperationExecutionIdGet(recievedNotification.getLifecycleOperationOccurrenceId(), NOKIA_LCM_API_VERSION);
+ OperationExecution closestInstantiationToOperation = findLastInstantiationBefore(operationExecutions, operationExecution);
+ String vimId;
+ try {
+ Object operationParams = cbamOperationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(closestInstantiationToOperation.getId(), NOKIA_LCM_API_VERSION);
+ vimId = getVimId(operationParams);
+ } catch (Exception e) {
+ logger.error("Unable to detect last instantiation operation", e);
+ throw new RuntimeException("Unable to detect last instantiation operation", e);
+ }
+ notificationSender.processNotification(recievedNotification, operationExecution, buildAffectedCps(operationExecution), vimId);
+ if (OperationType.TERMINATE.equals(recievedNotification.getOperation()) && terminalStatus.contains(recievedNotification.getStatus())) {
+ processedNotifications.add(new ProcessedNotification(recievedNotification.getLifecycleOperationOccurrenceId(), recievedNotification.getStatus()));
+ }
+ } catch (ApiException e) {
+ logger.error("Unable to retrieve the current VNF " + recievedNotification.getVnfInstanceId(), e);
+ throw new RuntimeException("Unable to retrieve the current VNF " + recievedNotification.getVnfInstanceId(), e);
+ }
+ }
+
+ @Override
+ public void waitForTerminationToBeProcessed(String operationExecutionId) {
+ while (true) {
+ com.google.common.base.Optional<ProcessedNotification> notification = Iterables.tryFind(processedNotifications, processedNotification -> processedNotification.getOperationExecutionId().equals(operationExecutionId));
+ if (notification.isPresent()) {
+ processedNotifications.remove(notification.get());
+ return;
+ }
+ SystemFunctions.systemFunctions().sleep(500);
+ }
+ }
+
+ private String getVimId(Object instantiationParameters) {
+ InstantiateVnfRequest request = new Gson().fromJson(new Gson().toJson(instantiationParameters), InstantiateVnfRequest.class);
+ return request.getVims().get(0).getId();
+ }
+
+ private ReportedAffectedConnectionPoints buildAffectedCps(OperationExecution operationExecution) {
+ switch (operationExecution.getOperationType()) {
+ case TERMINATE:
+ String terminationType = childElement(new Gson().toJsonTree(operationExecution.getOperationParams()).getAsJsonObject(), "terminationType").getAsString();
+ if (TerminationType.FORCEFUL.name().equals(terminationType)) {
+ //in case of force full termination the Ansible is not executed, so the connection points can not be
+ //calculated from operation execution result
+ logger.warn("Unable to send information related to affected connection points during forceful termination");
+ return null;
+ }
+ }
+ try {
+ JsonElement root = new Gson().toJsonTree(operationExecution.getAdditionalData());
+ if (root.getAsJsonObject().has("operationResult")) {
+ JsonObject operationResult = root.getAsJsonObject().get("operationResult").getAsJsonObject();
+ if (!isPresent(operationResult, "cbam_pre") || !isPresent(operationResult, "cbam_post")) {
+ handleFailure(operationExecution, null);
+ }
+ return new Gson().fromJson(operationResult, ReportedAffectedConnectionPoints.class);
+ }
+ } catch (Exception e) {
+ handleFailure(operationExecution, e);
+ }
+ return new ReportedAffectedConnectionPoints();
+ }
+
+ private boolean isPresent(JsonObject operationResult, String key) {
+ return operationResult.has(key) && operationResult.get(key).isJsonArray();
+ }
+
+ private void handleFailure(OperationExecution operationExecution, Exception e) {
+ switch (operationExecution.getStatus()) {
+ case FAILED:
+ case OTHER:
+ logger.warn("The operation failed and the affected connection points were not reported");
+ break;
+ case STARTED: //can not happen (the changed resources are only executed for terminal state
+ case FINISHED:
+ if (e != null) {
+ fatalFailure(logger, PROBLEM, e);
+ }
+ fatalFailure(logger, PROBLEM);
+ }
+ }
+}