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