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.CloudIdentity
37 import org.onap.so.db.catalog.beans.CloudSite
38 import org.onap.so.db.catalog.client.CatalogDbClient
39 import org.onap.so.rest.APIResponse
40 import org.onap.so.rest.RESTClient
41 import org.onap.so.rest.RESTConfig
42 import org.springframework.http.HttpEntity
43 import org.springframework.http.HttpHeaders
44 import org.springframework.web.util.UriComponentsBuilder
46 import javax.ws.rs.core.MediaType
47 import javax.ws.rs.core.UriBuilder
49 import static org.onap.so.bpmn.common.scripts.GenericUtils.*
52 ExceptionUtil exceptionUtil = new ExceptionUtil()
53 JsonUtils jsonUtil = new JsonUtils()
55 private AbstractServiceTaskProcessor utils
57 OofUtils(AbstractServiceTaskProcessor taskProcessor) {
58 this.utils = taskProcessor
62 * This method builds the service-agnostic
63 * OOF json request to get a homing solution
64 * and license solution
68 * @param decomposition - ServiceDecomposition object
69 * @param customerLocation -
70 * @param existingCandidates -
71 * @param excludedCandidates -
72 * @param requiredCandidates -
74 * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
76 String buildRequest(DelegateExecution execution,
78 ServiceDecomposition decomposition,
79 Subscriber subscriber = null,
81 ArrayList existingCandidates = null,
82 ArrayList excludedCandidates = null,
83 ArrayList requiredCandidates = null) {
84 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
85 utils.log("DEBUG", "Started Building OOF Request", isDebugEnabled)
86 String callbackEndpoint = UrnPropertiesReader.getVariable("mso.oof.callbackEndpoint", execution)
87 utils.log("DEBUG", "mso.oof.callbackEndpoint is: " + callbackEndpoint, isDebugEnabled)
89 def callbackUrl = utils.createHomingCallbackURL(callbackEndpoint, "oofResponse", requestId)
90 utils.log("DEBUG", "callbackUrl is: " + callbackUrl, isDebugEnabled)
93 def transactionId = requestId
94 utils.log("DEBUG", "transactionId is: " + transactionId, isDebugEnabled)
95 //ServiceInstance Info
96 ServiceInstance serviceInstance = decomposition.getServiceInstance()
97 def serviceInstanceId = ""
100 serviceInstanceId = execution.getVariable("serviceInstanceId")
101 utils.log("DEBUG", "serviceInstanceId is: " + serviceInstanceId, isDebugEnabled)
102 serviceName = execution.getVariable("subscriptionServiceType")
103 utils.log("DEBUG", "serviceName is: " + serviceName, isDebugEnabled)
105 if (serviceInstanceId == null || serviceInstanceId == "null") {
106 utils.log("DEBUG", "Unable to obtain Service Instance Id", isDebugEnabled)
107 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
108 "obtain Service Instance Id, execution.getVariable(\"serviceInstanceId\") is null")
110 if (serviceName == null || serviceName == "null") {
111 utils.log("DEBUG", "Unable to obtain Service Name", isDebugEnabled)
112 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
113 "obtain Service Name, execution.getVariable(\"subscriptionServiceType\") is null")
116 ModelInfo model = decomposition.getModelInfo()
117 utils.log("DEBUG", "ModelInfo: " + model.toString(), isDebugEnabled)
118 String modelType = model.getModelType()
119 String modelInvariantId = model.getModelInvariantUuid()
120 String modelVersionId = model.getModelUuid()
121 String modelName = model.getModelName()
122 String modelVersion = model.getModelVersion()
124 String subscriberId = ""
125 String subscriberName = ""
126 String commonSiteId = ""
127 if (subscriber != null) {
128 subscriberId = subscriber.getGlobalId()
129 subscriberName = subscriber.getName()
130 commonSiteId = subscriber.getCommonSiteId()
133 //Determine RequestType
134 //TODO Figure out better way to determine this
135 String requestType = "create"
136 List<Resource> resources = decomposition.getServiceResources()
137 for (Resource r : resources) {
138 HomingSolution currentSolution = (HomingSolution) r.getCurrentHomingSolution()
139 if (currentSolution != null) {
140 requestType = "speed changed"
145 String placementDemands = ""
146 StringBuilder sb = new StringBuilder()
147 List<AllottedResource> allottedResourceList = decomposition.getAllottedResources()
148 List<VnfResource> vnfResourceList = decomposition.getVnfResources()
150 if (allottedResourceList == null || allottedResourceList.isEmpty()) {
151 utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
154 for (AllottedResource resource : allottedResourceList) {
155 utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
157 def serviceResourceId = resource.getResourceId()
158 def toscaNodeType = resource.getToscaNodeType()
159 def resourceModuleName = toscaNodeType.substring(toscaNodeType.lastIndexOf(".") + 1)
160 def resourceModelInvariantId = resource.getModelInfo().getModelInvariantUuid()
161 def resourceModelVersionId = resource.getModelInfo().getModelUuid()
162 def resourceModelName = resource.getModelInfo().getModelName()
163 def resourceModelVersion = resource.getModelInfo().getModelVersion()
164 def resourceModelType = resource.getModelInfo().getModelType()
165 def tenantId = execution.getVariable("tenantId")
166 def requiredCandidatesJson = ""
168 requiredCandidatesJson = createCandidateJson(
175 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
176 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
177 " \"tenantId\": \"${tenantId}\",\n" +
178 " \"resourceModelInfo\": {\n" +
179 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
180 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
181 " \"modelName\": \"${resourceModelName}\",\n" +
182 " \"modelType\": \"${resourceModelType}\",\n" +
183 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
184 " \"modelCustomizationName\": \"\"\n" +
185 " }" + requiredCandidatesJson + "\n" +
188 placementDemands = sb.append(demand)
192 if (vnfResourceList == null || vnfResourceList.isEmpty()) {
193 utils.log("DEBUG", "VNF Resources List is empty",
197 for (VnfResource vnfResource : vnfResourceList) {
198 utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
200 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
201 def toscaNodeType = vnfResource.getToscaNodeType()
202 def resourceModuleName = toscaNodeType.substring(toscaNodeType.lastIndexOf(".") + 1)
203 def serviceResourceId = vnfResource.getResourceId()
204 def resourceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
205 def resourceModelName = vnfResourceModelInfo.getModelName()
206 def resourceModelVersion = vnfResourceModelInfo.getModelVersion()
207 def resourceModelVersionId = vnfResourceModelInfo.getModelUuid()
208 def resourceModelType = vnfResourceModelInfo.getModelType()
209 def tenantId = execution.getVariable("tenantId")
210 def requiredCandidatesJson = ""
213 String placementDemand =
215 " \"resourceModuleName\": \"${resourceModuleName}\",\n" +
216 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
217 " \"tenantId\": \"${tenantId}\",\n" +
218 " \"resourceModelInfo\": {\n" +
219 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
220 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
221 " \"modelName\": \"${resourceModelName}\",\n" +
222 " \"modelType\": \"${resourceModelType}\",\n" +
223 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
224 " \"modelCustomizationName\": \"\"\n" +
225 " }" + requiredCandidatesJson + "\n" +
228 placementDemands = sb.append(placementDemand)
230 placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
233 /* Commenting Out Licensing as OOF doesn't support for Beijing
234 String licenseDemands = ""
235 sb = new StringBuilder()
236 if (vnfResourceList.isEmpty() || vnfResourceList == null) {
237 utils.log("DEBUG", "Vnf Resources List is Empty", isDebugEnabled)
239 for (VnfResource vnfResource : vnfResourceList) {
240 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
241 def resourceInstanceType = vnfResource.getResourceType()
242 def serviceResourceId = vnfResource.getResourceId()
243 def resourceModuleName = vnfResource.getResourceType()
244 def resouceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
245 def resouceModelName = vnfResourceModelInfo.getModelName()
246 def resouceModelVersion = vnfResourceModelInfo.getModelVersion()
247 def resouceModelVersionId = vnfResourceModelInfo.getModelUuid()
248 def resouceModelType = vnfResourceModelInfo.getModelType()
250 // TODO Add Existing Licenses to demand
251 //"existingLicenses": {
252 //"entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
253 // "43257b49-9602-4fe5-9337-094e52bc9435"],
254 //"licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
255 // "43257b49-9602-4fe5-9337-094e52bc9435"]
258 String licenseDemand =
260 "\"resourceModuleName\": \"${resourceModuleName}\",\n" +
261 "\"serviceResourceId\": \"${serviceResourceId}\",\n" +
262 "\"resourceInstanceType\": \"${resourceInstanceType}\",\n" +
263 "\"resourceModelInfo\": {\n" +
264 " \"modelInvariantId\": \"${resouceModelInvariantId}\",\n" +
265 " \"modelVersionId\": \"${resouceModelVersionId}\",\n" +
266 " \"modelName\": \"${resouceModelName}\",\n" +
267 " \"modelType\": \"${resouceModelType}\",\n" +
268 " \"modelVersion\": \"${resouceModelVersion}\",\n" +
269 " \"modelCustomizationName\": \"\"\n" +
273 licenseDemands = sb.append(licenseDemand)
275 licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
280 " \"requestInfo\": {\n" +
281 " \"transactionId\": \"${transactionId}\",\n" +
282 " \"requestId\": \"${requestId}\",\n" +
283 " \"callbackUrl\": \"${callbackUrl}\",\n" +
284 " \"sourceId\": \"so\",\n" +
285 " \"requestType\": \"${requestType}\"," +
286 " \"numSolutions\": 1,\n" +
287 " \"optimizers\": [\"placement\"],\n" +
288 " \"timeout\": 600\n" +
290 " \"placementInfo\": {\n" +
291 " \"requestParameters\": {\n" +
292 " \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
293 " \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
294 " \"customerName\": \"${customerLocation.customerName}\"\n" +
296 " \"subscriberInfo\": { \n" +
297 " \"globalSubscriberId\": \"${subscriberId}\",\n" +
298 " \"subscriberName\": \"${subscriberName}\",\n" +
299 " \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
301 " \"placementDemands\": [\n" +
302 " ${placementDemands}\n" +
305 " \"serviceInfo\": {\n" +
306 " \"serviceInstanceId\": \"${serviceInstanceId}\",\n" +
307 " \"serviceName\": \"${serviceName}\",\n" +
308 " \"modelInfo\": {\n" +
309 " \"modelType\": \"${modelType}\",\n" +
310 " \"modelInvariantId\": \"${modelInvariantId}\",\n" +
311 " \"modelVersionId\": \"${modelVersionId}\",\n" +
312 " \"modelName\": \"${modelName}\",\n" +
313 " \"modelVersion\": \"${modelVersion}\",\n" +
314 " \"modelCustomizationName\": \"\"\n" +
320 utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
322 } catch (Exception ex) {
323 utils.log("DEBUG", "buildRequest Exception: " + ex, isDebugEnabled)
328 * This method validates the callback response
329 * from OOF. If the response contains an
330 * exception the method will build and throw
331 * a workflow exception.
334 * @param response - the async callback response from oof
336 Void validateCallbackResponse(DelegateExecution execution, String response) {
337 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
338 String placements = ""
339 if (isBlank(response)) {
340 exceptionUtil.buildAndThrowWorkflowException(execution, 5000, "OOF Async Callback Response is Empty")
342 if (JsonUtils.jsonElementExist(response, "solutions.placementSolutions")) {
343 placements = jsonUtil.getJsonValue(response, "solutions.placementSolutions")
344 if (isBlank(placements) || placements.equalsIgnoreCase("[]")) {
345 String statusMessage = jsonUtil.getJsonValue(response, "statusMessage")
346 if (isBlank(statusMessage)) {
347 utils.log("DEBUG", "Error Occurred in Homing: OOF Async Callback Response does " +
348 "not contain placement solution.", isDebugEnabled)
349 exceptionUtil.buildAndThrowWorkflowException(execution, 400,
350 "OOF Async Callback Response does not contain placement solution.")
352 utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
353 exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
358 } else if (response.contains("error") || response.contains("Error") ) {
359 String errorMessage = ""
360 if (response.contains("policyException")) {
361 String text = jsonUtil.getJsonValue(response, "requestError.policyException.text")
362 errorMessage = "OOF Async Callback Response contains a Request Error Policy Exception: " + text
363 } else if (response.contains("Unable to find any candidate for demand")) {
364 errorMessage = "OOF Async Callback Response contains error: Unable to find any candidate for " +
365 "demand *** Response: " + response.toString()
366 } else if (response.contains("serviceException")) {
367 String text = jsonUtil.getJsonValue(response, "requestError.serviceException.text")
368 errorMessage = "OOF Async Callback Response contains a Request Error Service Exception: " + text
370 errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
372 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
373 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
376 utils.log("DEBUG", "Error Occurred in Homing: Received an Unknown Async Callback Response from OOF.", isDebugEnabled)
377 exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Received an Unknown Async Callback Response from OOF.")
384 * This method creates candidates json for placement Demands.
387 * @param existingCandidates -
388 * @param excludedCandidates -
389 * @param requiredCandidates -
391 * @return candidatesJson - a JSON string with candidates
393 String createCandidateJson(ArrayList existingCandidates = null,
394 ArrayList excludedCandidates = null,
395 ArrayList requiredCandidates = null) {
396 def candidatesJson = ""
398 if (existingCandidates != null && existingCandidates != {}) {
399 sb = new StringBuilder()
401 " \"existingCandidates\": [\n")
402 def existingCandidateJson = ""
403 existingCandidates.each { existingCandidate ->
404 type = existingCandidate.get('identifierType')
405 if (type == 'vimId') {
406 def cloudOwner = existingCandidate.get('cloudOwner')
407 def cloudRegionId = existingCandidate.get('identifiers')
408 existingCandidateJson = "{\n" +
409 " \"identifierType\": \"vimId\",\n" +
410 " \"cloudOwner\": \"${cloudOwner}\",\n" +
411 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
413 sb.append(existingCandidateJson)
415 if (type == 'serviceInstanceId') {
416 def serviceInstanceId = existingCandidate.get('identifiers')
417 existingCandidateJson += "{\n" +
418 " \"identifierType\": \"serviceInstanceId\",\n" +
419 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
421 sb.append(existingCandidateJson)
424 if (existingCandidateJson != "") {
425 sb.setLength(sb.length() - 1)
426 candidatesJson = sb.append(",\n],")
429 if (excludedCandidates != null && excludedCandidates != {}) {
430 sb = new StringBuilder()
432 " \"excludedCandidates\": [\n")
433 def excludedCandidateJson = ""
434 excludedCandidates.each { excludedCandidate ->
435 type = excludedCandidate.get('identifierType')
436 if (type == 'vimId') {
437 def cloudOwner = excludedCandidate.get('cloudOwner')
438 def cloudRegionId = excludedCandidate.get('identifiers')
439 excludedCandidateJson = "{\n" +
440 " \"identifierType\": \"vimId\",\n" +
441 " \"cloudOwner\": \"${cloudOwner}\",\n" +
442 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
444 sb.append(excludedCandidateJson)
446 if (type == 'serviceInstanceId') {
447 def serviceInstanceId = excludedCandidate.get('identifiers')
448 excludedCandidateJson += "{\n" +
449 " \"identifierType\": \"serviceInstanceId\",\n" +
450 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
452 sb.append(excludedCandidateJson)
455 if (excludedCandidateJson != "") {
456 sb.setLength(sb.length() - 1)
457 candidatesJson = sb.append(",\n],")
460 if (requiredCandidates != null && requiredCandidates != {}) {
461 sb = new StringBuilder()
463 " \"requiredCandidates\": [\n")
464 def requiredCandidatesJson = ""
465 requiredCandidates.each { requiredCandidate ->
466 type = requiredCandidate.get('identifierType')
467 if (type == 'vimId') {
468 def cloudOwner = requiredCandidate.get('cloudOwner')
469 def cloudRegionId = requiredCandidate.get('identifiers')
470 requiredCandidatesJson = "{\n" +
471 " \"identifierType\": \"vimId\",\n" +
472 " \"cloudOwner\": \"${cloudOwner}\",\n" +
473 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
475 sb.append(requiredCandidatesJson)
477 if (type == 'serviceInstanceId') {
478 def serviceInstanceId = requiredCandidate.get('identifiers')
479 requiredCandidatesJson += "{\n" +
480 " \"identifierType\": \"serviceInstanceId\",\n" +
481 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
483 sb.append(requiredCandidatesJson)
486 if (requiredCandidatesJson != "") {
487 sb.setLength(sb.length() - 1)
488 candidatesJson = sb.append(",\n],")
491 if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
492 return candidatesJson
495 * This method creates a cloudsite in catalog database.
497 * @param CloudSite cloudSite
501 Void createCloudSiteCatalogDb(CloudSite cloudSite, DelegateExecution execution) {
502 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
503 String endpoint = UrnPropertiesReader.getVariable("mso.catalog.db.spring.endpoint", execution)
504 String auth = UrnPropertiesReader.getVariable("mso.db.auth", execution)
505 CloudSite getCloudsite = null
507 CatalogDbClient catalogDbClient = new CatalogDbClient(endpoint, auth)
509 getCloudsite = catalogDbClient.getCloudSite(cloudSite.getId().toString())
510 } catch (Exception e) {
512 utils.log("DEBUG", "Could not find cloudsite : " + cloudSite.getId(), isDebugEnabled)
513 utils.log("DEBUG", "Creating cloudSite: " + cloudSite.toString(), isDebugEnabled)
515 if (getCloudsite?.getId() != cloudSite.getId()) {
516 catalogDbClient.postCloudSite(cloudSite)
520 String getMsbHost(DelegateExecution execution) {
521 String msbHost = UrnPropertiesReader.getVariable("mso.msb.host", execution, "msb-iag.onap")
523 Integer msbPort = UrnPropertiesReader.getVariable("mso.msb.port", execution, "80").toInteger()
525 return UriBuilder.fromPath("").host(msbHost).port(msbPort).scheme("http").build().toString()