2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ============LICENSE_END=========================================================
24 package org.onap.appc.requesthandler.impl;
26 import com.att.eelf.i18n.EELFResourceManager;
27 import com.fasterxml.jackson.databind.JsonNode;
28 import com.fasterxml.jackson.databind.ObjectMapper;
29 import com.fasterxml.jackson.databind.node.ObjectNode;
31 import jline.internal.Log;
33 import org.apache.commons.io.IOUtils;
34 import org.apache.commons.lang.ObjectUtils;
35 import org.apache.commons.lang.StringUtils;
36 import org.apache.http.HttpResponse;
37 import org.json.JSONObject;
38 import org.onap.appc.domainmodel.lcm.Flags;
39 import org.onap.appc.domainmodel.lcm.RequestContext;
40 import org.onap.appc.domainmodel.lcm.RuntimeContext;
41 import org.onap.appc.domainmodel.lcm.TransactionRecord;
42 import org.onap.appc.domainmodel.lcm.VNFContext;
43 import org.onap.appc.domainmodel.lcm.VNFOperation;
44 import org.onap.appc.exceptions.APPCException;
45 import org.onap.appc.executor.objects.LCMCommandStatus;
46 import org.onap.appc.executor.objects.Params;
47 import org.onap.appc.i18n.Msg;
48 import org.onap.appc.lockmanager.api.LockException;
49 import org.onap.appc.lockmanager.api.LockManager;
50 import org.onap.appc.logging.LoggingConstants;
51 import org.onap.appc.logging.LoggingUtils;
52 import org.onap.appc.requesthandler.exceptions.DGWorkflowNotFoundException;
53 import org.onap.appc.requesthandler.exceptions.LCMOperationsDisabledException;
54 import org.onap.appc.requesthandler.exceptions.MissingVNFDataInAAIException;
55 import org.onap.appc.requesthandler.exceptions.RequestValidationException;
56 import org.onap.appc.requesthandler.exceptions.VNFNotFoundException;
57 import org.onap.appc.requesthandler.exceptions.WorkflowNotFoundException;
58 import org.onap.appc.requesthandler.model.ActionIdentifierModel;
59 import org.onap.appc.requesthandler.model.Input;
60 import org.onap.appc.requesthandler.model.Request;
61 import org.onap.appc.requesthandler.model.RequestData;
62 import org.onap.appc.requesthandler.model.RequestModel;
63 import org.onap.appc.requesthandler.model.ScopeOverlapModel;
64 import org.onap.appc.rest.client.RestClientInvoker;
65 import org.onap.appc.validationpolicy.RequestValidationPolicy;
66 import org.onap.appc.validationpolicy.executors.RuleExecutor;
67 import org.onap.appc.validationpolicy.objects.RuleResult;
68 import org.onap.appc.workflow.WorkFlowManager;
69 import org.onap.appc.workflow.objects.WorkflowExistsOutput;
70 import org.onap.appc.workflow.objects.WorkflowRequest;
71 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
72 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
73 import org.onap.ccsdk.sli.core.sli.SvcLogicResource;
74 import org.onap.ccsdk.sli.adaptors.aai.AAIService;
75 import org.osgi.framework.BundleContext;
76 import org.osgi.framework.FrameworkUtil;
77 import org.osgi.framework.ServiceReference;
79 import java.io.IOException;
80 import java.net.MalformedURLException;
82 import java.util.ArrayList;
83 import java.util.Date;
84 import java.util.List;
85 import java.util.Properties;
86 import java.util.stream.Collectors;
88 public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
90 private WorkFlowManager workflowManager;
91 private LockManager lockManager;
92 private AAIService aaiService;
93 private RequestValidationPolicy requestValidationPolicy;
94 private RestClientInvoker client;
97 static final String SCOPE_OVERLAP_ENDPOINT = "appc.LCM.scopeOverlap.endpoint";
98 static final String ODL_USER = "appc.LCM.provider.user";
99 static final String ODL_PASS = "appc.LCM.provider.pass";
101 public void initialize() throws APPCException {
102 logger.info("Initializing RequestValidatorImpl.");
103 String endpoint = null;
106 Properties properties = configuration.getProperties();
107 if (properties != null) {
108 endpoint = properties.getProperty(SCOPE_OVERLAP_ENDPOINT);
109 user = properties.getProperty(ODL_USER);
110 pass = properties.getProperty(ODL_PASS);
112 if (endpoint == null) {
113 String message = "End point is not defined for scope over lap service in appc.properties.";
114 logger.error(message);
115 // TODO throw following exception (and remove the "return") when
116 // entry in appc.properties file is made for scope overlap service
118 // and remove @Ignore in unit tests:
119 // testInitializeWithNullConfigProps,
120 // testInitializeWithoutEndpointProp
121 // throw new APPCException(message);
126 URL url = new URL(endpoint);
127 client = new RestClientInvoker(url);
128 client.setAuthentication(user, pass);
129 path = url.getPath();
131 } catch (MalformedURLException e) {
132 String message = "Invalid endpoint " + endpoint;
133 logger.error(message, e);
134 // TODO throw following exception when entry in appc.properties file
135 // is made for scope overlap service endpoint
136 // and remove @Ignore in unit test:
137 // testInitializeWithMalFormatEndpoint
138 // throw new APPCException(message);
142 private void getAAIservice() {
143 BundleContext bctx = FrameworkUtil.getBundle(AAIService.class).getBundleContext();
144 // Get AAIadapter reference
145 ServiceReference sref = bctx.getServiceReference(AAIService.class.getName());
147 logger.info("AAIService from bundlecontext");
148 aaiService = (AAIService) bctx.getService(sref);
151 logger.error("Cannot find service reference for org.onap.ccsdk.sli.adaptors.aai.AAIService");
156 public void setLockManager(LockManager lockManager) {
157 this.lockManager = lockManager;
160 public void setClient(RestClientInvoker client) {
161 this.client = client;
164 public void setWorkflowManager(WorkFlowManager workflowManager) {
165 this.workflowManager = workflowManager;
168 public void setRequestValidationPolicy(RequestValidationPolicy requestValidationPolicy) {
169 this.requestValidationPolicy = requestValidationPolicy;
172 public void validateRequest(RuntimeContext runtimeContext) throws Exception {
173 if (logger.isTraceEnabled()) {
175 "Entering to validateRequest with RequestHandlerInput = " + ObjectUtils.toString(runtimeContext));
177 if (!lcmStateManager.isLCMOperationEnabled()) {
178 LoggingUtils.logErrorMessage(LoggingConstants.TargetNames.REQUEST_VALIDATOR,
179 EELFResourceManager.format(Msg.LCM_OPERATIONS_DISABLED), this.getClass().getCanonicalName());
180 throw new LCMOperationsDisabledException("APPC LCM operations have been administratively disabled");
184 validateInput(runtimeContext);
185 String vnfId = runtimeContext.getRequestContext().getActionIdentifiers().getVnfId();
186 VNFContext vnfContext = queryAAI(vnfId);
187 runtimeContext.setVnfContext(vnfContext);
188 runtimeContext.getTransactionRecord().setTargetType(vnfContext.getType());
190 VNFOperation operation = runtimeContext.getRequestContext().getAction();
191 if (operation.isBuiltIn()) {
195 validateVNFLock(runtimeContext);
196 checkWorkflowExists(vnfContext, runtimeContext.getRequestContext());
198 if (runtimeContext.getRequestContext().getCommonHeader().getFlags().isForce()) {
202 List<TransactionRecord> inProgressTransactions = transactionRecorder
203 .getInProgressRequests(runtimeContext.getTransactionRecord());
204 logger.debug("In progress requests " + inProgressTransactions.toString());
206 Long exclusiveRequestCount = inProgressTransactions.stream()
207 .filter(record -> record.getMode().equals(Flags.Mode.EXCLUSIVE.name())).count();
208 if (exclusiveRequestCount > 0) {
209 String message = "Request rejected - Existing request in progress with exclusive mode for VNF: " + vnfId;
210 throw new RequestValidationException(message, LCMCommandStatus.EXLCUSIVE_REQUEST_IN_PROGRESS,
211 (new Params()).addParam("errorMsg", message));
214 Boolean scopeOverLap = checkScopeOverLap(runtimeContext.getRequestContext(), inProgressTransactions);
215 logger.debug("Scope overlap " + scopeOverLap);
217 List<VNFOperation> inProgressActions = inProgressTransactions.stream().map(TransactionRecord::getOperation)
218 .collect(Collectors.toList());
220 RuleExecutor ruleExecutor = requestValidationPolicy.getInProgressRuleExecutor();
221 RuleResult result = ruleExecutor.executeRule(operation.name(), inProgressActions);
222 logger.debug("Policy validation result " + result);
223 if (RuleResult.REJECT == result) {
224 String message = "Request rejected as per the request validation policy";
225 throw new RequestValidationException(message, LCMCommandStatus.POLICY_VALIDATION_FAILURE,
226 (new Params()).addParam("errorMsg", message));
231 private void validateVNFLock(RuntimeContext runtimeContext) throws LockException {
232 String vnfId = runtimeContext.getRequestContext().getActionIdentifiers().getVnfId();
233 String lockOwner = lockManager.getLockOwner(vnfId);
234 logger.debug("Current lock owner is " + lockOwner + " for vnf " + vnfId);
235 if (lockOwner != null
236 && !lockOwner.equals(runtimeContext.getRequestContext().getCommonHeader().getRequestId())) {
237 String message = new StringBuilder("VNF : ").append(vnfId).append(" is locked by request id :")
238 .append(lockOwner).toString();
239 throw new LockException(message);
244 * Do not remove this method, this is actual method for invoking scope
245 * overlap service When the service becomes available, its dummy
246 * implementation should be removed and this implementation should be used.
248 private Boolean checkScopeOverLap(RequestContext requestContext, List<TransactionRecord> inProgressTransactions)
249 throws APPCException {
250 Boolean scopeOverlap = null;
252 JsonNode inputJSON = convertToJsonInput(requestContext, inProgressTransactions);
253 logger.debug("Input to scope overlap service " + inputJSON.toString());
254 HttpResponse response = client.doPost(path, inputJSON.toString());
255 int httpCode = response.getStatusLine().getStatusCode();
256 if (httpCode < 200 || httpCode >= 300) {
257 logger.debug("http error code " + httpCode);
258 throw new APPCException("Exception occurred on invoking check scope overlap api");
260 String respBody = IOUtils.toString(response.getEntity().getContent());
261 logger.debug("response body " + respBody);
262 ObjectMapper mapper = new ObjectMapper();
263 JsonNode outputJSON = mapper.readTree(respBody);
264 scopeOverlap = readScopeOverlap(outputJSON);
265 } catch (IOException e) {
266 String message = "Error accessing check scope overlap service";
267 logger.error(message, e);
268 throw new APPCException(message);
273 private Boolean readScopeOverlap(JsonNode outputJSON) throws APPCException {
274 logger.debug("Response JSON " + outputJSON.toString());
275 String message = "Error reading response JSON from scope overlap service ";
276 JsonNode outputNode = outputJSON.get("output");
277 JsonNode statusNode = outputNode.get("status");
278 if (statusNode == null) {
279 throw new APPCException(message);
282 if (null == statusNode.get("message"))
283 throw new APPCException(message + "Status message is null.");
284 String responseStatusMessage = statusNode.get("message").textValue();
286 if (null == statusNode.get("code"))
287 throw new APPCException(message + "Status code is null.");
288 String code = statusNode.get("code").textValue();
290 JsonNode responseInfoNode = outputNode.get("response-info");
291 JsonNode blockNode = responseInfoNode.get("block");
292 String requestOverlapValue = null;
294 if (null != blockNode)
295 requestOverlapValue = blockNode.textValue();
297 logger.debug("Response JSON " + requestOverlapValue);
299 if (code.equals("400")) {
300 if(null==requestOverlapValue)
301 throw new APPCException("Response code is 400 but requestOverlapValue is null ");
302 if (requestOverlapValue.contains("true")) {
304 } else if (requestOverlapValue.contains("false")) {
305 return Boolean.FALSE;
307 throw new APPCException(
308 message + "requestOverlap value is other than True and False, it is " + requestOverlapValue);
310 } else if (code.equals("401")) {
311 throw new APPCException(message + responseStatusMessage);
313 throw new APPCException(message + "Status code is neither 400 nor 401, it is " + code);
318 private JsonNode convertToJsonInput(RequestContext requestContext, List<TransactionRecord> inProgressTransactions) {
319 ObjectMapper objectMapper = new ObjectMapper();
320 ScopeOverlapModel scopeOverlapModel = getScopeOverlapModel(requestContext, inProgressTransactions);
321 // Added for change in interface for action level
323 JsonNode jsonObject = objectMapper.valueToTree(scopeOverlapModel);
328 public ScopeOverlapModel getScopeOverlapModel(RequestContext requestContext,
329 List<TransactionRecord> inProgressTransactions) {
330 ScopeOverlapModel scopeOverlapModel = new ScopeOverlapModel();
331 RequestData requestData = new RequestData();
333 List<RequestModel> inProgressRequests = new ArrayList<>();
334 RequestModel requestModel = new RequestModel();
335 ActionIdentifierModel actionIdentifierModel = extractActionIdentifierModel(requestContext);
336 requestModel.setAction(requestContext.getAction().toString());
337 requestModel.setActionIdentifier(actionIdentifierModel);
339 if (requestModel.getActionIdentifier().getVnfId() != null) {
340 requestData.setVnfID(requestModel.getActionIdentifier().getVnfId());
343 if (requestModel.getActionIdentifier().getVnfcName() != null
344 || requestModel.getActionIdentifier().getVfModuleId() != null
345 || requestModel.getActionIdentifier().getVserverId() != null) {
347 requestModel.getActionIdentifier().setVnfId(null);
350 requestData.setCurrentRequest(requestModel);
352 for (TransactionRecord record : inProgressTransactions) {
353 RequestModel request = new RequestModel();
354 ActionIdentifierModel actionIdentifier = new ActionIdentifierModel();
356 actionIdentifier.setServiceInstanceId(record.getServiceInstanceId());
357 actionIdentifier.setVnfId(record.getTargetId());
358 actionIdentifier.setVnfcName(record.getVnfcName());
359 actionIdentifier.setVfModuleId(record.getVfModuleId());
360 actionIdentifier.setVserverId(record.getVserverId());
362 request.setAction(record.getOperation().name());
363 request.setActionIdentifier(actionIdentifier);
364 if (request.getActionIdentifier().getVnfcName() != null
365 || request.getActionIdentifier().getVfModuleId() != null
366 || request.getActionIdentifier().getVserverId() != null) {
368 request.getActionIdentifier().setVnfId(null);
370 inProgressRequests.add(request);
373 requestData.setInProgressRequests(inProgressRequests);
375 Request request = new Request();
377 Date date = new Date();
378 request.setRequestID("RequestId-ScopeOverlap " + date.toString());
379 request.setAction("isScopeOverlap");
380 ObjectMapper objectMapper = new ObjectMapper();
381 JsonNode json = objectMapper.valueToTree(requestData);
382 request.setRequestData(json.toString());
383 Input input = new Input();
384 input.setRequest(request);
385 scopeOverlapModel.setInput(input);
387 return scopeOverlapModel;
390 private ActionIdentifierModel extractActionIdentifierModel(RequestContext requestContext) {
391 ActionIdentifierModel actionIdentifierModel = new ActionIdentifierModel();
392 actionIdentifierModel.setServiceInstanceId(requestContext.getActionIdentifiers().getServiceInstanceId());
393 actionIdentifierModel.setVnfId(requestContext.getActionIdentifiers().getVnfId());
394 actionIdentifierModel.setVnfcName(requestContext.getActionIdentifiers().getVnfcName());
395 actionIdentifierModel.setVfModuleId(requestContext.getActionIdentifiers().getVfModuleId());
396 actionIdentifierModel.setVserverId(requestContext.getActionIdentifiers().getVserverId());
397 return actionIdentifierModel;
400 private VNFContext queryAAI(String vnfId) throws VNFNotFoundException, MissingVNFDataInAAIException {
401 SvcLogicContext ctx = new SvcLogicContext();
402 ctx = getVnfdata(vnfId, "vnf", ctx);
404 VNFContext vnfContext = new VNFContext();
405 populateVnfContext(vnfContext, ctx);
410 private SvcLogicContext getVnfdata(String vnf_id, String prefix, SvcLogicContext ctx) throws VNFNotFoundException {
411 if (logger.isTraceEnabled()) {
412 logger.trace("Entering to getVnfdata with vnfid = " + ObjectUtils.toString(vnf_id) + ", prefix = "
413 + ObjectUtils.toString(prefix) + ", SvcLogicContext" + ObjectUtils.toString(ctx));
415 String key = "vnf-id = '" + vnf_id + "'";
416 logger.debug("inside getVnfdata=== " + key);
418 Date beginTimestamp = new Date();
419 SvcLogicResource.QueryStatus response = aaiService.query("generic-vnf", false, null, key, prefix, null,
421 Date endTimestamp = new Date();
422 String status = SvcLogicResource.QueryStatus.SUCCESS.equals(response)
423 ? LoggingConstants.StatusCodes.COMPLETE : LoggingConstants.StatusCodes.ERROR;
424 LoggingUtils.logMetricsMessage(beginTimestamp.toInstant(), endTimestamp.toInstant(),
425 LoggingConstants.TargetNames.AAI, LoggingConstants.TargetServiceNames.AAIServiceNames.QUERY, status,
426 "", response.name(), this.getClass().getCanonicalName());
427 if (SvcLogicResource.QueryStatus.NOT_FOUND.equals(response)) {
428 throw new VNFNotFoundException("VNF not found for vnf_id = " + vnf_id, vnf_id);
429 } else if (SvcLogicResource.QueryStatus.FAILURE.equals(response)) {
430 throw new RuntimeException("Error Querying AAI with vnfID = " + vnf_id);
432 logger.info("AAIResponse: " + response.toString());
433 } catch (SvcLogicException e) {
435 LoggingUtils.logErrorMessage(LoggingConstants.TargetServiceNames.AAIServiceNames.GET_VNF_DATA,
436 "Error in getVnfdata" + e, this.getClass().getCanonicalName());
438 throw new RuntimeException(e);
440 if (logger.isTraceEnabled()) {
441 logger.trace("Exiting from getVnfdata with (SvcLogicContext = " + ObjectUtils.toString(ctx) + ")");
446 private void populateVnfContext(VNFContext vnfContext, SvcLogicContext ctx) throws MissingVNFDataInAAIException {
447 String vnfType = ctx.getAttribute("vnf.vnf-type");
448 if (StringUtils.isEmpty(vnfType)) {
449 throw new MissingVNFDataInAAIException("vnf-type", ctx.getAttribute("vnf.vnf-id"));
451 vnfContext.setType(vnfType);
452 vnfContext.setId(ctx.getAttribute("vnf.vnf-id"));
455 private void checkWorkflowExists(VNFContext vnfContext, RequestContext requestContext)
456 throws WorkflowNotFoundException, DGWorkflowNotFoundException {
457 WorkflowExistsOutput workflowExistsOutput = workflowManager
458 .workflowExists(getWorkflowQueryParams(vnfContext, requestContext));
459 if (!workflowExistsOutput.isMappingExist()) {
460 if (logger.isDebugEnabled()) {
461 logger.debug("WorkflowManager : Workflow mapping not found for vnfType = " + vnfContext.getType()
462 + ", version = " + vnfContext.getVersion() + ", command = "
463 + requestContext.getAction().name());
465 LoggingUtils.logErrorMessage(LoggingConstants.TargetNames.WORKFLOW_MANAGER, EELFResourceManager
466 .format(Msg.APPC_WORKFLOW_NOT_FOUND, vnfContext.getType(), requestContext.getAction().name()),
467 this.getClass().getCanonicalName());
468 throw new WorkflowNotFoundException(
469 "Workflow mapping not found for vnfType = " + vnfContext.getType() + ", command = "
470 + requestContext.getAction().name(),
471 vnfContext.getType(), requestContext.getAction().name());
473 if (!workflowExistsOutput.isDgExist()) {
474 if (logger.isDebugEnabled()) {
475 logger.debug("WorkflowManager : DG Workflow not found for vnfType = " + vnfContext.getType()
476 + ", version = " + vnfContext.getVersion() + ", command = " + requestContext.getAction().name()
477 + " " + workflowExistsOutput);
479 LoggingUtils.logErrorMessage(LoggingConstants.TargetNames.WORKFLOW_MANAGER, EELFResourceManager
480 .format(Msg.APPC_WORKFLOW_NOT_FOUND, vnfContext.getType(), requestContext.getAction().name()),
481 this.getClass().getCanonicalName());
482 throw new DGWorkflowNotFoundException(
483 "Workflow not found for vnfType = " + vnfContext.getType() + ", command = "
484 + requestContext.getAction().name(),
485 workflowExistsOutput.getWorkflowModule(), workflowExistsOutput.getWorkflowName(),
486 workflowExistsOutput.getWorkflowVersion(), vnfContext.getType(), requestContext.getAction().name());
490 private WorkflowRequest getWorkflowQueryParams(VNFContext vnfContext, RequestContext requestContext) {
491 WorkflowRequest workflowRequest = new WorkflowRequest();
492 workflowRequest.setVnfContext(vnfContext);
493 workflowRequest.setRequestContext(requestContext);
494 if (logger.isTraceEnabled()) {
495 logger.trace("Exiting from getWorkflowQueryParams with (WorkflowRequest = "
496 + ObjectUtils.toString(workflowRequest) + ")");
498 return workflowRequest;