Merge "fixed sonar issue in R_CloudConfigMigration.java"
[so.git] / bpmn / MSOCommonBPMN / src / main / groovy / org / onap / so / bpmn / common / scripts / OofUtils.groovy
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2018 Intel Corp. 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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20
21 package org.onap.so.bpmn.common.scripts
22
23 import org.camunda.bpm.engine.delegate.DelegateExecution
24 import org.onap.so.bpmn.common.scripts.AbstractServiceTaskProcessor
25 import org.onap.so.bpmn.common.scripts.ExceptionUtil
26 import org.onap.so.bpmn.common.scripts.MsoUtils
27 import org.onap.so.bpmn.core.domain.HomingSolution
28 import org.onap.so.bpmn.core.domain.ModelInfo
29 import org.onap.so.bpmn.core.domain.Resource
30 import org.onap.so.bpmn.core.domain.AllottedResource
31 import org.onap.so.bpmn.core.domain.ServiceDecomposition
32 import org.onap.so.bpmn.core.domain.ServiceInstance
33 import org.onap.so.bpmn.core.domain.Subscriber
34 import org.onap.so.bpmn.core.domain.VnfResource
35 import org.onap.so.bpmn.core.json.JsonUtils
36 import org.onap.so.logger.MsoLogger
37
38 import java.lang.reflect.Array
39
40 import static org.onap.so.bpmn.common.scripts.GenericUtils.*
41
42 class OofUtils {
43         private static final MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, OofUtils.class);
44     ExceptionUtil exceptionUtil = new ExceptionUtil()
45     JsonUtils jsonUtil = new JsonUtils()
46
47     private AbstractServiceTaskProcessor utils
48
49     public MsoUtils msoUtils = new MsoUtils()
50
51     public OofUtils(AbstractServiceTaskProcessor taskProcessor) {
52         this.utils = taskProcessor
53     }
54
55     /**
56      * This method builds the service-agnostic
57      * OOF json request to get a homing solution
58      * and license solution
59      *
60      * @param execution
61      * @param requestId
62      * @param decomposition - ServiceDecomposition object
63      * @param customerLocation -
64      * @param existingCandidates -
65      * @param excludedCandidates -
66      * @param requiredCandidates -
67      *
68      * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
69      */
70     String buildRequest(DelegateExecution execution,
71                         String requestId,
72                         ServiceDecomposition decomposition,
73                         Subscriber subscriber = null,
74                         Map customerLocation,
75                         ArrayList existingCandidates = null,
76                         ArrayList excludedCandidates = null,
77                         ArrayList requiredCandidates = null) {
78         def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
79         utils.log("DEBUG", "Started Building OOF Request", isDebugEnabled)
80         def callbackUrl = utils.createWorkflowMessageAdapterCallbackURL(execution, "oofResponse", requestId)
81         def transactionId = requestId
82         //ServiceInstance Info
83         ServiceInstance serviceInstance = decomposition.getServiceInstance()
84         def serviceInstanceId = ""
85         def serviceName = ""
86
87         serviceInstanceId = execution.getVariable("serviceInstanceId")
88         serviceName = execution.getVariable("subscriptionServiceType")
89
90         if (serviceInstanceId == null || serviceInstanceId == "null") {
91             utils.log("DEBUG", "Unable to obtain Service Instance Id", isDebugEnabled)
92             exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
93                     "obtain Service Instance Id, execution.getVariable(\"serviceInstanceId\") is null")
94         }
95         if (serviceName == null || serviceName == "null") {
96             utils.log("DEBUG", "Unable to obtain Service Name", isDebugEnabled)
97             exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
98                     "obtain Service Name, execution.getVariable(\"subscriptionServiceType\") is null")
99         }
100         //Model Info
101         ModelInfo model = decomposition.getModelInfo()
102         String modelType = model.getModelType()
103         String modelInvariantId = model.getModelInvariantUuid()
104         String modelVersionId = model.getModelUuid()
105         String modelName = model.getModelName()
106         String modelVersion = model.getModelVersion()
107         //Subscriber Info
108         String subscriberId = ""
109         String subscriberName = ""
110         String commonSiteId = ""
111         if (subscriber != null){
112             subscriberId = subscriber.getGlobalId()
113             subscriberName = subscriber.getName()
114             commonSiteId = subscriber.getCommonSiteId()
115         }
116
117         //Determine RequestType
118         //TODO Figure out better way to determine this
119         String requestType = "create"
120         List<Resource> resources = decomposition.getServiceResources()
121         for(Resource r:resources){
122             HomingSolution currentSolution = (HomingSolution) r.getCurrentHomingSolution()
123             if(currentSolution != null){
124                 requestType = "speed changed"
125             }
126         }
127
128         //Demands
129         String placementDemands = ""
130         StringBuilder sb = new StringBuilder()
131         List<AllottedResource> allottedResourceList = decomposition.getAllottedResources()
132         List<VnfResource> vnfResourceList = decomposition.getVnfResources()
133
134         if (allottedResourceList == null || allottedResourceList.isEmpty() ) {
135             utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
136                     isDebugEnabled)
137             allottedResourceList = decomposition.getVnfResources()
138         }
139
140         if (allottedResourceList == null || allottedResourceList.isEmpty()) {
141             utils.log("DEBUG", "Resources List is Empty", isDebugEnabled)
142         } else {
143             for (AllottedResource resource : allottedResourceList) {
144                 utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
145                         isDebugEnabled)
146                 def serviceResourceId = resource.getResourceId()
147                 def resourceModelInvariantId = resource.getModelInfo().getModelInvariantUuid()
148                 def resourceModelVersionId = resource.getModelInfo().getModelUuid()
149                 def resourceModelName = resource.getModelInfo().getModelName()
150                 def resourceModelVersion = resource.getModelInfo().getModelVersion()
151                 def resourceModelType = resource.getModelInfo().getModelType()
152                 def tenantId = execution.getVariable("tenantId")
153                 def requiredCandidatesJson = ""
154
155                 requiredCandidatesJson = createCandidateJson(
156                         existingCandidates,
157                         excludedCandidates,
158                         requiredCandidates)
159
160                 String demand =
161                         "      {\n" +
162                         "      \"resourceModuleName\": \"${resourceModelName}\",\n" +
163                         "      \"serviceResourceId\": \"${serviceResourceId}\",\n" +
164                         "      \"tenantId\": \"${tenantId}\",\n" +
165                         "      \"resourceModelInfo\": {\n" +
166                         "        \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
167                         "        \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
168                         "        \"modelName\": \"${resourceModelName}\",\n" +
169                         "        \"modelType\": \"${resourceModelType}\",\n" +
170                         "        \"modelVersion\": \"${resourceModelVersion}\",\n" +
171                         "        \"modelCustomizationName\": \"\"\n" +
172                         "        }" + requiredCandidatesJson + "\n" +
173                         "      },"
174
175                 placementDemands = sb.append(demand)
176             }
177             for (VnfResource vnfResource : vnfResourceList) {
178                 utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
179                         isDebugEnabled)
180                 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
181                 def serviceResourceId = vnfResource.getResourceId()
182                 def resourceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
183                 def resourceModelName = vnfResourceModelInfo.getModelName()
184                 def resourceModelVersion = vnfResourceModelInfo.getModelVersion()
185                 def resourceModelVersionId = vnfResourceModelInfo.getModelUuid()
186                 def resourceModelType = vnfResourceModelInfo.getModelType()
187                 def tenantId = execution.getVariable("tenantId")
188                 def requiredCandidatesJson = ""
189
190
191                 String placementDemand =
192                         "      {\n" +
193                         "      \"resourceModuleName\": \"${resourceModelName}\",\n" +
194                         "      \"serviceResourceId\": \"${serviceResourceId}\",\n" +
195                         "      \"tenantId\": \"${tenantId}\",\n" +
196                         "      \"resourceModelInfo\": {\n" +
197                         "        \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
198                         "        \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
199                         "        \"modelName\": \"${resourceModelName}\",\n" +
200                         "        \"modelType\": \"${resourceModelType}\",\n" +
201                         "        \"modelVersion\": \"${resourceModelVersion}\",\n" +
202                         "        \"modelCustomizationName\": \"\"\n" +
203                         "        }" + requiredCandidatesJson + "\n" +
204                         "      },"
205
206                 placementDemands = sb.append(placementDemand)
207             }
208             placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
209         }
210
211         /* Commenting Out Licensing as OOF doesn't support for Beijing
212         String licenseDemands = ""
213         sb = new StringBuilder()
214         if (vnfResourceList.isEmpty() || vnfResourceList == null) {
215             utils.log("DEBUG", "Vnf Resources List is Empty", isDebugEnabled)
216         } else {
217             for (VnfResource vnfResource : vnfResourceList) {
218                 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
219                 def resourceInstanceType = vnfResource.getResourceType()
220                 def serviceResourceId = vnfResource.getResourceId()
221                 def resourceModuleName = vnfResource.getResourceType()
222                 def resouceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
223                 def resouceModelName = vnfResourceModelInfo.getModelName()
224                 def resouceModelVersion = vnfResourceModelInfo.getModelVersion()
225                 def resouceModelVersionId = vnfResourceModelInfo.getModelUuid()
226                 def resouceModelType = vnfResourceModelInfo.getModelType()
227
228                 // TODO Add Existing Licenses to demand
229                 //"existingLicenses": {
230                 //"entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
231                 // "43257b49-9602-4fe5-9337-094e52bc9435"],
232                 //"licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
233                 // "43257b49-9602-4fe5-9337-094e52bc9435"]
234                 //}
235
236                     String licenseDemand =
237                         "{\n" +
238                         "\"resourceModuleName\": \"${resourceModuleName}\",\n" +
239                         "\"serviceResourceId\": \"${serviceResourceId}\",\n" +
240                         "\"resourceInstanceType\": \"${resourceInstanceType}\",\n" +
241                         "\"resourceModelInfo\": {\n" +
242                         "  \"modelInvariantId\": \"${resouceModelInvariantId}\",\n" +
243                         "  \"modelVersionId\": \"${resouceModelVersionId}\",\n" +
244                         "  \"modelName\": \"${resouceModelName}\",\n" +
245                         "  \"modelType\": \"${resouceModelType}\",\n" +
246                         "  \"modelVersion\": \"${resouceModelVersion}\",\n" +
247                         "  \"modelCustomizationName\": \"\"\n" +
248                         "  }\n"
249                         "},"
250
251                 licenseDemands = sb.append(licenseDemand)
252             }
253             licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
254         }*/
255
256         String request =
257                 "{\n" +
258                 "  \"requestInfo\": {\n" +
259                 "    \"transactionId\": \"${transactionId}\",\n" +
260                 "    \"requestId\": \"${requestId}\",\n" +
261                 "    \"callbackUrl\": \"${callbackUrl}\",\n" +
262                 "    \"sourceId\": \"so\",\n" +
263                 "    \"requestType\": \"${requestType}\"," +
264                 "    \"numSolutions\": 1,\n" +
265                 "    \"optimizers\": [\"placement\"],\n" +
266                 "    \"timeout\": 600\n" +
267                 "    },\n" +
268                 "  \"placementInfo\": {\n" +
269                 "    \"requestParameters\": {\n" +
270                 "      \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
271                 "      \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
272                 "      \"customerName\": \"${customerLocation.customerName}\"\n" +
273                 "    }," +
274                 "    \"subscriberInfo\": { \n" +
275                 "      \"globalSubscriberId\": \"${subscriberId}\",\n" +
276                 "      \"subscriberName\": \"${subscriberName}\",\n" +
277                 "      \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
278                 "    },\n" +
279                 "    \"placementDemands\": [\n" +
280                 "      ${placementDemands}\n" +
281                 "      ]\n" +
282                 "    },\n" +
283                 "  \"serviceInfo\": {\n" +
284                 "    \"serviceInstanceId\": \"${serviceInstanceId}\",\n" +
285                 "    \"serviceName\": \"${serviceName}\",\n" +
286                 "    \"modelInfo\": {\n" +
287                 "      \"modelType\": \"${modelType}\",\n" +
288                 "      \"modelInvariantId\": \"${modelInvariantId}\",\n" +
289                 "      \"modelVersionId\": \"${modelVersionId}\",\n" +
290                 "      \"modelName\": \"${modelName}\",\n" +
291                 "      \"modelVersion\": \"${modelVersion}\",\n" +
292                 "      \"modelCustomizationName\": \"\"\n" +
293                 "    }\n" +
294                 "  }\n" +
295                 "}"
296
297
298         utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
299         return request
300     }
301
302     /**
303      * This method validates the callback response
304      * from OOF. If the response contains an
305      * exception the method will build and throw
306      * a workflow exception.
307      *
308      * @param execution
309      * @param response - the async callback response from oof
310      */
311     Void validateCallbackResponse(DelegateExecution execution, String response) {
312         def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
313         String placements = ""
314         if (isBlank(response)) {
315             exceptionUtil.buildAndThrowWorkflowException(execution, 5000, "OOF Async Callback Response is Empty")
316         } else {
317             if (JsonUtils.jsonElementExist(response, "solutions.placementSolutions")) {
318                 placements = jsonUtil.getJsonValue(response, "solutions.placementSolutions")
319                 if (isBlank(placements) || placements.equalsIgnoreCase("[]")) {
320                     String statusMessage = jsonUtil.getJsonValue(response, "statusMessage")
321                     if (isBlank(statusMessage)) {
322                         utils.log("DEBUG", "Error Occurred in Homing: OOF Async Callback Response does " +
323                                 "not contain placement solution.", isDebugEnabled)
324                         exceptionUtil.buildAndThrowWorkflowException(execution, 400,
325                                 "OOF Async Callback Response does not contain placement solution.")
326                     } else {
327                         utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
328                         exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
329                     }
330                 } else {
331                     return
332                 }
333             } else if (response.contains("error") || response.contains("Error") ) {
334                 String errorMessage = ""
335                 if (response.contains("policyException")) {
336                     String text = jsonUtil.getJsonValue(response, "requestError.policyException.text")
337                     errorMessage = "OOF Async Callback Response contains a Request Error Policy Exception: " + text
338                 } else if (response.contains("Unable to find any candidate for demand")) {
339                     errorMessage = "OOF Async Callback Response contains error: Unable to find any candidate for " +
340                             "demand *** Response: " + response.toString()
341                 } else if (response.contains("serviceException")) {
342                     String text = jsonUtil.getJsonValue(response, "requestError.serviceException.text")
343                     errorMessage = "OOF Async Callback Response contains a Request Error Service Exception: " + text
344                 } else {
345                     errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
346                 }
347                 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
348                 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
349
350             } else {
351                 utils.log("DEBUG", "Error Occurred in Homing: Received an Unknown Async Callback Response from OOF.", isDebugEnabled)
352                 exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Received an Unknown Async Callback Response from OOF.")
353             }
354         }
355
356     }
357
358     /**
359      * This method creates candidates json for placement Demands.
360      *
361      * @param execution
362      * @param existingCandidates -
363      * @param excludedCandidates -
364      * @param requiredCandidates -
365      *
366      * @return candidatesJson - a JSON string with candidates
367      */
368     String createCandidateJson(ArrayList existingCandidates = null,
369                                ArrayList excludedCandidates = null,
370                                ArrayList requiredCandidates = null) {
371         def candidatesJson = ""
372         def type = ""
373         if (existingCandidates != null && existingCandidates != {}) {
374             sb = new StringBuilder()
375             sb.append(",\n" +
376                     "  \"existingCandidates\": [\n")
377             def existingCandidateJson = ""
378             existingCandidates.each { existingCandidate ->
379                 type = existingCandidate.get('identifierType')
380                 if (type == 'vimId') {
381                     def cloudOwner = existingCandidate.get('cloudOwner')
382                     def cloudRegionId = existingCandidate.get('identifiers')
383                     existingCandidateJson = "{\n" +
384                             "    \"identifierType\": \"vimId\",\n" +
385                             "    \"cloudOwner\": \"${cloudOwner}\",\n" +
386                             "    \"identifiers\": [\"${cloudRegionId}\"]\n" +
387                             "    },"
388                     sb.append(existingCandidateJson)
389                 }
390                 if (type == 'serviceInstanceId') {
391                     def serviceInstanceId = existingCandidate.get('identifiers')
392                     existingCandidateJson += "{\n" +
393                             "    \"identifierType\": \"serviceInstanceId\",\n" +
394                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
395                             "    },"
396                     sb.append(existingCandidateJson)
397                 }
398             }
399             if (existingCandidateJson != "") {
400                 sb.setLength(sb.length() - 1)
401                 candidatesJson = sb.append(",\n],")
402             }
403         }
404         if (excludedCandidates != null && excludedCandidates != {}) {
405             sb = new StringBuilder()
406             sb.append(",\n" +
407                     "  \"excludedCandidates\": [\n")
408             def excludedCandidateJson = ""
409             excludedCandidates.each { excludedCandidate ->
410                 type = excludedCandidate.get('identifierType')
411                 if (type == 'vimId') {
412                     def cloudOwner = excludedCandidate.get('cloudOwner')
413                     def cloudRegionId = excludedCandidate.get('identifiers')
414                     excludedCandidateJson = "{\n" +
415                             "    \"identifierType\": \"vimId\",\n" +
416                             "    \"cloudOwner\": \"${cloudOwner}\",\n" +
417                             "    \"identifiers\": [\"${cloudRegionId}\"]\n" +
418                             "    },"
419                     sb.append(excludedCandidateJson)
420                 }
421                 if (type == 'serviceInstanceId') {
422                     def serviceInstanceId = excludedCandidate.get('identifiers')
423                     excludedCandidateJson += "{\n" +
424                             "    \"identifierType\": \"serviceInstanceId\",\n" +
425                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
426                             "    },"
427                     sb.append(excludedCandidateJson)
428                 }
429             }
430             if (excludedCandidateJson != "") {
431                 sb.setLength(sb.length() - 1)
432                 candidatesJson = sb.append(",\n],")
433             }
434         }
435         if (requiredCandidates != null && requiredCandidates != {}) {
436             sb = new StringBuilder()
437             sb.append(",\n" +
438                     "  \"requiredCandidates\": [\n")
439             def requiredCandidatesJson = ""
440             requiredCandidates.each { requiredCandidate ->
441                 type = requiredCandidate.get('identifierType')
442                 if (type == 'vimId') {
443                     def cloudOwner = requiredCandidate.get('cloudOwner')
444                     def cloudRegionId = requiredCandidate.get('identifiers')
445                     requiredCandidatesJson = "{\n" +
446                             "    \"identifierType\": \"vimId\",\n" +
447                             "    \"cloudOwner\": \"${cloudOwner}\",\n" +
448                             "    \"identifiers\": [\"${cloudRegionId}\"]\n" +
449                             "    },"
450                     sb.append(requiredCandidatesJson)
451                 }
452                 if (type == 'serviceInstanceId') {
453                     def serviceInstanceId = requiredCandidate.get('identifiers')
454                     requiredCandidatesJson += "{\n" +
455                             "    \"identifierType\": \"serviceInstanceId\",\n" +
456                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
457                             "    },"
458                     sb.append(requiredCandidatesJson)
459                 }
460             }
461             if (requiredCandidatesJson != "") {
462                 sb.setLength(sb.length() - 1)
463                 candidatesJson = sb.append(",\n],")
464             }
465         }
466         if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
467         return candidatesJson
468     }
469 }