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