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