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