Merge "Reorder modifiers"
[so.git] / bpmn / MSOCommonBPMN / src / main / groovy / org / openecomp / mso / bpmn / common / scripts / OofUtils.groovy
1 package org.openecomp.mso.bpmn.common.scripts
2
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
16
17 import java.lang.reflect.Array
18
19 import static org.openecomp.mso.bpmn.common.scripts.GenericUtils.*
20
21 class OofUtils {
22
23     ExceptionUtil exceptionUtil = new ExceptionUtil()
24     JsonUtils jsonUtil = new JsonUtils()
25
26     private AbstractServiceTaskProcessor utils
27
28     public MsoUtils msoUtils = new MsoUtils()
29
30     public OofUtils(AbstractServiceTaskProcessor taskProcessor) {
31         this.utils = taskProcessor
32     }
33
34     /**
35      * This method builds the service-agnostic
36      * OOF json request to get a homing solution
37      * and license solution
38      *
39      * @param execution
40      * @param requestId
41      * @param decomposition - ServiceDecomposition object
42      * @param customerLocation -
43      * @param existingCandidates -
44      * @param excludedCandidates -
45      * @param requiredCandidates -
46      *
47      * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
48      */
49     String buildRequest(DelegateExecution execution,
50                         String requestId,
51                         ServiceDecomposition decomposition,
52                         Subscriber subscriber = null,
53                         Map customerLocation,
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 = ""
65
66         serviceInstanceId = execution.getVariable("serviceInstanceId")
67         serviceInstanceName = execution.getVariable("serviceInstanceName")
68
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")
73         }
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")
78         }
79         //Model Info
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()
86         //Subscriber Info
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()
94         }
95
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"
104             }
105         }
106
107         //Demands
108         String placementDemands = ""
109         StringBuilder sb = new StringBuilder()
110         List<AllottedResource> allottedResourceList = decomposition.getServiceAllottedResources()
111         List<VnfResource> vnfResourceList = decomposition.getServiceVnfs()
112
113         if (allottedResourceList.isEmpty() || allottedResourceList == null) {
114             utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
115                     isDebugEnabled)
116             allottedResourceList = decomposition.getServiceVnfs()
117         }
118
119         if (allottedResourceList.isEmpty() || allottedResourceList == null) {
120             utils.log("DEBUG", "Resources List is Empty", isDebugEnabled)
121         } else {
122             for (AllottedResource resource : allottedResourceList) {
123                 utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
124                         isDebugEnabled)
125                 def serviceResourceId = resource.getResourceId()
126                 def resourceModuleName = resource.getNfFunction()
127                 utils.log("DEBUG", "resourceModuleName: " + resourceModuleName,
128                         isDebugEnabled)
129                 def resourceModelInvariantId = "no-resourceModelInvariantId"
130                 def resourceModelVersionId = "no-resourceModelVersionId"
131
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
138                     }
139
140                 def resourceModelName = "" //Optional
141                 def resourceModelVersion = "" //Optional
142                 def resourceModelType = "" //Optional
143                 def tenantId = "" //Optional
144                 def requiredCandidatesJson = ""
145
146                 requiredCandidatesJson = createCandidateJson(
147                         existingCandidates,
148                         excludedCandidates,
149                         requiredCandidates)
150
151                 String demand =
152                         "      {\n" +
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" +
164                         "      },"
165
166                 placementDemands = sb.append(demand)
167             }
168             for (VnfResource vnfResource : vnfResourceList) {
169                 utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
170                         isDebugEnabled)
171                 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
172                 def serviceResourceId = vnfResource.getResourceId()
173                 def resourceModuleName = vnfResource.getNfFunction()
174                 utils.log("DEBUG", "resourceModuleName: " + resourceModuleName,
175                         isDebugEnabled)
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 = ""
183
184
185                 String placementDemand =
186                         "      {\n" +
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" +
198                         "      },"
199
200                 placementDemands = sb.append(placementDemand)
201             }
202             placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
203         }
204
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)
210         } else {
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()
221
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"]
228                 //}
229
230                     String licenseDemand =
231                         "{\n" +
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" +
242                         "  }\n"
243                         "},"
244
245                 licenseDemands = sb.append(licenseDemand)
246             }
247             licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
248         }*/
249
250         String request =
251                 "{\n" +
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" +
261                 "    },\n" +
262                 "  \"placementInfo\": {\n" +
263                 "    \"requestParameters\": {\n" +
264                 "      \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
265                 "      \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
266                 "      \"customerName\": \"${customerLocation.customerName}\"\n" +
267                 "    }," +
268                 "    \"subscriberInfo\": { \n" +
269                 "      \"globalSubscriberId\": \"${subscriberId}\",\n" +
270                 "      \"subscriberName\": \"${subscriberName}\",\n" +
271                 "      \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
272                 "    },\n" +
273                 "    \"placementDemands\": [\n" +
274                 "      ${placementDemands}\n" +
275                 "      ]\n" +
276                 "    },\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" +
287                 "    }\n" +
288                 "  }\n" +
289                 "}"
290
291
292         utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
293         return request
294     }
295
296     /**
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.
301      *
302      * @param execution
303      * @param response - the async callback response from oof
304      */
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")
310         } else {
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.")
320                     } else {
321                         utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
322                         exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
323                     }
324                 } else {
325                     return
326                 }
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
338                 } else {
339                     errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
340                 }
341                 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
342                 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
343
344             } else {
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.")
347             }
348         }
349
350     }
351
352     /**
353      * This method creates candidates json for placement Demands.
354      *
355      * @param execution
356      * @param existingCandidates -
357      * @param excludedCandidates -
358      * @param requiredCandidates -
359      *
360      * @return candidatesJson - a JSON string with candidates
361      */
362     String createCandidateJson(ArrayList existingCandidates = null,
363                                ArrayList excludedCandidates = null,
364                                ArrayList requiredCandidates = null) {
365         def candidatesJson = ""
366         def type = ""
367         if (existingCandidates != null && existingCandidates != {}) {
368             sb = new StringBuilder()
369             sb.append(",\n" +
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" +
381                             "    },"
382                     sb.append(existingCandidateJson)
383                 }
384                 if (type == 'serviceInstanceId') {
385                     def serviceInstanceId = existingCandidate.get('identifiers')
386                     existingCandidateJson += "{\n" +
387                             "    \"identifierType\": \"serviceInstanceId\",\n" +
388                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
389                             "    },"
390                     sb.append(existingCandidateJson)
391                 }
392             }
393             if (existingCandidateJson != "") {
394                 sb.setLength(sb.length() - 1)
395                 candidatesJson = sb.append(",\n],")
396             }
397         }
398         if (excludedCandidates != null && excludedCandidates != {}) {
399             sb = new StringBuilder()
400             sb.append(",\n" +
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" +
412                             "    },"
413                     sb.append(excludedCandidateJson)
414                 }
415                 if (type == 'serviceInstanceId') {
416                     def serviceInstanceId = excludedCandidate.get('identifiers')
417                     excludedCandidateJson += "{\n" +
418                             "    \"identifierType\": \"serviceInstanceId\",\n" +
419                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
420                             "    },"
421                     sb.append(excludedCandidateJson)
422                 }
423             }
424             if (excludedCandidateJson != "") {
425                 sb.setLength(sb.length() - 1)
426                 candidatesJson = sb.append(",\n],")
427             }
428         }
429         if (requiredCandidates != null && requiredCandidates != {}) {
430             sb = new StringBuilder()
431             sb.append(",\n" +
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" +
443                             "    },"
444                     sb.append(requiredCandidatesJson)
445                 }
446                 if (type == 'serviceInstanceId') {
447                     def serviceInstanceId = requiredCandidate.get('identifiers')
448                     requiredCandidatesJson += "{\n" +
449                             "    \"identifierType\": \"serviceInstanceId\",\n" +
450                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
451                             "    },"
452                     sb.append(requiredCandidatesJson)
453                 }
454             }
455             if (requiredCandidatesJson != "") {
456                 sb.setLength(sb.length() - 1)
457                 candidatesJson = sb.append(",\n],")
458             }
459         }
460         if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
461         return candidatesJson
462     }
463 }