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