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