68b2df213a724d888dc6af21e4ceacfdd0ba41e3
[so.git] / bpmn / MSOCommonBPMN / src / main / groovy / org / onap / so / bpmn / common / scripts / UpdateAAIVfModule.groovy
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. 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.BpmnError
24 import org.camunda.bpm.engine.delegate.DelegateExecution
25 import org.onap.so.bpmn.core.WorkflowException
26 import org.onap.so.bpmn.core.UrnPropertiesReader
27 import org.onap.so.rest.APIResponse
28 import org.springframework.web.util.UriUtils
29 import org.onap.so.logger.MessageEnum
30 import org.onap.so.logger.MsoLogger
31
32
33
34
35 public class UpdateAAIVfModule extends AbstractServiceTaskProcessor {
36         private static final MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, UpdateAAIVfModule.class);
37
38
39         private XmlParser xmlParser = new XmlParser()
40         ExceptionUtil exceptionUtil = new ExceptionUtil()
41
42         /**
43          * Initialize the flow's variables.
44          *
45          * @param execution The flow's execution instance.
46          */
47         public void initProcessVariables(DelegateExecution execution) {
48                 execution.setVariable('prefix', 'UAAIVfMod_')
49                 execution.setVariable('UAAIVfMod_vnfId', null)
50                 execution.setVariable('UAAIVfMod_vfModuleId', null)
51                 execution.setVariable('UAAIVfMod_orchestrationStatus', null)
52                 execution.setVariable('UAAIVfMod_heatStackId', null)
53                 execution.setVariable('UAAIVfMod_volumeGroupId', null)
54                 execution.setVariable('UAAIVfMod_getVfModuleResponseCode' ,null)
55                 execution.setVariable('UAAIVfMod_getVfModuleResponse', '')
56                 execution.setVariable('UAAIVfMod_updateVfModuleResponseCode', null)
57                 execution.setVariable('UAAIVfMod_updateVfModuleResponse', '')
58         }
59
60         /**
61          * Check for missing elements in the received request.
62          *
63          * @param execution The flow's execution instance.
64          */
65         public void preProcessRequest(DelegateExecution execution) {
66                 def method = getClass().getSimpleName() + '.preProcessRequest(' +
67                         'execution=' + execution.getId() +
68                         ')'
69                 msoLogger.trace('Entered ' + method)
70
71                 try {
72                         def xml = execution.getVariable('UpdateAAIVfModuleRequest')
73                         msoLogger.debug('Received request xml:\n' + xml)
74                         initProcessVariables(execution)
75
76                         def vnfId = getRequiredNodeText(execution, xml,'vnf-id')
77                         execution.setVariable('UAAIVfMod_vnfId', vnfId)
78
79                         def vfModuleId = getRequiredNodeText(execution, xml,'vf-module-id')
80                         execution.setVariable('UAAIVfMod_vfModuleId', vfModuleId)
81
82                         msoLogger.trace('Exited ' + method)
83                 } catch (BpmnError e) {
84                         throw e;
85                 } catch (Exception e) {
86                         msoLogger.error(e);
87                         exceptionUtil.buildAndThrowWorkflowException(execution, 1002, 'Error in preProcessRequest(): ' + e.getMessage())
88                 }
89         }
90
91         /**
92          * Using the received vnfId and vfModuleId, query AAI to get the corresponding VF Module.
93          * A 200 response is expected with the VF Module in the response body.
94          *
95          * @param execution The flow's execution instance.
96          */
97         public void getVfModule(DelegateExecution execution) {
98                 def method = getClass().getSimpleName() + '.getVfModule(' +
99                         'execution=' + execution.getId() +
100                         ')'
101                 msoLogger.trace('Entered ' + method)
102
103                 try {
104                         def vnfId = execution.getVariable('UAAIVfMod_vnfId')
105                         def vfModuleId = execution.getVariable('UAAIVfMod_vfModuleId')
106
107                         // Construct endpoint
108                         AaiUtil aaiUriUtil = new AaiUtil(this)
109                         def aai_uri = aaiUriUtil.getNetworkGenericVnfUri(execution)
110                         msoLogger.debug('AAI URI is: ' + aai_uri)
111                         String endPoint = UrnPropertiesReader.getVariable("aai.endpoint", execution) + aai_uri + '/' + UriUtils.encode(vnfId, "UTF-8") + '/vf-modules/vf-module/' + UriUtils.encode(vfModuleId, "UTF-8")
112
113                         try {
114                                 msoLogger.debug('sending GET to AAI endpoint \'' + endPoint + '\'')
115                                 msoLogger.debug("UpdateAAIVfModule sending GET to AAI endpoint: " + endPoint)
116
117                                 APIResponse response = aaiUriUtil.executeAAIGetCall(execution, endPoint)
118                                 def responseData = response.getResponseBodyAsString()
119                                 execution.setVariable('UAAIVfMod_getVfModuleResponseCode', response.getStatusCode())
120                                 execution.setVariable('UAAIVfMod_getVfModuleResponse', responseData)
121                                 msoLogger.debug('Response code:' + response.getStatusCode())
122                                 msoLogger.debug('Response:' + System.lineSeparator() + responseData)
123                                 msoLogger.debug("UpdateAAIVfModule response data: " + responseData)
124                         } catch (Exception ex) {
125                                 ex.printStackTrace()
126                                 msoLogger.debug('Exception occurred while executing AAI GET:' + ex.getMessage())
127                                 execution.setVariable('UAAIVfMod_getVfModuleResponseCode', 500)
128                                 execution.setVariable('UAAIVfMod_getVfModuleResponse', 'AAI GET Failed:' + ex.getMessage())
129                         }
130                         msoLogger.trace('Exited ' + method)
131                 } catch (BpmnError e) {
132                         throw e;
133                 } catch (Exception e) {
134                         msoLogger.error(e);
135                         exceptionUtil.buildAndThrowWorkflowException(execution, 1002, 'Error in getVfModule(): ' + e.getMessage())
136                 }
137         }
138
139         /**
140          * Construct and send a PATCH request to AAI to update the VF Module.
141          *
142          * @param execution The flow's execution instance.
143          */
144         public void updateVfModule(DelegateExecution execution) {
145                 def method = getClass().getSimpleName() + '.updateVfModule(' +
146                         'execution=' + execution.getId() +
147                         ')'
148                 msoLogger.trace('Entered ' + method)
149
150                 try {
151                         def vnfId = execution.getVariable('UAAIVfMod_vnfId')
152                         def vfModuleId = execution.getVariable('UAAIVfMod_vfModuleId')
153                         def vfModule = execution.getVariable('UAAIVfMod_getVfModuleResponse')
154                         def origRequest = execution.getVariable('UpdateAAIVfModuleRequest')
155                         def Node vfModuleNode = xmlParser.parseText(vfModule)
156                         
157                         msoLogger.debug("UpdateAAIVfModule request: " + origRequest)
158                         // Confirm resource-version is in retrieved VF Module
159                         if (utils.getChildNode(vfModuleNode, 'resource-version') == null) {
160                                 def msg = 'Can\'t update VF Module ' + vfModuleId + ' since \'resource-version\' is missing'
161                                 msoLogger.error(msg);
162                                 throw new Exception(msg)
163                         }
164                         
165                         // Handle persona-model-id/persona-model-version
166                         def boolean doPersonaModelVersion = true
167                         def String newPersonaModelId = utils.getNodeText(origRequest, 'persona-model-id')
168                         def String newPersonaModelVersion = utils.getNodeText(origRequest, 'persona-model-version')
169                         if ((newPersonaModelId == null) || (newPersonaModelVersion == null)) {
170                                 doPersonaModelVersion = false
171                         } else {
172                                 // Confirm "new" persona-model-id is same as "current" persona-model-id
173                                 def String currPersonaModelId = utils.getChildNodeText(vfModuleNode, 'model-invariant-id')
174                                 if (currPersonaModelId == null) {
175                                         // check the old attribute name
176                                         currPersonaModelId = utils.getChildNodeText(vfModuleNode, 'model-version-id')
177                                 }
178                                 if (currPersonaModelId == null) {
179                                         currPersonaModelId = ''
180                                 }
181                                 if (!newPersonaModelId.equals(currPersonaModelId)) {
182                                         def msg = 'Can\'t update VF Module ' + vfModuleId + ' since there is \'persona-model-id\' mismatch between the current and new values'
183                                         msoLogger.error(msg)
184                                         throw new Exception(msg)
185                                 }
186                         }
187                         
188                         // Construct payload
189                         String orchestrationStatusEntry = updateVfModuleNode(origRequest, vfModuleNode, 'orchestration-status')
190                         String heatStackIdEntry = updateVfModuleNode(origRequest, vfModuleNode, 'heat-stack-id')
191                         String personaModelVersionEntry = ""
192                         if (doPersonaModelVersion) {
193                                 personaModelVersionEntry = updateVfModuleNode(origRequest, vfModuleNode, 'persona-model-version')
194                         }
195                         String contrailServiceInstanceFqdnEntry = updateVfModuleNode(origRequest, vfModuleNode, 'contrail-service-instance-fqdn')
196                         def payload = """
197                                         {       ${orchestrationStatusEntry}
198                                                 ${heatStackIdEntry}
199                                                 ${personaModelVersionEntry}
200                                                 ${contrailServiceInstanceFqdnEntry}
201                                                 "vf-module-id": "${vfModuleId}"                                         
202                                         }
203                         """
204
205
206                         // Construct endpoint
207                         AaiUtil aaiUriUtil = new AaiUtil(this)
208                         def aai_uri = aaiUriUtil.getNetworkGenericVnfUri(execution)
209                         msoLogger.debug('AAI URI is: ' + aai_uri)
210                         String endPoint = UrnPropertiesReader.getVariable("aai.endpoint", execution) + aai_uri + '/' + UriUtils.encode(vnfId, "UTF-8") + '/vf-modules/vf-module/' + UriUtils.encode(vfModuleId, "UTF-8")
211
212                         try {
213                                 msoLogger.debug('sending PATCH to AAI endpoint \'' + endPoint + '\'' + 'with payload \n' + payload)
214                                 msoLogger.debug("Sending PATCH to AAI endpoint: " + endPoint)
215
216                                 APIResponse response = aaiUriUtil.executeAAIPatchCall(execution, endPoint, payload)
217                                 def responseData = response.getResponseBodyAsString()
218                                 execution.setVariable('UAAIVfMod_updateVfModuleResponseCode', response.getStatusCode())
219                                 execution.setVariable('UAAIVfMod_updateVfModuleResponse', responseData)
220                                 msoLogger.debug("UpdateAAIVfModule Response data: " + responseData)
221                                 msoLogger.debug('Response code:' + response.getStatusCode())
222                                 msoLogger.debug('Response:' + System.lineSeparator() + responseData)
223                         } catch (Exception ex) {
224                                 ex.printStackTrace()
225                                 msoLogger.debug('Exception occurred while executing AAI PATCH:' + ex.getMessage())
226                                 execution.setVariable('UAAIVfMod_updateVfModuleResponseCode', 500)
227                                 execution.setVariable('UAAIVfMod_updateVfModuleResponse', 'AAI PATCH Failed:' + ex.getMessage())
228                         }
229                         msoLogger.trace('Exited ' + method)
230                 } catch (BpmnError e) {
231                         throw e;
232                 } catch (Exception e) {
233                         msoLogger.error(e);
234                         exceptionUtil.buildAndThrowWorkflowException(execution, 1002, 'Error in updateVfModule(): ' + e.getMessage())
235                 }
236         }
237
238         /**
239          * Sets up json attributes for PATCH request for Update
240          *
241          * @param origRequest Incoming update request with VF Module elements to be updated.
242          * @param vfModule Current VF Module retrieved from AAI.
243          * @param element Name of element to be inserted.
244          */     
245         private String updateVfModuleNode(String origRequest, Node vfModuleNode, String elementName) {
246
247                 if (!utils.nodeExists(origRequest, elementName)) {
248                         return "" 
249                 }
250                 def elementValue = utils.getNodeText(origRequest, elementName)
251
252                 if (elementValue.equals('DELETE')) {
253                         // Set the element being deleted to null
254                         return """"${elementName}": null,"""
255                 }
256                 else {
257                         return """"${elementName}": "${elementValue}","""
258                 }               
259         }
260
261         
262         /**
263          * Check the Volume Group ID from the incoming update request against the Volume Group ID from the
264          * given VF Module.  If they are equal or if they are both 'null', then that is acceptable and 'null'
265          * is returned.  Otherwise a message describing how the values are unacceptable/incompatible is returned.
266          * 
267          * @param origRequest Incoming update request with VF Module elements to be updated.
268          * @param vfModuleNode VF Module (as a Node) retrieved from AAI.
269          * @param isDebugLogEnabled Is DEBUG log enabled?
270          * @return 'null' if the Volume Group IDs are acceptable. Otherwise return a message describing how the
271          * values are unacceptable/incompatible.
272          */
273         private String checkVolumeGroupId(String origRequest, Node vfModuleNode, String isDebugLogEnabled) {
274                 def requestVolumeGroupId = utils.getNodeText(origRequest, 'volume-group-id')
275                 def currVolumeGroupId = getCurrVolumeGroupId(vfModuleNode)
276                 
277                 msoLogger.debug('Check volume-group-id: volume-group-id in original request is \'' + requestVolumeGroupId + '\', volume-group-id from VF Module is \'' + currVolumeGroupId + '\'')
278                 
279                 def result = null
280                 
281                 if (requestVolumeGroupId == null) {
282                         if (currVolumeGroupId == null) {
283                                 // This is OK
284                         } else {
285                                 result = 'Cannot detach a volume group from an existing VF Module'
286                         }
287                 } else {
288                         if (currVolumeGroupId == null) {
289                                 result = 'Cannot add a volume gruop to an existing VF Module'
290                         } else {
291                                 if (!requestVolumeGroupId.equals(currVolumeGroupId)) {
292                                         result = 'Cannot change the volume group on an existing VF Module'
293                                 }
294                         }
295                 }
296                 
297                 return result
298         }
299         
300         /**
301          * Find and return the value of the Volume Group ID for the specified VF Module.  If
302          * the value of the Volume Group ID cannot be found for any reason, 'null' is returned.
303          * 
304          * @param vfModuleNode VF Module (as a Node) retrieved from AAI.
305          * @return the value of the Volume Group ID for the specified VF Module.  If the
306          * value of the Volume Group ID cannot be found for any reason, 'null' is returned.
307          */
308         private String getCurrVolumeGroupId(Node vfModuleNode) {
309                 def Node relationshipList = utils.getChildNode(vfModuleNode, 'relationship-list')
310                 if (relationshipList == null) {
311                         return null
312                 }
313                 def NodeList relationships = utils.getIdenticalChildren(relationshipList, 'relationship')
314                 for (Node relationshipNode in relationships) {
315                         def String relatedTo = utils.getChildNodeText(relationshipNode, 'related-to')
316                         if ((relatedTo != null) && relatedTo.equals('volume-group')) {
317                                 def NodeList relationshipDataList = utils.getIdenticalChildren(relationshipNode, 'relationship-data')
318                                 for (Node relationshipDataNode in relationshipDataList) {
319                                         def String relationshipKey = utils.getChildNodeText(relationshipDataNode, 'relationship-key')
320                                         if ((relationshipKey != null) && relationshipKey.equals('volume-group.volume-group-id')) {
321                                                 return utils.getChildNodeText(relationshipDataNode, 'relationship-value')
322                                         }
323                                 }
324                         }
325                 }
326                 return null
327         }
328
329         /**
330          * Generates a WorkflowException if the AAI query returns a response code other than 200.
331          *
332          * @param execution The flow's execution instance.
333          */
334         public void handleAAIQueryFailure(DelegateExecution execution) {
335                 def method = getClass().getSimpleName() + '.handleAAIQueryFailure(' +
336                         'execution=' + execution.getId() +
337                         ')'
338                 msoLogger.trace('Entered ' + method)
339
340                 msoLogger.error( 'Error occurred attempting to query AAI, Response Code ' + execution.getVariable('UAAIVfMod_getVfModuleResponseCode'));
341                 String processKey = getProcessKey(execution);
342                 WorkflowException exception = new WorkflowException(processKey, 5000,
343                         execution.getVariable('UAAIVfMod_getVfModuleResponse'))
344                 execution.setVariable('WorkflowException', exception)
345                 msoLogger.debug("UpdateAAIVfModule query failure: " + exception.getErrorMessage())
346                 msoLogger.trace('Exited ' + method)
347         }
348
349         /**
350          * Generates a WorkflowException if updating a VF Module in AAI returns a response code other than 200.
351          *
352          * @param execution The flow's execution instance.
353          */
354         public void handleUpdateVfModuleFailure(DelegateExecution execution) {
355                 def method = getClass().getSimpleName() + '.handleUpdateVfModuleFailure(' +
356                         'execution=' + execution.getId() +
357                         ')'
358                 msoLogger.trace('Entered ' + method)
359
360                 msoLogger.error('Error occurred attempting to update VF Module in AAI, Response Code ' + execution.getVariable('UAAIVfMod_updateVfModuleResponseCode'));
361                 String processKey = getProcessKey(execution);
362                 WorkflowException exception = new WorkflowException(processKey, 5000,
363                         execution.getVariable('UAAIVfMod_updateVfModuleResponse'))
364                 execution.setVariable('WorkflowException', exception)
365
366                 msoLogger.debug("UpdateAAIVfModule failure: " + exception.getErrorMessage())
367                 msoLogger.trace('Exited ' + method)
368         }
369 }