2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 - 2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.so.bpmn.buildingblock;
23 import static org.apache.commons.lang3.StringUtils.*;
25 import java.time.Duration;
26 import java.util.List;
28 import java.util.UUID;
30 import org.apache.commons.lang.SerializationUtils;
31 import org.camunda.bpm.engine.delegate.BpmnError;
32 import org.json.JSONArray;
33 import org.json.JSONObject;
34 import org.onap.so.bpmn.common.BuildingBlockExecution;
35 import org.onap.so.bpmn.core.json.JsonUtils;
36 import org.onap.so.bpmn.servicedecomposition.bbobjects.AllottedResource;
37 import org.onap.so.bpmn.servicedecomposition.bbobjects.CloudRegion;
38 import org.onap.so.bpmn.servicedecomposition.bbobjects.Customer;
39 import org.onap.so.bpmn.servicedecomposition.bbobjects.GenericVnf;
40 import org.onap.so.bpmn.servicedecomposition.bbobjects.Pnf;
41 import org.onap.so.bpmn.servicedecomposition.bbobjects.ServiceInstance;
42 import org.onap.so.bpmn.servicedecomposition.bbobjects.ServiceProxy;
43 import org.onap.so.bpmn.servicedecomposition.bbobjects.VpnBondingLink;
44 import org.onap.so.bpmn.servicedecomposition.entities.GeneralBuildingBlock;
45 import org.onap.so.bpmn.servicedecomposition.generalobjects.License;
46 import org.onap.so.bpmn.servicedecomposition.generalobjects.RequestContext;
47 import org.onap.so.bpmn.servicedecomposition.generalobjects.RequestParameters;
48 import org.onap.so.bpmn.servicedecomposition.homingobjects.Candidate;
49 import org.onap.so.bpmn.servicedecomposition.homingobjects.CandidateType;
50 import org.onap.so.bpmn.servicedecomposition.homingobjects.SolutionCandidates;
51 import org.onap.so.bpmn.servicedecomposition.homingobjects.SolutionInfo;
52 import org.onap.so.bpmn.servicedecomposition.modelinfo.ModelInfoMetadata;
53 import org.onap.so.bpmn.servicedecomposition.modelinfo.ModelInfoServiceInstance;
54 import org.onap.so.client.exception.BadResponseException;
55 import org.onap.so.client.exception.ExceptionBuilder;
57 import org.onap.so.client.sniro.SniroClient;
58 import static org.onap.so.client.sniro.SniroValidator.*;
60 import org.onap.so.client.sniro.beans.SniroManagerRequest;
61 import org.onap.so.db.catalog.beans.OrchestrationStatus;
62 import org.onap.so.logger.MsoLogger;
63 import org.springframework.beans.factory.annotation.Autowired;
64 import org.springframework.core.env.Environment;
65 import org.springframework.stereotype.Component;
66 import org.springframework.web.util.UriUtils;
70 * The sniro homing building block obtains licensing and homing solutions for a given
71 * resource or set of resources.
76 @Component("SniroHoming")
77 public class SniroHomingV2 {
79 private static final MsoLogger log = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, SniroHomingV2.class);
80 private JsonUtils jsonUtils = new JsonUtils();
82 private Environment env;
84 private SniroClient client;
86 private ExceptionBuilder exceptionUtil;
87 private static final String MODEL_NAME = "modelName";
88 private static final String MODEL_INVARIANT_ID = "modelInvariantId";
89 private static final String MODEL_VERSION_ID = "modelVersionId";
90 private static final String MODEL_VERSION = "modelVersion";
91 private static final String SERVICE_RESOURCE_ID = "serviceResourceId";
92 private static final String RESOURCE_MODULE_NAME = "resourceModuleName";
93 private static final String RESOURCE_MODEL_INFO = "resourceModelInfo";
94 private static final String IDENTIFIER_TYPE = "identifierType";
95 private static final String INVENTORY_TYPE = "inventoryType";
96 private static final String SOLUTIONS = "solutions";
97 private static final String RESOURCE_MISSING_DATA = "Resource does not contain: ";
98 private static final String SERVICE_MISSING_DATA = "Service Instance does not contain: ";
99 private static final String UNPROCESSABLE = "422";
100 private static final int INTERNAL = 500;
103 * Generates the request payload then sends to sniro manager to perform homing and
104 * licensing for the provided demands
108 public void callSniro(BuildingBlockExecution execution){
109 log.debug("Started Sniro Homing Call Sniro");
111 GeneralBuildingBlock bb = execution.getGeneralBuildingBlock();
113 RequestContext requestContext = bb.getRequestContext();
114 RequestParameters requestParams = requestContext.getRequestParameters();
115 String requestId = requestContext.getMsoRequestId();
117 ServiceInstance serviceInstance = bb.getCustomer().getServiceSubscription().getServiceInstances().get(0);
118 Customer customer = bb.getCustomer();
120 String timeout = execution.getVariable("timeout");
121 if(isBlank(timeout)){
122 timeout = env.getProperty("sniro.manager.timeout", "PT30M");
125 SniroManagerRequest request = new SniroManagerRequest(); //TODO Add additional pojos for each section
127 JSONObject requestInfo = buildRequestInfo(requestId, timeout);
128 request.setRequestInformation(requestInfo.toString());
130 JSONObject serviceInfo = buildServiceInfo(serviceInstance);
131 request.setServiceInformation(serviceInfo.toString());
133 JSONObject placementInfo = buildPlacementInfo(customer, requestParams);
135 JSONArray placementDemands = buildPlacementDemands(serviceInstance);
136 placementInfo.put("placementDemands", placementDemands);
137 request.setPlacementInformation(placementInfo.toString());
139 JSONObject licenseInfo = new JSONObject();
141 JSONArray licenseDemands = buildLicenseDemands(serviceInstance);
142 licenseInfo.put("licenseDemands", licenseDemands);
143 request.setLicenseInformation(licenseInfo.toString());
145 if(placementDemands.length() > 0 || licenseDemands.length() > 0){
146 client.postDemands(request);
148 log.debug(SERVICE_MISSING_DATA + "resources eligible for homing or licensing");
149 throw new BpmnError(UNPROCESSABLE, SERVICE_MISSING_DATA + "resources eligible for homing or licensing");
152 //Variables for ReceiveWorkflowMessage subflow
153 execution.setVariable("asyncCorrelator", requestId);
154 execution.setVariable("asyncMessageType", "SNIROResponse");
155 execution.setVariable("asyncTimeout", timeout);
157 log.trace("Completed Sniro Homing Call Sniro");
159 exceptionUtil.buildAndThrowWorkflowException(execution, Integer.parseInt(e.getErrorCode()), e.getMessage());
160 }catch(BadResponseException e){
161 exceptionUtil.buildAndThrowWorkflowException(execution, 400, e.getMessage());
163 exceptionUtil.buildAndThrowWorkflowException(execution, INTERNAL, "Internal Error - occurred while preparing sniro request: " + e.getMessage());
168 * Validates, processes, and sets the homing and licensing solutions that are returned by
172 * @param asyncResponse
174 public void processSolution(BuildingBlockExecution execution, String asyncResponse){
175 log.trace("Started Sniro Homing Process Solution");
177 //TODO improve handling multiple solutions but is dependent on sniro enhancing api + work with sniro conductor to improve "inventoryType" representation
178 validateSolution(asyncResponse);
179 ServiceInstance serviceInstance = execution.getGeneralBuildingBlock().getCustomer().getServiceSubscription().getServiceInstances().get(0);
181 log.debug("Processing sniro manager asyncronous response");
182 JSONObject response = new JSONObject(asyncResponse);
183 if(response.has(SOLUTIONS)){
184 JSONObject allSolutions = response.getJSONObject(SOLUTIONS);
185 if(allSolutions.has("placementSolutions")){
186 JSONArray placementSolutions = allSolutions.getJSONArray("placementSolutions");
187 for(int i = 0; i < placementSolutions.length(); i++){
188 JSONArray placements = placementSolutions.getJSONArray(i);
189 processPlacementSolution(serviceInstance, placements, i);
192 if(allSolutions.has("licenseSolutions")){
193 JSONArray licenseSolutions = allSolutions.getJSONArray("licenseSolutions");
194 if(licenseSolutions.length() > 0){
195 processLicenseSolution(serviceInstance, licenseSolutions);
199 throw new BpmnError(UNPROCESSABLE, "Sniro Managers response does not contain: " + SOLUTIONS);
202 execution.setVariable("generalBuildingBlock", execution.getGeneralBuildingBlock());
204 log.trace("Completed Sniro Homing Process Solution");
206 exceptionUtil.buildAndThrowWorkflowException(execution, Integer.parseInt(e.getErrorCode()), e.getMessage());
207 }catch(BadResponseException e){
208 exceptionUtil.buildAndThrowWorkflowException(execution, 400, e.getMessage());
210 exceptionUtil.buildAndThrowWorkflowException(execution, INTERNAL, "Internal Error - occurred while processing sniro asynchronous response: " + e.getMessage());
215 * Builds the request information section for the homing/licensing request
219 private JSONObject buildRequestInfo(String requestId, String timeout) throws Exception{
220 log.trace("Building request information");
221 JSONObject requestInfo = new JSONObject();
222 if(requestId != null){
223 String host = env.getProperty("mso.workflow.message.endpoint");
224 String callbackUrl = host + "/" + UriUtils.encodePathSegment("SNIROResponse", "UTF-8") + "/" + UriUtils.encodePathSegment(requestId, "UTF-8");
226 Duration d = Duration.parse(timeout);
227 long timeoutSeconds = d.getSeconds();
229 requestInfo.put("transactionId", requestId).put("requestId", requestId).put("callbackUrl", callbackUrl).put("sourceId", "mso").put("requestType", "create")
230 .put("timeout", timeoutSeconds);
232 throw new BpmnError(UNPROCESSABLE, "Request Context does not contain: requestId");
238 * Builds the request information section for the homing/licensing request
241 private JSONObject buildServiceInfo(ServiceInstance serviceInstance){
242 log.trace("Building service information");
243 JSONObject info = new JSONObject();
244 ModelInfoServiceInstance modelInfo = serviceInstance.getModelInfoServiceInstance();
245 if(isNotBlank(modelInfo.getModelInvariantUuid()) && isNotBlank(modelInfo.getModelUuid())){
246 info.put("serviceInstanceId", serviceInstance.getServiceInstanceId());
247 if(modelInfo.getServiceType() != null && modelInfo.getServiceType().length() > 0){ //temp solution
248 info.put("serviceName", modelInfo.getServiceType());
250 if(modelInfo.getServiceRole() != null){
251 info.put("serviceRole", modelInfo.getServiceRole());
253 info.put("modelInfo", buildModelInfo(serviceInstance.getModelInfoServiceInstance()));
255 throw new BpmnError(UNPROCESSABLE, SERVICE_MISSING_DATA + MODEL_VERSION_ID + ", " + MODEL_INVARIANT_ID);
261 * Builds initial section of placement info for the homing/licensing request
264 private JSONObject buildPlacementInfo(Customer customer, RequestParameters requestParams){
265 JSONObject placementInfo = new JSONObject();
266 if(customer != null){
267 log.debug("Adding subscriber to placement information");
268 placementInfo.put("subscriberInfo", new JSONObject().put("globalSubscriberId", customer.getGlobalCustomerId()).put("subscriberName", customer.getSubscriberName()).put("subscriberCommonSiteId", customer.getSubscriberCommonSiteId()));
269 if(requestParams != null){
270 log.debug("Adding request parameters to placement information");
271 placementInfo.put("requestParameters", new JSONObject(requestParams.toJsonString()));
274 throw new BpmnError(UNPROCESSABLE, SERVICE_MISSING_DATA + "customer");
276 return placementInfo;
281 * Builds the placement demand list for the homing/licensing request
284 private JSONArray buildPlacementDemands(ServiceInstance serviceInstance){
285 log.trace("Building placement information demands");
286 JSONArray placementDemands = new JSONArray();
288 List<AllottedResource> allottedResourceList = serviceInstance.getAllottedResources();
289 if(!allottedResourceList.isEmpty()){
290 log.debug("Adding allotted resources to placement demands list");
291 for(AllottedResource ar : allottedResourceList){
292 if(isBlank(ar.getId())){
293 ar.setId(UUID.randomUUID().toString());
295 JSONObject demand = buildDemand(ar.getId(), ar.getModelInfoAllottedResource());
296 addCandidates(ar, demand);
297 placementDemands.put(demand);
300 List<VpnBondingLink> vpnBondingLinkList = serviceInstance.getVpnBondingLinks();
301 if(!vpnBondingLinkList.isEmpty()){
302 log.debug("Adding vpn bonding links to placement demands list");
303 for(VpnBondingLink vbl:vpnBondingLinkList){
304 List<ServiceProxy> serviceProxyList = vbl.getServiceProxies();
305 for(ServiceProxy sp : serviceProxyList){
306 if(isBlank(sp.getId())){
307 sp.setId(UUID.randomUUID().toString());
309 JSONObject demand = buildDemand(sp.getId(), sp.getModelInfoServiceProxy());
310 addCandidates(sp, demand);
311 placementDemands.put(demand);
315 return placementDemands;
319 * Builds the license demand list for the homing/licensing request
322 private JSONArray buildLicenseDemands(ServiceInstance serviceInstance){
323 log.trace("Building license information");
324 JSONArray licenseDemands = new JSONArray();
325 List<GenericVnf> vnfList = serviceInstance.getVnfs();
326 if(!vnfList.isEmpty()){
327 log.debug("Adding vnfs to license demands list");
328 for(GenericVnf vnf : vnfList){
329 JSONObject demand = buildDemand(vnf.getVnfId(), vnf.getModelInfoGenericVnf());
330 licenseDemands.put(demand);
333 return licenseDemands;
337 * Builds a single demand object
340 private JSONObject buildDemand(String id, ModelInfoMetadata metadata){
341 log.debug("Building demand for service or resource: " + id);
342 JSONObject demand = new JSONObject();
343 if(isNotBlank(id) && isNotBlank(metadata.getModelInstanceName())){
344 demand.put(SERVICE_RESOURCE_ID, id);
345 demand.put(RESOURCE_MODULE_NAME, metadata.getModelInstanceName());
346 demand.put(RESOURCE_MODEL_INFO, buildModelInfo(metadata));
348 throw new BpmnError(UNPROCESSABLE, RESOURCE_MISSING_DATA + "modelInstanceName");
354 * Builds the resource model info section
357 private JSONObject buildModelInfo(ModelInfoMetadata metadata){
358 JSONObject object = new JSONObject();
359 String invariantUuid = metadata.getModelInvariantUuid();
360 String modelUuid = metadata.getModelUuid();
361 if(isNotBlank(invariantUuid) && isNotBlank(modelUuid)){
362 object.put(MODEL_INVARIANT_ID, invariantUuid).put(MODEL_VERSION_ID, modelUuid).put(MODEL_NAME, metadata.getModelName()).put(MODEL_VERSION, metadata.getModelVersion());
363 }else if(isNotBlank(invariantUuid)){
364 throw new BpmnError(UNPROCESSABLE, RESOURCE_MISSING_DATA + MODEL_VERSION_ID);
366 throw new BpmnError(UNPROCESSABLE, RESOURCE_MISSING_DATA + MODEL_INVARIANT_ID);
372 * Adds required, excluded, and existing candidates to a demand
375 private void addCandidates(SolutionCandidates candidates, JSONObject demand){
376 List<Candidate> required = candidates.getRequiredCandidates();
377 List<Candidate> excluded = candidates.getExcludedCandidates();
378 if(!required.isEmpty()){
379 demand.put("requiredCandidates", required);
381 if(!excluded.isEmpty()){
382 demand.put("excludedCandidates", excluded);
384 //TODO support existing candidates
388 * Processes the license solutions and sets to the corresponding generic vnf
391 private void processLicenseSolution(ServiceInstance serviceInstance, JSONArray licenseSolutions){
392 List<GenericVnf> vnfs = serviceInstance.getVnfs();
394 log.debug("Processing the license solution");
395 for(int i = 0; i < licenseSolutions.length(); i++){
396 JSONObject licenseSolution = licenseSolutions.getJSONObject(i);
397 for(GenericVnf vnf:vnfs){
398 if(licenseSolution.getString(SERVICE_RESOURCE_ID).equals(vnf.getVnfId())){
399 License license = new License();
400 JSONArray entitlementPools = licenseSolution.getJSONArray("entitlementPoolUUID");
401 List<String> entitlementPoolsList = jsonUtils.StringArrayToList(entitlementPools);
402 license.setEntitlementPoolUuids(entitlementPoolsList);
403 JSONArray licenseKeys = licenseSolution.getJSONArray("licenseKeyGroupUUID");
404 List<String> licenseKeysList = jsonUtils.StringArrayToList(licenseKeys);
405 license.setLicenseKeyGroupUuids(licenseKeysList);
407 vnf.setLicense(license);
414 * Processes a placement solution list then correlates and sets each placement solution
415 * to its corresponding resource
418 private void processPlacementSolution(ServiceInstance serviceInstance, JSONArray placements, int i){
419 List<VpnBondingLink> links = serviceInstance.getVpnBondingLinks();
420 List<AllottedResource> allottes = serviceInstance.getAllottedResources();
421 List<GenericVnf> vnfs = serviceInstance.getVnfs();
423 log.debug("Processing placement solution " + i+1);
424 for(int p = 0; p < placements.length(); p++){
425 JSONObject placement = placements.getJSONObject(p);
426 SolutionInfo solutionInfo = new SolutionInfo();
427 solutionInfo.setSolutionId(i + 1);
429 for(VpnBondingLink vbl:links){
430 List<ServiceProxy> proxies = vbl.getServiceProxies();
431 for(ServiceProxy sp:proxies){
432 if(placement.getString(SERVICE_RESOURCE_ID).equals(sp.getId())){
435 VpnBondingLink vblNew = (VpnBondingLink) SerializationUtils.clone(vbl);
436 vblNew.setVpnBondingLinkId(UUID.randomUUID().toString());
439 links.get(links.size() - 1).getServiceProxy(sp.getId()).setServiceInstance(setSolution(solutionInfo, placement));
441 sp.setServiceInstance(setSolution(solutionInfo, placement));
447 for(AllottedResource ar:allottes){
448 if(placement.getString(SERVICE_RESOURCE_ID).equals(ar.getId())){
449 ar.setParentServiceInstance(setSolution(solutionInfo, placement));
453 for(GenericVnf vnf:vnfs){
454 if(placement.getString(SERVICE_RESOURCE_ID).equals(vnf.getVnfId())){
455 ServiceInstance si = setSolution(solutionInfo, placement);
456 serviceInstance.setSolutionInfo(si.getSolutionInfo());
457 serviceInstance.getVnfs().add(si.getVnfs().get(0));
467 * Creates and sets necessary pojos with placement solution data for a given demand
470 private ServiceInstance setSolution(SolutionInfo solutionInfo, JSONObject placement){
471 log.debug("Mapping placement solution");
472 String invalidMessage = "Sniro Managers Response contains invalid: ";
474 JSONObject solution = placement.getJSONObject("solution");
475 String identifierType = solution.getString(IDENTIFIER_TYPE);
476 List<String> identifiersList = jsonUtils.StringArrayToList(solution.getJSONArray("identifiers").toString());
477 String identifierValue = identifiersList.get(0);
479 JSONArray assignments = placement.getJSONArray("assignmentInfo");
480 Map<String, String> assignmentsMap = jsonUtils.entryArrayToMap(assignments.toString(), "key", "value");
481 solutionInfo.setRehome(Boolean.parseBoolean(assignmentsMap.get("isRehome")));
482 String type = placement.getString(INVENTORY_TYPE);
484 ServiceInstance si = new ServiceInstance();
485 CloudRegion cloud = setCloud(assignmentsMap);
486 if(type.equals("service")){
487 if(identifierType.equals(CandidateType.SERVICE_INSTANCE_ID.toString())){
488 solutionInfo.setHomed(true);
489 si.setServiceInstanceId(identifierValue);
490 si.setOrchestrationStatus(OrchestrationStatus.CREATED);
491 cloud.setLcpCloudRegionId(assignmentsMap.get("cloudRegionId"));
492 if(assignmentsMap.containsKey("vnfHostName")){
493 log.debug("Resources has been homed to a vnf");
494 GenericVnf vnf = setVnf(assignmentsMap);
495 vnf.setCloudRegion(cloud);
496 si.getVnfs().add(vnf);
498 }else if(assignmentsMap.containsKey("primaryPnfName")){
499 log.debug("Resources has been homed to a pnf");
500 Pnf priPnf = setPnf(assignmentsMap, "primary");
501 priPnf.setCloudRegion(cloud);
502 si.getPnfs().add(priPnf);
503 if(assignmentsMap.containsKey("secondaryPnfName")){
504 Pnf secPnf = setPnf(assignmentsMap, "secondary");
505 secPnf.setCloudRegion(cloud);
506 si.getPnfs().add(secPnf);
510 log.debug(invalidMessage + IDENTIFIER_TYPE);
511 throw new BpmnError(UNPROCESSABLE, invalidMessage + IDENTIFIER_TYPE);
513 }else if(type.equals("cloud")){
514 if(identifierType.equals(CandidateType.CLOUD_REGION_ID.toString())){
515 log.debug("Resources has been homed to a cloud region");
516 cloud.setLcpCloudRegionId(identifierValue);
517 solutionInfo.setHomed(false);
518 solutionInfo.setTargetedCloudRegion(cloud);
519 si.setOrchestrationStatus(OrchestrationStatus.PRECREATED);
521 log.debug(invalidMessage + IDENTIFIER_TYPE);
522 throw new BpmnError(UNPROCESSABLE, invalidMessage + IDENTIFIER_TYPE);
525 log.debug(invalidMessage + INVENTORY_TYPE);
526 throw new BpmnError(UNPROCESSABLE, invalidMessage + INVENTORY_TYPE);
528 si.setSolutionInfo(solutionInfo);
533 * Sets the cloud data to a cloud region object
536 private CloudRegion setCloud(Map<String, String> assignmentsMap){
537 CloudRegion cloud = new CloudRegion();
538 cloud.setCloudOwner(assignmentsMap.get("cloudOwner"));
539 cloud.setCloudRegionVersion(assignmentsMap.get("aicVersion"));
540 cloud.setComplex(assignmentsMap.get("aicClli"));
545 * Sets the vnf data to a generic vnf object
548 private GenericVnf setVnf(Map<String, String> assignmentsMap){
549 GenericVnf vnf = new GenericVnf();
550 vnf.setOrchestrationStatus(OrchestrationStatus.CREATED);
551 vnf.setVnfName(assignmentsMap.get("vnfHostName"));
552 vnf.setVnfId(assignmentsMap.get("vnfId"));
557 * Sets the pnf data to a pnf object
560 private Pnf setPnf(Map<String, String> assignmentsMap, String role){
563 pnf.setOrchestrationStatus(OrchestrationStatus.CREATED);
564 pnf.setPnfName(assignmentsMap.get(role + "PnfName"));