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)
 
 145     public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 146             String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
 
 147             Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
 
 148             MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 149             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 150         Holder<String> networkFqdn = new Holder<>();
 
 151         createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
 
 152                 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
 
 153                 neutronNetworkId, networkFqdn, subnetIdMap, rollback, true);
 
 158     public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
 
 159             String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
 
 160             String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
 
 161             Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
 
 162             MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 163             Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
 
 164             throws NetworkException {
 
 165         createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
 
 166                 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
 
 167                 neutronNetworkId, networkFqdn, subnetIdMap, rollback, true);
 
 171      * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
 
 172      * the specified cloud and tenant. The tenant must exist at the time this service is called.
 
 174      * If a network with the same name already exists, this can be considered a success or failure, depending on the
 
 175      * value of the 'failIfExists' parameter.
 
 177      * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
 
 178      * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
 
 179      * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
 
 181      * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
 
 182      * multiple VLANs on the same physical network.
 
 184      * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
 
 185      * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
 
 186      * support some subset of the same input parameters: network_name, physical_network, vlan(s).
 
 188      * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
 
 189      * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
 
 190      * created but the orchestration fails on a subsequent operation.
 
 193     public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 194             String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
 
 195             String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
 
 196             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
 
 197             Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
 
 198             Holder<NetworkRollback> rollback, Boolean pollForCompletion) throws NetworkException {
 
 199         logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
 
 201         // Will capture execution time for metrics
 
 202         long startTime = System.currentTimeMillis();
 
 204         // Build a default rollback object (no actions performed)
 
 205         NetworkRollback networkRollback = new NetworkRollback();
 
 206         networkRollback.setCloudId(cloudSiteId);
 
 207         networkRollback.setTenantId(tenantId);
 
 208         networkRollback.setMsoRequest(msoRequest);
 
 209         networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
 
 211         // tenant query is not required here.
 
 212         // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
 
 213         // So this is just catching that error in a bit more obvious way up front.
 
 215         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 216         if (!cloudSiteOpt.isPresent()) {
 
 217             String error = String.format(
 
 218                     "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 219                     networkName, cloudSiteId, tenantId);
 
 220             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 221             // Set the detailed error as the Exception 'message'
 
 222             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 226         NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
 
 227                 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
 
 228         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 230         HeatTemplate heatTemplate = networkResource.getHeatTemplate();
 
 231         if (heatTemplate == null) {
 
 232             String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
 
 233             logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
 
 234             throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 237         logger.debug("Got HEAT Template from DB: {}", heatTemplate);
 
 239         // "Fix" the template if it has CR/LF (getting this from Oracle)
 
 240         String template = heatTemplate.getHeatTemplate();
 
 241         template = template.replaceAll("\r\n", "\n");
 
 243         boolean os3template = false;
 
 244         String os3nw = OS3_NW;
 
 246         os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
 
 248         if (template.contains(os3nw))
 
 251         // First, look up to see if the Network already exists (by name).
 
 252         // For HEAT orchestration of networks, the stack name will always match the network name
 
 253         StackInfo heatStack = null;
 
 255             heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
 
 256         } catch (MsoException me) {
 
 257             me.addContext(CREATE_NETWORK_CONTEXT);
 
 258             logger.error("{} {} Create Network (heat): query network {} in {}/{}: ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 259                     ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
 
 260             throw new NetworkException(me);
 
 263         if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
 
 264             // Stack exists. Return success or error depending on input directive
 
 265             if (failIfExists != null && failIfExists) {
 
 266                 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
 
 267                         cloudSiteId, tenantId, heatStack.getCanonicalName());
 
 268                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
 
 270                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 272                 // Populate the outputs from the existing stack.
 
 273                 networkId.value = heatStack.getCanonicalName();
 
 274                 Map<String, String> sMap = new HashMap<>();
 
 275                 if (heatStack.getOutputs() != null) {
 
 276                     neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
 
 277                     rollback.value = networkRollback; // Default rollback - no updates performed
 
 279                         networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
 
 281                     Map<String, Object> outputs = heatStack.getOutputs();
 
 283                     for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 284                         String key = entry.getKey();
 
 285                         if (key != null && key.startsWith("subnet")) {
 
 286                             if (os3template) // one subnet_id output
 
 288                                 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 290                             } else // multiples subnet_%aaid% outputs
 
 292                                 String subnetUUId = (String) outputs.get(key);
 
 293                                 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 298                 subnetIdMap.value = sMap;
 
 299                 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
 
 300                         MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
 
 301                         networkName, cloudSiteId, tenantId);
 
 303             heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
 
 307         // Ready to deploy the new Network
 
 308         // Build the common set of HEAT template parameters
 
 309         Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName, physicalNetworkName,
 
 310                 vlans, routeTargets, shared, external, os3template);
 
 312         // Validate (and update) the input parameters against the DB definition
 
 313         // Shouldn't happen unless DB config is wrong, since all networks use same inputs
 
 314         // and inputs were already validated.
 
 316             stackParams = heat.validateStackParams(stackParams, heatTemplate);
 
 317         } catch (IllegalArgumentException e) {
 
 318             String error = "Create Network: Configuration Error: " + e.getMessage();
 
 319             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
 
 320             // Input parameters were not valid
 
 321             throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 324         if (subnets != null) {
 
 327                     template = mergeSubnetsAIC3(template, subnets, stackParams);
 
 329                     template = mergeSubnets(template, subnets);
 
 331             } catch (MsoException me) {
 
 332                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 333                 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
 
 334                         MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
 
 335                         neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 336                 throw new NetworkException(me);
 
 340         if (policyFqdns != null && !policyFqdns.isEmpty() && os3template) {
 
 342                 mergePolicyRefs(policyFqdns, stackParams);
 
 343             } catch (MsoException me) {
 
 344                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 345                 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
 
 346                         MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
 
 347                         neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 348                 throw new NetworkException(me);
 
 352         if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
 
 354                 mergeRouteTableRefs(routeTableFqdns, stackParams);
 
 355             } catch (MsoException me) {
 
 356                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 357                 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
 
 358                         MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
 
 359                         neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 360                 throw new NetworkException(me);
 
 364         // Deploy the network stack
 
 365         // Ignore MsoStackAlreadyExists exception because we already checked.
 
 369             heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template, stackParams,
 
 370                     pollForCompletion, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue(),
 
 372         } catch (MsoException me) {
 
 373             me.addContext(CREATE_NETWORK_CONTEXT);
 
 374             logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
 
 375                     ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
 
 376             throw new NetworkException(me);
 
 379         // Reach this point if createStack is successful.
 
 381         // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
 
 382         // and the neutronNetworkId is the network UUID returned in stack outputs.
 
 383         networkId.value = heatStack.getCanonicalName();
 
 384         if (heatStack.getOutputs() != null) {
 
 385             neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
 
 387                 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
 
 390         Map<String, Object> outputs = heatStack.getOutputs();
 
 391         Map<String, String> sMap = new HashMap<>();
 
 392         if (outputs != null) {
 
 393             for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 394                 String key = entry.getKey();
 
 395                 if (key != null && key.startsWith("subnet")) {
 
 396                     if (os3template) // one subnet output expected
 
 398                         Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 400                     } else // multiples subnet_%aaid% outputs allowed
 
 402                         String subnetUUId = (String) outputs.get(key);
 
 403                         sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 407             networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
 
 409         subnetIdMap.value = sMap;
 
 411         rollback.value = networkRollback;
 
 412         // Populate remaining rollback info and response parameters.
 
 413         networkRollback.setNetworkStackId(heatStack.getCanonicalName());
 
 414         networkRollback.setNetworkCreated(true);
 
 415         networkRollback.setNetworkType(networkType);
 
 418             heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
 
 419         } catch (Exception e) {
 
 420             logger.warn("Exception while updating infra active request", e);
 
 423         logger.debug("Network {} successfully created via HEAT", networkName);
 
 431     public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 432             String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
 
 433             String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
 
 434             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 435         updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
 
 436                 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
 
 443     public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
 
 444             String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
 
 445             String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
 
 446             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
 
 447             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 448         updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
 
 449                 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
 
 454      * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
 
 455      * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
 
 456      * remove a VLAN), but other properties may be updated as well.
 
 458      * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
 
 459      * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
 
 460      * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
 
 462      * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
 
 463      * VLANs on the same physical network.
 
 465      * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
 
 466      * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
 
 467      * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
 
 469      * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
 
 470      * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
 
 471      * a subsequent operation.
 
 473     public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 474             String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
 
 475             List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
 
 476             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
 
 477             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 479         logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
 
 480                 cloudSiteId, tenantId);
 
 482         // Will capture execution time for metrics
 
 483         long startTime = System.currentTimeMillis();
 
 485         // Build a default rollback object (no actions performed)
 
 486         NetworkRollback networkRollback = new NetworkRollback();
 
 487         networkRollback.setCloudId(cloudSiteId);
 
 488         networkRollback.setTenantId(tenantId);
 
 489         networkRollback.setMsoRequest(msoRequest);
 
 491         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 492         if (!cloudSiteOpt.isPresent()) {
 
 493             String error = String.format(
 
 494                     "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 495                     networkName, cloudSiteId, tenantId);
 
 496             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 497             // Set the detailed error as the Exception 'message'
 
 498             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 503         NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
 
 504                 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
 
 505         String mode = networkResource.getOrchestrationMode();
 
 506         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 508         // Use an MsoNeutronUtils for all Neutron commands
 
 510         if (NEUTRON_MODE.equals(mode)) {
 
 512             // Verify that the Network exists
 
 513             // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
 
 514             NetworkInfo netInfo = null;
 
 516                 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
 
 517             } catch (MsoException me) {
 
 518                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 519                 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 520                         ErrorCode.BusinessProcessError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 521                 throw new NetworkException(me);
 
 524             if (netInfo == null) {
 
 525                 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
 
 526                         cloudSiteId, tenantId);
 
 527                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
 
 528                         ErrorCode.BusinessProcessError.getValue(), error);
 
 529                 // Does not exist. Throw an exception (can't update a non-existent network)
 
 530                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 533                 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
 
 534                         physicalNetworkName, vlans);
 
 535             } catch (MsoException me) {
 
 536                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 537                 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
 
 538                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 539                 throw new NetworkException(me);
 
 542             // Add the network ID and previously queried vlans to the rollback object
 
 543             networkRollback.setNetworkId(netInfo.getId());
 
 544             networkRollback.setNeutronNetworkId(netInfo.getId());
 
 545             networkRollback.setNetworkType(networkType);
 
 546             // Save previous parameters
 
 547             networkRollback.setNetworkName(netInfo.getName());
 
 548             networkRollback.setPhysicalNetwork(netInfo.getProvider());
 
 549             networkRollback.setVlans(netInfo.getVlans());
 
 551             logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
 
 552         } else if ("HEAT".equals(mode)) {
 
 554             // First, look up to see that the Network already exists.
 
 555             // For Heat-based orchestration, the networkId is the network Stack ID.
 
 556             StackInfo heatStack = null;
 
 558                 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
 
 559             } catch (MsoException me) {
 
 560                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 561                 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 562                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 563                 throw new NetworkException(me);
 
 566             if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
 
 567                 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
 
 568                         cloudSiteId, tenantId);
 
 569                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 571                 // Network stack does not exist. Return an error
 
 572                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 575             // Get the previous parameters for rollback
 
 576             Map<String, Object> heatParams = heatStack.getParameters();
 
 578             String previousNetworkName = (String) heatParams.get("network_name");
 
 579             String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
 
 581             List<Integer> previousVlans = new ArrayList<>();
 
 582             String vlansParam = (String) heatParams.get(VLANS);
 
 583             if (vlansParam != null) {
 
 584                 for (String vlan : vlansParam.split(",")) {
 
 586                         previousVlans.add(Integer.parseInt(vlan));
 
 587                     } catch (NumberFormatException e) {
 
 588                         logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
 
 589                                 ErrorCode.DataError.getValue(), vlansParam, e);
 
 593             logger.debug("Update Stack:  Previous VLANS: {}", previousVlans);
 
 595             // Ready to deploy the updated Network via Heat
 
 598             HeatTemplate heatTemplate = networkResource.getHeatTemplate();
 
 599             if (heatTemplate == null) {
 
 600                 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
 
 601                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 603                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 606             logger.debug("Got HEAT Template from DB: {}", heatTemplate);
 
 608             // "Fix" the template if it has CR/LF (getting this from Oracle)
 
 609             String template = heatTemplate.getHeatTemplate();
 
 610             template = template.replaceAll("\r\n", "\n");
 
 612             boolean os3template = false;
 
 613             String os3nw = OS3_NW;
 
 615             os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
 
 617             if (template.contains(os3nw))
 
 620             // Build the common set of HEAT template parameters
 
 621             Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
 
 622                     physicalNetworkName, vlans, routeTargets, shared, external, os3template);
 
 624             // Validate (and update) the input parameters against the DB definition
 
 625             // Shouldn't happen unless DB config is wrong, since all networks use same inputs
 
 627                 stackParams = heat.validateStackParams(stackParams, heatTemplate);
 
 628             } catch (IllegalArgumentException e) {
 
 629                 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
 
 630                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 631                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
 
 634             if (subnets != null) {
 
 637                         template = mergeSubnetsAIC3(template, subnets, stackParams);
 
 639                         template = mergeSubnets(template, subnets);
 
 641                 } catch (MsoException me) {
 
 642                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 643                     logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
 
 644                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 645                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 646                     throw new NetworkException(me);
 
 650             if (policyFqdns != null && os3template) {
 
 652                     mergePolicyRefs(policyFqdns, stackParams);
 
 653                 } catch (MsoException me) {
 
 654                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 655                     logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
 
 656                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 657                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 658                     throw new NetworkException(me);
 
 662             if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
 
 664                     mergeRouteTableRefs(routeTableFqdns, stackParams);
 
 665                 } catch (MsoException me) {
 
 666                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 667                     logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
 
 668                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 669                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 670                     throw new NetworkException(me);
 
 674             // Update the network stack
 
 675             // Ignore MsoStackNotFound exception because we already checked.
 
 677                 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
 
 678                         stackParams, false, heatTemplate.getTimeoutMinutes());
 
 679             } catch (MsoException me) {
 
 680                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 681                 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
 
 682                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 683                 throw new NetworkException(me);
 
 686             Map<String, Object> outputs = heatStack.getOutputs();
 
 687             Map<String, String> sMap = new HashMap<>();
 
 688             if (outputs != null) {
 
 689                 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 690                     String key = entry.getKey();
 
 691                     if (key != null && key.startsWith("subnet")) {
 
 692                         if (os3template) // one subnet output expected
 
 694                             Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 696                         } else // multiples subnet_%aaid% outputs allowed
 
 698                             String subnetUUId = (String) outputs.get(key);
 
 699                             sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 704             subnetIdMap.value = sMap;
 
 706             // Reach this point if createStack is successful.
 
 707             // Populate remaining rollback info and response parameters.
 
 708             networkRollback.setNetworkStackId(heatStack.getCanonicalName());
 
 709             if (null != outputs) {
 
 710                 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
 
 712                 logger.debug("outputs is NULL");
 
 714             networkRollback.setNetworkType(networkType);
 
 715             // Save previous parameters
 
 716             networkRollback.setNetworkName(previousNetworkName);
 
 717             networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
 
 718             networkRollback.setVlans(previousVlans);
 
 720             rollback.value = networkRollback;
 
 722             logger.debug("Network {} successfully updated via HEAT", networkId);
 
 728     private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
 
 729             String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
 
 730             String cloudSiteId, CloudSite cloudSite) throws NetworkException {
 
 731         // Retrieve the Network Resource definition
 
 732         NetworkResource networkResource = null;
 
 733         NetworkResourceCustomization networkCust = null;
 
 734         CollectionNetworkResourceCustomization collectionNetworkCust = null;
 
 735         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
 736             if (!commonUtils.isNullOrEmpty(networkType)) {
 
 737                 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
 
 740             networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 741             if (networkCust == null) {
 
 742                 collectionNetworkCust =
 
 743                         collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 746         if (networkCust != null) {
 
 747             logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
 
 749             networkResource = networkCust.getNetworkResource();
 
 750         } else if (collectionNetworkCust != null) {
 
 751             logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
 
 752             networkResource = collectionNetworkCust.getNetworkResource();
 
 754         if (networkResource == null) {
 
 755             String error = String.format(
 
 756                     "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
 
 757                     networkType, modelCustomizationUuid);
 
 758             logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
 
 760             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 762         logger.debug(LOG_DEBUG_MSG, networkResource);
 
 764         String mode = networkResource.getOrchestrationMode();
 
 765         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 767         // All Networks are orchestrated via HEAT or Neutron
 
 768         if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
 
 769             String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
 
 770             logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
 
 771                     ErrorCode.DataError.getValue(), error);
 
 772             throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 775         MavenLikeVersioning osV = new MavenLikeVersioning();
 
 776         osV.setVersion(cloudSite.getCloudVersion());
 
 777         if ((osV.isMoreRecentThan(networkResource.getAicVersionMin())
 
 778                 || osV.isTheSameVersion(networkResource.getAicVersionMin())) // os
 
 781                 && (osV.isTheSameVersion(networkResource.getAicVersionMax())
 
 782                         || !(osV.isMoreRecentThan(networkResource.getAicVersionMax())))) // os <= max
 
 784             logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
 
 785                     networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
 
 786                     cloudSite.getCloudVersion());
 
 788             String error = String.format(
 
 789                     "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
 
 790                     networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
 
 791                     cloudSite.getCloudVersion());
 
 792             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 793             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 796         // Validate the Network parameters.
 
 798                 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
 
 799         if (!missing.isEmpty()) {
 
 800             String error = "Create Network: Missing parameters: " + missing;
 
 801             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
 803             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 806         return networkResource;
 
 810     public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 811             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 812             Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
 
 813             throws NetworkException {
 
 814         queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
 
 815                 status, vlans, null, subnetIdMap);
 
 819     public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 820             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 821             Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
 
 822             Holder<Map<String, String>> subnetIdMap) throws NetworkException {
 
 823         queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
 
 824                 status, null, routeTargets, subnetIdMap);
 
 828      * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
 
 829      * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
 
 830      * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
 
 832     private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 833             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 834             Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
 
 835             Holder<Map<String, String>> subnetIdMap) throws NetworkException {
 
 837         logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
 
 839         if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
 
 840                 || commonUtils.isNullOrEmpty(networkNameOrId)) {
 
 842             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
 
 843             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
 844             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 847         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 848         if (!cloudSiteOpt.isPresent()) {
 
 849             String error = String.format(
 
 850                     "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 851                     networkNameOrId, cloudSiteId, tenantId);
 
 852             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 853             // Set the detailed error as the Exception 'message'
 
 854             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 857         // Use MsoNeutronUtils for all NEUTRON commands
 
 859         String neutronId = null;
 
 860         // Try Heat first, since networks may be named the same as the Heat stack
 
 861         StackInfo heatStack = null;
 
 863             heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
 
 864         } catch (MsoException me) {
 
 865             me.addContext("QueryNetwork");
 
 866             logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 867                     ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
 
 868             throw new NetworkException(me);
 
 871         // Populate the outputs based on the returned Stack information
 
 872         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
 
 873             // Found it. Get the neutronNetworkId for further query
 
 874             Map<String, String> sMap = new HashMap<>();
 
 875             Map<String, Object> outputs = heatStack.getOutputs();
 
 876             if (outputs != null) {
 
 877                 neutronId = (String) outputs.get(NETWORK_ID);
 
 879                 for (String key : outputs.keySet()) {
 
 880                     if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
 
 882                         String subnetUUId = (String) outputs.get(key);
 
 883                         sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 884                     } else if (key != null && key.startsWith("subnet")) // one subnet output expected
 
 886                         Map<String, String> map = getSubnetUUId(key, outputs, null);
 
 892             subnetIdMap.value = sMap;
 
 895         // Query directly against the Neutron Network for the details
 
 896         // no RouteTargets available for ContrailV2 in neutron net-show
 
 897         // networkId is heatStackId
 
 899             NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
 
 900             if (netInfo != null) {
 
 901                 // Found. Populate the output elements
 
 902                 networkExists.value = Boolean.TRUE;
 
 903                 if (heatStack != null) {
 
 904                     networkId.value = heatStack.getCanonicalName();
 
 906                     networkId.value = netInfo.getId();
 
 908                 neutronNetworkId.value = netInfo.getId();
 
 909                 status.value = netInfo.getStatus();
 
 911                     vlans.value = netInfo.getVlans();
 
 913                 logger.debug("Network {}, ID = {}{}", networkNameOrId, networkId.value,
 
 914                         (",NeutronId = " + neutronNetworkId.value));
 
 916                 // Not found. Populate the status fields, leave the rest null
 
 917                 networkExists.value = Boolean.FALSE;
 
 918                 status.value = NetworkStatus.NOTFOUND;
 
 919                 neutronNetworkId.value = null;
 
 921                     vlans.value = new ArrayList<>();
 
 923                 logger.debug("Network {} not found", networkNameOrId);
 
 925         } catch (MsoException me) {
 
 926             me.addContext("QueryNetwork");
 
 927             logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 928                     ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
 
 929             throw new NetworkException(me);
 
 935      * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
 
 938      * If the network is not found, it is treated as a success.
 
 940      * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
 
 941      * network orchestration mode for each network type is declared in its catalog definition.
 
 943      * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
 
 944      * networkId should be the Neutron network UUID.
 
 946      * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
 
 947      * will require manual fallout in the client.
 
 950     public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 951             String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted, Boolean pollForCompletion)
 
 952             throws NetworkException {
 
 953         logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
 
 954         if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
 
 955                 || commonUtils.isNullOrEmpty(networkId)) {
 
 956             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
 
 957             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
 958             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 961         if (pollForCompletion == null) {
 
 962             pollForCompletion = true;
 
 965         // Retrieve the Network Resource definition
 
 966         NetworkResource networkResource = null;
 
 967         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
 968             if (!commonUtils.isNullOrEmpty(networkType)) {
 
 969                 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
 
 972             NetworkResourceCustomization nrc =
 
 973                     networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 975                 networkResource = nrc.getNetworkResource();
 
 979         int timeoutMinutes = 118;
 
 980         if (networkResource != null) {
 
 981             logger.debug(LOG_DEBUG_MSG, networkResource.toString());
 
 982             networkResource.getHeatTemplate().getTimeoutMinutes();
 
 983             HeatTemplate heat = networkResource.getHeatTemplate();
 
 984             if (heat != null && heat.getTimeoutMinutes() != null) {
 
 985                 if (heat.getTimeoutMinutes() < 118) {
 
 986                     timeoutMinutes = heat.getTimeoutMinutes();
 
 993                     heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, timeoutMinutes);
 
 994             networkDeleted.value = stack.isOperationPerformed();
 
 995         } catch (MsoException me) {
 
 996             me.addContext("DeleteNetwork");
 
 997             logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
 
 998                     ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 999             throw new NetworkException(me);
 
1003             heat.updateResourceStatus(msoRequest.getRequestId(),
 
1004                     networkDeleted.value ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
 
1005         } catch (Exception e) {
 
1006             logger.warn("Exception while updating infra active request", e);
 
1011      * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
 
1012      * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
 
1013      * to undo the creation.
 
1015      * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
 
1019     public void rollbackNetwork(NetworkRollback rollback, Boolean pollForCompletion) throws NetworkException {
 
1020         if (rollback == null) {
 
1021             logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
 
1025         if (pollForCompletion == null) {
 
1026             pollForCompletion = true;
 
1029         // Get the elements of the VnfRollback object for easier access
 
1030         String cloudSiteId = rollback.getCloudId();
 
1031         String tenantId = rollback.getTenantId();
 
1032         String networkId = rollback.getNetworkStackId();
 
1034         logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
 
1036         if (rollback.getNetworkCreated()) {
 
1038                 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, 120);
 
1039             } catch (MsoException me) {
 
1040                 me.addContext("RollbackNetwork");
 
1041                 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
 
1042                         MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
 
1043                         cloudSiteId, tenantId, me);
 
1044                 throw new NetworkException(me);
 
1050     private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
 
1051             List<Integer> vlans, List<RouteTarget> routeTargets) {
 
1053         StringBuilder missing = new StringBuilder();
 
1054         if (commonUtils.isNullOrEmpty(networkName)) {
 
1055             missing.append("networkName");
 
1059         if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1060             if (commonUtils.isNullOrEmpty(physicalNetwork)) {
 
1061                 missing.append(sep).append("physicalNetworkName");
 
1064             if (vlans == null || vlans.isEmpty()) {
 
1065                 missing.append(sep).append(VLANS);
 
1069         return missing.toString();
 
1072     private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
 
1073             String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
 
1074             boolean os3template) {
 
1075         // Build the common set of HEAT template parameters
 
1076         Map<String, Object> stackParams = new HashMap<>();
 
1077         stackParams.put("network_name", networkName);
 
1079         if (neutronNetworkType == NetworkType.PROVIDER) {
 
1080             // For Provider type
 
1081             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1082             stackParams.put("vlan", vlans.get(0).toString());
 
1083         } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1084             // For Multi-provider, PO supports a custom resource extension of ProviderNet.
 
1085             // It supports all ProviderNet properties except segmentation_id, and adds a
 
1086             // comma-separated-list of VLANs as a "segments" property.
 
1087             // Note that this does not match the Neutron definition of Multi-Provider network,
 
1088             // which contains a list of 'segments', each having physical_network, network_type,
 
1089             // and segmentation_id.
 
1090             StringBuilder buf = new StringBuilder();
 
1092             for (Integer vlan : vlans) {
 
1093                 buf.append(sep).append(vlan.toString());
 
1096             String csl = buf.toString();
 
1098             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1099             stackParams.put(VLANS, csl);
 
1101         if (routeTargets != null) {
 
1103             String rtGlobal = "";
 
1104             String rtImport = "";
 
1105             String rtExport = "";
 
1107             for (RouteTarget rt : routeTargets) {
 
1108                 boolean rtIsNull = false;
 
1110                     String routeTarget = rt.getRouteTarget();
 
1111                     String routeTargetRole = rt.getRouteTargetRole();
 
1112                     logger.debug("Checking for an actually null route target: {}", rt);
 
1113                     if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
 
1115                     if (routeTargetRole == null || routeTargetRole.equals("")
 
1116                             || routeTargetRole.equalsIgnoreCase("null"))
 
1122                     logger.debug("Input RT:{}", rt);
 
1123                     String role = rt.getRouteTargetRole();
 
1124                     String rtValue = rt.getRouteTarget();
 
1126                     if ("IMPORT".equalsIgnoreCase(role)) {
 
1127                         sep = rtImport.isEmpty() ? "" : ",";
 
1128                         rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
 
1129                     } else if ("EXPORT".equalsIgnoreCase(role)) {
 
1130                         sep = rtExport.isEmpty() ? "" : ",";
 
1131                         rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
 
1132                     } else // covers BOTH, empty etc
 
1134                         sep = rtGlobal.isEmpty() ? "" : ",";
 
1135                         rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
 
1141             if (!rtImport.isEmpty()) {
 
1142                 stackParams.put("route_targets_import", rtImport);
 
1144             if (!rtExport.isEmpty()) {
 
1145                 stackParams.put("route_targets_export", rtExport);
 
1147             if (!rtGlobal.isEmpty()) {
 
1148                 stackParams.put("route_targets", rtGlobal);
 
1151         if (commonUtils.isNullOrEmpty(shared)) {
 
1152             stackParams.put("shared", "False");
 
1154             stackParams.put("shared", shared);
 
1156         if (commonUtils.isNullOrEmpty(external)) {
 
1157             stackParams.put("external", "False");
 
1159             stackParams.put("external", external);
 
1167      * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
 
1168      * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
 
1169      * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
 
1170      * "network_policy_refs_data_sequence_minor": "0" } } ]
 
1172     private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
 
1173         // Resource Property
 
1174         List<ContrailPolicyRef> prlist = new ArrayList<>();
 
1177         if (pFqdns != null) {
 
1178             for (String pf : pFqdns) {
 
1179                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1180                     ContrailPolicyRef pr = new ContrailPolicyRef();
 
1181                     ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
 
1184                     logger.debug("Contrail PolicyRefs Data:{}", pr);
 
1189             String error = "Null pFqdns at start of mergePolicyRefs";
 
1190             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
 
1192             throw new MsoAdapterException(error);
 
1195         JsonNode node = null;
 
1197             ObjectMapper mapper = new ObjectMapper();
 
1198             node = mapper.convertValue(prlist, JsonNode.class);
 
1199             String jsonString = mapper.writeValueAsString(prlist);
 
1200             logger.debug("Json PolicyRefs Data:{}", jsonString);
 
1201         } catch (Exception e) {
 
1202             String error = "Error creating JsonNode for policyRefs Data";
 
1203             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
 
1205             throw new MsoAdapterException(error);
 
1207         // update parameters
 
1208         if (pFqdns != null && node != null) {
 
1209             StringBuilder buf = new StringBuilder();
 
1211             for (String pf : pFqdns) {
 
1212                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1213                     buf.append(sep).append(pf);
 
1217             String csl = buf.toString();
 
1218             stackParams.put("policy_refs", csl);
 
1219             stackParams.put("policy_refsdata", node);
 
1222         logger.debug("StackParams updated with policy refs");
 
1226     private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
 
1228         // update parameters
 
1229         if (rtFqdns != null) {
 
1230             StringBuilder buf = new StringBuilder();
 
1232             for (String rtf : rtFqdns) {
 
1233                 if (!commonUtils.isNullOrEmpty(rtf)) {
 
1234                     buf.append(sep).append(rtf);
 
1238             String csl = buf.toString();
 
1239             stackParams.put("route_table_refs", csl);
 
1242         logger.debug("StackParams updated with route_table refs");
 
1248      * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
 
1249      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
 
1250      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
 
1251      * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
 
1252      * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
 
1253      * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
 
1254      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
 
1255      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
 
1256      * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
 
1257      * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
 
1258      * "host_routes": null }
 
1260     private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
 
1261             throws MsoException {
 
1263         // Resource Property
 
1264         List<ContrailSubnet> cslist = new ArrayList<>();
 
1265         for (Subnet subnet : subnets) {
 
1266             logger.debug("Input Subnet:{}", subnet);
 
1267             ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
 
1268             logger.debug("Contrail Subnet:{}", cs);
 
1272         JsonNode node = null;
 
1274             ObjectMapper mapper = new ObjectMapper();
 
1275             node = mapper.convertValue(cslist, JsonNode.class);
 
1276             String jsonString = mapper.writeValueAsString(cslist);
 
1277             logger.debug("Json Subnet List:{}", jsonString);
 
1278         } catch (Exception e) {
 
1279             String error = "Error creating JsonNode from input subnets";
 
1280             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
 
1281             throw new MsoAdapterException(error);
 
1283         // update parameters
 
1285             stackParams.put("subnet_list", node);
 
1287         // Outputs - All subnets are in one ipam_subnets structure
 
1288         String outputTempl = "  subnet:\n" + "    description: Openstack subnet identifier\n"
 
1289                 + "    value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
 
1291         // append outputs in heatTemplate
 
1292         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1293         heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
 
1294         logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
 
1295         return heatTemplate;
 
1299     private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
 
1301         String resourceTempl = "  subnet_%subnetId%:\n" + "    type: OS::Neutron::Subnet\n" + "    properties:\n"
 
1302                 + "      name: %name%\n" + "      network_id: { get_resource: network }\n" + "      cidr: %cidr%\n";
 
1305          * make these optional + "      ip_version: %ipversion%\n" + "      enable_dhcp: %enabledhcp%\n" +
 
1306          * "      gateway_ip: %gatewayip%\n" + "      allocation_pools:\n" + "       - start: %poolstart%\n" +
 
1307          * "         end: %poolend%\n";
 
1311         String outputTempl = "  subnet_id_%subnetId%:\n" + "    description: Openstack subnet identifier\n"
 
1312                 + "    value: {get_resource: subnet_%subnetId%}\n";
 
1316         StringBuilder resourcesBuf = new StringBuilder();
 
1317         StringBuilder outputsBuf = new StringBuilder();
 
1318         for (Subnet subnet : subnets) {
 
1320             // build template for each subnet
 
1321             curR = resourceTempl;
 
1322             if (subnet.getSubnetId() != null) {
 
1323                 curR = curR.replace("%subnetId%", subnet.getSubnetId());
 
1325                 String error = "Missing Required AAI SubnetId 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.getSubnetName() != null) {
 
1331                 curR = curR.replace("%name%", subnet.getSubnetName());
 
1333                 curR = curR.replace("%name%", subnet.getSubnetId());
 
1336             if (subnet.getCidr() != null) {
 
1337                 curR = curR.replace("%cidr%", subnet.getCidr());
 
1339                 String error = "Missing Required cidr for subnet in HEAT Template";
 
1340                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1341                 throw new MsoAdapterException(error);
 
1344             if (subnet.getIpVersion() != null) {
 
1345                 curR = curR + "      ip_version: " + subnet.getIpVersion() + "\n";
 
1347             if (subnet.getEnableDHCP() != null) {
 
1348                 curR = curR + "      enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
 
1350             if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
 
1351                 curR = curR + "      gateway_ip: " + subnet.getGatewayIp() + "\n";
 
1354             if (subnet.getAllocationPools() != null) {
 
1355                 StringBuilder tempBuf = new StringBuilder();
 
1356                 tempBuf.append(curR);
 
1357                 tempBuf.append("      allocation_pools:\n");
 
1358                 for (Pool pool : subnet.getAllocationPools()) {
 
1359                     if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
 
1360                         tempBuf.append("       - start: ");
 
1361                         tempBuf.append(pool.getStart());
 
1362                         tempBuf.append("\n         end: ");
 
1363                         tempBuf.append(pool.getEnd());
 
1364                         tempBuf.append("\n");
 
1367                 curR = tempBuf.toString();
 
1370             resourcesBuf.append(curR);
 
1373             curO = curO.replace("%subnetId%", subnet.getSubnetId());
 
1375             outputsBuf.append(curO);
 
1377         // append resources and outputs in heatTemplate
 
1378         logger.debug("Tempate initial:{}", heatTemplate);
 
1379         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1380         heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
 
1381         int resourcesIdx = heatTemplate.indexOf("resources:");
 
1382         heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
 
1384         logger.debug("Template updated with all subnets:{}", heatTemplate);
 
1385         return heatTemplate;
 
1388     private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
 
1390         Map<String, String> sMap = new HashMap<>();
 
1393             Object obj = outputs.get(key);
 
1394             ObjectMapper mapper = new ObjectMapper();
 
1395             String jStr = mapper.writeValueAsString(obj);
 
1396             logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
 
1398             JsonNode rootNode = mapper.readTree(jStr);
 
1399             if (rootNode != null) {
 
1400                 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
 
1401                     logger.debug("Output Subnet Node {}", sNode);
 
1402                     String name = sNode.path("subnet_name").textValue();
 
1403                     String uuid = sNode.path("subnet_uuid").textValue();
 
1404                     String aaiId = name; // default
 
1405                     // try to find aaiId for name in input subnetList
 
1406                     if (subnets != null) {
 
1407                         for (Subnet subnet : subnets) {
 
1408                             if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
 
1409                                     && subnet.getSubnetName().equals(name)) {
 
1410                                 aaiId = subnet.getSubnetId();
 
1415                     sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
 
1418                 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
 
1419                         ErrorCode.DataError.getValue());
 
1421         } catch (Exception e) {
 
1422             logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
 
1423                     ErrorCode.DataError.getValue(), e);
 
1426         logger.debug("Return sMap {}", sMap);
 
1430     private static String insertStr(String template, String snippet, int index) {
 
1432         String updatedTemplate;
 
1434         logger.debug("Index:{} Snippet:{}", index, snippet);
 
1436         String templateBeg = template.substring(0, index);
 
1437         String templateEnd = template.substring(index);
 
1439         updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
 
1441         logger.debug("Template updated with a subnet:{}", updatedTemplate);
 
1442         return updatedTemplate;