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.core.UrnPropertiesReader
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.db.catalog.beans.CloudSite
37 import org.onap.so.db.catalog.beans.HomingInstance
38 import javax.ws.rs.core.UriBuilder
39 import org.onap.so.bpmn.common.util.OofInfraUtils
40 import static org.onap.so.bpmn.common.scripts.GenericUtils.*
43 ExceptionUtil exceptionUtil = new ExceptionUtil()
44 JsonUtils jsonUtil = new JsonUtils()
45 OofInfraUtils oofInfraUtils = new OofInfraUtils()
47 private AbstractServiceTaskProcessor utils
49 OofUtils(AbstractServiceTaskProcessor taskProcessor) {
50 this.utils = taskProcessor
54 * This method builds the service-agnostic
55 * OOF json request to get a homing solution
56 * and license solution
60 * @param decomposition - ServiceDecomposition object
61 * @param customerLocation -
62 * @param existingCandidates -
63 * @param excludedCandidates -
64 * @param requiredCandidates -
66 * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
68 String buildRequest(DelegateExecution execution,
70 ServiceDecomposition decomposition,
71 Subscriber subscriber = null,
73 ArrayList existingCandidates = null,
74 ArrayList excludedCandidates = null,
75 ArrayList requiredCandidates = null) {
76 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
77 utils.log("DEBUG", "Started Building OOF Request", isDebugEnabled)
78 String callbackEndpoint = UrnPropertiesReader.getVariable("mso.oof.callbackEndpoint", execution)
79 utils.log("DEBUG", "mso.oof.callbackEndpoint is: " + callbackEndpoint, isDebugEnabled)
81 def callbackUrl = utils.createHomingCallbackURL(callbackEndpoint, "oofResponse", requestId)
82 utils.log("DEBUG", "callbackUrl is: " + callbackUrl, isDebugEnabled)
85 def transactionId = requestId
86 utils.log("DEBUG", "transactionId is: " + transactionId, isDebugEnabled)
87 //ServiceInstance Info
88 ServiceInstance serviceInstance = decomposition.getServiceInstance()
89 def serviceInstanceId = ""
92 serviceInstanceId = execution.getVariable("serviceInstanceId")
93 utils.log("DEBUG", "serviceInstanceId is: " + serviceInstanceId, isDebugEnabled)
94 serviceName = execution.getVariable("subscriptionServiceType")
95 utils.log("DEBUG", "serviceName is: " + serviceName, isDebugEnabled)
97 if (serviceInstanceId == null || serviceInstanceId == "null") {
98 utils.log("DEBUG", "Unable to obtain Service Instance Id", isDebugEnabled)
99 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
100 "obtain Service Instance Id, execution.getVariable(\"serviceInstanceId\") is null")
102 if (serviceName == null || serviceName == "null") {
103 utils.log("DEBUG", "Unable to obtain Service Name", isDebugEnabled)
104 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
105 "obtain Service Name, execution.getVariable(\"subscriptionServiceType\") is null")
108 ModelInfo model = decomposition.getModelInfo()
109 utils.log("DEBUG", "ModelInfo: " + model.toString(), isDebugEnabled)
110 String modelType = model.getModelType()
111 String modelInvariantId = model.getModelInvariantUuid()
112 String modelVersionId = model.getModelUuid()
113 String modelName = model.getModelName()
114 String modelVersion = model.getModelVersion()
116 String subscriberId = ""
117 String subscriberName = ""
118 String commonSiteId = ""
119 if (subscriber != null) {
120 subscriberId = subscriber.getGlobalId()
121 subscriberName = subscriber.getName()
122 commonSiteId = subscriber.getCommonSiteId()
125 //Determine RequestType
126 //TODO Figure out better way to determine this
127 String requestType = "create"
128 List<Resource> resources = decomposition.getServiceResources()
129 for (Resource r : resources) {
130 HomingSolution currentSolution = (HomingSolution) r.getCurrentHomingSolution()
131 if (currentSolution != null) {
132 requestType = "speed changed"
137 String placementDemands = ""
138 StringBuilder sb = new StringBuilder()
139 List<AllottedResource> allottedResourceList = decomposition.getAllottedResources()
140 List<VnfResource> vnfResourceList = decomposition.getVnfResources()
142 if (allottedResourceList == null || allottedResourceList.isEmpty()) {
143 utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
146 for (AllottedResource resource : allottedResourceList) {
147 utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
149 def serviceResourceId = resource.getResourceId()
150 def toscaNodeType = resource.getToscaNodeType()
151 def resourceModuleName = toscaNodeType.substring(toscaNodeType.lastIndexOf(".") + 1)
152 def resourceModelInvariantId = resource.getModelInfo().getModelInvariantUuid()
153 def resourceModelVersionId = resource.getModelInfo().getModelUuid()
154 def resourceModelName = resource.getModelInfo().getModelName()
155 def resourceModelVersion = resource.getModelInfo().getModelVersion()
156 def resourceModelType = resource.getModelInfo().getModelType()
157 def tenantId = execution.getVariable("tenantId")
158 def requiredCandidatesJson = ""
160 requiredCandidatesJson = createCandidateJson(
167 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
168 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
169 " \"tenantId\": \"${tenantId}\",\n" +
170 " \"resourceModelInfo\": {\n" +
171 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
172 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
173 " \"modelName\": \"${resourceModelName}\",\n" +
174 " \"modelType\": \"${resourceModelType}\",\n" +
175 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
176 " \"modelCustomizationName\": \"\"\n" +
177 " }" + requiredCandidatesJson + "\n" +
180 placementDemands = sb.append(demand)
184 if (vnfResourceList == null || vnfResourceList.isEmpty()) {
185 utils.log("DEBUG", "VNF Resources List is empty",
189 for (VnfResource vnfResource : vnfResourceList) {
190 utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
192 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
193 def toscaNodeType = vnfResource.getToscaNodeType()
194 def resourceModuleName = toscaNodeType.substring(toscaNodeType.lastIndexOf(".") + 1)
195 def serviceResourceId = vnfResource.getResourceId()
196 def resourceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
197 def resourceModelName = vnfResourceModelInfo.getModelName()
198 def resourceModelVersion = vnfResourceModelInfo.getModelVersion()
199 def resourceModelVersionId = vnfResourceModelInfo.getModelUuid()
200 def resourceModelType = vnfResourceModelInfo.getModelType()
201 def tenantId = execution.getVariable("tenantId")
202 def requiredCandidatesJson = ""
205 String placementDemand =
207 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
208 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
209 " \"tenantId\": \"${tenantId}\",\n" +
210 " \"resourceModelInfo\": {\n" +
211 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
212 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
213 " \"modelName\": \"${resourceModelName}\",\n" +
214 " \"modelType\": \"${resourceModelType}\",\n" +
215 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
216 " \"modelCustomizationName\": \"\"\n" +
217 " }" + requiredCandidatesJson + "\n" +
220 placementDemands = sb.append(placementDemand)
222 placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
225 /* Commenting Out Licensing as OOF doesn't support for Beijing
226 String licenseDemands = ""
227 sb = new StringBuilder()
228 if (vnfResourceList.isEmpty() || vnfResourceList == null) {
229 utils.log("DEBUG", "Vnf Resources List is Empty", isDebugEnabled)
231 for (VnfResource vnfResource : vnfResourceList) {
232 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
233 def resourceInstanceType = vnfResource.getResourceType()
234 def serviceResourceId = vnfResource.getResourceId()
235 def resourceModuleName = vnfResource.getResourceType()
236 def resouceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
237 def resouceModelName = vnfResourceModelInfo.getModelName()
238 def resouceModelVersion = vnfResourceModelInfo.getModelVersion()
239 def resouceModelVersionId = vnfResourceModelInfo.getModelUuid()
240 def resouceModelType = vnfResourceModelInfo.getModelType()
242 // TODO Add Existing Licenses to demand
243 //"existingLicenses": {
244 //"entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
245 // "43257b49-9602-4fe5-9337-094e52bc9435"],
246 //"licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
247 // "43257b49-9602-4fe5-9337-094e52bc9435"]
250 String licenseDemand =
252 "\"resourceModuleName\": \"${resourceModuleName}\",\n" +
253 "\"serviceResourceId\": \"${serviceResourceId}\",\n" +
254 "\"resourceInstanceType\": \"${resourceInstanceType}\",\n" +
255 "\"resourceModelInfo\": {\n" +
256 " \"modelInvariantId\": \"${resouceModelInvariantId}\",\n" +
257 " \"modelVersionId\": \"${resouceModelVersionId}\",\n" +
258 " \"modelName\": \"${resouceModelName}\",\n" +
259 " \"modelType\": \"${resouceModelType}\",\n" +
260 " \"modelVersion\": \"${resouceModelVersion}\",\n" +
261 " \"modelCustomizationName\": \"\"\n" +
265 licenseDemands = sb.append(licenseDemand)
267 licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
272 " \"requestInfo\": {\n" +
273 " \"transactionId\": \"${transactionId}\",\n" +
274 " \"requestId\": \"${requestId}\",\n" +
275 " \"callbackUrl\": \"${callbackUrl}\",\n" +
276 " \"sourceId\": \"so\",\n" +
277 " \"requestType\": \"${requestType}\"," +
278 " \"numSolutions\": 1,\n" +
279 " \"optimizers\": [\"placement\"],\n" +
280 " \"timeout\": 600\n" +
282 " \"placementInfo\": {\n" +
283 " \"requestParameters\": {\n" +
284 " \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
285 " \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
286 " \"customerName\": \"${customerLocation.customerName}\"\n" +
288 " \"subscriberInfo\": { \n" +
289 " \"globalSubscriberId\": \"${subscriberId}\",\n" +
290 " \"subscriberName\": \"${subscriberName}\",\n" +
291 " \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
293 " \"placementDemands\": [\n" +
294 " ${placementDemands}\n" +
297 " \"serviceInfo\": {\n" +
298 " \"serviceInstanceId\": \"${serviceInstanceId}\",\n" +
299 " \"serviceName\": \"${serviceName}\",\n" +
300 " \"modelInfo\": {\n" +
301 " \"modelType\": \"${modelType}\",\n" +
302 " \"modelInvariantId\": \"${modelInvariantId}\",\n" +
303 " \"modelVersionId\": \"${modelVersionId}\",\n" +
304 " \"modelName\": \"${modelName}\",\n" +
305 " \"modelVersion\": \"${modelVersion}\",\n" +
306 " \"modelCustomizationName\": \"\"\n" +
312 utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
314 } catch (Exception ex) {
315 utils.log("DEBUG", "buildRequest Exception: " + ex, isDebugEnabled)
320 * This method validates the callback response
321 * from OOF. If the response contains an
322 * exception the method will build and throw
323 * a workflow exception.
326 * @param response - the async callback response from oof
328 Void validateCallbackResponse(DelegateExecution execution, String response) {
329 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
330 String placements = ""
331 if (isBlank(response)) {
332 exceptionUtil.buildAndThrowWorkflowException(execution, 5000, "OOF Async Callback Response is Empty")
334 if (JsonUtils.jsonElementExist(response, "solutions.placementSolutions")) {
335 placements = jsonUtil.getJsonValue(response, "solutions.placementSolutions")
336 if (isBlank(placements) || placements.equalsIgnoreCase("[]")) {
337 String statusMessage = jsonUtil.getJsonValue(response, "statusMessage")
338 if (isBlank(statusMessage)) {
339 utils.log("DEBUG", "Error Occurred in Homing: OOF Async Callback Response does " +
340 "not contain placement solution.", isDebugEnabled)
341 exceptionUtil.buildAndThrowWorkflowException(execution, 400,
342 "OOF Async Callback Response does not contain placement solution.")
344 utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
345 exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
350 } else if (response.contains("error") || response.contains("Error") ) {
351 String errorMessage = ""
352 if (response.contains("policyException")) {
353 String text = jsonUtil.getJsonValue(response, "requestError.policyException.text")
354 errorMessage = "OOF Async Callback Response contains a Request Error Policy Exception: " + text
355 } else if (response.contains("Unable to find any candidate for demand")) {
356 errorMessage = "OOF Async Callback Response contains error: Unable to find any candidate for " +
357 "demand *** Response: " + response.toString()
358 } else if (response.contains("serviceException")) {
359 String text = jsonUtil.getJsonValue(response, "requestError.serviceException.text")
360 errorMessage = "OOF Async Callback Response contains a Request Error Service Exception: " + text
362 errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
364 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
365 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
368 utils.log("DEBUG", "Error Occurred in Homing: Received an Unknown Async Callback Response from OOF.", isDebugEnabled)
369 exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Received an Unknown Async Callback Response from OOF.")
376 * This method creates candidates json for placement Demands.
379 * @param existingCandidates -
380 * @param excludedCandidates -
381 * @param requiredCandidates -
383 * @return candidatesJson - a JSON string with candidates
385 String createCandidateJson(ArrayList existingCandidates = null,
386 ArrayList excludedCandidates = null,
387 ArrayList requiredCandidates = null) {
388 def candidatesJson = ""
390 if (existingCandidates != null && existingCandidates != {}) {
391 sb = new StringBuilder()
393 " \"existingCandidates\": [\n")
394 def existingCandidateJson = ""
395 existingCandidates.each { existingCandidate ->
396 type = existingCandidate.get('identifierType')
397 if (type == 'vimId') {
398 def cloudOwner = existingCandidate.get('cloudOwner')
399 def cloudRegionId = existingCandidate.get('identifiers')
400 existingCandidateJson = "{\n" +
401 " \"identifierType\": \"vimId\",\n" +
402 " \"cloudOwner\": \"${cloudOwner}\",\n" +
403 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
405 sb.append(existingCandidateJson)
407 if (type == 'serviceInstanceId') {
408 def serviceInstanceId = existingCandidate.get('identifiers')
409 existingCandidateJson += "{\n" +
410 " \"identifierType\": \"serviceInstanceId\",\n" +
411 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
413 sb.append(existingCandidateJson)
416 if (existingCandidateJson != "") {
417 sb.setLength(sb.length() - 1)
418 candidatesJson = sb.append(",\n],")
421 if (excludedCandidates != null && excludedCandidates != {}) {
422 sb = new StringBuilder()
424 " \"excludedCandidates\": [\n")
425 def excludedCandidateJson = ""
426 excludedCandidates.each { excludedCandidate ->
427 type = excludedCandidate.get('identifierType')
428 if (type == 'vimId') {
429 def cloudOwner = excludedCandidate.get('cloudOwner')
430 def cloudRegionId = excludedCandidate.get('identifiers')
431 excludedCandidateJson = "{\n" +
432 " \"identifierType\": \"vimId\",\n" +
433 " \"cloudOwner\": \"${cloudOwner}\",\n" +
434 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
436 sb.append(excludedCandidateJson)
438 if (type == 'serviceInstanceId') {
439 def serviceInstanceId = excludedCandidate.get('identifiers')
440 excludedCandidateJson += "{\n" +
441 " \"identifierType\": \"serviceInstanceId\",\n" +
442 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
444 sb.append(excludedCandidateJson)
447 if (excludedCandidateJson != "") {
448 sb.setLength(sb.length() - 1)
449 candidatesJson = sb.append(",\n],")
452 if (requiredCandidates != null && requiredCandidates != {}) {
453 sb = new StringBuilder()
455 " \"requiredCandidates\": [\n")
456 def requiredCandidatesJson = ""
457 requiredCandidates.each { requiredCandidate ->
458 type = requiredCandidate.get('identifierType')
459 if (type == 'vimId') {
460 def cloudOwner = requiredCandidate.get('cloudOwner')
461 def cloudRegionId = requiredCandidate.get('identifiers')
462 requiredCandidatesJson = "{\n" +
463 " \"identifierType\": \"vimId\",\n" +
464 " \"cloudOwner\": \"${cloudOwner}\",\n" +
465 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
467 sb.append(requiredCandidatesJson)
469 if (type == 'serviceInstanceId') {
470 def serviceInstanceId = requiredCandidate.get('identifiers')
471 requiredCandidatesJson += "{\n" +
472 " \"identifierType\": \"serviceInstanceId\",\n" +
473 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
475 sb.append(requiredCandidatesJson)
478 if (requiredCandidatesJson != "") {
479 sb.setLength(sb.length() - 1)
480 candidatesJson = sb.append(",\n],")
483 if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
484 return candidatesJson
488 * This method creates a cloudsite in catalog database.
490 * @param CloudSite cloudSite
494 Void createCloudSite(CloudSite cloudSite, DelegateExecution execution) {
495 oofInfraUtils.createCloudSite(cloudSite, execution)
499 * This method creates a HomingInstance in catalog database.
501 * @param HomingInstance homingInstance
505 Void createHomingInstance(HomingInstance homingInstance, DelegateExecution execution) {
506 oofInfraUtils.createHomingInstance(homingInstance, execution)
509 String getMsbHost(DelegateExecution execution) {
510 String msbHost = UrnPropertiesReader.getVariable("mso.msb.host", execution, "msb-iag.onap")
512 Integer msbPort = UrnPropertiesReader.getVariable("mso.msb.port", execution, "80").toInteger()
514 return UriBuilder.fromPath("").host(msbHost).port(msbPort).scheme("http").build().toString()