Initial OpenECOMP MSO commit
[so.git] / bpmn / MSOGammaBPMN / src / main / groovy / com / att / bpm / scripts / UpdateAAIVfModule.groovy
1 /*-
2  * ============LICENSE_START=======================================================
3  * OPENECOMP - MSO
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 com.att.bpm.scripts
22
23 import groovy.util.Node
24 import groovy.util.XmlParser;
25 import groovy.xml.QName
26
27 import java.io.Serializable;
28
29 import org.camunda.bpm.engine.delegate.BpmnError
30 import org.camunda.bpm.engine.runtime.Execution
31 import org.springframework.web.util.UriUtils
32
33 import org.openecomp.mso.rest.APIResponse
34 import org.openecomp.mso.rest.RESTClient
35 import org.openecomp.mso.rest.RESTConfig
36 import org.openecomp.mso.bpmn.core.RollbackData
37 import org.openecomp.mso.bpmn.core.WorkflowException
38
39
40 public class UpdateAAIVfModule extends AbstractServiceTaskProcessor {
41
42         private XmlParser xmlParser = new XmlParser()
43
44         /**
45          * Initialize the flow's variables.
46          *
47          * @param execution The flow's execution instance.
48          */
49         public void initProcessVariables(Execution execution) {
50                 execution.setVariable('prefix', 'UAAIVfMod_')
51                 execution.setVariable('UAAIVfMod_vnfId', null)
52                 execution.setVariable('UAAIVfMod_vfModuleId', null)
53                 execution.setVariable('UAAIVfMod_orchestrationStatus', null)
54                 execution.setVariable('UAAIVfMod_heatStackId', null)
55                 execution.setVariable('UAAIVfMod_volumeGroupId', null)
56                 execution.setVariable('UAAIVfMod_getVfModuleResponseCode' ,null)
57                 execution.setVariable('UAAIVfMod_getVfModuleResponse', '')
58                 execution.setVariable('UAAIVfMod_updateVfModuleResponseCode', null)
59                 execution.setVariable('UAAIVfMod_updateVfModuleResponse', '')
60         }
61
62         /**
63          * Check for missing elements in the received request.
64          *
65          * @param execution The flow's execution instance.
66          */
67         public void preProcessRequest(Execution execution) {
68                 def method = getClass().getSimpleName() + '.preProcessRequest(' +
69                         'execution=' + execution.getId() +
70                         ')'
71                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
72                 logDebug('Entered ' + method, isDebugLogEnabled)
73
74                 try {
75                         def xml = execution.getVariable('UpdateAAIVfModuleRequest')
76                         logDebug('Received request xml:\n' + xml, isDebugLogEnabled)
77                         initProcessVariables(execution)
78
79                         def vnfId = getRequiredNodeText(execution, xml,'vnf-id')
80                         execution.setVariable('UAAIVfMod_vnfId', vnfId)
81
82                         def vfModuleId = getRequiredNodeText(execution, xml,'vf-module-id')
83                         execution.setVariable('UAAIVfMod_vfModuleId', vfModuleId)
84
85                         logDebug('Exited ' + method, isDebugLogEnabled)
86                 } catch (BpmnError e) {
87                         throw e;
88                 } catch (Exception e) {
89                         logError('Caught exception in ' + method, e)
90                         createWorkflowException(execution, 1002, 'Error in preProcessRequest(): ' + e.getMessage())
91                 }
92         }
93
94         /**
95          * Using the received vnfId and vfModuleId, query AAI to get the corresponding VF Module.
96          * A 200 response is expected with the VF Module in the response body.
97          *
98          * @param execution The flow's execution instance.
99          */
100         public void getVfModule(Execution execution) {
101                 def method = getClass().getSimpleName() + '.getVfModule(' +
102                         'execution=' + execution.getId() +
103                         ')'
104                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
105                 logDebug('Entered ' + method, isDebugLogEnabled)
106
107                 try {
108                         def vnfId = execution.getVariable('UAAIVfMod_vnfId')
109                         def vfModuleId = execution.getVariable('UAAIVfMod_vfModuleId')
110
111                         // Construct endpoint
112                         AaiUtil aaiUriUtil = new AaiUtil(this)
113                         def aai_uri = aaiUriUtil.getNetworkGenericVnfUri(execution)
114                         logDebug('AAI URI is: ' + aai_uri, isDebugLogEnabled)
115                         String endPoint = execution.getVariable('URN_aai_endpoint') + aai_uri + '/' + UriUtils.encode(vnfId, "UTF-8") + '/vf-modules/vf-module/' + UriUtils.encode(vfModuleId, "UTF-8")
116
117                         String basicAuthCred = utils.getBasicAuth(execution.getVariable("URN_aai_auth"),execution.getVariable("URN_mso_msoKey"))
118
119                         try {
120                                 RESTConfig config = new RESTConfig(endPoint);
121                                 def responseData = ''
122                                 def aaiRequestId = UUID.randomUUID().toString()
123                                 RESTClient client = new RESTClient(config).
124                                         addHeader('X-TransactionId', aaiRequestId).
125                                         addHeader('X-FromAppId', 'MSO').
126                                         addHeader('Content-Type', 'application/xml').
127                                         addHeader('Accept','application/xml');
128                                 if (basicAuthCred != null && !"".equals(basicAuthCred)) {
129                                         client.addAuthorizationHeader(basicAuthCred)
130                                 }
131
132                                 logDebug('sending GET to AAI endpoint \'' + endPoint + '\'', isDebugLogEnabled)
133                                 APIResponse response = client.httpGet()
134
135                                 responseData = response.getResponseBodyAsString()
136                                 execution.setVariable('UAAIVfMod_getVfModuleResponseCode', response.getStatusCode())
137                                 execution.setVariable('UAAIVfMod_getVfModuleResponse', responseData)
138                                 logDebug('Response code:' + response.getStatusCode(), isDebugLogEnabled)
139                                 logDebug('Response:' + System.lineSeparator() + responseData, isDebugLogEnabled)
140                         } catch (Exception ex) {
141                                 ex.printStackTrace()
142                                 logDebug('Exception occurred while executing AAI GET:' + ex.getMessage(),isDebugLogEnabled)
143                                 execution.setVariable('UAAIVfMod_getVfModuleResponseCode', 500)
144                                 execution.setVariable('UAAIVfMod_getVfModuleResponse', 'AAI GET Failed:' + ex.getMessage())
145                         }
146                         logDebug('Exited ' + method, isDebugLogEnabled)
147                 } catch (BpmnError e) {
148                         throw e;
149                 } catch (Exception e) {
150                         logError('Caught exception in ' + method, e)
151                         createWorkflowException(execution, 1002, 'Error in getVfModule(): ' + e.getMessage())
152                 }
153         }
154
155         /**
156          * Construct and send a PUT request to AAI to update the VF Module.
157          *
158          * @param execution The flow's execution instance.
159          */
160         public void updateVfModule(Execution execution) {
161                 def method = getClass().getSimpleName() + '.updateVfModule(' +
162                         'execution=' + execution.getId() +
163                         ')'
164                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
165                 logDebug('Entered ' + method, isDebugLogEnabled)
166
167                 try {
168                         def vnfId = execution.getVariable('UAAIVfMod_vnfId')
169                         def vfModuleId = execution.getVariable('UAAIVfMod_vfModuleId')
170                         def vfModule = execution.getVariable('UAAIVfMod_getVfModuleResponse')
171                         def origRequest = execution.getVariable('UpdateAAIVfModuleRequest')
172                         def Node vfModuleNode = xmlParser.parseText(vfModule)
173                         
174                         // Confirm resource-version is in retrieved VF Module
175                         if (utils.getChildNode(vfModuleNode, 'resource-version') == null) {
176                                 def msg = 'Can\'t update VF Module ' + vfModuleId + ' since \'resource-version\' is missing'
177                                 logError(msg)
178                                 throw new Exception(msg)
179                         }
180                         
181                         // TEMPORARY!!! Disable Volume Group Check
182                         // Check volume-group-id conditions
183                         //def checkVgiResult = checkVolumeGroupId(origRequest, vfModuleNode, isDebugLogEnabled)
184                         //if (checkVgiResult != null) {
185                         //      def msg = 'Can\'t update VF Module ' + vfModuleId + ': ' + checkVgiResult
186                         //      logError(msg)
187                         //      throw new Exception(msg)
188                         //}
189                         
190                         // Handle persona-model-id/persona-model-version
191                         def boolean doPersonaModelVersion = true
192                         def String newPersonaModelId = utils.getNodeText1(origRequest, 'persona-model-id')
193                         def String newPersonaModelVersion = utils.getNodeText1(origRequest, 'persona-model-version')
194                         if ((newPersonaModelId == null) || (newPersonaModelVersion == null)) {
195                                 doPersonaModelVersion = false
196                         } else {
197                                 // Confirm "new" persona-model-id is same as "current" persona-model-id
198                                 def String currPersonaModelId = utils.getChildNodeText(vfModuleNode, 'persona-model-id')
199                                 if (currPersonaModelId == null) {
200                                         currPersonaModelId = ''
201                                 }
202                                 if (!newPersonaModelId.equals(currPersonaModelId)) {
203                                         def msg = 'Can\'t update VF Module ' + vfModuleId + ' since there is \'persona-model-id\' mismatch between the current and new values'
204                                         logError(msg)
205                                         throw new Exception(msg)
206                                 }
207                         }
208                         
209                         // Construct payload
210                         updateVfModuleNode(origRequest, vfModuleNode, 'orchestration-status')
211                         updateVfModuleNode(origRequest, vfModuleNode, 'heat-stack-id')
212                         if (doPersonaModelVersion) {
213                                 updateVfModuleNode(origRequest, vfModuleNode, 'persona-model-version')
214                         }
215                         updateVfModuleNode(origRequest, vfModuleNode, 'contrail-service-instance-fqdn')
216                         def payload = utils.nodeToString(vfModuleNode)
217
218                         // Construct endpoint
219                         AaiUtil aaiUriUtil = new AaiUtil(this)
220                         def aai_uri = aaiUriUtil.getNetworkGenericVnfUri(execution)
221                         logDebug('AAI URI is: ' + aai_uri, isDebugLogEnabled)
222                         String endPoint = execution.getVariable('URN_aai_endpoint') + aai_uri + '/' + UriUtils.encode(vnfId, "UTF-8") + '/vf-modules/vf-module/' + UriUtils.encode(vfModuleId, "UTF-8")
223
224                         String basicAuthCred = utils.getBasicAuth(execution.getVariable("URN_aai_auth"),execution.getVariable("URN_mso_msoKey"))
225
226                         try {
227                                 RESTConfig config = new RESTConfig(endPoint);
228                                 def responseData = ''
229                                 def aaiRequestId = UUID.randomUUID().toString()
230                                 RESTClient client = new RESTClient(config).
231                                         addHeader('X-TransactionId', aaiRequestId).
232                                         addHeader('X-FromAppId', 'MSO').
233                                         addHeader('Content-Type', 'application/xml').
234                                         addHeader('Accept','application/xml');
235                                 if (basicAuthCred != null && !"".equals(basicAuthCred)) {
236                                         client.addAuthorizationHeader(basicAuthCred)
237                                 }
238
239                                 logDebug('sending PUT to AAI endpoint \'' + endPoint + '\'' + 'with payload \n' + payload, isDebugLogEnabled)
240                                 APIResponse response = client.httpPut(payload)
241
242                                 responseData = response.getResponseBodyAsString()
243                                 execution.setVariable('UAAIVfMod_updateVfModuleResponseCode', response.getStatusCode())
244                                 execution.setVariable('UAAIVfMod_updateVfModuleResponse', responseData)
245                                 logDebug('Response code:' + response.getStatusCode(), isDebugLogEnabled)
246                                 logDebug('Response:' + System.lineSeparator() + responseData, isDebugLogEnabled)
247                         } catch (Exception ex) {
248                                 ex.printStackTrace()
249                                 logDebug('Exception occurred while executing AAI PUT:' + ex.getMessage(),isDebugLogEnabled)
250                                 execution.setVariable('UAAIVfMod_updateVfModuleResponseCode', 500)
251                                 execution.setVariable('UAAIVfMod_updateVfModuleResponse', 'AAI PUT Failed:' + ex.getMessage())
252                         }
253                         logDebug('Exited ' + method, isDebugLogEnabled)
254                 } catch (BpmnError e) {
255                         throw e;
256                 } catch (Exception e) {
257                         logError('Caught exception in ' + method, e)
258                         createWorkflowException(execution, 1002, 'Error in updateVfModule(): ' + e.getMessage())
259                 }
260         }
261
262         /**
263          * Insert a new Node, replace the value of an existing Node, or delete an existing Node in the current
264          * VF Module Node, as necessary.
265          *
266          * If the Node with the same name already exists in current VF Module, but is not being updated, then do
267          * nothing. If the element is being updated and it already exists in the current VF Module, then check
268          * the value specified in the original request. If the value is 'DELETE', remove that Node from the
269          * current VF Module.  Otherwise, change the value to the specified new value. If the element is
270          * being updated but doesn't exist in the current VF Module, and the new value is not 'DELETE', then
271          * create an appropriate new node and add it to the VF Module.
272          *
273          * @param origRequest Incoming update request with VF Module elements to be updated.
274          * @param vfModule Current VF Module retrieved from AAI.
275          * @param element Name of element to be inserted.
276          */
277         private void updateVfModuleNode(String origRequest, Node vfModuleNode, String elementName) {
278
279                 if (!utils.nodeExists(origRequest, elementName)) {
280                         return
281                 }
282                 def elementValue = utils.getNodeText(origRequest, elementName)
283
284                 def Node childNode = utils.getChildNode(vfModuleNode, elementName)
285                 if (childNode == null) {
286                         if (elementValue.equals('DELETE')) {
287                                 // Element doesn't exist but is being deleted, so do nothing
288                                 return
289                         }
290                         // Node doesn't exist, create a new Node as a child
291                         new Node(vfModuleNode, elementName, elementValue)
292                 } else {
293                         if (elementValue.equals('DELETE')) {
294                                 // Node exists, but should be deleted
295                                 vfModuleNode.remove(childNode)
296                         } else {
297                                 // Node already exists, just give it a new value
298                                 childNode.setValue(elementValue)
299                         }
300                 }
301         }
302         
303         /**
304          * Check the Volume Group ID from the incoming update request against the Volume Group ID from the
305          * given VF Module.  If they are equal or if they are both 'null', then that is acceptable and 'null'
306          * is returned.  Otherwise a message describing how the values are unacceptable/incompatible is returned.
307          * 
308          * @param origRequest Incoming update request with VF Module elements to be updated.
309          * @param vfModuleNode VF Module (as a Node) retrieved from AAI.
310          * @param isDebugLogEnabled Is DEBUG log enabled?
311          * @return 'null' if the Volume Group IDs are acceptable. Otherwise return a message describing how the
312          * values are unacceptable/incompatible.
313          */
314         private String checkVolumeGroupId(String origRequest, Node vfModuleNode, String isDebugLogEnabled) {
315                 def requestVolumeGroupId = utils.getNodeText1(origRequest, 'volume-group-id')
316                 def currVolumeGroupId = getCurrVolumeGroupId(vfModuleNode)
317                 
318                 logDebug('Check volume-group-id: volume-group-id in original request is \'' + requestVolumeGroupId +
319                         '\', volume-group-id from VF Module is \'' + currVolumeGroupId + '\'', isDebugLogEnabled)
320                 
321                 def result = null
322                 
323                 if (requestVolumeGroupId == null) {
324                         if (currVolumeGroupId == null) {
325                                 // This is OK
326                         } else {
327                                 result = 'Cannot detach a volume group from an existing VF Module'
328                         }
329                 } else {
330                         if (currVolumeGroupId == null) {
331                                 result = 'Cannot add a volume gruop to an existing VF Module'
332                         } else {
333                                 if (!requestVolumeGroupId.equals(currVolumeGroupId)) {
334                                         result = 'Cannot change the volume group on an existing VF Module'
335                                 }
336                         }
337                 }
338                 
339                 return result
340         }
341         
342         /**
343          * Find and return the value of the Volume Group ID for the specified VF Module.  If
344          * the value of the Volume Group ID cannot be found for any reason, 'null' is returned.
345          * 
346          * @param vfModuleNode VF Module (as a Node) retrieved from AAI.
347          * @return the value of the Volume Group ID for the specified VF Module.  If the
348          * value of the Volume Group ID cannot be found for any reason, 'null' is returned.
349          */
350         private String getCurrVolumeGroupId(Node vfModuleNode) {
351                 def Node relationshipList = utils.getChildNode(vfModuleNode, 'relationship-list')
352                 if (relationshipList == null) {
353                         return null
354                 }
355                 def NodeList relationships = utils.getIdenticalChildren(relationshipList, 'relationship')
356                 for (Node relationshipNode in relationships) {
357                         def String relatedTo = utils.getChildNodeText(relationshipNode, 'related-to')
358                         if ((relatedTo != null) && relatedTo.equals('volume-group')) {
359                                 def NodeList relationshipDataList = utils.getIdenticalChildren(relationshipNode, 'relationship-data')
360                                 for (Node relationshipDataNode in relationshipDataList) {
361                                         def String relationshipKey = utils.getChildNodeText(relationshipDataNode, 'relationship-key')
362                                         if ((relationshipKey != null) && relationshipKey.equals('volume-group.volume-group-id')) {
363                                                 return utils.getChildNodeText(relationshipDataNode, 'relationship-value')
364                                         }
365                                 }
366                         }
367                 }
368                 return null
369         }
370
371         /**
372          * Generates a WorkflowException if the AAI query returns a response code other than 200.
373          *
374          * @param execution The flow's execution instance.
375          */
376         public void handleAAIQueryFailure(Execution execution) {
377                 def method = getClass().getSimpleName() + '.handleAAIQueryFailure(' +
378                         'execution=' + execution.getId() +
379                         ')'
380                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
381                 logDebug('Entered ' + method, isDebugLogEnabled)
382
383                 logError('Error occurred attempting to query AAI, Response Code ' +
384                         execution.getVariable('UAAIVfMod_getVfModuleResponseCode') + ', Error Response ' +
385                         execution.getVariable('UAAIVfMod_getVfModuleResponse'))
386                 String processKey = getProcessKey(execution);
387                 WorkflowException exception = new WorkflowException(processKey, 5000,
388                         execution.getVariable('UAAIVfMod_getVfModuleResponse'))
389                 execution.setVariable('WorkflowException', exception)
390
391                 logDebug('Exited ' + method, isDebugLogEnabled)
392         }
393
394         /**
395          * Generates a WorkflowException if updating a VF Module in AAI returns a response code other than 200.
396          *
397          * @param execution The flow's execution instance.
398          */
399         public void handleUpdateVfModuleFailure(Execution execution) {
400                 def method = getClass().getSimpleName() + '.handleUpdateVfModuleFailure(' +
401                         'execution=' + execution.getId() +
402                         ')'
403                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
404                 logDebug('Entered ' + method, isDebugLogEnabled)
405
406                 logError('Error occurred attempting to update VF Module in AAI, Response Code ' +
407                         execution.getVariable('UAAIVfMod_updateVfModuleResponseCode') + ', Error Response ' +
408                         execution.getVariable('UAAIVfMod_updateVfModuleResponse'))
409                 String processKey = getProcessKey(execution);
410                 WorkflowException exception = new WorkflowException(processKey, 5000,
411                         execution.getVariable('UAAIVfMod_updateVfModuleResponse'))
412                 execution.setVariable('WorkflowException', exception)
413
414                 logDebug('Exited ' + method, isDebugLogEnabled)
415         }
416 }