2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 Intel Corp. All rights reserved.
6 * ================================================================================
7 * Modifications Copyright (c) 2019 Samsung
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.
20 * ============LICENSE_END=========================================================
23 package org.onap.so.bpmn.common.scripts
25 import static org.onap.so.bpmn.common.scripts.GenericUtils.*
27 import javax.ws.rs.core.UriBuilder
29 import org.camunda.bpm.engine.delegate.DelegateExecution
30 import org.onap.so.bpmn.common.util.OofInfraUtils
31 import org.onap.so.bpmn.core.UrnPropertiesReader
32 import org.onap.so.bpmn.core.domain.AllottedResource
33 import org.onap.so.bpmn.core.domain.HomingSolution
34 import org.onap.so.bpmn.core.domain.ModelInfo
35 import org.onap.so.bpmn.core.domain.Resource
36 import org.onap.so.bpmn.core.domain.ServiceDecomposition
37 import org.onap.so.bpmn.core.domain.ServiceInstance
38 import org.onap.so.bpmn.core.domain.Subscriber
39 import org.onap.so.bpmn.core.domain.VnfResource
40 import org.onap.so.bpmn.core.json.JsonUtils
41 import org.onap.so.db.catalog.beans.CloudSite
42 import org.onap.so.db.catalog.beans.HomingInstance
43 import org.slf4j.Logger
44 import org.slf4j.LoggerFactory
46 import com.fasterxml.jackson.databind.ObjectMapper
49 private static final Logger logger = LoggerFactory.getLogger( OofUtils.class);
51 ExceptionUtil exceptionUtil = new ExceptionUtil()
52 JsonUtils jsonUtil = new JsonUtils()
53 OofInfraUtils oofInfraUtils = new OofInfraUtils()
55 private AbstractServiceTaskProcessor utils
57 OofUtils(AbstractServiceTaskProcessor taskProcessor) {
58 this.utils = taskProcessor
62 * This method builds the service-agnostic
63 * OOF json request to get a homing solution
64 * and license solution
68 * @param decomposition - ServiceDecomposition object
69 * @param customerLocation -
70 * @param existingCandidates -
71 * @param excludedCandidates -
72 * @param requiredCandidates -
74 * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
76 String buildRequest(DelegateExecution execution,
78 ServiceDecomposition decomposition,
79 Subscriber subscriber = null,
81 ArrayList existingCandidates = null,
82 ArrayList excludedCandidates = null,
83 ArrayList requiredCandidates = null) {
84 logger.debug( "Started Building OOF Request")
85 String callbackEndpoint = UrnPropertiesReader.getVariable("mso.oof.callbackEndpoint", execution)
86 logger.debug( "mso.oof.callbackEndpoint is: " + callbackEndpoint)
88 def callbackUrl = utils.createHomingCallbackURL(callbackEndpoint, "oofResponse", requestId)
89 logger.debug( "callbackUrl is: " + callbackUrl)
92 def transactionId = requestId
93 logger.debug( "transactionId is: " + transactionId)
94 //ServiceInstance Info
95 ServiceInstance serviceInstance = decomposition.getServiceInstance()
96 def serviceInstanceId = ""
99 serviceInstanceId = execution.getVariable("serviceInstanceId")
100 logger.debug( "serviceInstanceId is: " + serviceInstanceId)
101 serviceName = execution.getVariable("subscriptionServiceType")
102 logger.debug( "serviceName is: " + serviceName)
104 if (serviceInstanceId == null || serviceInstanceId == "null") {
105 logger.debug( "Unable to obtain Service Instance Id")
106 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
107 "obtain Service Instance Id, execution.getVariable(\"serviceInstanceId\") is null")
109 if (serviceName == null || serviceName == "null") {
110 logger.debug( "Unable to obtain Service Name")
111 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
112 "obtain Service Name, execution.getVariable(\"subscriptionServiceType\") is null")
115 ModelInfo model = decomposition.getModelInfo()
116 logger.debug( "ModelInfo: " + model.toString())
117 String modelType = model.getModelType()
118 String modelInvariantId = model.getModelInvariantUuid()
119 String modelVersionId = model.getModelUuid()
120 String modelName = model.getModelName()
121 String modelVersion = model.getModelVersion()
123 String subscriberId = ""
124 String subscriberName = ""
125 String commonSiteId = ""
126 if (subscriber != null) {
127 subscriberId = subscriber.getGlobalId()
128 subscriberName = subscriber.getName()
129 commonSiteId = subscriber.getCommonSiteId()
132 //Determine RequestType
133 //TODO Figure out better way to determine this
134 String requestType = "create"
135 List<Resource> resources = decomposition.getServiceResources()
136 for (Resource r : resources) {
137 HomingSolution currentSolution = (HomingSolution) r.getCurrentHomingSolution()
138 if (currentSolution != null) {
139 requestType = "speed changed"
144 String placementDemands = ""
145 StringBuilder sb = new StringBuilder()
146 List<AllottedResource> allottedResourceList = decomposition.getAllottedResources()
147 List<VnfResource> vnfResourceList = decomposition.getVnfResources()
149 if (allottedResourceList == null || allottedResourceList.isEmpty()) {
150 logger.debug( "Allotted Resources List is empty - will try to get service VNFs instead.")
152 for (AllottedResource resource : allottedResourceList) {
153 logger.debug( "Allotted Resource: " + resource.toString())
154 def serviceResourceId = resource.getResourceId()
155 def toscaNodeType = resource.getToscaNodeType()
156 def resourceModuleName = toscaNodeType.substring(toscaNodeType.lastIndexOf(".") + 1)
157 def resourceModelInvariantId = resource.getModelInfo().getModelInvariantUuid()
158 def resourceModelVersionId = resource.getModelInfo().getModelUuid()
159 def resourceModelName = resource.getModelInfo().getModelName()
160 def resourceModelVersion = resource.getModelInfo().getModelVersion()
161 def resourceModelType = resource.getModelInfo().getModelType()
162 def tenantId = execution.getVariable("tenantId")
163 def requiredCandidatesJson = ""
165 requiredCandidatesJson = createCandidateJson(
172 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
173 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
174 " \"tenantId\": \"${tenantId}\",\n" +
175 " \"resourceModelInfo\": {\n" +
176 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
177 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
178 " \"modelName\": \"${resourceModelName}\",\n" +
179 " \"modelType\": \"${resourceModelType}\",\n" +
180 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
181 " \"modelCustomizationName\": \"\"\n" +
182 " }" + requiredCandidatesJson + "\n" +
185 placementDemands = sb.append(demand)
189 if (vnfResourceList == null || vnfResourceList.isEmpty()) {
190 logger.debug( "VNF Resources List is empty")
193 for (VnfResource vnfResource : vnfResourceList) {
194 logger.debug( "VNF Resource: " + vnfResource.toString())
195 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
196 def toscaNodeType = vnfResource.getToscaNodeType()
197 def resourceModuleName = toscaNodeType.substring(toscaNodeType.lastIndexOf(".") + 1)
198 def serviceResourceId = vnfResource.getResourceId()
199 def resourceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
200 def resourceModelName = vnfResourceModelInfo.getModelName()
201 def resourceModelVersion = vnfResourceModelInfo.getModelVersion()
202 def resourceModelVersionId = vnfResourceModelInfo.getModelUuid()
203 def resourceModelType = vnfResourceModelInfo.getModelType()
204 def tenantId = execution.getVariable("tenantId")
205 def requiredCandidatesJson = ""
208 String placementDemand =
210 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
211 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
212 " \"tenantId\": \"${tenantId}\",\n" +
213 " \"resourceModelInfo\": {\n" +
214 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
215 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
216 " \"modelName\": \"${resourceModelName}\",\n" +
217 " \"modelType\": \"${resourceModelType}\",\n" +
218 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
219 " \"modelCustomizationName\": \"\"\n" +
220 " }" + requiredCandidatesJson + "\n" +
223 placementDemands = sb.append(placementDemand)
225 placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
228 /* Commenting Out Licensing as OOF doesn't support for Beijing
229 String licenseDemands = ""
230 sb = new StringBuilder()
231 if (vnfResourceList.isEmpty() || vnfResourceList == null) {
232 logger.debug( "Vnf Resources List is Empty")
234 for (VnfResource vnfResource : vnfResourceList) {
235 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
236 def resourceInstanceType = vnfResource.getResourceType()
237 def serviceResourceId = vnfResource.getResourceId()
238 def resourceModuleName = vnfResource.getResourceType()
239 def resouceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
240 def resouceModelName = vnfResourceModelInfo.getModelName()
241 def resouceModelVersion = vnfResourceModelInfo.getModelVersion()
242 def resouceModelVersionId = vnfResourceModelInfo.getModelUuid()
243 def resouceModelType = vnfResourceModelInfo.getModelType()
245 // TODO Add Existing Licenses to demand
246 //"existingLicenses": {
247 //"entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
248 // "43257b49-9602-4fe5-9337-094e52bc9435"],
249 //"licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
250 // "43257b49-9602-4fe5-9337-094e52bc9435"]
253 String licenseDemand =
255 "\"resourceModuleName\": \"${resourceModuleName}\",\n" +
256 "\"serviceResourceId\": \"${serviceResourceId}\",\n" +
257 "\"resourceInstanceType\": \"${resourceInstanceType}\",\n" +
258 "\"resourceModelInfo\": {\n" +
259 " \"modelInvariantId\": \"${resouceModelInvariantId}\",\n" +
260 " \"modelVersionId\": \"${resouceModelVersionId}\",\n" +
261 " \"modelName\": \"${resouceModelName}\",\n" +
262 " \"modelType\": \"${resouceModelType}\",\n" +
263 " \"modelVersion\": \"${resouceModelVersion}\",\n" +
264 " \"modelCustomizationName\": \"\"\n" +
268 licenseDemands = sb.append(licenseDemand)
270 licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
275 " \"requestInfo\": {\n" +
276 " \"transactionId\": \"${transactionId}\",\n" +
277 " \"requestId\": \"${requestId}\",\n" +
278 " \"callbackUrl\": \"${callbackUrl}\",\n" +
279 " \"sourceId\": \"so\",\n" +
280 " \"requestType\": \"${requestType}\"," +
281 " \"numSolutions\": 1,\n" +
282 " \"optimizers\": [\"placement\"],\n" +
283 " \"timeout\": 600\n" +
285 " \"placementInfo\": {\n" +
286 " \"requestParameters\": {\n" +
287 " \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
288 " \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
289 " \"customerName\": \"${customerLocation.customerName}\"\n" +
291 " \"subscriberInfo\": { \n" +
292 " \"globalSubscriberId\": \"${subscriberId}\",\n" +
293 " \"subscriberName\": \"${subscriberName}\",\n" +
294 " \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
296 " \"placementDemands\": [\n" +
297 " ${placementDemands}\n" +
300 " \"serviceInfo\": {\n" +
301 " \"serviceInstanceId\": \"${serviceInstanceId}\",\n" +
302 " \"serviceName\": \"${serviceName}\",\n" +
303 " \"modelInfo\": {\n" +
304 " \"modelType\": \"${modelType}\",\n" +
305 " \"modelInvariantId\": \"${modelInvariantId}\",\n" +
306 " \"modelVersionId\": \"${modelVersionId}\",\n" +
307 " \"modelName\": \"${modelName}\",\n" +
308 " \"modelVersion\": \"${modelVersion}\",\n" +
309 " \"modelCustomizationName\": \"\"\n" +
315 logger.debug( "Completed Building OOF Request")
317 } catch (Exception ex) {
318 logger.debug( "buildRequest Exception: " + ex)
323 * This method validates the callback response
324 * from OOF. If the response contains an
325 * exception the method will build and throw
326 * a workflow exception.
329 * @param response - the async callback response from oof
331 Void validateCallbackResponse(DelegateExecution execution, String response) {
332 String placements = ""
333 if (isBlank(response)) {
334 exceptionUtil.buildAndThrowWorkflowException(execution, 5000, "OOF Async Callback Response is Empty")
336 if (JsonUtils.jsonElementExist(response, "solutions.placementSolutions")) {
337 placements = jsonUtil.getJsonValue(response, "solutions.placementSolutions")
338 if (isBlank(placements) || placements.equalsIgnoreCase("[]")) {
339 String statusMessage = jsonUtil.getJsonValue(response, "statusMessage")
340 if (isBlank(statusMessage)) {
341 logger.debug( "Error Occurred in Homing: OOF Async Callback Response does " +
342 "not contain placement solution.")
343 exceptionUtil.buildAndThrowWorkflowException(execution, 400,
344 "OOF Async Callback Response does not contain placement solution.")
346 logger.debug( "Error Occurred in Homing: " + statusMessage)
347 exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
352 } else if (response.contains("error") || response.contains("Error") ) {
353 String errorMessage = ""
354 if (response.contains("policyException")) {
355 String text = jsonUtil.getJsonValue(response, "requestError.policyException.text")
356 errorMessage = "OOF Async Callback Response contains a Request Error Policy Exception: " + text
357 } else if (response.contains("Unable to find any candidate for demand")) {
358 errorMessage = "OOF Async Callback Response contains error: Unable to find any candidate for " +
359 "demand *** Response: " + response.toString()
360 } else if (response.contains("serviceException")) {
361 String text = jsonUtil.getJsonValue(response, "requestError.serviceException.text")
362 errorMessage = "OOF Async Callback Response contains a Request Error Service Exception: " + text
364 errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
366 logger.debug( "Error Occurred in Homing: " + errorMessage)
367 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
370 logger.debug( "Error Occurred in Homing: Received an Unknown Async Callback Response from OOF.")
371 exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Received an Unknown Async Callback Response from OOF.")
378 * This method creates candidates json for placement Demands.
381 * @param existingCandidates -
382 * @param excludedCandidates -
383 * @param requiredCandidates -
385 * @return candidatesJson - a JSON string with candidates
387 String createCandidateJson(ArrayList existingCandidates = null,
388 ArrayList excludedCandidates = null,
389 ArrayList requiredCandidates = null) {
390 def candidatesJson = ""
392 if (existingCandidates != null && existingCandidates != {}) {
393 sb = new StringBuilder()
395 " \"existingCandidates\": [\n")
396 def existingCandidateJson = ""
397 existingCandidates.each { existingCandidate ->
398 type = existingCandidate.get('identifierType')
399 if (type == 'vimId') {
400 def cloudOwner = existingCandidate.get('cloudOwner')
401 def cloudRegionId = existingCandidate.get('identifiers')
402 existingCandidateJson = "{\n" +
403 " \"identifierType\": \"vimId\",\n" +
404 " \"cloudOwner\": \"${cloudOwner}\",\n" +
405 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
407 sb.append(existingCandidateJson)
409 if (type == 'serviceInstanceId') {
410 def serviceInstanceId = existingCandidate.get('identifiers')
411 existingCandidateJson += "{\n" +
412 " \"identifierType\": \"serviceInstanceId\",\n" +
413 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
415 sb.append(existingCandidateJson)
418 if (existingCandidateJson != "") {
419 sb.setLength(sb.length() - 1)
420 candidatesJson = sb.append(",\n],")
423 if (excludedCandidates != null && excludedCandidates != {}) {
424 sb = new StringBuilder()
426 " \"excludedCandidates\": [\n")
427 def excludedCandidateJson = ""
428 excludedCandidates.each { excludedCandidate ->
429 type = excludedCandidate.get('identifierType')
430 if (type == 'vimId') {
431 def cloudOwner = excludedCandidate.get('cloudOwner')
432 def cloudRegionId = excludedCandidate.get('identifiers')
433 excludedCandidateJson = "{\n" +
434 " \"identifierType\": \"vimId\",\n" +
435 " \"cloudOwner\": \"${cloudOwner}\",\n" +
436 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
438 sb.append(excludedCandidateJson)
440 if (type == 'serviceInstanceId') {
441 def serviceInstanceId = excludedCandidate.get('identifiers')
442 excludedCandidateJson += "{\n" +
443 " \"identifierType\": \"serviceInstanceId\",\n" +
444 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
446 sb.append(excludedCandidateJson)
449 if (excludedCandidateJson != "") {
450 sb.setLength(sb.length() - 1)
451 candidatesJson = sb.append(",\n],")
454 if (requiredCandidates != null && requiredCandidates != {}) {
455 sb = new StringBuilder()
457 " \"requiredCandidates\": [\n")
458 def requiredCandidatesJson = ""
459 requiredCandidates.each { requiredCandidate ->
460 type = requiredCandidate.get('identifierType')
461 if (type == 'vimId') {
462 def cloudOwner = requiredCandidate.get('cloudOwner')
463 def cloudRegionId = requiredCandidate.get('identifiers')
464 requiredCandidatesJson = "{\n" +
465 " \"identifierType\": \"vimId\",\n" +
466 " \"cloudOwner\": \"${cloudOwner}\",\n" +
467 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
469 sb.append(requiredCandidatesJson)
471 if (type == 'serviceInstanceId') {
472 def serviceInstanceId = requiredCandidate.get('identifiers')
473 requiredCandidatesJson += "{\n" +
474 " \"identifierType\": \"serviceInstanceId\",\n" +
475 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
477 sb.append(requiredCandidatesJson)
480 if (requiredCandidatesJson != "") {
481 sb.setLength(sb.length() - 1)
482 candidatesJson = sb.append(",\n],")
485 if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
486 return candidatesJson
490 * This method creates a cloudsite in catalog database.
492 * @param CloudSite cloudSite
496 Void createCloudSite(CloudSite cloudSite, DelegateExecution execution) {
497 oofInfraUtils.createCloudSite(cloudSite, execution)
501 * This method creates a HomingInstance in catalog database.
503 * @param HomingInstance homingInstance
507 Void createHomingInstance(HomingInstance homingInstance, DelegateExecution execution) {
508 oofInfraUtils.createHomingInstance(homingInstance, execution)
511 String getMsbHost(DelegateExecution execution) {
512 String msbHost = UrnPropertiesReader.getVariable("mso.msb.host", execution, "msb-iag.onap")
514 Integer msbPort = UrnPropertiesReader.getVariable("mso.msb.port", execution, "80").toInteger()
516 return UriBuilder.fromPath("").host(msbHost).port(msbPort).scheme("http").build().toString()
519 public String buildSelectNSTRequest(String requestId,String messageType, Map<String, Object> profileInfo) {
520 def transactionId = requestId
521 logger.debug( "transactionId is: " + transactionId)
522 String correlator = requestId
523 String callbackUrl = UrnPropertiesReader.getVariable("mso.adapters.oof.callback.endpoint") + "/" + messageType + "/" + correlator
524 ObjectMapper objectMapper = new ObjectMapper()
525 String json = objectMapper.writeValueAsString(profileInfo)
526 StringBuilder response = new StringBuilder()
529 " \"requestInfo\": {\n" +
530 " \"transactionId\": \"${transactionId}\",\n" +
531 " \"requestId\": \"${requestId}\",\n" +
532 " \"sourceId\": \"so\",\n" +
533 " \"timeout\": 600,\n" +
534 " \"callbackUrl\": \"${callbackUrl}\"\n" +
536 response.append(" \"serviceProfile\": {\n" +
537 " \"serviceProfileParameters\": ")
538 response.append(json);
539 response.append("\n }\n")
540 response.append("\n}\n")
541 return response.toString()
544 public String buildSelectNSIRequest(String requestId, String nstInfo,String messageType, Map<String, Object> profileInfo){
546 def transactionId = requestId
547 logger.debug( "transactionId is: " + transactionId)
548 String correlator = requestId
549 String callbackUrl = UrnPropertiesReader.getVariable("mso.adapters.oof.callback.endpoint") + "/" + messageType + "/" + correlator
550 ObjectMapper objectMapper = new ObjectMapper();
551 String json = objectMapper.writeValueAsString(profileInfo);
552 StringBuilder response = new StringBuilder();
555 " \"requestInfo\": {\n" +
556 " \"transactionId\": \"${transactionId}\",\n" +
557 " \"requestId\": \"${requestId}\",\n" +
558 " \"sourceId\": \"so\",\n" +
559 " \"timeout\": 600,\n" +
560 " \"callbackUrl\": \"${callbackUrl}\"\n" +
562 " \"serviceInfo\": {\n" +
563 " \"serviceInstanceId\": \"\",\n" +
564 " \"serviceName\": \"\"\n" +
566 " \"NSTInfoList\": [\n")
567 response.append(nstInfo);
568 response.append("\n ],\n")
569 response.append("\n \"serviceProfile\": \n")
570 response.append(json);
571 response.append("\n }\n")
572 return response.toString()