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.rest.APIResponse
38 import org.onap.so.rest.RESTClient
39 import org.onap.so.rest.RESTConfig
40 import org.springframework.http.HttpEntity
41 import org.springframework.http.HttpHeaders
42 import org.springframework.http.HttpMethod
43 import org.springframework.http.ResponseEntity
44 import org.springframework.http.client.BufferingClientHttpRequestFactory
45 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
46 import org.springframework.web.client.RestTemplate
47 import org.springframework.web.util.UriComponentsBuilder
49 import javax.ws.rs.core.MediaType
50 import javax.ws.rs.core.Response
51 import javax.xml.ws.http.HTTPException
53 import static org.onap.so.bpmn.common.scripts.GenericUtils.*
56 ExceptionUtil exceptionUtil = new ExceptionUtil()
57 JsonUtils jsonUtil = new JsonUtils()
59 private AbstractServiceTaskProcessor utils
61 OofUtils(AbstractServiceTaskProcessor taskProcessor) {
62 this.utils = taskProcessor
66 * This method builds the service-agnostic
67 * OOF json request to get a homing solution
68 * and license solution
72 * @param decomposition - ServiceDecomposition object
73 * @param customerLocation -
74 * @param existingCandidates -
75 * @param excludedCandidates -
76 * @param requiredCandidates -
78 * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
80 String buildRequest(DelegateExecution execution,
82 ServiceDecomposition decomposition,
83 Subscriber subscriber = null,
85 ArrayList existingCandidates = null,
86 ArrayList excludedCandidates = null,
87 ArrayList requiredCandidates = null) {
88 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
89 utils.log("DEBUG", "Started Building OOF Request", isDebugEnabled)
90 String callbackEndpoint = UrnPropertiesReader.getVariable("mso.oof.callbackEndpoint", execution)
91 utils.log("DEBUG", "mso.oof.callbackEndpoint is: " + callbackEndpoint, isDebugEnabled)
92 def callbackUrl = utils.createWorkflowMessageAdapterCallbackURL(callbackEndpoint, "oofResponse", requestId)
93 def transactionId = requestId
94 //ServiceInstance Info
95 ServiceInstance serviceInstance = decomposition.getServiceInstance()
96 def serviceInstanceId = ""
99 serviceInstanceId = execution.getVariable("serviceInstanceId")
100 serviceName = execution.getVariable("subscriptionServiceType")
102 if (serviceInstanceId == null || serviceInstanceId == "null") {
103 utils.log("DEBUG", "Unable to obtain Service Instance Id", isDebugEnabled)
104 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
105 "obtain Service Instance Id, execution.getVariable(\"serviceInstanceId\") is null")
107 if (serviceName == null || serviceName == "null") {
108 utils.log("DEBUG", "Unable to obtain Service Name", isDebugEnabled)
109 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
110 "obtain Service Name, execution.getVariable(\"subscriptionServiceType\") is null")
113 ModelInfo model = decomposition.getModelInfo()
114 String modelType = model.getModelType()
115 String modelInvariantId = model.getModelInvariantUuid()
116 String modelVersionId = model.getModelUuid()
117 String modelName = model.getModelName()
118 String modelVersion = model.getModelVersion()
120 String subscriberId = ""
121 String subscriberName = ""
122 String commonSiteId = ""
123 if (subscriber != null){
124 subscriberId = subscriber.getGlobalId()
125 subscriberName = subscriber.getName()
126 commonSiteId = subscriber.getCommonSiteId()
129 //Determine RequestType
130 //TODO Figure out better way to determine this
131 String requestType = "create"
132 List<Resource> resources = decomposition.getServiceResources()
133 for(Resource r:resources){
134 HomingSolution currentSolution = (HomingSolution) r.getCurrentHomingSolution()
135 if(currentSolution != null){
136 requestType = "speed changed"
141 String placementDemands = ""
142 StringBuilder sb = new StringBuilder()
143 List<AllottedResource> allottedResourceList = decomposition.getAllottedResources()
144 List<VnfResource> vnfResourceList = decomposition.getVnfResources()
146 if (allottedResourceList == null || allottedResourceList.isEmpty() ) {
147 utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
149 allottedResourceList = decomposition.getVnfResources()
152 if (allottedResourceList == null || allottedResourceList.isEmpty()) {
153 utils.log("DEBUG", "Resources List is Empty", isDebugEnabled)
155 for (AllottedResource resource : allottedResourceList) {
156 utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
158 def serviceResourceId = resource.getResourceId()
159 def resourceModelInvariantId = resource.getModelInfo().getModelInvariantUuid()
160 def resourceModelVersionId = resource.getModelInfo().getModelUuid()
161 def resourceModelName = resource.getModelInfo().getModelName()
162 def resourceModelVersion = resource.getModelInfo().getModelVersion()
163 def resourceModelType = resource.getModelInfo().getModelType()
164 def tenantId = execution.getVariable("tenantId")
165 def requiredCandidatesJson = ""
167 requiredCandidatesJson = createCandidateJson(
174 " \"resourceModuleName\": \"${resourceModelName}\",\n" +
175 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
176 " \"tenantId\": \"${tenantId}\",\n" +
177 " \"resourceModelInfo\": {\n" +
178 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
179 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
180 " \"modelName\": \"${resourceModelName}\",\n" +
181 " \"modelType\": \"${resourceModelType}\",\n" +
182 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
183 " \"modelCustomizationName\": \"\"\n" +
184 " }" + requiredCandidatesJson + "\n" +
187 placementDemands = sb.append(demand)
189 for (VnfResource vnfResource : vnfResourceList) {
190 utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
192 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
193 def serviceResourceId = vnfResource.getResourceId()
194 def resourceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
195 def resourceModelName = vnfResourceModelInfo.getModelName()
196 def resourceModelVersion = vnfResourceModelInfo.getModelVersion()
197 def resourceModelVersionId = vnfResourceModelInfo.getModelUuid()
198 def resourceModelType = vnfResourceModelInfo.getModelType()
199 def tenantId = execution.getVariable("tenantId")
200 def requiredCandidatesJson = ""
203 String placementDemand =
205 " \"resourceModuleName\": \"${resourceModelName}\",\n" +
206 " \"serviceResourceId\": \"${serviceResourceId}\",\n" +
207 " \"tenantId\": \"${tenantId}\",\n" +
208 " \"resourceModelInfo\": {\n" +
209 " \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
210 " \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
211 " \"modelName\": \"${resourceModelName}\",\n" +
212 " \"modelType\": \"${resourceModelType}\",\n" +
213 " \"modelVersion\": \"${resourceModelVersion}\",\n" +
214 " \"modelCustomizationName\": \"\"\n" +
215 " }" + requiredCandidatesJson + "\n" +
218 placementDemands = sb.append(placementDemand)
220 placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
223 /* Commenting Out Licensing as OOF doesn't support for Beijing
224 String licenseDemands = ""
225 sb = new StringBuilder()
226 if (vnfResourceList.isEmpty() || vnfResourceList == null) {
227 utils.log("DEBUG", "Vnf Resources List is Empty", isDebugEnabled)
229 for (VnfResource vnfResource : vnfResourceList) {
230 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
231 def resourceInstanceType = vnfResource.getResourceType()
232 def serviceResourceId = vnfResource.getResourceId()
233 def resourceModuleName = vnfResource.getResourceType()
234 def resouceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
235 def resouceModelName = vnfResourceModelInfo.getModelName()
236 def resouceModelVersion = vnfResourceModelInfo.getModelVersion()
237 def resouceModelVersionId = vnfResourceModelInfo.getModelUuid()
238 def resouceModelType = vnfResourceModelInfo.getModelType()
240 // TODO Add Existing Licenses to demand
241 //"existingLicenses": {
242 //"entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
243 // "43257b49-9602-4fe5-9337-094e52bc9435"],
244 //"licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
245 // "43257b49-9602-4fe5-9337-094e52bc9435"]
248 String licenseDemand =
250 "\"resourceModuleName\": \"${resourceModuleName}\",\n" +
251 "\"serviceResourceId\": \"${serviceResourceId}\",\n" +
252 "\"resourceInstanceType\": \"${resourceInstanceType}\",\n" +
253 "\"resourceModelInfo\": {\n" +
254 " \"modelInvariantId\": \"${resouceModelInvariantId}\",\n" +
255 " \"modelVersionId\": \"${resouceModelVersionId}\",\n" +
256 " \"modelName\": \"${resouceModelName}\",\n" +
257 " \"modelType\": \"${resouceModelType}\",\n" +
258 " \"modelVersion\": \"${resouceModelVersion}\",\n" +
259 " \"modelCustomizationName\": \"\"\n" +
263 licenseDemands = sb.append(licenseDemand)
265 licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
270 " \"requestInfo\": {\n" +
271 " \"transactionId\": \"${transactionId}\",\n" +
272 " \"requestId\": \"${requestId}\",\n" +
273 " \"callbackUrl\": \"${callbackUrl}\",\n" +
274 " \"sourceId\": \"so\",\n" +
275 " \"requestType\": \"${requestType}\"," +
276 " \"numSolutions\": 1,\n" +
277 " \"optimizers\": [\"placement\"],\n" +
278 " \"timeout\": 600\n" +
280 " \"placementInfo\": {\n" +
281 " \"requestParameters\": {\n" +
282 " \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
283 " \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
284 " \"customerName\": \"${customerLocation.customerName}\"\n" +
286 " \"subscriberInfo\": { \n" +
287 " \"globalSubscriberId\": \"${subscriberId}\",\n" +
288 " \"subscriberName\": \"${subscriberName}\",\n" +
289 " \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
291 " \"placementDemands\": [\n" +
292 " ${placementDemands}\n" +
295 " \"serviceInfo\": {\n" +
296 " \"serviceInstanceId\": \"${serviceInstanceId}\",\n" +
297 " \"serviceName\": \"${serviceName}\",\n" +
298 " \"modelInfo\": {\n" +
299 " \"modelType\": \"${modelType}\",\n" +
300 " \"modelInvariantId\": \"${modelInvariantId}\",\n" +
301 " \"modelVersionId\": \"${modelVersionId}\",\n" +
302 " \"modelName\": \"${modelName}\",\n" +
303 " \"modelVersion\": \"${modelVersion}\",\n" +
304 " \"modelCustomizationName\": \"\"\n" +
310 utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
315 * This method validates the callback response
316 * from OOF. If the response contains an
317 * exception the method will build and throw
318 * a workflow exception.
321 * @param response - the async callback response from oof
323 Void validateCallbackResponse(DelegateExecution execution, String response) {
324 def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
325 String placements = ""
326 if (isBlank(response)) {
327 exceptionUtil.buildAndThrowWorkflowException(execution, 5000, "OOF Async Callback Response is Empty")
329 if (JsonUtils.jsonElementExist(response, "solutions.placementSolutions")) {
330 placements = jsonUtil.getJsonValue(response, "solutions.placementSolutions")
331 if (isBlank(placements) || placements.equalsIgnoreCase("[]")) {
332 String statusMessage = jsonUtil.getJsonValue(response, "statusMessage")
333 if (isBlank(statusMessage)) {
334 utils.log("DEBUG", "Error Occurred in Homing: OOF Async Callback Response does " +
335 "not contain placement solution.", isDebugEnabled)
336 exceptionUtil.buildAndThrowWorkflowException(execution, 400,
337 "OOF Async Callback Response does not contain placement solution.")
339 utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
340 exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
345 } else if (response.contains("error") || response.contains("Error") ) {
346 String errorMessage = ""
347 if (response.contains("policyException")) {
348 String text = jsonUtil.getJsonValue(response, "requestError.policyException.text")
349 errorMessage = "OOF Async Callback Response contains a Request Error Policy Exception: " + text
350 } else if (response.contains("Unable to find any candidate for demand")) {
351 errorMessage = "OOF Async Callback Response contains error: Unable to find any candidate for " +
352 "demand *** Response: " + response.toString()
353 } else if (response.contains("serviceException")) {
354 String text = jsonUtil.getJsonValue(response, "requestError.serviceException.text")
355 errorMessage = "OOF Async Callback Response contains a Request Error Service Exception: " + text
357 errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
359 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
360 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
363 utils.log("DEBUG", "Error Occurred in Homing: Received an Unknown Async Callback Response from OOF.", isDebugEnabled)
364 exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Received an Unknown Async Callback Response from OOF.")
371 * This method creates candidates json for placement Demands.
374 * @param existingCandidates -
375 * @param excludedCandidates -
376 * @param requiredCandidates -
378 * @return candidatesJson - a JSON string with candidates
380 String createCandidateJson(ArrayList existingCandidates = null,
381 ArrayList excludedCandidates = null,
382 ArrayList requiredCandidates = null) {
383 def candidatesJson = ""
385 if (existingCandidates != null && existingCandidates != {}) {
386 sb = new StringBuilder()
388 " \"existingCandidates\": [\n")
389 def existingCandidateJson = ""
390 existingCandidates.each { existingCandidate ->
391 type = existingCandidate.get('identifierType')
392 if (type == 'vimId') {
393 def cloudOwner = existingCandidate.get('cloudOwner')
394 def cloudRegionId = existingCandidate.get('identifiers')
395 existingCandidateJson = "{\n" +
396 " \"identifierType\": \"vimId\",\n" +
397 " \"cloudOwner\": \"${cloudOwner}\",\n" +
398 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
400 sb.append(existingCandidateJson)
402 if (type == 'serviceInstanceId') {
403 def serviceInstanceId = existingCandidate.get('identifiers')
404 existingCandidateJson += "{\n" +
405 " \"identifierType\": \"serviceInstanceId\",\n" +
406 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
408 sb.append(existingCandidateJson)
411 if (existingCandidateJson != "") {
412 sb.setLength(sb.length() - 1)
413 candidatesJson = sb.append(",\n],")
416 if (excludedCandidates != null && excludedCandidates != {}) {
417 sb = new StringBuilder()
419 " \"excludedCandidates\": [\n")
420 def excludedCandidateJson = ""
421 excludedCandidates.each { excludedCandidate ->
422 type = excludedCandidate.get('identifierType')
423 if (type == 'vimId') {
424 def cloudOwner = excludedCandidate.get('cloudOwner')
425 def cloudRegionId = excludedCandidate.get('identifiers')
426 excludedCandidateJson = "{\n" +
427 " \"identifierType\": \"vimId\",\n" +
428 " \"cloudOwner\": \"${cloudOwner}\",\n" +
429 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
431 sb.append(excludedCandidateJson)
433 if (type == 'serviceInstanceId') {
434 def serviceInstanceId = excludedCandidate.get('identifiers')
435 excludedCandidateJson += "{\n" +
436 " \"identifierType\": \"serviceInstanceId\",\n" +
437 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
439 sb.append(excludedCandidateJson)
442 if (excludedCandidateJson != "") {
443 sb.setLength(sb.length() - 1)
444 candidatesJson = sb.append(",\n],")
447 if (requiredCandidates != null && requiredCandidates != {}) {
448 sb = new StringBuilder()
450 " \"requiredCandidates\": [\n")
451 def requiredCandidatesJson = ""
452 requiredCandidates.each { requiredCandidate ->
453 type = requiredCandidate.get('identifierType')
454 if (type == 'vimId') {
455 def cloudOwner = requiredCandidate.get('cloudOwner')
456 def cloudRegionId = requiredCandidate.get('identifiers')
457 requiredCandidatesJson = "{\n" +
458 " \"identifierType\": \"vimId\",\n" +
459 " \"cloudOwner\": \"${cloudOwner}\",\n" +
460 " \"identifiers\": [\"${cloudRegionId}\"]\n" +
462 sb.append(requiredCandidatesJson)
464 if (type == 'serviceInstanceId') {
465 def serviceInstanceId = requiredCandidate.get('identifiers')
466 requiredCandidatesJson += "{\n" +
467 " \"identifierType\": \"serviceInstanceId\",\n" +
468 " \"identifiers\": [\"${serviceInstanceId}\"]\n" +
470 sb.append(requiredCandidatesJson)
473 if (requiredCandidatesJson != "") {
474 sb.setLength(sb.length() - 1)
475 candidatesJson = sb.append(",\n],")
478 if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
479 return candidatesJson
482 * This method creates a cloudsite in catalog database.
484 * @param CloudSite cloudSite
488 Void createCloudSiteCatalogDb(CloudSite cloudSite, DelegateExecution execution) {
490 String endpoint = UrnPropertiesReader.getVariable("mso.catalog.db.spring.endpoint", execution)
491 String auth = UrnPropertiesReader.getVariable("mso.db.auth", execution)
492 String uri = "/cloudSite"
494 HttpHeaders headers = new HttpHeaders()
496 headers.set(HttpHeaders.AUTHORIZATION, auth)
497 headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
498 headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
500 UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(endpoint + uri)
501 HttpEntity<CloudSite> request = new HttpEntity<CloudSite>(cloudSite, headers)
502 RESTConfig config = new RESTConfig(endpoint + uri)
503 RESTClient client = new RESTClient(config).addAuthorizationHeader(auth).
504 addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON).addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
505 APIResponse response = client.httpPost(request.getBody().toString())
507 int responseCode = response.getStatusCode()
508 logDebug("CatalogDB response code is: " + responseCode, isDebugEnabled)
509 String syncResponse = response.getResponseBodyAsString()
510 logDebug("CatalogDB response is: " + syncResponse, isDebugEnabled)
512 if(responseCode != 202){
513 exceptionUtil.buildAndThrowWorkflowException(execution, responseCode, "Received a Bad Sync Response from CatalogDB.")