Initial OpenECOMP MSO commit
[so.git] / adapters / mso-network-adapter / src / main / java / org / openecomp / mso / adapters / network / MsoNetworkAdapterImpl.java
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 org.openecomp.mso.adapters.network;
22
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import javax.jws.WebParam;
29 import javax.jws.WebService;
30 import javax.xml.ws.Holder;
31
32 import org.codehaus.jackson.JsonNode;
33 import org.codehaus.jackson.map.ObjectMapper;
34
35 import org.openecomp.mso.adapters.network.exceptions.NetworkException;
36 import org.openecomp.mso.cloud.CloudConfig;
37 import org.openecomp.mso.cloud.CloudConfigFactory;
38 import org.openecomp.mso.cloud.CloudSite;
39 import org.openecomp.mso.db.catalog.CatalogDatabase;
40 import org.openecomp.mso.db.catalog.beans.HeatTemplate;
41 import org.openecomp.mso.db.catalog.beans.NetworkResource;
42 import org.openecomp.mso.db.catalog.utils.MavenLikeVersioning;
43 import org.openecomp.mso.entity.MsoRequest;
44 import org.openecomp.mso.logger.MessageEnum;
45 import org.openecomp.mso.logger.MsoAlarmLogger;
46 import org.openecomp.mso.logger.MsoLogger;
47 import org.openecomp.mso.openstack.beans.HeatStatus;
48 import org.openecomp.mso.openstack.beans.NetworkInfo;
49 import org.openecomp.mso.openstack.beans.NetworkRollback;
50 import org.openecomp.mso.openstack.beans.NetworkStatus;
51 import org.openecomp.mso.openstack.beans.Pool;
52 import org.openecomp.mso.openstack.beans.StackInfo;
53 import org.openecomp.mso.openstack.beans.Subnet;
54 import org.openecomp.mso.openstack.exceptions.MsoAdapterException;
55 import org.openecomp.mso.openstack.exceptions.MsoException;
56 import org.openecomp.mso.openstack.exceptions.MsoExceptionCategory;
57 import org.openecomp.mso.openstack.utils.MsoCommonUtils;
58 import org.openecomp.mso.openstack.utils.MsoHeatUtils;
59 import org.openecomp.mso.openstack.utils.MsoHeatUtilsWithUpdate;
60 import org.openecomp.mso.openstack.utils.MsoNeutronUtils;
61 import org.openecomp.mso.openstack.utils.MsoNeutronUtils.NetworkType;
62 import org.openecomp.mso.properties.MsoPropertiesException;
63 import org.openecomp.mso.properties.MsoPropertiesFactory;
64
65 import static org.openecomp.mso.openstack.utils.MsoCommonUtils.isNullOrEmpty;
66
67 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.openecomp.mso.adapters.network.MsoNetworkAdapter", targetNamespace = "http://com.att.mso/network")
68 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
69
70         MsoPropertiesFactory msoPropertiesFactory=new MsoPropertiesFactory();
71
72         CloudConfigFactory cloudConfigFactory=new CloudConfigFactory();
73
74         private static final String AIC3_NW_PROPERTY= "org.openecomp.mso.adapters.network.aic3nw";
75         private static final String AIC3_NW="OS::ContrailV2::VirtualNetwork";
76         public static final String MSO_PROP_NETWORK_ADAPTER="MSO_PROP_NETWORK_ADAPTER";
77     private static final String VLANS = "vlans";
78     private static final String PHYSICAL_NETWORK = "physical_network";
79     private static final String UPDATE_NETWORK_CONTEXT = "UpdateNetwork";
80     private static final String NETWORK_ID = "network_id";
81     private static final String NETWORK_FQDN = "network_fqdn";
82     private static final String CREATE_NETWORK_CONTEXT = "CreateNetwork";
83     private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
84     private static final String NEUTRON_MODE = "NEUTRON";
85     private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA);
86     private static MsoAlarmLogger alarmLogger = new MsoAlarmLogger ();
87     protected CloudConfig cloudConfig;
88
89     /**
90      * Health Check web method. Does nothing but return to show the adapter is deployed.
91      */
92     @Override
93     public void healthCheck () {
94         LOGGER.debug ("Health check call in Network Adapter");
95     }
96
97     /**
98      * Do not use this constructor or the msoPropertiesFactory will be NULL.
99      *
100          * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
101      */
102     public MsoNetworkAdapterImpl() {
103     }
104
105     /**
106      * This constructor MUST be used if this class if called with the new operator.
107      * @param msoPropFactory
108
109      */
110     public MsoNetworkAdapterImpl(MsoPropertiesFactory msoPropFactory,CloudConfigFactory cloudConfigFact) {
111         this.msoPropertiesFactory = msoPropFactory;
112         this.cloudConfigFactory=cloudConfigFact;
113         cloudConfig = cloudConfigFactory.getCloudConfig ();
114     }
115
116     @Override
117     public void createNetwork (String cloudSiteId,
118                                String tenantId,
119                                String networkType,
120                                String networkName,
121                                String physicalNetworkName,
122                                List <Integer> vlans,
123                                Boolean failIfExists,
124                                Boolean backout,
125                                List <Subnet> subnets,
126                                MsoRequest msoRequest,
127                                Holder <String> networkId,
128                                Holder <String> neutronNetworkId,
129                                Holder <Map <String, String>> subnetIdMap,
130                                Holder <NetworkRollback> rollback) throws NetworkException {
131         Holder <String> networkFqdn = new Holder <String> ();
132         createNetwork (cloudSiteId,
133                        tenantId,
134                        networkType,
135                        networkName,
136                        physicalNetworkName,
137                        vlans,
138                        null,
139                        null,
140                        null,
141                        failIfExists,
142                        backout,
143                        subnets,
144                        null,
145                        null,
146                        msoRequest,
147                        networkId,
148                        neutronNetworkId,
149                        networkFqdn,
150                        subnetIdMap,
151                        rollback);
152     }
153
154     @Override
155     public void createNetworkContrail (String cloudSiteId,
156                                        String tenantId,
157                                        String networkType,
158                                        String networkName,
159                                        List <String> routeTargets,
160                                        String shared,
161                                        String external,
162                                        Boolean failIfExists,
163                                        Boolean backout,
164                                        List <Subnet> subnets,
165                                        List <String> policyFqdns,
166                                        List<String> routeTableFqdns,
167                                        MsoRequest msoRequest,
168                                        Holder <String> networkId,
169                                        Holder <String> neutronNetworkId,
170                                        Holder <String> networkFqdn,
171                                        Holder <Map <String, String>> subnetIdMap,
172                                        Holder <NetworkRollback> rollback) throws NetworkException {
173         createNetwork (cloudSiteId,
174                        tenantId,
175                        networkType,
176                        networkName,
177                        null,
178                        null,
179                        routeTargets,
180                        shared,
181                        external,
182                        failIfExists,
183                        backout,
184                        subnets,
185                        policyFqdns,
186                        routeTableFqdns,
187                        msoRequest,
188                        networkId,
189                        neutronNetworkId,
190                        networkFqdn,
191                        subnetIdMap,
192                        rollback);
193     }
194
195     /**
196      * This is the "Create Network" web service implementation.
197      * It will create a new Network of the requested type in the specified cloud
198      * and tenant. The tenant must exist at the time this service is called.
199      *
200      * If a network with the same name already exists, this can be considered a
201      * success or failure, depending on the value of the 'failIfExists' parameter.
202      *
203      * There will be a pre-defined set of network types defined in the MSO Catalog.
204      * All such networks will have a similar configuration, based on the allowable
205      * Openstack networking definitions. This includes basic networks, provider
206      * networks (with a single VLAN), and multi-provider networks (one or more VLANs)
207      *
208      * Initially, all provider networks must be "vlan" type, and multiple segments in
209      * a multi-provider network must be multiple VLANs on the same physical network.
210      *
211      * This service supports two modes of Network creation/update:
212      * - via Heat Templates
213      * - via Neutron API
214      * The network orchestration mode for each network type is declared in its
215      * catalog definition. All Heat-based templates must support some subset of
216      * the same input parameters: network_name, physical_network, vlan(s).
217      *
218      * The method returns the network ID and a NetworkRollback object. This latter
219      * object can be passed as-is to the rollbackNetwork operation to undo everything
220      * that was created. This is useful if a network is successfully created but
221      * the orchestration fails on a subsequent operation.
222      */
223
224     private void createNetwork (String cloudSiteId,
225                                String tenantId,
226                                String networkType,
227                                String networkName,
228                                String physicalNetworkName,
229                                List <Integer> vlans,
230                                List <String> routeTargets,
231                                String shared,
232                                String external,
233                                Boolean failIfExists,
234                                Boolean backout,
235                                List <Subnet> subnets,
236                                List <String> policyFqdns,
237                                List <String> routeTableFqdns,
238                                MsoRequest msoRequest,
239                                Holder <String> networkId,
240                                Holder <String> neutronNetworkId,
241                                Holder <String> networkFqdn,
242                                Holder <Map <String, String>> subnetIdMap,
243                                Holder <NetworkRollback> rollback) throws NetworkException {
244         MsoLogger.setLogContext (msoRequest);
245         MsoLogger.setServiceName ("CreateNetwork");
246
247         LOGGER.debug ("*** CREATE Network: " + networkName
248                       + " of type "
249                       + networkType
250                       + " in "
251                       + cloudSiteId
252                       + "/"
253                       + tenantId);
254
255         // Will capture execution time for metrics
256         long startTime = System.currentTimeMillis ();
257
258         // Build a default rollback object (no actions performed)
259         NetworkRollback networkRollback = new NetworkRollback ();
260         networkRollback.setCloudId (cloudSiteId);
261         networkRollback.setTenantId (tenantId);
262         networkRollback.setMsoRequest (msoRequest);
263
264         // tenant query is not required here.
265         // If the tenant doesn’t exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
266         // So this is just catching that error in a bit more obvious way up front.
267
268         cloudConfig = cloudConfigFactory.getCloudConfig ();
269         CloudSite cloudSite = cloudConfig.getCloudSite (cloudSiteId);
270         if (cloudSite == null)
271         {
272                 String error = "Configuration Error. Stack " + networkName + " in "
273                                 + cloudSiteId
274                                 + "/"
275                                 + tenantId
276                                 + ": "
277                                 + " CloudSite does not exist in MSO Configuration";
278                 LOGGER.error (MessageEnum.RA_CONFIG_EXC, error, "", "", MsoLogger.ErrorCode.DataError, "Configuration Error");
279             LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataError, error);
280                 // Set the detailed error as the Exception 'message'
281                 throw new NetworkException (error, MsoExceptionCategory.USERDATA);
282         }
283
284         try (CatalogDatabase db = getCatalogDb()) {
285             NetworkResource networkResource = networkCheck (db,
286                                                             startTime,
287                                                             networkType,
288                                                             networkName,
289                                                             physicalNetworkName,
290                                                             vlans,
291                                                             routeTargets,
292                                                             cloudSite);
293             String mode = networkResource.getOrchestrationMode ();
294             NetworkType neutronNetworkType = NetworkType.valueOf (networkResource.getNeutronNetworkType ());
295
296             if (NEUTRON_MODE.equals (mode)) {
297
298                 // Use an MsoNeutronUtils for all neutron commands
299                 MsoNeutronUtils neutron = new MsoNeutronUtils (MSO_PROP_NETWORK_ADAPTER, cloudConfigFactory);
300
301                 // See if the Network already exists (by name)
302                 NetworkInfo netInfo = null;
303                 long queryNetworkStarttime = System.currentTimeMillis ();
304                 try {
305                     netInfo = neutron.queryNetwork (networkName, tenantId, cloudSiteId);
306                     LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Response successfully received from OpenStack", "OpenStack", "QueryNetwork", null);
307                 } catch (MsoException me) {
308                     LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while querying network from OpenStack", "OpenStack", "QueryNetwork", null);
309                     String error = "Create Network (neutron): query network " + networkName
310                                    + " in "
311                                    + cloudSiteId
312                                    + "/"
313                                    + tenantId
314                                    + ": "
315                                    + me;
316                     LOGGER.error (MessageEnum.RA_QUERY_NETWORK_EXC, networkName, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception while querying network from OpenStack", me);
317                     me.addContext (CREATE_NETWORK_CONTEXT);
318                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while querying network from OpenStack");
319                     throw new NetworkException (me);
320                 }
321
322                 if (netInfo != null) {
323                     // Exists. If that's OK, return success with the network ID.
324                     // Otherwise, return an exception.
325                     if (failIfExists != null && failIfExists) {
326                         String error = "Create Nework: Network " + networkName
327                                        + " already exists in "
328                                        + cloudSiteId
329                                        + "/"
330                                        + tenantId
331                                        + " with ID " + netInfo.getId();
332                         LOGGER.error (MessageEnum.RA_NETWORK_ALREADY_EXIST, networkName, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Network already exists");
333                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
334                         throw new NetworkException(error, MsoExceptionCategory.USERDATA);
335                     } else {
336                         // Populate the outputs from the existing network.
337                         networkId.value = netInfo.getId ();
338                         neutronNetworkId.value = netInfo.getId ();
339                         rollback.value = networkRollback; // Default rollback - no updates performed
340                         String msg = "Found Existing network, status=" + netInfo.getStatus () + " for Neutron mode";
341                         LOGGER.warn (MessageEnum.RA_NETWORK_ALREADY_EXIST, networkName, cloudSiteId, tenantId, "", "", MsoLogger.ErrorCode.DataError, "Found Existing network, status=" + netInfo.getStatus ());
342                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, msg);
343                     }
344                     return;
345                 }
346
347                 long createNetworkStarttime = System.currentTimeMillis ();
348                 try {
349                     netInfo = neutron.createNetwork (cloudSiteId,
350                                                      tenantId,
351                                                      neutronNetworkType,
352                                                      networkName,
353                                                      physicalNetworkName,
354                                                      vlans);
355                     LOGGER.recordMetricEvent (createNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Response successfully received from OpenStack", "OpenStack", "CreateNetwork", null);
356                 } catch (MsoException me) {
357                         me.addContext (CREATE_NETWORK_CONTEXT);
358                     LOGGER.recordMetricEvent (createNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while communicate with OpenStack", "OpenStack", "CreateNetwork", null);
359                         String error = "Create Network: type " + neutronNetworkType
360                                    + " in "
361                                    + cloudSiteId
362                                    + "/"
363                                    + tenantId
364                                    + ": "
365                                    + me;
366                     LOGGER.error (MessageEnum.RA_CREATE_NETWORK_EXC, networkName, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception while communicate with OpenStack", me);
367                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
368
369                     throw new NetworkException (me);
370                 }
371
372                 // Note: ignoring MsoNetworkAlreadyExists because we already checked.
373
374                 // If reach this point, network creation is successful.
375                 // Since directly created via Neutron, networkId tracked by MSO is the same
376                 // as the neutron network ID.
377                 networkId.value = netInfo.getId ();
378                 neutronNetworkId.value = netInfo.getId ();
379
380                 networkRollback.setNetworkCreated (true);
381                 networkRollback.setNetworkId (netInfo.getId ());
382                 networkRollback.setNeutronNetworkId (netInfo.getId ());
383                 networkRollback.setNetworkType (networkType);
384
385                 LOGGER.debug ("Network " + networkName + " created, id = " + netInfo.getId ());
386             } else if ("HEAT".equals (mode)) {
387
388                 // Use an MsoHeatUtils for all Heat commands
389                 MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_NETWORK_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
390                 
391                 HeatTemplate heatTemplate = db.getHeatTemplate (networkResource.getTemplateId ());
392                 if (heatTemplate == null) {
393                     String error = "Network error - undefined Heat Template. Network Type = " + networkType;
394                     LOGGER.error (MessageEnum.RA_PARAM_NOT_FOUND, "Heat Template", "Network Type", networkType, "Openstack", "", MsoLogger.ErrorCode.DataError, "Network error - undefined Heat Template. Network Type = " + networkType);
395                     alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error); // Alarm on this
396                                                                                                      // error,
397                                                                                                      // configuration
398                                                                                                      // must be fixed
399                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataNotFound, error);
400                     throw new NetworkException (error, MsoExceptionCategory.INTERNAL);
401                 }
402
403                 LOGGER.debug ("Got HEAT Template from DB: " + heatTemplate.toString ());
404                 
405                 // "Fix" the template if it has CR/LF (getting this from Oracle)
406                 String template = heatTemplate.getHeatTemplate ();
407                 template = template.replaceAll ("\r\n", "\n");
408                 
409                 boolean aic3template=false;
410                 String aic3nw = AIC3_NW;
411                 try {
412                         aic3nw = msoPropertiesFactory.getMsoJavaProperties(MSO_PROP_NETWORK_ADAPTER).getProperty(AIC3_NW_PROPERTY, AIC3_NW);
413                         } catch (MsoPropertiesException e) {
414                                 String error = "Unable to get properties:" + MSO_PROP_NETWORK_ADAPTER;
415                                 LOGGER.error (MessageEnum.RA_CONFIG_EXC, error, "", "", MsoLogger.ErrorCode.DataError, "Exception - Unable to get properties", e);
416                         }
417                 
418                 if (template.contains(aic3nw))
419                         aic3template = true;
420                 
421                 // First, look up to see if the Network already exists (by name).
422                 // For HEAT orchestration of networks, the stack name will always match the network name
423                 StackInfo heatStack = null;
424                 long queryNetworkStarttime = System.currentTimeMillis ();
425                 try {
426                     heatStack = heat.queryStack (cloudSiteId, tenantId, networkName);
427                     LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Response successfully received from OpenStack", "OpenStack", "QueryNetwork", null);
428                 } catch (MsoException me) {
429                     me.addContext (CREATE_NETWORK_CONTEXT);
430                     LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, "Exception while querying stack from OpenStack", "OpenStack", "QueryNetwork", null);
431                         String error = "Create Network (heat): query network " + networkName
432                                    + " in "
433                                    + cloudSiteId
434                                    + "/"
435                                    + tenantId
436                                    + ": "
437                                    + me;
438                     LOGGER.error (MessageEnum.RA_QUERY_NETWORK_EXC, networkName, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception while querying stack from OpenStack", me);
439                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
440                     throw new NetworkException (me);
441                 }
442
443                 if (heatStack != null && !(heatStack.getStatus () == HeatStatus.NOTFOUND)) {
444                     // Stack exists. Return success or error depending on input directive
445                     if (failIfExists != null && failIfExists) {
446                         String error = "CreateNetwork: Stack " + networkName
447                                        + " already exists in "
448                                        + cloudSiteId
449                                        + "/"
450                                        + tenantId
451                                        + " as " + heatStack.getCanonicalName();
452                         LOGGER.error (MessageEnum.RA_NETWORK_ALREADY_EXIST, networkName, cloudSiteId, tenantId, "", "", MsoLogger.ErrorCode.DataError, "Network already exists");
453                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Conflict, error);
454                         throw new NetworkException(error, MsoExceptionCategory.USERDATA);
455                     } else {
456                         // Populate the outputs from the existing stack.
457                         networkId.value = heatStack.getCanonicalName ();
458                         neutronNetworkId.value = (String) heatStack.getOutputs ().get (NETWORK_ID);
459                         rollback.value = networkRollback; // Default rollback - no updates performed
460                         if (aic3template)
461                         {
462                                 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
463                         }
464                         Map <String, Object> outputs = heatStack.getOutputs ();
465                         Map <String, String> sMap = new HashMap <String, String> ();
466                         if (outputs != null) {
467                                 for (String key : outputs.keySet ()) {
468                                         if (key != null && key.startsWith ("subnet")) {
469                                                 if (aic3template) //one subnet_id output 
470                                                 {
471                                                          Map <String, String> map = getSubnetUUId(key, outputs, subnets); 
472                                                          sMap.putAll(map);
473                                                 }
474                                                 else //multiples subnet_%aaid% outputs
475                                                 {
476                                                         String subnetUUId = (String) outputs.get(key); 
477                                                         sMap.put (key.substring("subnet_id_".length()), subnetUUId);
478                                                 }
479                                         }
480                                 }
481                         }
482                         subnetIdMap.value = sMap;
483                         String msg = "Found Existing network stack, status=" + heatStack.getStatus () + " for Heat mode";
484                         LOGGER.warn (MessageEnum.RA_NETWORK_ALREADY_EXIST, networkName, cloudSiteId, tenantId, "", "", MsoLogger.ErrorCode.DataError, "Found Existing network stack, status=" + heatStack.getStatus ());
485                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Suc, "Found Existing network stack");
486                     }
487                     return;
488                 }
489
490                 // Ready to deploy the new Network
491                 // Build the common set of HEAT template parameters
492                 Map <String, Object> stackParams = populateNetworkParams (neutronNetworkType,
493                                                                           networkName,
494                                                                           physicalNetworkName,
495                                                                           vlans,
496                                                                           routeTargets,
497                                                                           shared, 
498                                                                           external,
499                                                                           aic3template);
500
501                 // Validate (and update) the input parameters against the DB definition
502                 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
503                 // and inputs were already validated.
504                 try {
505                     stackParams = heat.validateStackParams (stackParams, heatTemplate);
506                 } catch (IllegalArgumentException e) {
507                     String error = "Create Network: Configuration Error: " + e.getMessage ();
508                     LOGGER.error (MessageEnum.RA_CONFIG_EXC, e.getMessage(), "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception - Create Network, Configuration Error", e);
509                     alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error); // Alarm on this
510                                                                                                      // error,
511                                                                                                      // configuration
512                                                                                                      // must be fixed
513                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataError, error);
514                     // Input parameters were not valid
515                     throw new NetworkException (error, MsoExceptionCategory.INTERNAL);
516                 }
517
518                 if (subnets != null) {
519                         try {
520                                 if (aic3template)
521                                 {
522                                         template = mergeSubnetsAIC3 (template, subnets, stackParams);
523                                 }
524                                 else
525                                 {
526                                         template = mergeSubnets (template, subnets);
527                                 }
528                         } catch (MsoException me) {
529                                 me.addContext (CREATE_NETWORK_CONTEXT);
530                                 String error = "Create Network (heat): type " + neutronNetworkType
531                                                 + " in "
532                                                 + cloudSiteId
533                                                 + "/"
534                                                 + tenantId
535                                                 + ": "
536                                                 + me;
537                                 LOGGER.error (MessageEnum.RA_CREATE_NETWORK_EXC, neutronNetworkType.toString(), cloudSiteId, tenantId, "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception Create Network, merging subnets", me);
538                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error);
539                                 throw new NetworkException (me);
540                         }
541                 }
542
543                 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
544                     try {
545                         mergePolicyRefs (policyFqdns, stackParams);
546                     } catch (MsoException me) {
547                         me.addContext (CREATE_NETWORK_CONTEXT);
548                         String error = "Create Network (heat) mergePolicyRefs type " + neutronNetworkType
549                                        + " in "
550                                        + cloudSiteId
551                                        + "/"
552                                        + tenantId
553                                        + ": "
554                                        + me;
555                         LOGGER.error (MessageEnum.RA_CREATE_NETWORK_EXC, neutronNetworkType.toString(), cloudSiteId, tenantId, "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception Create Network, merging policyRefs", me);
556                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error);
557                         throw new NetworkException (me);
558                     }
559                 }
560                 
561                 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
562                     try {
563                         mergeRouteTableRefs (routeTableFqdns, stackParams);
564                     } catch (MsoException me) {
565                         me.addContext (CREATE_NETWORK_CONTEXT);
566                         String error = "Create Network (heat) mergeRouteTableRefs type " + neutronNetworkType
567                                        + " in "
568                                        + cloudSiteId
569                                        + "/"
570                                        + tenantId
571                                        + ": "
572                                        + me;
573                         LOGGER.error (MessageEnum.RA_CREATE_NETWORK_EXC, neutronNetworkType.toString(), cloudSiteId, tenantId, "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception Create Network, merging routeTableRefs", me);
574                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error);
575                         throw new NetworkException (me);
576                     }
577                 }
578
579                 // Deploy the network stack
580                 // Ignore MsoStackAlreadyExists exception because we already checked.
581                 long createStackStartTime = System.currentTimeMillis ();
582                 try {
583                         if (backout == null)
584                                 backout = true;
585                     heatStack = heat.createStack (cloudSiteId,
586                                                   tenantId,
587                                                   networkName,
588                                                   template,
589                                                   stackParams,
590                                                   true,
591                                                   heatTemplate.getTimeoutMinutes (),
592                                                   null,
593                                                   null,
594                                                   null,
595                                                   backout.booleanValue());
596                 } catch (MsoException me) {
597                     me.addContext (CREATE_NETWORK_CONTEXT);
598                         String error = "Create Network (heat): type " + neutronNetworkType
599                                    + " in "
600                                    + cloudSiteId
601                                    + "/"
602                                    + tenantId
603                                    + ": "
604                                    + me;
605                     LOGGER.error (MessageEnum.RA_CREATE_NETWORK_EXC, networkName, cloudSiteId, tenantId, "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception creating network", me);
606                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
607                     throw new NetworkException (me);
608                 }
609
610                 // Reach this point if createStack is successful.
611
612                 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
613                 // and the neutronNetworkId is the network UUID returned in stack outputs.
614                 networkId.value = heatStack.getCanonicalName ();
615                 neutronNetworkId.value = (String) heatStack.getOutputs ().get (NETWORK_ID);
616                 if (aic3template)
617                 {
618                         networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
619                 }
620                 Map <String, Object> outputs = heatStack.getOutputs ();
621                 Map <String, String> sMap = new HashMap <String, String> ();
622                 if (outputs != null) {
623                     for (String key : outputs.keySet ()) {
624                         if (key != null && key.startsWith ("subnet")) {
625                                 if (aic3template) //one subnet output expected
626                                         {
627                                                  Map <String, String> map = getSubnetUUId(key, outputs, subnets); 
628                                                  sMap.putAll(map);
629                                         }
630                                         else //multiples subnet_%aaid% outputs allowed
631                                         {
632                                                 String subnetUUId = (String) outputs.get(key); 
633                                                 sMap.put (key.substring("subnet_id_".length()), subnetUUId);
634                                         }
635                         }
636                     }
637                 }
638                 subnetIdMap.value = sMap;
639
640                 rollback.value = networkRollback;
641                 // Populate remaining rollback info and response parameters.
642                 networkRollback.setNetworkStackId (heatStack.getCanonicalName ());
643                 networkRollback.setNeutronNetworkId ((String) heatStack.getOutputs ().get (NETWORK_ID));
644                 networkRollback.setNetworkCreated (true);
645                 networkRollback.setNetworkType (networkType);
646
647                 LOGGER.debug ("Network " + networkName + " successfully created via HEAT");
648             }
649         }
650         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.Suc, "Successfully created network");
651         return;
652     }
653
654     @Override
655     public void updateNetwork (String cloudSiteId,
656                                String tenantId,
657                                String networkType,
658                                String networkId,
659                                String networkName,
660                                String physicalNetworkName,
661                                List <Integer> vlans,
662                                List <Subnet> subnets,
663                                MsoRequest msoRequest,
664                                Holder <Map <String, String>> subnetIdMap,
665                                Holder <NetworkRollback> rollback) throws NetworkException {
666         updateNetwork (cloudSiteId,
667                        tenantId,
668                        networkType,
669                        networkId,
670                        networkName,
671                        physicalNetworkName,
672                        vlans,
673                        null,
674                        null,
675                        null,
676                        subnets,
677                        null,
678                        null,
679                        msoRequest,
680                        subnetIdMap,
681                        rollback);
682
683     }
684
685     @Override
686     public void updateNetworkContrail (String cloudSiteId,
687                                        String tenantId,
688                                        String networkType,
689                                        String networkId,
690                                        String networkName,
691                                        List <String> routeTargets,
692                                        String shared,
693                                        String external,
694                                        List <Subnet> subnets,
695                                        List <String> policyFqdns,
696                                        List<String> routeTableFqdns,
697                                        MsoRequest msoRequest,
698                                        Holder <Map <String, String>> subnetIdMap,
699                                        Holder <NetworkRollback> rollback) throws NetworkException {
700         updateNetwork (cloudSiteId,
701                        tenantId,
702                        networkType,
703                        networkId,
704                        networkName,
705                        null,
706                        null,
707                        routeTargets,
708                        shared,
709                        external,
710                        subnets,
711                        policyFqdns,
712                        routeTableFqdns,
713                        msoRequest,
714                        subnetIdMap,
715                        rollback);
716     }
717
718     /**
719      * This is the "Update Network" web service implementation.
720      * It will update an existing Network of the requested type in the specified cloud
721      * and tenant. The typical use will be to replace the VLANs with the supplied
722      * list (to add or remove a VLAN), but other properties may be updated as well.
723      *
724      * There will be a pre-defined set of network types defined in the MSO Catalog.
725      * All such networks will have a similar configuration, based on the allowable
726      * Openstack networking definitions. This includes basic networks, provider
727      * networks (with a single VLAN), and multi-provider networks (one or more VLANs).
728      *
729      * Initially, all provider networks must currently be "vlan" type, and multi-provider
730      * networks must be multiple VLANs on the same physical network.
731      *
732      * This service supports two modes of Network update:
733      * - via Heat Templates
734      * - via Neutron API
735      * The network orchestration mode for each network type is declared in its
736      * catalog definition. All Heat-based templates must support some subset of
737      * the same input parameters: network_name, physical_network, vlan, segments.
738      *
739      * The method returns a NetworkRollback object. This object can be passed
740      * as-is to the rollbackNetwork operation to undo everything that was updated.
741      * This is useful if a network is successfully updated but orchestration
742      * fails on a subsequent operation.
743      */
744     private void updateNetwork (String cloudSiteId,
745                                String tenantId,
746                                String networkType,
747                                String networkId,
748                                String networkName,
749                                String physicalNetworkName,
750                                List <Integer> vlans,
751                                List <String> routeTargets,
752                                String shared,
753                                String external,
754                                List <Subnet> subnets,
755                                List <String> policyFqdns,
756                                List<String> routeTableFqdns,
757                                MsoRequest msoRequest,
758                                Holder <Map <String, String>> subnetIdMap,
759                                Holder <NetworkRollback> rollback) throws NetworkException {
760         MsoLogger.setLogContext (msoRequest);
761         MsoLogger.setServiceName ("UpdateNetwork");
762         LOGGER.debug ("***UPDATE Network adapter with Network: " + networkName
763                 + " of type "
764                 + networkType
765                 + " in "
766                 + cloudSiteId
767                 + "/"
768                 + tenantId);
769
770
771         // Will capture execution time for metrics
772         long startTime = System.currentTimeMillis ();
773
774         // Build a default rollback object (no actions performed)
775         NetworkRollback networkRollback = new NetworkRollback ();
776         networkRollback.setCloudId (cloudSiteId);
777         networkRollback.setTenantId (tenantId);
778         networkRollback.setMsoRequest (msoRequest);
779
780         cloudConfig = cloudConfigFactory.getCloudConfig ();
781         CloudSite cloudSite = cloudConfig.getCloudSite (cloudSiteId);
782         if (cloudSite == null) {
783                    String error = "UpdateNetwork: Configuration Error. Stack " + networkName + " in "
784                        + cloudSiteId
785                        + "/"
786                        + tenantId
787                        + ": "
788                        + " CloudSite does not exist in MSO Configuration";
789                    LOGGER.error (MessageEnum.RA_CONFIG_EXC, error, "Openstack", "", MsoLogger.ErrorCode.DataError, "CloudSite does not exist in MSO Configuration");
790                LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataError, error);
791                    // Set the detailed error as the Exception 'message'
792                    throw new NetworkException (error, MsoExceptionCategory.USERDATA);
793         }
794
795         try(CatalogDatabase db = getCatalogDb()) {
796             NetworkResource networkResource = networkCheck (db,
797                                                             startTime,
798                                                             networkType,
799                                                             networkName,
800                                                             physicalNetworkName,
801                                                             vlans,
802                                                             routeTargets,
803                                                             cloudSite);
804             String mode = networkResource.getOrchestrationMode ();
805             NetworkType neutronNetworkType = NetworkType.valueOf (networkResource.getNeutronNetworkType ());
806
807             // Use an MsoNeutronUtils for all Neutron commands
808             MsoNeutronUtils neutron = new MsoNeutronUtils (MSO_PROP_NETWORK_ADAPTER, cloudConfigFactory);
809
810             if (NEUTRON_MODE.equals (mode)) {
811
812                 // Verify that the Network exists
813                 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
814                 NetworkInfo netInfo = null;
815                 long queryNetworkStarttime = System.currentTimeMillis ();
816                 try {
817                     netInfo = neutron.queryNetwork (networkId, tenantId, cloudSiteId);
818                     LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryNetwork", null);
819                 } catch (MsoException me) {
820                     me.addContext (UPDATE_NETWORK_CONTEXT);
821                         String error = "Update Network (neutron): query " + networkId
822                                    + " in "
823                                    + cloudSiteId
824                                    + "/"
825                                    + tenantId
826                                    + ": "
827                                    + me;
828                     LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryNetwork", null);
829                     LOGGER.error (MessageEnum.RA_QUERY_NETWORK_EXC, networkId, cloudSiteId, tenantId, "OpenStack", "QueryNetwork", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - queryNetwork", me);
830                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
831                     throw new NetworkException (me);
832                 }
833
834                 if (netInfo == null) {
835                     String error = "Update Nework: Network " + networkId
836                                    + " does not exist in "
837                                    + cloudSiteId
838                                    + "/"
839                                    + tenantId;
840                     LOGGER.error (MessageEnum.RA_NETWORK_NOT_FOUND, networkId, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Network not found");
841                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
842                     // Does not exist. Throw an exception (can't update a non-existent network)
843                     throw new NetworkException(error, MsoExceptionCategory.USERDATA);
844                 }
845                 long updateNetworkStarttime = System.currentTimeMillis ();
846                 try {
847                     netInfo = neutron.updateNetwork (cloudSiteId,
848                                                      tenantId,
849                                                      networkId,
850                                                      neutronNetworkType,
851                                                      physicalNetworkName,
852                                                      vlans);
853                     LOGGER.recordMetricEvent (updateNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "UpdateNetwork", null);
854                 } catch (MsoException me) {
855                     me.addContext (UPDATE_NETWORK_CONTEXT);
856                     String error = "Update Network (neutron): " + networkId
857                                    + " in "
858                                    + cloudSiteId
859                                    + "/"
860                                    + tenantId
861                                    + ": "
862                                    + me;
863                     LOGGER.error (MessageEnum.RA_UPDATE_NETWORK_ERR, networkId, cloudSiteId, tenantId, "Openstack", "updateNetwork", MsoLogger.ErrorCode.DataError, "Exception - updateNetwork", me);
864                     LOGGER.recordMetricEvent (updateNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "UpdateNetwork", null);
865                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
866                     throw new NetworkException (me);
867                 }
868
869                 // Add the network ID and previously queried vlans to the rollback object
870                 networkRollback.setNetworkId (netInfo.getId ());
871                 networkRollback.setNeutronNetworkId (netInfo.getId ());
872                 networkRollback.setNetworkType (networkType);
873                 // Save previous parameters
874                 networkRollback.setNetworkName (netInfo.getName ());
875                 networkRollback.setPhysicalNetwork (netInfo.getProvider ());
876                 networkRollback.setVlans (netInfo.getVlans ());
877
878                 LOGGER.debug ("Network " + networkId + " updated, id = " + netInfo.getId ());
879             } else if ("HEAT".equals (mode)) {
880
881                 // Use an MsoHeatUtils for all Heat commands
882                 MsoHeatUtilsWithUpdate heat = new MsoHeatUtilsWithUpdate (MSO_PROP_NETWORK_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
883
884                 // First, look up to see that the Network already exists.
885                 // For Heat-based orchestration, the networkId is the network Stack ID.
886                 StackInfo heatStack = null;
887                 long queryStackStarttime = System.currentTimeMillis ();
888                 try {
889                     heatStack = heat.queryStack (cloudSiteId, tenantId, networkName);
890                     LOGGER.recordMetricEvent (queryStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", null);
891                 } catch (MsoException me) {
892                     me.addContext (UPDATE_NETWORK_CONTEXT);
893                     String error = "UpdateNetwork (heat): query " + networkName
894                                    + " in "
895                                    + cloudSiteId
896                                    + "/"
897                                    + tenantId
898                                    + ": "
899                                    + me;
900                     LOGGER.recordMetricEvent (queryStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryStack", null);
901                     LOGGER.error (MessageEnum.RA_QUERY_NETWORK_EXC, networkId, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Exception - QueryStack", me);
902                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
903                     throw new NetworkException (me);
904                 }
905
906                 if (heatStack == null || (heatStack.getStatus () == HeatStatus.NOTFOUND)) {
907                     String error = "UpdateNetwork: Stack " + networkName
908                                    + " does not exist in "
909                                    + cloudSiteId
910                                    + "/"
911                                    + tenantId;
912                     LOGGER.error (MessageEnum.RA_NETWORK_NOT_FOUND, networkId, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Network not found");
913                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
914                     // Network stack does not exist. Return an error
915                     throw new NetworkException(error, MsoExceptionCategory.USERDATA);
916                 }
917
918                 // Get the previous parameters for rollback
919                 Map <String, Object> heatParams = heatStack.getParameters ();
920
921                 String previousNetworkName = (String) heatParams.get ("network_name");
922                 String previousPhysicalNetwork = (String) heatParams.get (PHYSICAL_NETWORK);
923
924                 List <Integer> previousVlans = new ArrayList <Integer> ();
925                 String vlansParam = (String) heatParams.get (VLANS);
926                 if (vlansParam != null) {
927                     for (String vlan : vlansParam.split (",")) {
928                         try {
929                             previousVlans.add (Integer.parseInt (vlan));
930                         } catch (NumberFormatException e) {
931                             LOGGER.warn (MessageEnum.RA_VLAN_PARSE, networkId, vlansParam, "", "", MsoLogger.ErrorCode.DataError, "Exception - VLAN parse", e);
932                         }
933                     }
934                 }
935                 LOGGER.debug ("Update Stack:  Previous VLANS: " + previousVlans);
936
937                 // Ready to deploy the updated Network via Heat
938
939                 HeatTemplate heatTemplate = db.getHeatTemplate (networkResource.getTemplateId ());
940                 if (heatTemplate == null) {
941                     String error = "Network error - undefined Heat Template. Network Type=" + networkType;
942                     LOGGER.error (MessageEnum.RA_PARAM_NOT_FOUND, "Heat Template", "Network Type", networkType, "OpenStack", "getHeatTemplate", MsoLogger.ErrorCode.DataError, "Network error - undefined Heat Template. Network Type=" + networkType);
943                     alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
944                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
945                     throw new NetworkException (error, MsoExceptionCategory.INTERNAL);
946                 }
947
948                 LOGGER.debug ("Got HEAT Template from DB: " + heatTemplate.toString ());
949                 
950                 // "Fix" the template if it has CR/LF (getting this from Oracle)
951                 String template = heatTemplate.getHeatTemplate ();
952                 template = template.replaceAll ("\r\n", "\n");
953                 
954                 boolean aic3template=false;
955                 String aic3nw = AIC3_NW;
956                 try {
957                         aic3nw = msoPropertiesFactory.getMsoJavaProperties(MSO_PROP_NETWORK_ADAPTER).getProperty(AIC3_NW_PROPERTY, AIC3_NW);
958                         } catch (MsoPropertiesException e) {
959                                 String error = "Unable to get properties:" + MSO_PROP_NETWORK_ADAPTER;
960                                 LOGGER.error (MessageEnum.RA_CONFIG_EXC, error, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception - Unable to get properties", e);
961                         }
962                 if (template.contains(aic3nw))
963                         aic3template = true;
964
965                 // Build the common set of HEAT template parameters
966                 Map <String, Object> stackParams = populateNetworkParams (neutronNetworkType,
967                                                                           networkName,
968                                                                           physicalNetworkName,
969                                                                           vlans,
970                                                                           routeTargets,
971                                                                           shared,
972                                                                           external,
973                                                                           aic3template);
974
975                 // Validate (and update) the input parameters against the DB definition
976                 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
977                 try {
978                     stackParams = heat.validateStackParams (stackParams, heatTemplate);
979                 } catch (IllegalArgumentException e) {
980                     String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
981                     LOGGER.error (MessageEnum.RA_CONFIG_EXC, "Network Type=" + networkType, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception - UpdateNetwork: Configuration Error");
982                     alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
983                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.SchemaError, error);
984                     throw new NetworkException (error, MsoExceptionCategory.INTERNAL, e);
985                 }
986
987                 if (subnets != null) {
988                     try {
989                         if (aic3template)
990                                 {
991                                         template = mergeSubnetsAIC3 (template, subnets, stackParams);
992                                 }
993                                 else
994                                 {
995                                         template = mergeSubnets (template, subnets);
996                                 }
997                     } catch (MsoException me) {
998                         me.addContext (UPDATE_NETWORK_CONTEXT);
999                         String error = "Update Network (heat): type " + neutronNetworkType
1000                                        + " in "
1001                                        + cloudSiteId
1002                                        + "/"
1003                                        + tenantId
1004                                        + ": "
1005                                        + me;
1006                         LOGGER.error (MessageEnum.RA_UPDATE_NETWORK_ERR, neutronNetworkType.toString(), cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception - UpdateNetwork mergeSubnets ", me);
1007                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error);
1008                         throw new NetworkException (me);
1009                     }
1010                 }
1011
1012                 if (policyFqdns != null && aic3template) {
1013                     try {
1014                         mergePolicyRefs (policyFqdns, stackParams);
1015                     } catch (MsoException me) {
1016                         me.addContext (UPDATE_NETWORK_CONTEXT);
1017                         String error = "UpdateNetwork (heat) mergePolicyRefs type " + neutronNetworkType
1018                                        + " in "
1019                                        + cloudSiteId
1020                                        + "/"
1021                                        + tenantId
1022                                        + ": "
1023                                        + me;
1024                         LOGGER.error (MessageEnum.RA_UPDATE_NETWORK_ERR, neutronNetworkType.toString(), cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception - UpdateNetwork mergePolicyRefs", me);
1025                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error);
1026                         throw new NetworkException (me);
1027                     }
1028                 }
1029                 
1030                 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
1031                     try {
1032                         mergeRouteTableRefs (routeTableFqdns, stackParams);
1033                     } catch (MsoException me) {
1034                         me.addContext (UPDATE_NETWORK_CONTEXT);
1035                         String error = "UpdateNetwork (heat) mergeRouteTableRefs type " + neutronNetworkType
1036                                        + " in "
1037                                        + cloudSiteId
1038                                        + "/"
1039                                        + tenantId
1040                                        + ": "
1041                                        + me;
1042                         LOGGER.error (MessageEnum.RA_UPDATE_NETWORK_ERR, neutronNetworkType.toString(), cloudSiteId, tenantId, "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception - UpdateNetwork mergeRouteTableRefs", me);
1043                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.InternalError, error);
1044                         throw new NetworkException (me);
1045                     }
1046                 }
1047                 
1048                 // Update the network stack
1049                 // Ignore MsoStackNotFound exception because we already checked.
1050                 long updateStackStarttime = System.currentTimeMillis ();
1051                 try {
1052                     heatStack = heat.updateStack (cloudSiteId,
1053                                                   tenantId,
1054                                                   networkId,
1055                                                   template,
1056                                                   stackParams,
1057                                                   true,
1058                                                   heatTemplate.getTimeoutMinutes ());
1059                     LOGGER.recordMetricEvent (updateStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "UpdateStack", null);
1060                 } catch (MsoException me) {
1061                     me.addContext (UPDATE_NETWORK_CONTEXT);
1062                     String error = "Update Network: " + networkId + " in " + cloudSiteId + "/" + tenantId + ": " + me;
1063                     LOGGER.recordMetricEvent (updateStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "UpdateStack", null);
1064                     LOGGER.error (MessageEnum.RA_UPDATE_NETWORK_ERR, networkId, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception - update network", me);
1065                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1066                     throw new NetworkException (me);
1067                 }
1068
1069                 Map <String, Object> outputs = heatStack.getOutputs ();
1070                 Map <String, String> sMap = new HashMap <String, String> ();
1071                 if (outputs != null) {
1072                     for (String key : outputs.keySet ()) {
1073                         if (key != null && key.startsWith ("subnet")) {
1074                                 if (aic3template) //one subnet output expected
1075                                         {
1076                                                  Map <String, String> map = getSubnetUUId(key, outputs, subnets); 
1077                                                  sMap.putAll(map);
1078                                         }
1079                                         else //multiples subnet_%aaid% outputs allowed
1080                                         {
1081                                                 String subnetUUId = (String) outputs.get(key); 
1082                                                 sMap.put (key.substring("subnet_id_".length()), subnetUUId);
1083                                         }
1084                         }
1085                     }
1086                 }
1087                 subnetIdMap.value = sMap;
1088
1089                 // Reach this point if createStack is successful.
1090                 // Populate remaining rollback info and response parameters.
1091                 networkRollback.setNetworkStackId (heatStack.getCanonicalName ());
1092                 networkRollback.setNeutronNetworkId ((String) outputs.get (NETWORK_ID));
1093                 networkRollback.setNetworkType (networkType);
1094                 // Save previous parameters
1095                 networkRollback.setNetworkName (previousNetworkName);
1096                 networkRollback.setPhysicalNetwork (previousPhysicalNetwork);
1097                 networkRollback.setVlans (previousVlans);
1098
1099                 rollback.value = networkRollback;
1100
1101                 LOGGER.debug ("Network " + networkId + " successfully updated via HEAT");
1102             }
1103         }
1104         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully updated network");
1105         return;
1106     }
1107
1108     private NetworkResource networkCheck (CatalogDatabase db,
1109                                           long startTime,
1110                                           String networkType,
1111                                           String networkName,
1112                                           String physicalNetworkName,
1113                                           List <Integer> vlans,
1114                                           List <String> routeTargets,
1115                                           CloudSite cloudSite) throws NetworkException {
1116         // Retrieve the Network Resource definition
1117         NetworkResource networkResource = db.getNetworkResource (networkType);
1118         if (networkResource == null) {
1119             String error = "CreateNetwork: Unknown Network Type: " + networkType;
1120             LOGGER.error (MessageEnum.RA_UNKOWN_PARAM, "Network Type", networkType, "OpenStack", "", MsoLogger.ErrorCode.DataError, "CreateNetwork: Unknown Network Type");
1121
1122             throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1123         }
1124         LOGGER.debug ("Got Network definition from Catalog: " + networkResource.toString ());
1125
1126         String mode = networkResource.getOrchestrationMode ();
1127         NetworkType neutronNetworkType = NetworkType.valueOf (networkResource.getNeutronNetworkType ());
1128
1129         // All Networks are orchestrated via HEAT or Neutron
1130         if (!("HEAT".equals (mode) || NEUTRON_MODE.equals (mode))) {
1131             String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
1132             LOGGER.error (MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT, mode, "OpenStack", "", MsoLogger.ErrorCode.DataError, "CreateNetwork: Configuration Error");
1133             // Alarm on this error, configuration must be fixed
1134             alarmLogger.sendAlarm (MSO_CONFIGURATION_ERROR, MsoAlarmLogger.CRITICAL, error);
1135
1136             throw new NetworkException (error, MsoExceptionCategory.INTERNAL);
1137         }
1138
1139         MavenLikeVersioning aicV = new MavenLikeVersioning();
1140                 aicV.setVersion(cloudSite.getAic_version());
1141                 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin()) || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic >= min
1142                                 && (aicV.isTheSameVersion(networkResource.getAicVersionMax()) || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) //aic <= max
1143                 {
1144                         LOGGER.debug ("Network Type:" + networkType 
1145                                 +   " VersionMin:" + networkResource.getAicVersionMin() 
1146                                 +       " VersionMax:" + networkResource.getAicVersionMax() 
1147                                 +       " supported on Cloud:" + cloudSite.getId()
1148                                 +       " with AIC_Version:" + cloudSite.getAic_version());
1149                 }
1150                 else
1151                 {
1152                         String error = "Network Type:" + networkType
1153                                         + " Version_Min:" + networkResource.getAicVersionMin() 
1154                                         + " Version_Max:" + networkResource.getAicVersionMax() 
1155                                         + " not supported on Cloud:" + cloudSite.getId()
1156                                         + " with AIC_Version:"  + cloudSite.getAic_version();
1157                         LOGGER.error (MessageEnum.RA_CONFIG_EXC, error, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Network Type not supported on Cloud");
1158             LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataError, error);
1159                         throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1160                 }
1161
1162
1163         // Validate the Network parameters.
1164         String missing = validateNetworkParams (neutronNetworkType,
1165                                                 networkName,
1166                                                 physicalNetworkName,
1167                                                 vlans,
1168                                                 routeTargets);
1169         if (!missing.isEmpty ()) {
1170             String error = "Create Network: Missing parameters: " + missing;
1171             LOGGER.error (MessageEnum.RA_MISSING_PARAM, missing, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Create Network: Missing parameters");
1172
1173             throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1174         }
1175         return networkResource;
1176     }
1177
1178     @Override
1179     public void queryNetwork (String cloudSiteId,
1180                               String tenantId,
1181                               String networkNameOrId,
1182                               MsoRequest msoRequest,
1183                               Holder <Boolean> networkExists,
1184                               Holder <String> networkId,
1185                               Holder <String> neutronNetworkId,
1186                               Holder <NetworkStatus> status,
1187                               Holder <List <Integer>> vlans,
1188                               Holder <Map <String, String>> subnetIdMap) throws NetworkException {
1189         queryNetwork (cloudSiteId,
1190                       tenantId,
1191                       networkNameOrId,
1192                       msoRequest,
1193                       networkExists,
1194                       networkId,
1195                       neutronNetworkId,
1196                       status,
1197                       vlans,
1198                       null,
1199                       subnetIdMap);
1200     }
1201
1202     @Override
1203     public void queryNetworkContrail (String cloudSiteId,
1204                                       String tenantId,
1205                                       String networkNameOrId,
1206                                       MsoRequest msoRequest,
1207                                       Holder <Boolean> networkExists,
1208                                       Holder <String> networkId,
1209                                       Holder <String> neutronNetworkId,
1210                                       Holder <NetworkStatus> status,
1211                                       Holder <List <String>> routeTargets,
1212                                       Holder <Map <String, String>> subnetIdMap) throws NetworkException {
1213         queryNetwork (cloudSiteId,
1214                       tenantId,
1215                       networkNameOrId,
1216                       msoRequest,
1217                       networkExists,
1218                       networkId,
1219                       neutronNetworkId,
1220                       status,
1221                       null,
1222                       routeTargets,
1223                       subnetIdMap);
1224     }
1225
1226     /**
1227      * This is the queryNetwork method. It returns the existence and status of
1228      * the specified network, along with its Neutron UUID and list of VLANs.
1229      * This method attempts to find the network using both Heat and Neutron.
1230      * Heat stacks are first searched based on the provided network name/id.
1231      * If none is found, the Neutron is directly queried.
1232      */
1233     private void queryNetwork (String cloudSiteId,
1234                               String tenantId,
1235                               String networkNameOrId,
1236                               MsoRequest msoRequest,
1237                               Holder <Boolean> networkExists,
1238                               Holder <String> networkId,
1239                               Holder <String> neutronNetworkId,
1240                               Holder <NetworkStatus> status,
1241                               Holder <List <Integer>> vlans,
1242                               Holder <List <String>> routeTargets,
1243                               Holder <Map <String, String>> subnetIdMap) throws NetworkException {
1244         MsoLogger.setLogContext (msoRequest);
1245         MsoLogger.setServiceName ("QueryNetwork");
1246         LOGGER.debug ("*** QUERY Network with Network: " + networkNameOrId
1247                 + " in "
1248                 + cloudSiteId
1249                 + "/"
1250                 + tenantId);
1251
1252         // Will capture execution time for metrics
1253         long startTime = System.currentTimeMillis ();
1254
1255         if (isNullOrEmpty (cloudSiteId)
1256             || isNullOrEmpty(tenantId)
1257             || isNullOrEmpty(networkNameOrId)) {
1258
1259             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1260             LOGGER.error (MessageEnum.RA_MISSING_PARAM, "cloudSiteId or tenantId or networkNameOrId", "OpenStack", "", MsoLogger.ErrorCode.DataError, "Missing mandatory parameter cloudSiteId, tenantId or networkId");
1261             LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
1262             throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1263         }
1264
1265         cloudConfig = cloudConfigFactory.getCloudConfig ();
1266         CloudSite cloudSite = cloudConfig.getCloudSite (cloudSiteId);
1267         if (cloudSite == null)
1268         {
1269                 String error = "Configuration Error. Stack " + networkNameOrId + " in "
1270                                 + cloudSiteId
1271                                 + "/"
1272                                 + tenantId
1273                                 + ": "
1274                                 + " CloudSite does not exist in MSO Configuration";
1275                 LOGGER.error (MessageEnum.RA_CONFIG_EXC, error, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Configuration Error");
1276             LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.DataError, error);
1277                 // Set the detailed error as the Exception 'message'
1278                 throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1279         }
1280
1281         // Use MsoNeutronUtils for all NEUTRON commands
1282         MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_NETWORK_ADAPTER,msoPropertiesFactory,cloudConfigFactory);
1283         MsoNeutronUtils neutron = new MsoNeutronUtils (MSO_PROP_NETWORK_ADAPTER, cloudConfigFactory);
1284
1285         String mode = null;
1286         String neutronId = null;
1287         // Try Heat first, since networks may be named the same as the Heat stack
1288         StackInfo heatStack = null;
1289         long queryStackStarttime = System.currentTimeMillis ();
1290         try {
1291             heatStack = heat.queryStack (cloudSiteId, tenantId, networkNameOrId);
1292             LOGGER.recordMetricEvent (queryStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryStack", null);
1293         } catch (MsoException me) {
1294                 me.addContext ("QueryNetwork");
1295             String error = "Query Network (heat): " + networkNameOrId
1296                            + " in "
1297                            + cloudSiteId
1298                            + "/"
1299                            + tenantId
1300                            + ": "
1301                            + me;
1302             LOGGER.recordMetricEvent (queryStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "BPMN", "QueryStack", null);
1303             LOGGER.error (MessageEnum.RA_QUERY_NETWORK_EXC, networkNameOrId, cloudSiteId, tenantId, "OpenStack", "queryStack", MsoLogger.ErrorCode.DataError, "Exception - Query Network (heat)", me);
1304             LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1305             throw new NetworkException (me);
1306         }
1307
1308         // Populate the outputs based on the returned Stack information
1309         if (heatStack != null && heatStack.getStatus () != HeatStatus.NOTFOUND) {
1310             // Found it. Get the neutronNetworkId for further query
1311             Map <String, Object> outputs = heatStack.getOutputs ();
1312             neutronId = (String) outputs.get (NETWORK_ID);
1313             mode = "HEAT";
1314
1315             Map <String, String> sMap = new HashMap <String, String> ();
1316             if (outputs != null) {
1317                 for (String key : outputs.keySet ()) {
1318                         if (key != null && key.startsWith ("subnet_id_")) //multiples subnet_%aaid% outputs
1319                         {
1320                                 String subnetUUId = (String) outputs.get(key); 
1321                                 sMap.put (key.substring("subnet_id_".length()), subnetUUId);
1322                         }
1323                         else if (key != null && key.startsWith ("subnet")) //one subnet output expected
1324                         {
1325                                 Map <String, String> map = getSubnetUUId(key, outputs, null); 
1326                                 sMap.putAll(map);
1327                         }
1328
1329                 }
1330             }
1331             subnetIdMap.value = sMap;
1332         } else {
1333             // Input ID was not a Heat stack ID. Try it directly in Neutron
1334             neutronId = networkNameOrId;
1335             mode = NEUTRON_MODE;
1336         }
1337
1338         // Query directly against the Neutron Network for the details
1339         // no RouteTargets available for ContrailV2 in neutron net-show
1340         // networkId is heatStackId
1341         long queryNetworkStarttime = System.currentTimeMillis ();
1342         try {
1343             NetworkInfo netInfo = neutron.queryNetwork (neutronId, tenantId, cloudSiteId);
1344             LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "QueryNetwork", null);
1345             if (netInfo != null) {
1346                 // Found. Populate the output elements
1347                 networkExists.value = Boolean.TRUE;
1348                 if ("HEAT".equals (mode)) {
1349                     networkId.value = heatStack.getCanonicalName ();
1350                 } else {
1351                     networkId.value = netInfo.getId ();
1352                 }
1353                 neutronNetworkId.value = netInfo.getId ();
1354                 status.value = netInfo.getStatus ();
1355                 if (vlans != null)
1356                         vlans.value = netInfo.getVlans ();
1357
1358                 LOGGER.debug ("Network " + networkNameOrId
1359                               + " found ("
1360                               + mode
1361                               + "), ID = "
1362                               + networkId.value
1363                               + ("HEAT".equals (mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
1364             } else {
1365                 // Not found. Populate the status fields, leave the rest null
1366                 networkExists.value = Boolean.FALSE;
1367                 status.value = NetworkStatus.NOTFOUND;
1368                 neutronNetworkId.value = null;
1369                 if (vlans != null)
1370                         vlans.value = new ArrayList <Integer> ();
1371
1372                 LOGGER.debug ("Network " + networkNameOrId + " not found");
1373             }
1374         } catch (MsoException me) {
1375             me.addContext ("QueryNetwork");
1376             String error = "Query Network (neutron): " + networkNameOrId
1377                            + " in "
1378                            + cloudSiteId
1379                            + "/"
1380                            + tenantId
1381                            + ": "
1382                            + me;
1383             LOGGER.recordMetricEvent (queryNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "QueryNetwork", null);
1384             LOGGER.error (MessageEnum.RA_QUERY_NETWORK_EXC, networkNameOrId, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.DataError, "Exception - Query Network (neutron)", me);
1385             LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1386             throw new NetworkException (me);
1387         }
1388         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully queried network");
1389         return;
1390     }
1391
1392     /**
1393      * This is the "Delete Network" web service implementation.
1394      * It will delete a Network in the specified cloud and tenant.
1395      *
1396      * If the network is not found, it is treated as a success.
1397      *
1398      * This service supports two modes of Network creation/update/delete:
1399      * - via Heat Templates
1400      * - via Neutron API
1401      * The network orchestration mode for each network type is declared in its
1402      * catalog definition.
1403      *
1404      * For Heat-based orchestration, the networkId should be the stack ID.
1405      * For Neutron-based orchestration, the networkId should be the Neutron network UUID.
1406      *
1407      * The method returns nothing on success. Rollback is not possible for delete
1408      * commands, so any failure on delete will require manual fallout in the client.
1409      */
1410     @Override
1411     public void deleteNetwork (String cloudSiteId,
1412                                String tenantId,
1413                                String networkType,
1414                                String networkId,
1415                                MsoRequest msoRequest,
1416                                Holder <Boolean> networkDeleted) throws NetworkException {
1417         MsoLogger.setLogContext (msoRequest);
1418         MsoLogger.setServiceName ("DeleteNetwork");
1419         LOGGER.debug ("*** DELETE Network adapter with Network: " + networkId
1420                                       + " in "
1421                                       + cloudSiteId
1422                                       + "/"
1423                                       + tenantId);
1424
1425         // Will capture execution time for metrics
1426         long startTime = System.currentTimeMillis ();
1427
1428         try (CatalogDatabase db = getCatalogDb()) {
1429             if (isNullOrEmpty (cloudSiteId)
1430                             || isNullOrEmpty(tenantId)
1431                             || isNullOrEmpty(networkId)) {
1432                 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1433                 LOGGER.error (MessageEnum.RA_MISSING_PARAM, "cloudSiteId or tenantId or networkId", "Openstack", "", MsoLogger.ErrorCode.DataError, "Missing mandatory parameter cloudSiteId, tenantId or networkId");
1434                 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
1435                 throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1436             }
1437
1438             // Retrieve the Network Resource definition
1439             NetworkResource networkResource = db.getNetworkResource (networkType);
1440             if (networkResource == null) {
1441                 String error = "Unknown Network Type: " + networkType;
1442                 LOGGER.error (MessageEnum.RA_UNKOWN_PARAM, "Network Type", networkType, "Openstack", "", MsoLogger.ErrorCode.DataError, "Unknown Network Type");
1443                 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
1444                 throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1445             }
1446             LOGGER.debug ("Got Network definition from Catalog: " + networkResource.toString ());
1447
1448             String mode = networkResource.getOrchestrationMode ();
1449
1450             if (NEUTRON_MODE.equals (mode)) {
1451
1452                 // Use MsoNeutronUtils for all NEUTRON commands
1453                 MsoNeutronUtils neutron = new MsoNeutronUtils (MSO_PROP_NETWORK_ADAPTER, cloudConfigFactory);
1454                 long deleteNetworkStarttime = System.currentTimeMillis ();
1455                 try {
1456                     // The deleteNetwork function in MsoNeutronUtils returns success if the network
1457                     // was not found. So don't bother to query first.
1458                     boolean deleted = neutron.deleteNetwork (networkId, tenantId, cloudSiteId);
1459                     LOGGER.recordMetricEvent (deleteNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "DeleteNetwork", null);
1460                     networkDeleted.value = deleted;
1461                 } catch (MsoException me) {
1462                     me.addContext ("DeleteNetwork");
1463                         String error = "Delete Network (neutron): " + networkId
1464                                    + " in "
1465                                    + cloudSiteId
1466                                    + "/"
1467                                    + tenantId
1468                                    + ": "
1469                                    + me;
1470                     LOGGER.recordMetricEvent (deleteNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "DeleteNetwork", null);
1471                     LOGGER.error (MessageEnum.RA_DELETE_NETWORK_EXC, networkId, cloudSiteId, tenantId, "Openstack", "", MsoLogger.ErrorCode.DataError, "Delete Network (neutron)", me);
1472                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1473                     throw new NetworkException (me);
1474                 }
1475             } else if ("HEAT".equals (mode)) {
1476                 long deleteStackStarttime = System.currentTimeMillis ();
1477                 // Use MsoHeatUtils for all HEAT commands
1478                 MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_NETWORK_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
1479
1480                 try {
1481                     // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack was deleted.
1482                     //  So query first to report back if stack WAS deleted or just NOTOFUND 
1483                         StackInfo heatStack = null;
1484                         heatStack = heat.queryStack(cloudSiteId, tenantId, networkId);
1485                         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND)
1486                         {
1487                                 heat.deleteStack (tenantId, cloudSiteId, networkId, true);
1488                                 LOGGER.recordMetricEvent (deleteStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "DeleteStack", null);
1489                                 networkDeleted.value = true;
1490                         }
1491                         else
1492                         {
1493                                 networkDeleted.value = false;
1494                         }
1495                 } catch (MsoException me) {
1496                     me.addContext ("DeleteNetwork");
1497                         String error = "Delete Network (heat): " + networkId
1498                                    + " in "
1499                                    + cloudSiteId
1500                                    + "/"
1501                                    + tenantId
1502                                    + ": "
1503                                    + me;
1504                     LOGGER.recordMetricEvent (deleteStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "DeleteStack", null);
1505                     LOGGER.error (MessageEnum.RA_DELETE_NETWORK_EXC, networkId, cloudSiteId, tenantId, "Openstack", "", MsoLogger.ErrorCode.DataError, "Delete Network (heat)", me);
1506                     LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1507                     throw new NetworkException (me);
1508                 }
1509             }
1510         }
1511
1512         // On success, nothing is returned.
1513         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully deleted network");
1514         return;
1515     }
1516
1517     public CatalogDatabase getCatalogDb() {
1518         return new CatalogDatabase();
1519     }
1520
1521     /**
1522      * This web service endpoint will rollback a previous Create VNF operation.
1523      * A rollback object is returned to the client in a successful creation
1524      * response. The client can pass that object as-is back to the rollbackVnf
1525      * operation to undo the creation.
1526      *
1527      * The rollback includes removing the VNF and deleting the tenant if the
1528      * tenant did not exist prior to the VNF creation.
1529      */
1530     @Override
1531     public void rollbackNetwork (NetworkRollback rollback) throws NetworkException {
1532         MsoLogger.setServiceName ("RollbackNetwork");
1533         // Will capture execution time for metrics
1534         long startTime = System.currentTimeMillis ();
1535
1536         if (rollback == null) {
1537                 LOGGER.error (MessageEnum.RA_ROLLBACK_NULL, "Openstack", "", MsoLogger.ErrorCode.DataError, "rollback is null");
1538             LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, "No action to perform");
1539             return;
1540         }
1541
1542         MsoLogger.setLogContext (rollback.getMsoRequest());
1543
1544         // Get the elements of the VnfRollback object for easier access
1545         String cloudSiteId = rollback.getCloudId ();
1546         String tenantId = rollback.getTenantId ();
1547         String networkId = rollback.getNetworkStackId ();
1548         String networkType = rollback.getNetworkType ();
1549
1550         LOGGER.debug ("*** ROLLBACK Network " + networkId + " in " + cloudSiteId + "/" + tenantId);
1551
1552         // rollback may be null (e.g. if network already existed when Create was called)
1553         // Get a handle to the Catalog Database
1554
1555         try (CatalogDatabase db = getCatalogDb()){
1556
1557             // Retrieve the Network Resource definition
1558             NetworkResource networkResource = db.getNetworkResource (networkType);
1559             if (networkResource == null) {
1560                 String error = "Rollback Network: Unknown Network Type: " + networkType;
1561                 LOGGER.error (MessageEnum.RA_UNKOWN_PARAM, "Network Type", networkType, "Openstack", "", MsoLogger.ErrorCode.DataError, "Rollback Network: Unknown Network Type");
1562                 LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.BadRequest, error);
1563                 throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1564             }
1565             LOGGER.debug ("Got Network definition from Catalog: " + networkResource.toString ());
1566
1567             String mode = networkResource.getOrchestrationMode ();
1568
1569             if (rollback.getNetworkCreated ()) {
1570                 // Rolling back a newly created network, so delete it.
1571                 if (NEUTRON_MODE.equals (mode)) {
1572                     // Use MsoNeutronUtils for all NEUTRON commands
1573                     MsoNeutronUtils neutron = new MsoNeutronUtils (MSO_PROP_NETWORK_ADAPTER, cloudConfigFactory);
1574                     long deleteNetworkStarttime = System.currentTimeMillis ();
1575                     try {
1576                         // The deleteNetwork function in MsoNeutronUtils returns success if the network
1577                         // was not found. So don't bother to query first.
1578                         neutron.deleteNetwork (networkId, tenantId, cloudSiteId);
1579                         LOGGER.recordMetricEvent (deleteNetworkStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "DeleteNetwork", null);
1580                     } catch (MsoException me) {
1581                         me.addContext ("RollbackNetwork");
1582                         String error = "Rollback Network (neutron): " + networkId
1583                                        + " in "
1584                                        + cloudSiteId
1585                                        + "/"
1586                                        + tenantId
1587                                        + ": "
1588                                        + me;
1589                         LOGGER.recordMetricEvent (deleteNetworkStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "DeleteNetwork", null);
1590                         LOGGER.error (MessageEnum.RA_DELETE_NETWORK_EXC, networkId, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - Rollback Network (neutron)", me);
1591                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1592                         throw new NetworkException (me);
1593                     }
1594                 } else if ("HEAT".equals (mode)) {
1595                     // Use MsoHeatUtils for all HEAT commands
1596                     MsoHeatUtils heat = new MsoHeatUtils (MSO_PROP_NETWORK_ADAPTER, msoPropertiesFactory,cloudConfigFactory);
1597                     long deleteStackStarttime = System.currentTimeMillis ();
1598                     try {
1599                         // The deleteStack function in MsoHeatUtils returns success if the stack
1600                         // was not found. So don't bother to query first.
1601                         heat.deleteStack (tenantId, cloudSiteId, networkId, true);
1602                         LOGGER.recordMetricEvent (deleteStackStarttime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully received response from Open Stack", "OpenStack", "DeleteStack", null);
1603                     } catch (MsoException me) {
1604                         me.addContext ("RollbackNetwork");
1605                         String error = "Rollback Network (heat): " + networkId
1606                                        + " in "
1607                                        + cloudSiteId
1608                                        + "/"
1609                                        + tenantId
1610                                        + ": "
1611                                        + me;
1612                         LOGGER.recordMetricEvent (deleteStackStarttime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error, "OpenStack", "DeleteStack", null);
1613                         LOGGER.error (MessageEnum.RA_DELETE_NETWORK_EXC, networkId, cloudSiteId, tenantId, "OpenStack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception - Rollback Network (heat)", me);
1614                         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.ERROR, MsoLogger.ResponseCode.CommunicationError, error);
1615                         throw new NetworkException (me);
1616                     }
1617                 }
1618             } 
1619         }
1620         LOGGER.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, "Successfully rolled back network");
1621         return;
1622     }
1623
1624     private String validateNetworkParams (NetworkType neutronNetworkType,
1625                                           String networkName,
1626                                           String physicalNetwork,
1627                                           List <Integer> vlans,
1628                                           List <String> routeTargets) {
1629         String sep = "";
1630         StringBuilder missing = new StringBuilder ();
1631         if (isNullOrEmpty(networkName)) {
1632             missing.append ("networkName");
1633             sep = ",";
1634         }
1635
1636         if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1637             if (isNullOrEmpty(physicalNetwork)) {
1638                 missing.append (sep).append ("physicalNetworkName");
1639                 sep = ",";
1640             }
1641             if (vlans == null || vlans.isEmpty ()) {
1642                 missing.append (sep).append (VLANS);
1643             }
1644         }
1645
1646         return missing.toString ();
1647     }
1648
1649     private Map <String, Object> populateNetworkParams (NetworkType neutronNetworkType,
1650                                                         String networkName,
1651                                                         String physicalNetwork,
1652                                                         List <Integer> vlans,
1653                                                         List <String> routeTargets,
1654                                                         String shared,
1655                                                         String external,
1656                                                         boolean aic3template) {
1657         // Build the common set of HEAT template parameters
1658         Map <String, Object> stackParams = new HashMap <String, Object> ();
1659         stackParams.put ("network_name", networkName);
1660
1661         if (neutronNetworkType == NetworkType.PROVIDER) {
1662             // For Provider type
1663             stackParams.put (PHYSICAL_NETWORK, physicalNetwork);
1664             stackParams.put ("vlan", vlans.get (0).toString ());
1665         } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1666             // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1667             // It supports all ProviderNet properties except segmentation_id, and adds a
1668             // comma-separated-list of VLANs as a "segments" property.
1669             // Note that this does not match the Neutron definition of Multi-Provider network,
1670             // which contains a list of 'segments', each having physical_network, network_type,
1671             // and segmentation_id.
1672             StringBuilder buf = new StringBuilder ();
1673             String sep = "";
1674             for (Integer vlan : vlans) {
1675                 buf.append (sep).append (vlan.toString ());
1676                 sep = ",";
1677             }
1678             String csl = buf.toString ();
1679
1680             stackParams.put (PHYSICAL_NETWORK, physicalNetwork);
1681             stackParams.put (VLANS, csl);
1682         }
1683         if (routeTargets != null && !routeTargets.isEmpty()) {
1684             StringBuilder buf = new StringBuilder ();
1685             String sep = "";
1686             for (String rt : routeTargets) {
1687                 if (!isNullOrEmpty(rt))
1688                 {
1689                         if (aic3template)
1690                                 buf.append (sep).append ("target:" + rt.toString ());
1691                         else
1692                                 buf.append (sep).append (rt.toString ());
1693
1694                         sep = ",";
1695                 }
1696             }
1697             String csl = buf.toString ();
1698
1699             stackParams.put ("route_targets", csl);
1700         }
1701         if (isNullOrEmpty(shared)) {
1702             stackParams.put ("shared", "False");
1703         } else {
1704             stackParams.put ("shared", shared);
1705         }
1706         if (isNullOrEmpty(external)) {
1707             stackParams.put ("external", "False");
1708         } else {
1709             stackParams.put ("external", external);
1710         }
1711         return stackParams;
1712     }
1713     
1714
1715     
1716     /** policyRef_list structure in stackParams
1717     [
1718      {
1719          "network_policy_refs_data_sequence": {
1720              "network_policy_refs_data_sequence_major": "1",
1721              "network_policy_refs_data_sequence_minor": "0"
1722          }
1723      },
1724      {
1725          "network_policy_refs_data_sequence": {
1726              "network_policy_refs_data_sequence_major": "2",
1727              "network_policy_refs_data_sequence_minor": "0"
1728          }
1729      }
1730         ]
1731     **/
1732     private void mergePolicyRefs(List <String> pFqdns, Map <String, Object> stackParams) throws MsoException {
1733                 //Resource Property
1734                 List<ContrailPolicyRef> prlist =  new ArrayList <ContrailPolicyRef> ();
1735                 int index = 1;
1736                 for (String pf : pFqdns) {
1737                         if (!isNullOrEmpty(pf))
1738                         {
1739                                 ContrailPolicyRef pr = new ContrailPolicyRef();
1740                                 pr.populate(String.valueOf(index), "0");
1741                                 index++;
1742                                 LOGGER.debug("Contrail PolicyRefs Data:" + pr.toString());
1743                                 prlist.add(pr);
1744                         }
1745                 }
1746                 
1747                 JsonNode node = null;
1748                 try
1749                 {
1750                         ObjectMapper mapper = new ObjectMapper(); 
1751                         node = mapper.convertValue(prlist, JsonNode.class);
1752                         String jsonString = mapper.writeValueAsString(prlist);
1753                         LOGGER.debug("Json PolicyRefs Data:" + jsonString);
1754                 }
1755                 catch (Exception e)
1756                 {
1757                         String error = "Error creating JsonNode for policyRefs Data";
1758                         LOGGER.error (MessageEnum.RA_MARSHING_ERROR, error, "Openstack", "", MsoLogger.ErrorCode.BusinessProcesssError, "Exception creating JsonNode for policyRefs Data", e);
1759                         throw new MsoAdapterException (error);
1760                 }
1761                 //update parameters
1762                 if (pFqdns != null && node != null)
1763                 {
1764                         StringBuilder buf = new StringBuilder ();
1765                         String sep = "";
1766                         for (String pf : pFqdns) {
1767                                 if (!isNullOrEmpty(pf))
1768                                 {
1769                                         buf.append (sep).append (pf.toString ());
1770                                         sep = ",";
1771                                 }
1772                         }
1773                         String csl = buf.toString ();
1774                         stackParams.put ("policy_refs", csl);
1775                         stackParams.put ("policy_refsdata", node);
1776                 }
1777
1778                 LOGGER.debug ("StackParams updated with policy refs");
1779                 return;
1780     }
1781     
1782     private void mergeRouteTableRefs(List <String> rtFqdns, Map <String, Object> stackParams) throws MsoException {
1783                 
1784                 //update parameters
1785                 if (rtFqdns != null)
1786                 {
1787                         StringBuilder buf = new StringBuilder ();
1788                         String sep = "";
1789                         for (String rtf : rtFqdns) {
1790                                 if (!isNullOrEmpty(rtf))
1791                                 {
1792                                         buf.append (sep).append (rtf.toString ());
1793                                         sep = ",";
1794                                 }
1795                         }
1796                         String csl = buf.toString ();
1797                         stackParams.put ("route_table_refs", csl);
1798                 }
1799
1800                 LOGGER.debug ("StackParams updated with route_table refs");
1801                 return;
1802     }
1803
1804     
1805     /*** Subnet Output structure from Juniper
1806      {
1807     "ipam_subnets": [
1808         {
1809             "subnet": {
1810                 "ip_prefix": "10.100.1.0",
1811                 "ip_prefix_len": 28
1812             },
1813             "addr_from_start": null,
1814             "enable_dhcp": false,
1815             "default_gateway": "10.100.1.1",
1816             "dns_nameservers": [],
1817             "dhcp_option_list": null,
1818             "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1819             "allocation_pools": [
1820                 {
1821                     "start": "10.100.1.3",
1822                     "end": "10.100.1.5"
1823                 },
1824                 {
1825                     "start": "10.100.1.6",
1826                     "end": "10.100.1.9"
1827                 }
1828             ],
1829             "host_routes": null,
1830             "dns_server_address": "10.100.1.13",
1831             "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0"
1832         },
1833         {
1834             "subnet": {
1835                 "ip_prefix": "10.100.2.16",
1836                 "ip_prefix_len": 28
1837             },
1838             "addr_from_start": null,
1839             "enable_dhcp": true,
1840             "default_gateway": "10.100.2.17",
1841             "dns_nameservers": [],
1842             "dhcp_option_list": null,
1843             "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1844             "allocation_pools": [
1845                 {
1846                     "start": "10.100.2.18",
1847                     "end": "10.100.2.20"
1848                 }
1849             ],
1850             "host_routes": null,
1851             "dns_server_address": "10.100.2.29",
1852             "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1"
1853         }
1854     ],
1855     "host_routes": null
1856         }
1857     ***/
1858     private String mergeSubnetsAIC3 (String heatTemplate, List <Subnet> subnets, Map <String, Object> stackParams) throws MsoException {
1859
1860                 //Resource Property
1861                 List<ContrailSubnet> cslist =  new ArrayList <ContrailSubnet> ();
1862                 for (Subnet subnet : subnets) {
1863                         ContrailSubnet cs = new ContrailSubnet();
1864                         LOGGER.debug("Input Subnet:" + subnet.toString());
1865                         cs.populateWith(subnet);
1866                         LOGGER.debug("Contrail Subnet:" + cs.toString());
1867                         cslist.add(cs);
1868                 }
1869
1870                 JsonNode node = null;
1871                 try
1872                 {
1873                         ObjectMapper mapper = new ObjectMapper();
1874                         node = mapper.convertValue(cslist, JsonNode.class);
1875                         String jsonString = mapper.writeValueAsString(cslist);
1876                         LOGGER.debug("Json Subnet List:" + jsonString);
1877                 }
1878                 catch (Exception e)
1879                 {
1880                         String error = "Error creating JsonNode from input subnets";
1881                         LOGGER.error (MessageEnum.RA_MARSHING_ERROR, error, "", "", MsoLogger.ErrorCode.DataError, "Exception creating JsonNode from input subnets", e);
1882                         throw new MsoAdapterException (error);
1883                 }
1884                 //update parameters
1885                 if (node != null)
1886                 {
1887                         stackParams.put ("subnet_list", node);
1888                 }
1889                 //Outputs - All subnets are in one ipam_subnets structure
1890                 String outputTempl = "  subnet:\n" + "    description: Openstack subnet identifier\n"
1891                                 + "    value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1892
1893                 // append outputs in heatTemplate
1894                 int outputsIdx = heatTemplate.indexOf ("outputs:");
1895                 heatTemplate = insertStr (heatTemplate, outputTempl, outputsIdx + 8);
1896                 LOGGER.debug ("Template updated with all AIC3.0 subnets:" + heatTemplate);
1897                 return heatTemplate;
1898     }
1899
1900
1901     private String mergeSubnets (String heatTemplate, List <Subnet> subnets) throws MsoException {
1902
1903                 String resourceTempl = "  subnet_%subnetId%:\n" + "    type: OS::Neutron::Subnet\n"
1904                                 + "    properties:\n"
1905                                 + "      name: %name%\n"
1906                                 + "      network_id: { get_resource: network }\n"
1907                                 + "      cidr: %cidr%\n";
1908
1909                 /* make these optional
1910                                + "      ip_version: %ipversion%\n"
1911                                + "      enable_dhcp: %enabledhcp%\n"
1912                                + "      gateway_ip: %gatewayip%\n" 
1913                                + "      allocation_pools:\n"
1914                                + "       - start: %poolstart%\n"
1915                                + "         end: %poolend%\n";
1916
1917                  */
1918                 
1919                 String outputTempl = "  subnet_id_%subnetId%:\n" + "    description: Openstack subnet identifier\n"
1920                                 + "    value: {get_resource: subnet_%subnetId%}\n";
1921
1922                 String curR = "";
1923                 String curO = "";
1924                 StringBuilder resourcesBuf = new StringBuilder ();
1925                 StringBuilder outputsBuf = new StringBuilder ();
1926                 for (Subnet subnet : subnets) {
1927
1928                         // build template for each subnet
1929                         curR = resourceTempl;
1930                         if (subnet.getSubnetId () != null) {
1931                                 curR = curR.replace ("%subnetId%", subnet.getSubnetId ());
1932                         } else {
1933                                 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1934                                 LOGGER.error (MessageEnum.RA_MISSING_PARAM, error, "Openstack", "", MsoLogger.ErrorCode.DataError, "Missing Required AAI ID  for subnet in HEAT Template");
1935                                 throw new MsoAdapterException (error);
1936                         }
1937                         
1938                         if (subnet.getSubnetName () != null) {
1939                                 curR = curR.replace ("%name%", subnet.getSubnetName ());
1940                         } else {
1941                                 curR = curR.replace ("%name%", subnet.getSubnetId ());
1942                         }
1943                         
1944                         if (subnet.getCidr () != null) {
1945                                 curR = curR.replace ("%cidr%", subnet.getCidr ());
1946                         } else {
1947                                 String error = "Missing Required cidr for subnet in HEAT Template";
1948                                 LOGGER.error (MessageEnum.RA_MISSING_PARAM, error, "Openstack", "", MsoLogger.ErrorCode.DataError, "Missing Required cidr for subnet in HEAT Template");
1949                                 throw new MsoAdapterException (error);
1950                         }
1951
1952                         if (subnet.getIpVersion () != null) {
1953                                 curR = curR + "      ip_version: " + subnet.getIpVersion () + "\n";
1954                         }
1955                         if (subnet.getEnableDHCP () != null) {
1956                                 curR = curR + "      enable_dhcp: " +  Boolean.toString (subnet.getEnableDHCP ()) + "\n";
1957                         }
1958                         if (subnet.getGatewayIp () != null && !subnet.getGatewayIp ().isEmpty() ) {
1959                                 curR = curR + "      gateway_ip: " + subnet.getGatewayIp () + "\n";
1960                         }       
1961
1962                         if (subnet.getAllocationPools() != null) { 
1963                                 curR = curR + "      allocation_pools:\n";
1964                                 for (Pool pool : subnet.getAllocationPools()) 
1965                                 {
1966                                         if (!isNullOrEmpty(pool.getStart()) && !isNullOrEmpty(pool.getEnd()))
1967                                         {
1968                                                 curR = curR + "       - start: " + pool.getStart () + "\n";
1969                                                 curR = curR + "         end: " + pool.getEnd () + "\n";
1970                                         }
1971                                 }
1972                         }
1973
1974                         resourcesBuf.append (curR);
1975
1976                         curO = outputTempl;
1977                         curO = curO.replace ("%subnetId%", subnet.getSubnetId ());
1978
1979                         outputsBuf.append (curO);
1980
1981                 }
1982                 // append resources and outputs in heatTemplate
1983                 LOGGER.debug ("Tempate initial:" + heatTemplate);
1984                 int outputsIdx = heatTemplate.indexOf ("outputs:");
1985                 heatTemplate = insertStr (heatTemplate, outputsBuf.toString (), outputsIdx + 8);
1986                 int resourcesIdx = heatTemplate.indexOf ("resources:");
1987                 heatTemplate = insertStr (heatTemplate, resourcesBuf.toString (), resourcesIdx + 10);
1988
1989                 LOGGER.debug ("Template updated with all subnets:" + heatTemplate);
1990                 return heatTemplate;
1991     }
1992
1993     private Map <String, String> getSubnetUUId(String key,  Map <String, Object> outputs, List <Subnet> subnets) {
1994
1995         Map <String, String> sMap = new HashMap <String, String> ();
1996
1997         try{
1998                 Object obj = outputs.get(key);
1999                 ObjectMapper mapper = new ObjectMapper();
2000                 String jStr = mapper.writeValueAsString(obj);
2001                 LOGGER.debug ("Subnet_Ipam Output JSON String:" + obj.getClass() + " " + jStr);
2002
2003                 JsonNode rootNode = mapper.readTree(jStr);
2004                 for (JsonNode sNode : rootNode.path("ipam_subnets"))
2005                 {
2006                         LOGGER.debug("Output Subnet Node" + sNode.toString());
2007                         String name = sNode.path("subnet_name").getTextValue();
2008                         String uuid = sNode.path("subnet_uuid").getTextValue();
2009                         String aaiId = name; // default
2010                         // try to find aaiId for name in input subnetList
2011                         if (subnets != null)
2012                         {
2013                                 for (Subnet subnet : subnets)
2014                                 {
2015                                         if ( subnet !=  null && !isNullOrEmpty(subnet.getSubnetName()))
2016                                         {               
2017                                                 if (subnet.getSubnetName().equals(name))
2018                                                 {
2019                                                         aaiId = subnet.getSubnetId(); 
2020                                                         break;
2021                                                 }
2022                                         }
2023                                 }
2024                         }
2025                         sMap.put(aaiId, uuid); //bpmn needs aaid to uuid map
2026                 }
2027         }
2028         catch (Exception e)
2029         {
2030                 LOGGER.error (MessageEnum.RA_MARSHING_ERROR, "error getting subnet-uuids", "Openstack", "", MsoLogger.ErrorCode.DataError, "Exception getting subnet-uuids", e);
2031         }
2032
2033         LOGGER.debug ("Return sMap" + sMap.toString());
2034         return sMap;
2035     }
2036
2037     private static String insertStr (String template, String snippet, int index) {
2038
2039         String updatedTemplate = "";
2040
2041         LOGGER.debug ("Index:" + index + " Snippet:" + snippet);
2042
2043         String templateBeg = template.substring (0, index);
2044         String templateEnd = template.substring (index);
2045
2046         updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
2047
2048         LOGGER.debug ("Template updated with a subnet:" + updatedTemplate);
2049         return updatedTemplate;
2050     }
2051
2052 }