1 package org.openecomp.mso.bpmn.common.scripts
3 import org.camunda.bpm.engine.delegate.DelegateExecution
4 import org.openecomp.mso.bpmn.common.scripts.AbstractServiceTaskProcessor
5 import org.openecomp.mso.bpmn.common.scripts.ExceptionUtil
6 import org.openecomp.mso.bpmn.common.scripts.MsoUtils
7 import org.openecomp.mso.bpmn.core.domain.HomingSolution
8 import org.openecomp.mso.bpmn.core.domain.ModelInfo
9 import org.openecomp.mso.bpmn.core.domain.Resource
10 import org.openecomp.mso.bpmn.core.domain.AllottedResource
11 import org.openecomp.mso.bpmn.core.domain.ServiceDecomposition
12 import org.openecomp.mso.bpmn.core.domain.ServiceInstance
13 import org.openecomp.mso.bpmn.core.domain.Subscriber
14 import org.openecomp.mso.bpmn.core.domain.VnfResource
15 import org.openecomp.mso.bpmn.core.json.JsonUtils
17 import java.lang.reflect.Array
19 import static org.openecomp.mso.bpmn.common.scripts.GenericUtils.*
23 ExceptionUtil exceptionUtil = new ExceptionUtil()
24 JsonUtils jsonUtil = new JsonUtils()
26 private AbstractServiceTaskProcessor utils
28 public MsoUtils msoUtils = new MsoUtils()
30 public OofUtils(AbstractServiceTaskProcessor taskProcessor) {
31 this.utils = taskProcessor
35 * This method builds the service-agnostic
36 * OOF json request to get a homing solution
37 * and license solution
41 * @param decomposition - ServiceDecomposition object
42 * @param customerLocation -
43 * @param existingCandidates -
44 * @param excludedCandidates -
45 * @param requiredCandidates -
47 * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
49 String buildRequest(DelegateExecution execution,
51 ServiceDecomposition decomposition,
52 Subscriber subscriber = null,
54 ArrayList existingCandidates = null,
55 ArrayList excludedCandidates = null,
56 ArrayList requiredCandidates = null) {
57 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
58 utils.log("DEBUG", "Started Building OOF Request", isDebugEnabled)
59 def callbackUrl = utils.createWorkflowMessageAdapterCallbackURL(execution, "oofResponse", requestId)
60 def transactionId = requestId
61 //ServiceInstance Info
62 ServiceInstance serviceInstance = decomposition.getServiceInstance()
63 def serviceInstanceId = ""
64 def serviceInstanceName = ""
66 serviceInstanceId = execution.getVariable("serviceInstanceId")
67 serviceInstanceName = execution.getVariable("serviceInstanceName")
69 if (serviceInstanceId == null || serviceInstanceId == "null") {
70 utils.log("DEBUG", "Unable to obtain Service Instance Id", isDebugEnabled)
71 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
72 "obtain Service Instance Id, execution.getVariable(\"serviceInstanceName\") is null")
74 if (serviceInstanceName == null || serviceInstanceName == "null") {
75 utils.log("DEBUG", "Unable to obtain Service Instance Name", isDebugEnabled)
76 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
77 "obtain Service Instance Name, execution.getVariable(\"serviceInstanceName\") is null")
80 ModelInfo model = decomposition.getModelInfo()
81 String modelType = model.getModelType()
82 String modelInvariantId = model.getModelInvariantUuid()
83 String modelVersionId = model.getModelUuid()
84 String modelName = model.getModelName()
85 String modelVersion = model.getModelVersion()
87 String subscriberId = ""
88 String subscriberName = ""
89 String commonSiteId = ""
90 if (subscriber != null){
91 subscriberId = subscriber.getGlobalId()
92 subscriberName = subscriber.getName()
93 commonSiteId = subscriber.getCommonSiteId()
96 //Determine RequestType
97 //TODO Figure out better way to determine this
98 String requestType = "create"
99 List<Resource> resources = decomposition.getServiceResources()
100 for(Resource r:resources){
101 HomingSolution currentSolution = (HomingSolution) r.getCurrentHomingSolution()
102 if(currentSolution != null){
103 requestType = "speed changed"
108 String placementDemands = ""
109 StringBuilder sb = new StringBuilder()
110 List<AllottedResource> allottedResourceList = decomposition.getServiceAllottedResources()
111 List<VnfResource> vnfResourceList = decomposition.getServiceVnfs()
113 if (allottedResourceList.isEmpty() || allottedResourceList == null) {
114 utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
116 allottedResourceList = decomposition.getServiceVnfs()
119 if (allottedResourceList.isEmpty() || allottedResourceList == null) {
120 utils.log("DEBUG", "Resources List is Empty", isDebugEnabled)
122 for (AllottedResource resource : allottedResourceList) {
123 utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
125 def serviceResourceId = resource.getResourceId()
126 def resourceModuleName = resource.getNfFunction()
127 utils.log("DEBUG", "resourceModuleName: " + resourceModuleName,
129 def resourceModelInvariantId = "no-resourceModelInvariantId"
130 def resourceModelVersionId = "no-resourceModelVersionId"
132 List modelIdLst = execution.getVariable("homingModelIds")
133 utils.log("DEBUG", "Incoming modelIdLst is: " + modelIdLst.toString(), isDebugEnabled)
134 for (Map modelId : modelIdLst )
135 if (resourceModuleName == modelId.resourceModuleName) {
136 resourceModelInvariantId = modelId.resourceModelInvariantId
137 resourceModelVersionId = modelId.resourceModelVersionId
140 def resourceModelName = "" //Optional
141 def resourceModelVersion = "" //Optional
142 def resourceModelType = "" //Optional
143 def tenantId = "" //Optional
144 def requiredCandidatesJson = ""
146 requiredCandidatesJson = createCandidateJson(
153 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
154 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
155 " \"tenantId\": \"${tenantId}\",\n" +
156 " \"resourceModelInfo\": {\n" +
157 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
158 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
159 " \"modelName\": \"${resourceModelName}\",\n" +
160 " \"modelType\": \"${resourceModelType}\",\n" +
161 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
162 " \"modelCustomizationName\": \"\"\n" +
163 " }" + requiredCandidatesJson + "\n" +
166 placementDemands = sb.append(demand)
168 for (VnfResource vnfResource : vnfResourceList) {
169 utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
171 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
172 def serviceResourceId = vnfResource.getResourceId()
173 def resourceModuleName = vnfResource.getNfFunction()
174 utils.log("DEBUG", "resourceModuleName: " + resourceModuleName,
176 def resourceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
177 def resourceModelName = vnfResourceModelInfo.getModelName()
178 def resourceModelVersion = vnfResourceModelInfo.getModelVersion()
179 def resourceModelVersionId = vnfResourceModelInfo.getModelUuid()
180 def resourceModelType = vnfResourceModelInfo.getModelType()
181 def tenantId = "" //Optional
182 def requiredCandidatesJson = ""
185 String placementDemand =
187 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
188 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
189 " \"tenantId\": \"${tenantId}\",\n" +
190 " \"resourceModelInfo\": {\n" +
191 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
192 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
193 " \"modelName\": \"${resourceModelName}\",\n" +
194 " \"modelType\": \"${resourceModelType}\",\n" +
195 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
196 " \"modelCustomizationName\": \"\"\n" +
197 " }" + requiredCandidatesJson + "\n" +
200 placementDemands = sb.append(placementDemand)
202 placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
205 /* Commenting Out Licensing as OOF doesn't support for Beijing
206 String licenseDemands = ""
207 sb = new StringBuilder()
208 if (vnfResourceList.isEmpty() || vnfResourceList == null) {
209 utils.log("DEBUG", "Vnf Resources List is Empty", isDebugEnabled)
211 for (VnfResource vnfResource : vnfResourceList) {
212 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
213 def resourceInstanceType = vnfResource.getResourceType()
214 def serviceResourceId = vnfResource.getResourceId()
215 def resourceModuleName = vnfResource.getResourceType()
216 def resouceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
217 def resouceModelName = vnfResourceModelInfo.getModelName()
218 def resouceModelVersion = vnfResourceModelInfo.getModelVersion()
219 def resouceModelVersionId = vnfResourceModelInfo.getModelUuid()
220 def resouceModelType = vnfResourceModelInfo.getModelType()
222 // TODO Add Existing Licenses to demand
223 //"existingLicenses": {
224 //"entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
225 // "43257b49-9602-4fe5-9337-094e52bc9435"],
226 //"licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
227 // "43257b49-9602-4fe5-9337-094e52bc9435"]
230 String licenseDemand =
232 "\"resourceModuleName\": \"${resourceModuleName}\",\n" +
233 "\"serviceResourceId\": \"${serviceResourceId}\",\n" +
234 "\"resourceInstanceType\": \"${resourceInstanceType}\",\n" +
235 "\"resourceModelInfo\": {\n" +
236 " \"modelInvariantId\": \"${resouceModelInvariantId}\",\n" +
237 " \"modelVersionId\": \"${resouceModelVersionId}\",\n" +
238 " \"modelName\": \"${resouceModelName}\",\n" +
239 " \"modelType\": \"${resouceModelType}\",\n" +
240 " \"modelVersion\": \"${resouceModelVersion}\",\n" +
241 " \"modelCustomizationName\": \"\"\n" +
245 licenseDemands = sb.append(licenseDemand)
247 licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
252 " \"requestInfo\": {\n" +
253 " \"transactionId\": \"${transactionId}\",\n" +
254 " \"requestId\": \"${requestId}\",\n" +
255 " \"callbackUrl\": \"${callbackUrl}\",\n" +
256 " \"sourceId\": \"so\",\n" +
257 " \"requestType\": \"${requestType}\"," +
258 " \"numSolutions\": 1,\n" +
259 " \"optimizers\": [\"placement\"],\n" +
260 " \"timeout\": 600\n" +
262 " \"placementInfo\": {\n" +
263 " \"requestParameters\": {\n" +
264 " \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
265 " \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
266 " \"customerName\": \"${customerLocation.customerName}\"\n" +
268 " \"subscriberInfo\": { \n" +
269 " \"globalSubscriberId\": \"${subscriberId}\",\n" +
270 " \"subscriberName\": \"${subscriberName}\",\n" +
271 " \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
273 " \"placementDemands\": [\n" +
274 " ${placementDemands}\n" +
277 " \"serviceInfo\": {\n" +
278 " \"serviceInstanceId\": \"${serviceInstanceId}\",\n" +
279 " \"serviceName\": \"${serviceInstanceName}\",\n" +
280 " \"modelInfo\": {\n" +
281 " \"modelType\": \"${modelType}\",\n" +
282 " \"modelInvariantId\": \"${modelInvariantId}\",\n" +
283 " \"modelVersionId\": \"${modelVersionId}\",\n" +
284 " \"modelName\": \"${modelName}\",\n" +
285 " \"modelVersion\": \"${modelVersion}\",\n" +
286 " \"modelCustomizationName\": \"\"\n" +
292 utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
297 * This method validates the callback response
298 * from OOF. If the response contains an
299 * exception the method will build and throw
300 * a workflow exception.
303 * @param response - the async callback response from oof
305 Void validateCallbackResponse(DelegateExecution execution, String response) {
306 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
307 String placements = ""
308 if (isBlank(response)) {
309 exceptionUtil.buildAndThrowWorkflowException(execution, 5000, "OOF Async Callback Response is Empty")
311 if (JsonUtils.jsonElementExist(response, "solutions.placementSolutions")) {
312 placements = jsonUtil.getJsonValue(response, "solutions.placementSolutions")
313 if (isBlank(placements) || placements.equalsIgnoreCase("[]")) {
314 String statusMessage = jsonUtil.getJsonValue(response, "statusMessage")
315 if (isBlank(statusMessage)) {
316 utils.log("DEBUG", "Error Occurred in Homing: OOF Async Callback Response does " +
317 "not contain placement solution.", isDebugEnabled)
318 exceptionUtil.buildAndThrowWorkflowException(execution, 400,
319 "OOF Async Callback Response does not contain placement solution.")
321 utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
322 exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
327 } else if (response.contains("error") || response.contains("Error") ) {
328 String errorMessage = ""
329 if (response.contains("policyException")) {
330 String text = jsonUtil.getJsonValue(response, "requestError.policyException.text")
331 errorMessage = "OOF Async Callback Response contains a Request Error Policy Exception: " + text
332 } else if (response.contains("Unable to find any candidate for demand")) {
333 errorMessage = "OOF Async Callback Response contains error: Unable to find any candidate for " +
334 "demand *** Response: " + response.toString()
335 } else if (response.contains("serviceException")) {
336 String text = jsonUtil.getJsonValue(response, "requestError.serviceException.text")
337 errorMessage = "OOF Async Callback Response contains a Request Error Service Exception: " + text
339 errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
341 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
342 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
345 utils.log("DEBUG", "Error Occurred in Homing: Received an Unknown Async Callback Response from OOF.", isDebugEnabled)
346 exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Received an Unknown Async Callback Response from OOF.")
353 * This method creates candidates json for placement Demands.
356 * @param existingCandidates -
357 * @param excludedCandidates -
358 * @param requiredCandidates -
360 * @return candidatesJson - a JSON string with candidates
362 String createCandidateJson(ArrayList existingCandidates = null,
363 ArrayList excludedCandidates = null,
364 ArrayList requiredCandidates = null) {
365 def candidatesJson = ""
367 if (existingCandidates != null && existingCandidates != {}) {
368 sb = new StringBuilder()
370 " \"existingCandidates\": [\n")
371 def existingCandidateJson = ""
372 existingCandidates.each { existingCandidate ->
373 type = existingCandidate.get('identifierType')
374 if (type == 'vimId') {
375 def cloudOwner = existingCandidate.get('cloudOwner')
376 def cloudRegionId = existingCandidate.get('identifiers')
377 existingCandidateJson = "{\n" +
378 " \"identifierType\": \"vimId\",\n" +
379 " \"cloudOwner\": \"${cloudOwner}\",\n" +
380 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
382 sb.append(existingCandidateJson)
384 if (type == 'serviceInstanceId') {
385 def serviceInstanceId = existingCandidate.get('identifiers')
386 existingCandidateJson += "{\n" +
387 " \"identifierType\": \"serviceInstanceId\",\n" +
388 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
390 sb.append(existingCandidateJson)
393 if (existingCandidateJson != "") {
394 sb.setLength(sb.length() - 1)
395 candidatesJson = sb.append(",\n],")
398 if (excludedCandidates != null && excludedCandidates != {}) {
399 sb = new StringBuilder()
401 " \"excludedCandidates\": [\n")
402 def excludedCandidateJson = ""
403 excludedCandidates.each { excludedCandidate ->
404 type = excludedCandidate.get('identifierType')
405 if (type == 'vimId') {
406 def cloudOwner = excludedCandidate.get('cloudOwner')
407 def cloudRegionId = excludedCandidate.get('identifiers')
408 excludedCandidateJson = "{\n" +
409 " \"identifierType\": \"vimId\",\n" +
410 " \"cloudOwner\": \"${cloudOwner}\",\n" +
411 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
413 sb.append(excludedCandidateJson)
415 if (type == 'serviceInstanceId') {
416 def serviceInstanceId = excludedCandidate.get('identifiers')
417 excludedCandidateJson += "{\n" +
418 " \"identifierType\": \"serviceInstanceId\",\n" +
419 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
421 sb.append(excludedCandidateJson)
424 if (excludedCandidateJson != "") {
425 sb.setLength(sb.length() - 1)
426 candidatesJson = sb.append(",\n],")
429 if (requiredCandidates != null && requiredCandidates != {}) {
430 sb = new StringBuilder()
432 " \"requiredCandidates\": [\n")
433 def requiredCandidatesJson = ""
434 requiredCandidates.each { requiredCandidate ->
435 type = requiredCandidate.get('identifierType')
436 if (type == 'vimId') {
437 def cloudOwner = requiredCandidate.get('cloudOwner')
438 def cloudRegionId = requiredCandidate.get('identifiers')
439 requiredCandidatesJson = "{\n" +
440 " \"identifierType\": \"vimId\",\n" +
441 " \"cloudOwner\": \"${cloudOwner}\",\n" +
442 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
444 sb.append(requiredCandidatesJson)
446 if (type == 'serviceInstanceId') {
447 def serviceInstanceId = requiredCandidate.get('identifiers')
448 requiredCandidatesJson += "{\n" +
449 " \"identifierType\": \"serviceInstanceId\",\n" +
450 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
452 sb.append(requiredCandidatesJson)
455 if (requiredCandidatesJson != "") {
456 sb.setLength(sb.length() - 1)
457 candidatesJson = sb.append(",\n],")
460 if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
461 return candidatesJson