2  * ============LICENSE_START=======================================================
 
   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
 
  15  *      http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  25 package org.onap.so.adapters.network;
 
  27 import java.util.ArrayList;
 
  28 import java.util.HashMap;
 
  29 import java.util.List;
 
  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;
 
  80 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.onap.so.adapters.network.MsoNetworkAdapter",
 
  81         targetNamespace = "http://org.onap.so/network")
 
  82 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
 
  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";
 
 103     private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
 
 106     private CloudConfig cloudConfig;
 
 108     private Environment environment;
 
 110     private MsoNeutronUtils neutron;
 
 112     private MsoHeatUtils heat;
 
 114     private MsoHeatUtilsWithUpdate heatWithUpdate;
 
 116     private MsoCommonUtils commonUtils;
 
 119     private NetworkResourceCustomizationRepository networkCustomRepo;
 
 122     private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
 
 125     private NetworkResourceRepository networkResourceRepo;
 
 127     public MsoNetworkAdapterImpl() {}
 
 130      * Health Check web method. Does nothing but return to show the adapter is deployed.
 
 133     public void healthCheck() {
 
 134         logger.debug("Health check call in Network Adapter");
 
 138      * Do not use this constructor or the msoPropertiesFactory will be NULL.
 
 140      * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
 
 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);
 
 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);
 
 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.
 
 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.
 
 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)
 
 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.
 
 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).
 
 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.
 
 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);
 
 199         // Will capture execution time for metrics
 
 200         long startTime = System.currentTimeMillis();
 
 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);
 
 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.
 
 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);
 
 224         NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
 
 225                 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
 
 226         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 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);
 
 235         logger.debug("Got HEAT Template from DB: {}", heatTemplate);
 
 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");
 
 241         boolean os3template = false;
 
 242         String os3nw = OS3_NW;
 
 244         os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
 
 246         if (template.contains(os3nw))
 
 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;
 
 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);
 
 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(),
 
 268                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 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
 
 277                         networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
 
 279                     Map<String, Object> outputs = heatStack.getOutputs();
 
 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
 
 286                                 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 288                             } else // multiples subnet_%aaid% outputs
 
 290                                 String subnetUUId = (String) outputs.get(key);
 
 291                                 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 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);
 
 301             heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
 
 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);
 
 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.
 
 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);
 
 322         if (subnets != null) {
 
 325                     template = mergeSubnetsAIC3(template, subnets, stackParams);
 
 327                     template = mergeSubnets(template, subnets);
 
 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);
 
 338         if (policyFqdns != null && !policyFqdns.isEmpty() && os3template) {
 
 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);
 
 350         if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
 
 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);
 
 362         // Deploy the network stack
 
 363         // Ignore MsoStackAlreadyExists exception because we already checked.
 
 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);
 
 376         // Reach this point if createStack is successful.
 
 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);
 
 384                 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
 
 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
 
 395                         Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 397                     } else // multiples subnet_%aaid% outputs allowed
 
 399                         String subnetUUId = (String) outputs.get(key);
 
 400                         sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 404             networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
 
 406         subnetIdMap.value = sMap;
 
 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);
 
 415             heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
 
 416         } catch (Exception e) {
 
 417             logger.warn("Exception while updating infra active request", e);
 
 420         logger.debug("Network {} successfully created via HEAT", networkName);
 
 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,
 
 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,
 
 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.
 
 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).
 
 457      * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
 
 458      * VLANs on the same physical network.
 
 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.
 
 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.
 
 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 {
 
 474         logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
 
 475                 cloudSiteId, tenantId);
 
 477         // Will capture execution time for metrics
 
 478         long startTime = System.currentTimeMillis();
 
 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);
 
 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);
 
 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());
 
 503         // Use an MsoNeutronUtils for all Neutron commands
 
 505         if (NEUTRON_MODE.equals(mode)) {
 
 507             // Verify that the Network exists
 
 508             // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
 
 509             NetworkInfo netInfo = null;
 
 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);
 
 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);
 
 527             long updateNetworkStarttime = System.currentTimeMillis();
 
 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);
 
 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());
 
 547             logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
 
 548         } else if ("HEAT".equals(mode)) {
 
 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;
 
 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);
 
 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(),
 
 567                 // Network stack does not exist. Return an error
 
 568                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 571             // Get the previous parameters for rollback
 
 572             Map<String, Object> heatParams = heatStack.getParameters();
 
 574             String previousNetworkName = (String) heatParams.get("network_name");
 
 575             String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
 
 577             List<Integer> previousVlans = new ArrayList<>();
 
 578             String vlansParam = (String) heatParams.get(VLANS);
 
 579             if (vlansParam != null) {
 
 580                 for (String vlan : vlansParam.split(",")) {
 
 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);
 
 589             logger.debug("Update Stack:  Previous VLANS: {}", previousVlans);
 
 591             // Ready to deploy the updated Network via Heat
 
 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(),
 
 599                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 602             logger.debug("Got HEAT Template from DB: {}", heatTemplate);
 
 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");
 
 608             boolean os3template = false;
 
 609             String os3nw = OS3_NW;
 
 611             os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
 
 613             if (template.contains(os3nw))
 
 616             // Build the common set of HEAT template parameters
 
 617             Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
 
 618                     physicalNetworkName, vlans, routeTargets, shared, external, os3template);
 
 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
 
 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);
 
 630             if (subnets != null) {
 
 633                         template = mergeSubnetsAIC3(template, subnets, stackParams);
 
 635                         template = mergeSubnets(template, subnets);
 
 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);
 
 646             if (policyFqdns != null && os3template) {
 
 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);
 
 658             if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
 
 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);
 
 670             // Update the network stack
 
 671             // Ignore MsoStackNotFound exception because we already checked.
 
 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);
 
 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
 
 690                             Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 692                         } else // multiples subnet_%aaid% outputs allowed
 
 694                             String subnetUUId = (String) outputs.get(key);
 
 695                             sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 700             subnetIdMap.value = sMap;
 
 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));
 
 708                 logger.debug("outputs is NULL");
 
 710             networkRollback.setNetworkType(networkType);
 
 711             // Save previous parameters
 
 712             networkRollback.setNetworkName(previousNetworkName);
 
 713             networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
 
 714             networkRollback.setVlans(previousVlans);
 
 716             rollback.value = networkRollback;
 
 718             logger.debug("Network {} successfully updated via HEAT", networkId);
 
 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);
 
 736             networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 737             if (networkCust == null) {
 
 738                 collectionNetworkCust =
 
 739                         collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 742         if (networkCust != null) {
 
 743             logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
 
 745             networkResource = networkCust.getNetworkResource();
 
 746         } else if (collectionNetworkCust != null) {
 
 747             logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
 
 748             networkResource = collectionNetworkCust.getNetworkResource();
 
 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);
 
 756             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 758         logger.debug(LOG_DEBUG_MSG, networkResource);
 
 760         String mode = networkResource.getOrchestrationMode();
 
 761         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 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);
 
 771         MavenLikeVersioning osV = new MavenLikeVersioning();
 
 772         osV.setVersion(cloudSite.getCloudVersion());
 
 773         if ((osV.isMoreRecentThan(networkResource.getAicVersionMin())
 
 774                 || osV.isTheSameVersion(networkResource.getAicVersionMin())) // os
 
 777                 && (osV.isTheSameVersion(networkResource.getAicVersionMax())
 
 778                         || !(osV.isMoreRecentThan(networkResource.getAicVersionMax())))) // os <= max
 
 780             logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
 
 781                     networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
 
 782                     cloudSite.getCloudVersion());
 
 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);
 
 792         // Validate the Network parameters.
 
 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);
 
 799             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 802         return networkResource;
 
 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);
 
 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);
 
 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.
 
 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 {
 
 833         logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
 
 835         if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
 
 836                 || commonUtils.isNullOrEmpty(networkNameOrId)) {
 
 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);
 
 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);
 
 853         // Use MsoNeutronUtils for all NEUTRON commands
 
 855         String neutronId = null;
 
 856         // Try Heat first, since networks may be named the same as the Heat stack
 
 857         StackInfo heatStack = null;
 
 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);
 
 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);
 
 875                 for (String key : outputs.keySet()) {
 
 876                     if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
 
 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
 
 882                         Map<String, String> map = getSubnetUUId(key, outputs, null);
 
 888             subnetIdMap.value = sMap;
 
 891         // Query directly against the Neutron Network for the details
 
 892         // no RouteTargets available for ContrailV2 in neutron net-show
 
 893         // networkId is heatStackId
 
 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();
 
 902                     networkId.value = netInfo.getId();
 
 904                 neutronNetworkId.value = netInfo.getId();
 
 905                 status.value = netInfo.getStatus();
 
 907                     vlans.value = netInfo.getVlans();
 
 909                 logger.debug("Network {}, ID = {}{}", networkNameOrId, networkId.value,
 
 910                         (",NeutronId = " + neutronNetworkId.value));
 
 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;
 
 917                     vlans.value = new ArrayList<>();
 
 919                 logger.debug("Network {} not found", networkNameOrId);
 
 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);
 
 931      * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
 
 934      * If the network is not found, it is treated as a success.
 
 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.
 
 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.
 
 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.
 
 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);
 
 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);
 
 963             NetworkResourceCustomization nrc =
 
 964                     networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 966                 networkResource = nrc.getNetworkResource();
 
 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();
 
 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);
 
 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);
 
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.
 
1005      * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
 
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());
 
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();
 
1020         logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
 
1022         if (rollback.getNetworkCreated()) {
 
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);
 
1036     private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
 
1037             List<Integer> vlans, List<RouteTarget> routeTargets) {
 
1039         StringBuilder missing = new StringBuilder();
 
1040         if (commonUtils.isNullOrEmpty(networkName)) {
 
1041             missing.append("networkName");
 
1045         if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1046             if (commonUtils.isNullOrEmpty(physicalNetwork)) {
 
1047                 missing.append(sep).append("physicalNetworkName");
 
1050             if (vlans == null || vlans.isEmpty()) {
 
1051                 missing.append(sep).append(VLANS);
 
1055         return missing.toString();
 
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);
 
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();
 
1078             for (Integer vlan : vlans) {
 
1079                 buf.append(sep).append(vlan.toString());
 
1082             String csl = buf.toString();
 
1084             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1085             stackParams.put(VLANS, csl);
 
1087         if (routeTargets != null) {
 
1089             String rtGlobal = "";
 
1090             String rtImport = "";
 
1091             String rtExport = "";
 
1093             for (RouteTarget rt : routeTargets) {
 
1094                 boolean rtIsNull = false;
 
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"))
 
1101                     if (routeTargetRole == null || routeTargetRole.equals("")
 
1102                             || routeTargetRole.equalsIgnoreCase("null"))
 
1108                     logger.debug("Input RT:{}", rt);
 
1109                     String role = rt.getRouteTargetRole();
 
1110                     String rtValue = rt.getRouteTarget();
 
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
 
1120                         sep = rtGlobal.isEmpty() ? "" : ",";
 
1121                         rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
 
1127             if (!rtImport.isEmpty()) {
 
1128                 stackParams.put("route_targets_import", rtImport);
 
1130             if (!rtExport.isEmpty()) {
 
1131                 stackParams.put("route_targets_export", rtExport);
 
1133             if (!rtGlobal.isEmpty()) {
 
1134                 stackParams.put("route_targets", rtGlobal);
 
1137         if (commonUtils.isNullOrEmpty(shared)) {
 
1138             stackParams.put("shared", "False");
 
1140             stackParams.put("shared", shared);
 
1142         if (commonUtils.isNullOrEmpty(external)) {
 
1143             stackParams.put("external", "False");
 
1145             stackParams.put("external", external);
 
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" } } ]
 
1158     private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
 
1159         // Resource Property
 
1160         List<ContrailPolicyRef> prlist = new ArrayList<>();
 
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");
 
1170                     logger.debug("Contrail PolicyRefs Data:{}", pr);
 
1175             String error = "Null pFqdns at start of mergePolicyRefs";
 
1176             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
 
1178             throw new MsoAdapterException(error);
 
1181         JsonNode node = null;
 
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(),
 
1191             throw new MsoAdapterException(error);
 
1193         // update parameters
 
1194         if (pFqdns != null && node != null) {
 
1195             StringBuilder buf = new StringBuilder();
 
1197             for (String pf : pFqdns) {
 
1198                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1199                     buf.append(sep).append(pf);
 
1203             String csl = buf.toString();
 
1204             stackParams.put("policy_refs", csl);
 
1205             stackParams.put("policy_refsdata", node);
 
1208         logger.debug("StackParams updated with policy refs");
 
1212     private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
 
1214         // update parameters
 
1215         if (rtFqdns != null) {
 
1216             StringBuilder buf = new StringBuilder();
 
1218             for (String rtf : rtFqdns) {
 
1219                 if (!commonUtils.isNullOrEmpty(rtf)) {
 
1220                     buf.append(sep).append(rtf);
 
1224             String csl = buf.toString();
 
1225             stackParams.put("route_table_refs", csl);
 
1228         logger.debug("StackParams updated with route_table refs");
 
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 }
 
1246     private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
 
1247             throws MsoException {
 
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);
 
1258         JsonNode node = null;
 
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);
 
1269         // update parameters
 
1271             stackParams.put("subnet_list", node);
 
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";
 
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;
 
1285     private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
 
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";
 
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";
 
1297         String outputTempl = "  subnet_id_%subnetId%:\n" + "    description: Openstack subnet identifier\n"
 
1298                 + "    value: {get_resource: subnet_%subnetId%}\n";
 
1302         StringBuilder resourcesBuf = new StringBuilder();
 
1303         StringBuilder outputsBuf = new StringBuilder();
 
1304         for (Subnet subnet : subnets) {
 
1306             // build template for each subnet
 
1307             curR = resourceTempl;
 
1308             if (subnet.getSubnetId() != null) {
 
1309                 curR = curR.replace("%subnetId%", subnet.getSubnetId());
 
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);
 
1316             if (subnet.getSubnetName() != null) {
 
1317                 curR = curR.replace("%name%", subnet.getSubnetName());
 
1319                 curR = curR.replace("%name%", subnet.getSubnetId());
 
1322             if (subnet.getCidr() != null) {
 
1323                 curR = curR.replace("%cidr%", subnet.getCidr());
 
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);
 
1330             if (subnet.getIpVersion() != null) {
 
1331                 curR = curR + "      ip_version: " + subnet.getIpVersion() + "\n";
 
1333             if (subnet.getEnableDHCP() != null) {
 
1334                 curR = curR + "      enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
 
1336             if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
 
1337                 curR = curR + "      gateway_ip: " + subnet.getGatewayIp() + "\n";
 
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");
 
1353                 curR = tempBuf.toString();
 
1356             resourcesBuf.append(curR);
 
1359             curO = curO.replace("%subnetId%", subnet.getSubnetId());
 
1361             outputsBuf.append(curO);
 
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);
 
1370         logger.debug("Template updated with all subnets:{}", heatTemplate);
 
1371         return heatTemplate;
 
1374     private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
 
1376         Map<String, String> sMap = new HashMap<>();
 
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);
 
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();
 
1401                     sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
 
1404                 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
 
1405                         ErrorCode.DataError.getValue());
 
1407         } catch (Exception e) {
 
1408             logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
 
1409                     ErrorCode.DataError.getValue(), e);
 
1412         logger.debug("Return sMap {}", sMap);
 
1416     private static String insertStr(String template, String snippet, int index) {
 
1418         String updatedTemplate;
 
1420         logger.debug("Index:{} Snippet:{}", index, snippet);
 
1422         String templateBeg = template.substring(0, index);
 
1423         String templateEnd = template.substring(index);
 
1425         updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
 
1427         logger.debug("Template updated with a subnet:{}", updatedTemplate);
 
1428         return updatedTemplate;