Initial OpenECOMP MSO commit
[so.git] / bpmn / MSOGammaBPMN / src / main / groovy / com / att / bpm / scripts / VnfAdapterRestV1.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.xml.XmlUtil
24
25 import java.text.SimpleDateFormat
26 import java.net.URLEncoder
27
28 import org.apache.commons.codec.binary.Base64
29 import org.apache.commons.lang3.*
30 import org.camunda.bpm.engine.delegate.BpmnError
31 import org.camunda.bpm.engine.runtime.Execution
32
33 import org.openecomp.mso.bpmn.core.WorkflowException
34 import org.openecomp.mso.rest.APIResponse
35 import org.openecomp.mso.rest.RESTClient
36 import org.openecomp.mso.rest.RESTConfig
37
38 class VnfAdapterRestV1 extends AbstractServiceTaskProcessor {
39
40         ExceptionUtil exceptionUtil = new ExceptionUtil()
41
42         // VNF Response Processing
43         public void preProcessRequest (Execution execution) {
44                 def method = getClass().getSimpleName() + '.preProcessRequest(' +
45                         'execution=' + execution.getId() +
46                         ')'
47                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
48                 logDebug('Entered ' + method, isDebugLogEnabled)
49
50                 def prefix="VNFREST_"
51                 execution.setVariable("prefix", prefix)
52                 setSuccessIndicator(execution, false)
53
54                 try {
55                         String request = validateRequest(execution, "att-mso-request-id")
56
57                         // Get the request type (the name of the root element) from the request
58
59                         Node root = new XmlParser().parseText(request)
60                         String requestType = root.name()
61                         execution.setVariable(prefix + 'requestType', requestType)
62                         logDebug(getProcessKey(execution) + ': ' + prefix + 'requestType = ' + requestType, isDebugLogEnabled)
63
64                         utils.logAudit('VnfAdapterRestV1, request: ' + request)
65                         // Get the messageId from the request
66
67                         String messageId = getChildText(root, 'messageId')
68
69                         if (messageId == null || messageId.isEmpty()) {
70                                 String msg = getProcessKey(execution) + ': no messageId in ' + requestType
71                                 logError(msg)
72                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
73                         }
74
75                         execution.setVariable(prefix + 'messageId', messageId)
76                         logDebug(getProcessKey(execution) + ': ' + prefix + 'messageId = ' + messageId, isDebugLogEnabled)
77
78                         // Get the notificationUrl from the request
79
80                         String notificationUrl = getChildText(root, 'notificationUrl')
81
82                         if (notificationUrl == null || notificationUrl.isEmpty()) {
83                                 String msg = getProcessKey(execution) + ': no notificationUrl in ' + requestType
84                                 logError(msg)
85                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
86                         }
87
88                         execution.setVariable(prefix + 'notificationUrl', notificationUrl)
89                         logDebug(getProcessKey(execution) + ': ' + prefix + 'notificationUrl = ' + notificationUrl, isDebugLogEnabled)
90
91                         // Determine the VnfAdapter endpoint
92
93                         String vnfAdapterEndpoint = execution.getVariable("URN_mso_adapters_vnf_rest_endpoint")
94
95                         if (vnfAdapterEndpoint == null || vnfAdapterEndpoint.isEmpty()) {
96                                 String msg = getProcessKey(execution) + ': mso:adapters:vnf:rest:endpoint URN mapping is not defined'
97                                 logError(msg)
98                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
99                         }
100
101                         while (vnfAdapterEndpoint.endsWith('/')) {
102                                 vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, vnfAdapterEndpoint.length()-1)
103                         }
104
105                         String vnfAdapterMethod = null
106                         String vnfAdapterUrl = null
107                         String vnfAdapterRequest = request
108
109                         if ('createVfModuleRequest'.equals(requestType)) {
110                                 String vnfId = getChildText(root, 'vnfId')
111
112                                 if (vnfId == null || vnfId.isEmpty()) {
113                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
114                                         logError(msg)
115                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
116                                 }
117
118                                 vnfAdapterMethod = 'POST'
119                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') + '/vf-modules'
120
121                         } else if ('updateVfModuleRequest'.equals(requestType)) {
122                                 String vnfId = getChildText(root, 'vnfId')
123
124                                 if (vnfId == null || vnfId.isEmpty()) {
125                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
126                                         logError(msg)
127                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
128                                 }
129
130                                 String vfModuleId = getChildText(root, 'vfModuleId')
131
132                                 if (vfModuleId == null || vfModuleId.isEmpty()) {
133                                         String msg = getProcessKey(execution) + ': no vfModuleId in ' + requestType
134                                         logError(msg)
135                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
136                                 }
137
138                                 vnfAdapterMethod = 'PUT'
139                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') +
140                                         '/vf-modules/' + URLEncoder.encode(vfModuleId, 'UTF-8')
141
142                         } else if ('deleteVfModuleRequest'.equals(requestType)) {
143                                 String vnfId = getChildText(root, 'vnfId')
144
145                                 if (vnfId == null || vnfId.isEmpty()) {
146                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
147                                         logError(msg)
148                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
149                                 }
150
151                                 String vfModuleId = getChildText(root, 'vfModuleId')
152
153                                 if (vfModuleId == null || vfModuleId.isEmpty()) {
154                                         String msg = getProcessKey(execution) + ': no vfModuleId in ' + requestType
155                                         logError(msg)
156                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
157                                 }
158
159                                 vnfAdapterMethod = 'DELETE'
160                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') +
161                                         '/vf-modules/' + URLEncoder.encode(vfModuleId, 'UTF-8')
162
163                         } else if ('rollbackVfModuleRequest'.equals(requestType)) {
164                                 Node vfModuleRollbackNode = getChild(root, 'vfModuleRollback')
165
166                                 if (vfModuleRollbackNode == null) {
167                                         String msg = getProcessKey(execution) + ': no vfModuleRollback in ' + requestType
168                                         logError(msg)
169                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
170                                 }
171
172                                 String vnfId = getChildText(vfModuleRollbackNode, 'vnfId')
173
174                                 if (vnfId == null || vnfId.isEmpty()) {
175                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
176                                         logError(msg)
177                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
178                                 }
179
180                                 String vfModuleId = getChildText(vfModuleRollbackNode, 'vfModuleId')
181
182                                 if (vfModuleId == null || vfModuleId.isEmpty()) {
183                                         String msg = getProcessKey(execution) + ': no vfModuleId in ' + requestType
184                                         logError(msg)
185                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
186                                 }
187
188                                 vnfAdapterMethod = 'DELETE'
189                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') +
190                                         '/vf-modules/' + URLEncoder.encode(vfModuleId, 'UTF-8') + '/rollback'
191
192                         } else if ('createVolumeGroupRequest'.equals(requestType)) {
193                                 vnfAdapterMethod = 'POST'
194                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
195                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
196                                 }
197                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups'
198
199                         } else if ('updateVolumeGroupRequest'.equals(requestType)) {
200                                 String volumeGroupId = getChildText(root, 'volumeGroupId')
201
202                                 if (volumeGroupId == null || volumeGroupId.isEmpty()) {
203                                         String msg = getProcessKey(execution) + ': no volumeGroupId in ' + requestType
204                                         logError(msg)
205                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
206                                 }
207
208                                 vnfAdapterMethod = 'PUT'
209                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
210                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
211                                 }
212                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups/' + URLEncoder.encode(volumeGroupId, 'UTF-8')
213
214                         } else if ('deleteVolumeGroupRequest'.equals(requestType)) {
215                                 String volumeGroupId = getChildText(root, 'volumeGroupId')
216
217                                 if (volumeGroupId == null || volumeGroupId.isEmpty()) {
218                                         String msg = getProcessKey(execution) + ': no volumeGroupId in ' + requestType
219                                         logError(msg)
220                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
221                                 }
222
223                                 vnfAdapterMethod = 'DELETE'
224                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
225                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
226                                 }
227                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups/' + URLEncoder.encode(volumeGroupId, 'UTF-8')
228
229                         } else if ('rollbackVolumeGroupRequest'.equals(requestType)) {
230                                 String volumeGroupId = getChildText(root, 'volumeGroupId')
231
232                                 if (volumeGroupId == null || volumeGroupId.isEmpty()) {
233                                         String msg = getProcessKey(execution) + ': no volumeGroupId in ' + requestType
234                                         logError(msg)
235                                         createWorkflowException(execution, 2000, msg)
236                                 }
237
238                                 vnfAdapterMethod = 'DELETE'
239                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
240                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
241                                 }
242                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups/' + URLEncoder.encode(volumeGroupId, 'UTF-8')  + '/rollback'
243
244                         } else {
245                                 String msg = getProcessKey(execution) + ': Unsupported request type: ' + requestType
246                                 logError(msg)
247                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
248                         }
249
250                         execution.setVariable(prefix + 'vnfAdapterMethod', vnfAdapterMethod)
251                         logDebug(getProcessKey(execution) + ': ' + prefix + 'vnfAdapterMethod = ' + vnfAdapterMethod, isDebugLogEnabled)
252                         execution.setVariable(prefix + 'vnfAdapterUrl', vnfAdapterUrl)
253                         logDebug(getProcessKey(execution) + ': ' + prefix + 'vnfAdapterUrl = ' + vnfAdapterUrl, isDebugLogEnabled)
254                         execution.setVariable(prefix + 'vnfAdapterRequest', vnfAdapterRequest)
255                         logDebug(getProcessKey(execution) + ': ' + prefix + 'vnfAdapterRequest = \n' + vnfAdapterRequest, isDebugLogEnabled)
256
257                         // Get the Basic Auth credentials for the VnfAdapter
258
259                         String basicAuthValue = execution.getVariable("URN_mso_adapters_po_auth")
260
261                         if (basicAuthValue == null || basicAuthValue.isEmpty()) {
262                                 logError(getProcessKey(execution) + ": mso:adapters:po:auth URN mapping is not defined")
263                         } else {
264                                 logDebug(getProcessKey(execution) + ": Obtained BasicAuth credentials for VnfAdapter:" +
265                                         basicAuthValue, isDebugLogEnabled)
266                                 try {
267                                         def encodedString = utils.getBasicAuth(basicAuthValue, execution.getVariable("URN_mso_msoKey"))
268                                         execution.setVariable(prefix + 'basicAuthHeaderValue', encodedString)
269                                 } catch (IOException ex) {
270                                         logError(getProcessKey(execution) + ": Unable to encode BasicAuth credentials for VnfAdapter")
271                                 }
272                         }
273
274                 } catch (BpmnError e) {
275                         logDebug(" Rethrowing MSOWorkflowException", isDebugLogEnabled)
276                         throw e
277                 } catch (Exception e) {
278                         String msg = 'Caught exception in ' + method + ": " + e
279                         logError(msg)
280                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
281                 }
282         }
283
284         /**
285          * This method is used instead of an HTTP Connector task because the
286          * connector does not allow DELETE with a body.
287          */
288         public void sendRequestToVnfAdapter(Execution execution) {
289                 def method = getClass().getSimpleName() + '.sendRequestToVnfAdapter(' +
290                         'execution=' + execution.getId() +
291                         ')'
292                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
293                 logDebug('Entered ' + method, isDebugLogEnabled)
294
295                 String prefix = execution.getVariable('prefix')
296
297                 try {
298                         String vnfAdapterMethod = execution.getVariable(prefix + 'vnfAdapterMethod')
299                         String vnfAdapterUrl = execution.getVariable(prefix + 'vnfAdapterUrl')
300                         String vnfAdapterRequest = execution.getVariable(prefix + 'vnfAdapterRequest')
301
302                         RESTConfig config = new RESTConfig(vnfAdapterUrl)
303                         RESTClient client = new RESTClient(config).
304                                 addHeader("Content-Type", "application/xml").
305                                 addAuthorizationHeader(execution.getVariable(prefix + "basicAuthHeaderValue"));
306
307                         APIResponse response;
308
309                         if ("GET".equals(vnfAdapterMethod)) {
310                                 response = client.httpGet()
311                         } else if ("PUT".equals(vnfAdapterMethod)) {
312                                 response = client.httpPut(vnfAdapterRequest)
313                         } else if ("POST".equals(vnfAdapterMethod)) {
314                                 response = client.httpPost(vnfAdapterRequest)
315                         } else if ("DELETE".equals(vnfAdapterMethod)) {
316                                 response = client.httpDelete(vnfAdapterRequest)
317                         } else {
318                                 String msg = 'Unsupported HTTP method "' + vnfAdapterMethod + '" in ' + method + ": " + e
319                                 logError(msg)
320                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
321                         }
322
323                         execution.setVariable(prefix + "vnfAdapterStatusCode", response.getStatusCode())
324                         execution.setVariable(prefix + "vnfAdapterResponse", response.getResponseBodyAsString())
325                 } catch (BpmnError e) {
326                         throw e
327                 } catch (Exception e) {
328                         String msg = 'Caught exception in ' + method + ": " + e
329                         logError(msg)
330                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
331                 }
332         }
333
334         public void processCallback(Execution execution){
335                 def method = getClass().getSimpleName() + '.processCallback(' +
336                         'execution=' + execution.getId() +
337                         ')'
338                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
339                 logDebug('Entered ' + method, isDebugLogEnabled)
340
341                 String prefix = execution.getVariable('prefix')
342                 String callback = execution.getVariable(prefix + 'callback')
343
344                 try {
345                         logDebug(getProcessKey(execution) + ": received callback:\n" + callback, isDebugLogEnabled)
346
347                         Node root = new XmlParser().parseText(callback)
348                         if (root.name().endsWith('Exception')) {
349                                 vnfAdapterWorkflowException(execution, callback)
350                         }
351
352                         // The XML callback is available to the calling flow in any case,
353                         // even if a WorkflowException was generated.
354                         execution.setVariable(getProcessKey(execution) + 'Response', callback)
355                         // TODO: Should deprecate use of processKey+Response variable for the response. Will use "WorkflowResponse" instead.
356                         execution.setVariable("WorkflowResponse", callback)
357                 } catch(BpmnError b){
358                         throw b
359                 } catch (Exception e) {
360                         e.printStackTrace()
361                         callback = callback == null || String.valueOf(callback).isEmpty() ? "NONE" : callback
362                         String msg = "Received error from VnfAdapter: " + callback
363                         logDebug(getProcessKey(execution) + ': ' + msg, isDebugLogEnabled)
364                         exceptionUtil.buildAndThrowWorkflowException(execution, 7020, msg)
365                 }
366         }
367
368         /**
369          * Tries to parse the response as XML to extract the information to create
370          * a WorkflowException.  If the response cannot be parsed, a more generic
371          * WorkflowException is created.
372          */
373         public void vnfAdapterWorkflowException(Execution execution, Object response) {
374                 try {
375                         Node root = new XmlParser().parseText(response)
376                         String category = getChildText(root, "category")
377                         category = category == null || category.isEmpty() ? "" : " category='" + category + "'"
378                         String message = getChildText(root, "message")
379                         message = message == null || message.isEmpty() ? "" : " message='" + message + "'"
380                         String rolledBack = getChildText(root, "rolledBack")
381                         rolledBack = rolledBack == null || rolledBack.isEmpty() ? "" : " rolledBack='" + rolledBack + "'"
382                         exceptionUtil.buildAndThrowWorkflowException(execution, 7020, "Received " + root.name() +
383                                 " from VnfAdapter:" + category + message + rolledBack);
384                 }catch(BpmnError b){
385                         throw b
386                 }catch (Exception e) {
387                         response = response == null || String.valueOf(response).isEmpty() ? "NONE" : response
388                         exceptionUtil.buildAndThrowWorkflowException(execution, 7020, "Received error from VnfAdapter: " + response)
389                 }
390         }
391
392         /**
393          * Gets the named child of the specified node.
394          * @param node the node
395          * @param name the child name
396          * @return the child node, or null if no such child exists
397          */
398         private Node getChild(Node node, String name) {
399                 for (Node child : node.children()) {
400                         if (child.name() == name) {
401                                 return child
402                         }
403                 }
404                 return null
405         }
406
407         /**
408          * Gets the text of the named child of the specified node.
409          * @param node the node
410          * @param name the child name
411          * @return the child node text, or null if no such child exists
412          */
413         private String getChildText(Node node, String name) {
414                 Node child = getChild(node, name)
415                 return child == null ? null : child.text()
416         }
417 }