2 * ============LICENSE_START=======================================================
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
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.common.scripts
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
38 import java.lang.reflect.Array
40 import static org.onap.so.bpmn.common.scripts.GenericUtils.*
43 private static final MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, OofUtils.class);
44 ExceptionUtil exceptionUtil = new ExceptionUtil()
45 JsonUtils jsonUtil = new JsonUtils()
47 private AbstractServiceTaskProcessor utils
49 public MsoUtils msoUtils = new MsoUtils()
51 public OofUtils(AbstractServiceTaskProcessor taskProcessor) {
52 this.utils = taskProcessor
56 * This method builds the service-agnostic
57 * OOF json request to get a homing solution
58 * and license solution
62 * @param decomposition - ServiceDecomposition object
63 * @param customerLocation -
64 * @param existingCandidates -
65 * @param excludedCandidates -
66 * @param requiredCandidates -
68 * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
70 String buildRequest(DelegateExecution execution,
72 ServiceDecomposition decomposition,
73 Subscriber subscriber = null,
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 = ""
87 serviceInstanceId = execution.getVariable("serviceInstanceId")
88 serviceName = execution.getVariable("subscriptionServiceType")
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")
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")
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()
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()
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"
129 String placementDemands = ""
130 StringBuilder sb = new StringBuilder()
131 List<AllottedResource> allottedResourceList = decomposition.getAllottedResources()
132 List<VnfResource> vnfResourceList = decomposition.getVnfResources()
134 if (allottedResourceList == null || allottedResourceList.isEmpty() ) {
135 utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
137 allottedResourceList = decomposition.getVnfResources()
140 if (allottedResourceList == null || allottedResourceList.isEmpty()) {
141 utils.log("DEBUG", "Resources List is Empty", isDebugEnabled)
143 for (AllottedResource resource : allottedResourceList) {
144 utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
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 = ""
155 requiredCandidatesJson = createCandidateJson(
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" +
175 placementDemands = sb.append(demand)
177 for (VnfResource vnfResource : vnfResourceList) {
178 utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
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 = ""
191 String placementDemand =
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" +
206 placementDemands = sb.append(placementDemand)
208 placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
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)
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()
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"]
236 String licenseDemand =
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" +
251 licenseDemands = sb.append(licenseDemand)
253 licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
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" +
268 " \"placementInfo\": {\n" +
269 " \"requestParameters\": {\n" +
270 " \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
271 " \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
272 " \"customerName\": \"${customerLocation.customerName}\"\n" +
274 " \"subscriberInfo\": { \n" +
275 " \"globalSubscriberId\": \"${subscriberId}\",\n" +
276 " \"subscriberName\": \"${subscriberName}\",\n" +
277 " \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
279 " \"placementDemands\": [\n" +
280 " ${placementDemands}\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" +
298 utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
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.
309 * @param response - the async callback response from oof
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")
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.")
327 utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
328 exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
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
345 errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
347 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
348 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
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.")
359 * This method creates candidates json for placement Demands.
362 * @param existingCandidates -
363 * @param excludedCandidates -
364 * @param requiredCandidates -
366 * @return candidatesJson - a JSON string with candidates
368 String createCandidateJson(ArrayList existingCandidates = null,
369 ArrayList excludedCandidates = null,
370 ArrayList requiredCandidates = null) {
371 def candidatesJson = ""
373 if (existingCandidates != null && existingCandidates != {}) {
374 sb = new StringBuilder()
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" +
388 sb.append(existingCandidateJson)
390 if (type == 'serviceInstanceId') {
391 def serviceInstanceId = existingCandidate.get('identifiers')
392 existingCandidateJson += "{\n" +
393 " \"identifierType\": \"serviceInstanceId\",\n" +
394 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
396 sb.append(existingCandidateJson)
399 if (existingCandidateJson != "") {
400 sb.setLength(sb.length() - 1)
401 candidatesJson = sb.append(",\n],")
404 if (excludedCandidates != null && excludedCandidates != {}) {
405 sb = new StringBuilder()
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" +
419 sb.append(excludedCandidateJson)
421 if (type == 'serviceInstanceId') {
422 def serviceInstanceId = excludedCandidate.get('identifiers')
423 excludedCandidateJson += "{\n" +
424 " \"identifierType\": \"serviceInstanceId\",\n" +
425 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
427 sb.append(excludedCandidateJson)
430 if (excludedCandidateJson != "") {
431 sb.setLength(sb.length() - 1)
432 candidatesJson = sb.append(",\n],")
435 if (requiredCandidates != null && requiredCandidates != {}) {
436 sb = new StringBuilder()
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" +
450 sb.append(requiredCandidatesJson)
452 if (type == 'serviceInstanceId') {
453 def serviceInstanceId = requiredCandidate.get('identifiers')
454 requiredCandidatesJson += "{\n" +
455 " \"identifierType\": \"serviceInstanceId\",\n" +
456 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
458 sb.append(requiredCandidatesJson)
461 if (requiredCandidatesJson != "") {
462 sb.setLength(sb.length() - 1)
463 candidatesJson = sb.append(",\n],")
466 if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
467 return candidatesJson