Fix sonar issues
[vfc/nfvo/driver/vnfm/svnfm.git] / nokiav2 / driver / src / main / java / org / onap / vfc / nfvo / driver / vnfm / svnfm / nokia / vnfm / notification / LifecycleChangeNotificationManager.java
1 /*
2  * Copyright 2016-2017, Nokia Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification;
17
18 import com.google.common.annotations.VisibleForTesting;
19 import com.google.common.collect.Iterables;
20 import com.google.common.collect.Ordering;
21 import com.google.common.collect.Sets;
22 import com.google.gson.Gson;
23 import com.google.gson.JsonElement;
24 import com.google.gson.JsonObject;
25 import com.nokia.cbam.lcm.v32.ApiException;
26 import com.nokia.cbam.lcm.v32.api.OperationExecutionsApi;
27 import com.nokia.cbam.lcm.v32.api.VnfsApi;
28 import com.nokia.cbam.lcm.v32.model.*;
29 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.INotificationSender;
30 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions;
31 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider;
32 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties;
33 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager;
34 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.LifecycleManager;
35 import org.slf4j.Logger;
36 import org.springframework.beans.factory.annotation.Autowired;
37 import org.springframework.stereotype.Component;
38
39 import java.util.List;
40 import java.util.NoSuchElementException;
41 import java.util.Set;
42
43 import static com.google.common.collect.Iterables.filter;
44 import static com.google.common.collect.Iterables.tryFind;
45 import static com.google.common.collect.Sets.newConcurrentHashSet;
46 import static com.nokia.cbam.lcm.v32.model.OperationType.INSTANTIATE;
47 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.buildFatalFailure;
48 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.childElement;
49 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
50 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCN_API_VERSION;
51 import static org.slf4j.LoggerFactory.getLogger;
52
53 /**
54  * Responsible for handling lifecycle change notifications from CBAM.
55  * The received LCNs are transformed into ONAP LCNs.
56  * The following CBAM LCNs are processed:
57  * - HEAL
58  * - INSTANTIATE
59  * - SCALE
60  * - TERMINATE
61  * The current limitations
62  * - if a LCN can not be be processed due to VNF having been deleted the problem is logged and CBAM is notified that
63  * the LCN has been processed (even if not in reality) because the signaling of failed LCN delivery blocks the delivery
64  * on all LCN deliveries. The consequence of this is that the information known by VF-C / A&AI may be inconsistent with
65  * reality (VNF having been deleted)
66  */
67 @Component
68 public class LifecycleChangeNotificationManager implements ILifecycleChangeNotificationManager {
69
70     public static final String PROBLEM = "All operations must return the { \"operationResult\" : { \"cbam_pre\" : [<fillMeOut>], \"cbam_post\" : [<fillMeOut>] } } structure";
71     /**
72      * Order the operations by start time (latest first)
73      */
74     public static final Ordering<OperationExecution> NEWEST_OPERATIONS_FIRST = new Ordering<OperationExecution>() {
75         @Override
76         public int compare(OperationExecution left, OperationExecution right) {
77             return right.getStartTime().toLocalDate().compareTo(left.getStartTime().toLocalDate());
78         }
79     };
80     /**
81      * < Separates the VNF id and the resource id within a VNF
82      */
83     private static final Set<OperationStatus> terminalStatus = Sets.newHashSet(OperationStatus.FINISHED, OperationStatus.FAILED);
84     private static Logger logger = getLogger(LifecycleChangeNotificationManager.class);
85
86     private final CbamRestApiProvider restApiProvider;
87     private final DriverProperties driverProperties;
88     private final INotificationSender notificationSender;
89     private Set<ProcessedNotification> processedNotifications = newConcurrentHashSet();
90
91     @Autowired
92     LifecycleChangeNotificationManager(CbamRestApiProvider restApiProvider, DriverProperties driverProperties, INotificationSender notificationSender) {
93         this.notificationSender = notificationSender;
94         this.driverProperties = driverProperties;
95         this.restApiProvider = restApiProvider;
96     }
97
98     @VisibleForTesting
99     static OperationExecution findLastInstantiationBefore(List<OperationExecution> operationExecutions, OperationExecution currentOperation) {
100         for (OperationExecution opExs : filter(NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions), (OperationExecution opex2) -> !opex2.getStartTime().isAfter(currentOperation.getStartTime()))) {
101             if (INSTANTIATE.equals(opExs.getOperationType()) &&
102                     (opExs.getStartTime().toLocalDate().isBefore(currentOperation.getStartTime().toLocalDate()) ||
103                             opExs.getStartTime().toLocalDate().isEqual(currentOperation.getStartTime().toLocalDate())
104                     )) {
105                 return opExs;
106             }
107         }
108         throw new NoSuchElementException();
109     }
110
111     @Override
112     public void handleLcn(VnfLifecycleChangeNotification recievedNotification) {
113         if (logger.isInfoEnabled()) {
114             logger.info("Received LCN: " + new Gson().toJson(recievedNotification));
115         }
116         VnfsApi cbamLcmApi = restApiProvider.getCbamLcmApi(driverProperties.getVnfmId());
117         try {
118             List<VnfInfo> vnfs = cbamLcmApi.vnfsGet(NOKIA_LCM_API_VERSION);
119             com.google.common.base.Optional<VnfInfo> currentVnf = tryFind(vnfs, vnf -> vnf.getId().equals(recievedNotification.getVnfInstanceId()));
120             String vnfHeader = "The VNF with " + recievedNotification.getVnfInstanceId() + " identifier";
121             if (!currentVnf.isPresent()) {
122                 logger.warn(vnfHeader + " disappeared before being able to process the LCN");
123                 //swallow LCN
124                 return;
125             } else {
126                 VnfInfo vnf = cbamLcmApi.vnfsVnfInstanceIdGet(recievedNotification.getVnfInstanceId(), NOKIA_LCN_API_VERSION);
127                 com.google.common.base.Optional<VnfProperty> externalVnfmId = tryFind(vnf.getExtensions(), prop -> prop.getName().equals(LifecycleManager.EXTERNAL_VNFM_ID));
128                 if (!externalVnfmId.isPresent()) {
129                     logger.warn(vnfHeader + " is not a managed VNF");
130                     return;
131                 }
132                 if (!externalVnfmId.get().getValue().equals(driverProperties.getVnfmId())) {
133                     logger.warn(vnfHeader + " is not a managed by the VNFM with id " + externalVnfmId.get().getValue());
134                     return;
135                 }
136             }
137         } catch (Exception e) {
138             throw buildFatalFailure(logger, "Unable to list VNFs / query VNF", e);
139         }
140         OperationExecutionsApi cbamOperationExecutionApi = restApiProvider.getCbamOperationExecutionApi(driverProperties.getVnfmId());
141         try {
142             List<OperationExecution> operationExecutions = cbamLcmApi.vnfsVnfInstanceIdOperationExecutionsGet(recievedNotification.getVnfInstanceId(), NOKIA_LCM_API_VERSION);
143             OperationExecution operationExecution = cbamOperationExecutionApi.operationExecutionsOperationExecutionIdGet(recievedNotification.getLifecycleOperationOccurrenceId(), NOKIA_LCM_API_VERSION);
144             OperationExecution closestInstantiationToOperation = findLastInstantiationBefore(operationExecutions, operationExecution);
145             String vimId = getVimId(cbamOperationExecutionApi, closestInstantiationToOperation);
146             notificationSender.processNotification(recievedNotification, operationExecution, buildAffectedCps(operationExecution), vimId);
147             if (OperationType.TERMINATE.equals(recievedNotification.getOperation()) && terminalStatus.contains(recievedNotification.getStatus())) {
148                 processedNotifications.add(new ProcessedNotification(recievedNotification.getLifecycleOperationOccurrenceId(), recievedNotification.getStatus()));
149             }
150         } catch (ApiException e) {
151             throw buildFatalFailure(logger, "Unable to retrieve the current VNF " + recievedNotification.getVnfInstanceId(), e);
152         }
153     }
154
155     private String getVimId(OperationExecutionsApi cbamOperationExecutionApi, OperationExecution closestInstantiationToOperation) {
156         try {
157             Object operationParams = cbamOperationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(closestInstantiationToOperation.getId(), NOKIA_LCM_API_VERSION);
158             return getVimId(operationParams);
159         } catch (Exception e) {
160             throw buildFatalFailure(logger, "Unable to detect last instantiation operation", e);
161         }
162     }
163
164     @Override
165     public void waitForTerminationToBeProcessed(String operationExecutionId) {
166         while (true) {
167             com.google.common.base.Optional<ProcessedNotification> notification = Iterables.tryFind(processedNotifications, processedNotification -> processedNotification.getOperationExecutionId().equals(operationExecutionId));
168             if (notification.isPresent()) {
169                 processedNotifications.remove(notification.get());
170                 return;
171             }
172             SystemFunctions.systemFunctions().sleep(500);
173         }
174     }
175
176     private String getVimId(Object instantiationParameters) {
177         InstantiateVnfRequest request = new Gson().fromJson(new Gson().toJson(instantiationParameters), InstantiateVnfRequest.class);
178         return request.getVims().get(0).getId();
179     }
180
181     private ReportedAffectedConnectionPoints buildAffectedCps(OperationExecution operationExecution) {
182         if (operationExecution.getOperationType() == OperationType.TERMINATE) {
183             String terminationType = childElement(new Gson().toJsonTree(operationExecution.getOperationParams()).getAsJsonObject(), "terminationType").getAsString();
184             if (TerminationType.FORCEFUL.name().equals(terminationType)) {
185                 //in case of force full termination the Ansible is not executed, so the connection points can not be
186                 //calculated from operation execution result
187                 logger.warn("Unable to send information related to affected connection points during forceful termination");
188                 return null;
189             }
190         }
191         try {
192             JsonElement root = new Gson().toJsonTree(operationExecution.getAdditionalData());
193             if (root.getAsJsonObject().has("operationResult")) {
194                 JsonObject operationResult = root.getAsJsonObject().get("operationResult").getAsJsonObject();
195                 if (!isPresent(operationResult, "cbam_pre") || !isPresent(operationResult, "cbam_post")) {
196                     handleFailure(operationExecution, null);
197                 }
198                 return new Gson().fromJson(operationResult, ReportedAffectedConnectionPoints.class);
199             }
200         } catch (Exception e) {
201             handleFailure(operationExecution, e);
202         }
203         return new ReportedAffectedConnectionPoints();
204     }
205
206     private boolean isPresent(JsonObject operationResult, String key) {
207         return operationResult.has(key) && operationResult.get(key).isJsonArray();
208     }
209
210     private void handleFailure(OperationExecution operationExecution, Exception e) {
211         switch (operationExecution.getStatus()) {
212             case FAILED:
213             case OTHER:
214                 logger.warn("The operation failed and the affected connection points were not reported");
215                 break;
216             case STARTED: //can not happen (the changed resources are only executed for terminal state
217             case FINISHED:
218                 if (e != null) {
219                     throw buildFatalFailure(logger, PROBLEM, e);
220                 }
221                 throw buildFatalFailure(logger, PROBLEM);
222         }
223     }
224 }