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