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