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