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