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