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         int timeoutMinutes = heat.getNetworkHeatTimeoutValue(modelCustomizationUuid, networkType);
 
 969                     heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, timeoutMinutes);
 
 970             networkDeleted.value = stack.isOperationPerformed();
 
 971         } catch (MsoException me) {
 
 972             me.addContext("DeleteNetwork");
 
 973             logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
 
 974                     ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 975             throw new NetworkException(me);
 
 979             heat.updateResourceStatus(msoRequest.getRequestId(),
 
 980                     networkDeleted.value ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
 
 981         } catch (Exception e) {
 
 982             logger.warn("Exception while updating infra active request", e);
 
 987      * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
 
 988      * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
 
 989      * to undo the creation.
 
 991      * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
 
 995     public void rollbackNetwork(NetworkRollback rollback, Boolean pollForCompletion) throws NetworkException {
 
 996         if (rollback == null) {
 
 997             logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
 
1001         if (pollForCompletion == null) {
 
1002             pollForCompletion = true;
 
1005         // Get the elements of the VnfRollback object for easier access
 
1006         String cloudSiteId = rollback.getCloudId();
 
1007         String tenantId = rollback.getTenantId();
 
1008         String networkId = rollback.getNetworkStackId();
 
1010         logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
 
1012         if (rollback.getNetworkCreated()) {
 
1014                 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, 120);
 
1015             } catch (MsoException me) {
 
1016                 me.addContext("RollbackNetwork");
 
1017                 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
 
1018                         MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
 
1019                         cloudSiteId, tenantId, me);
 
1020                 throw new NetworkException(me);
 
1026     private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
 
1027             List<Integer> vlans, List<RouteTarget> routeTargets) {
 
1029         StringBuilder missing = new StringBuilder();
 
1030         if (commonUtils.isNullOrEmpty(networkName)) {
 
1031             missing.append("networkName");
 
1035         if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1036             if (commonUtils.isNullOrEmpty(physicalNetwork)) {
 
1037                 missing.append(sep).append("physicalNetworkName");
 
1040             if (vlans == null || vlans.isEmpty()) {
 
1041                 missing.append(sep).append(VLANS);
 
1045         return missing.toString();
 
1048     private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
 
1049             String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
 
1050             boolean os3template) {
 
1051         // Build the common set of HEAT template parameters
 
1052         Map<String, Object> stackParams = new HashMap<>();
 
1053         stackParams.put("network_name", networkName);
 
1055         if (neutronNetworkType == NetworkType.PROVIDER) {
 
1056             // For Provider type
 
1057             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1058             stackParams.put("vlan", vlans.get(0).toString());
 
1059         } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1060             // For Multi-provider, PO supports a custom resource extension of ProviderNet.
 
1061             // It supports all ProviderNet properties except segmentation_id, and adds a
 
1062             // comma-separated-list of VLANs as a "segments" property.
 
1063             // Note that this does not match the Neutron definition of Multi-Provider network,
 
1064             // which contains a list of 'segments', each having physical_network, network_type,
 
1065             // and segmentation_id.
 
1066             StringBuilder buf = new StringBuilder();
 
1068             for (Integer vlan : vlans) {
 
1069                 buf.append(sep).append(vlan.toString());
 
1072             String csl = buf.toString();
 
1074             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1075             stackParams.put(VLANS, csl);
 
1077         if (routeTargets != null) {
 
1079             String rtGlobal = "";
 
1080             String rtImport = "";
 
1081             String rtExport = "";
 
1083             for (RouteTarget rt : routeTargets) {
 
1084                 boolean rtIsNull = false;
 
1086                     String routeTarget = rt.getRouteTarget();
 
1087                     String routeTargetRole = rt.getRouteTargetRole();
 
1088                     logger.debug("Checking for an actually null route target: {}", rt);
 
1089                     if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
 
1091                     if (routeTargetRole == null || routeTargetRole.equals("")
 
1092                             || routeTargetRole.equalsIgnoreCase("null"))
 
1098                     logger.debug("Input RT:{}", rt);
 
1099                     String role = rt.getRouteTargetRole();
 
1100                     String rtValue = rt.getRouteTarget();
 
1102                     if ("IMPORT".equalsIgnoreCase(role)) {
 
1103                         sep = rtImport.isEmpty() ? "" : ",";
 
1104                         rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
 
1105                     } else if ("EXPORT".equalsIgnoreCase(role)) {
 
1106                         sep = rtExport.isEmpty() ? "" : ",";
 
1107                         rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
 
1108                     } else // covers BOTH, empty etc
 
1110                         sep = rtGlobal.isEmpty() ? "" : ",";
 
1111                         rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
 
1117             if (!rtImport.isEmpty()) {
 
1118                 stackParams.put("route_targets_import", rtImport);
 
1120             if (!rtExport.isEmpty()) {
 
1121                 stackParams.put("route_targets_export", rtExport);
 
1123             if (!rtGlobal.isEmpty()) {
 
1124                 stackParams.put("route_targets", rtGlobal);
 
1127         if (commonUtils.isNullOrEmpty(shared)) {
 
1128             stackParams.put("shared", "False");
 
1130             stackParams.put("shared", shared);
 
1132         if (commonUtils.isNullOrEmpty(external)) {
 
1133             stackParams.put("external", "False");
 
1135             stackParams.put("external", external);
 
1143      * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
 
1144      * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
 
1145      * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
 
1146      * "network_policy_refs_data_sequence_minor": "0" } } ]
 
1148     private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
 
1149         // Resource Property
 
1150         List<ContrailPolicyRef> prlist = new ArrayList<>();
 
1153         if (pFqdns != null) {
 
1154             for (String pf : pFqdns) {
 
1155                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1156                     ContrailPolicyRef pr = new ContrailPolicyRef();
 
1157                     ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
 
1160                     logger.debug("Contrail PolicyRefs Data:{}", pr);
 
1165             String error = "Null pFqdns at start of mergePolicyRefs";
 
1166             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
 
1168             throw new MsoAdapterException(error);
 
1171         JsonNode node = null;
 
1173             ObjectMapper mapper = new ObjectMapper();
 
1174             node = mapper.convertValue(prlist, JsonNode.class);
 
1175             String jsonString = mapper.writeValueAsString(prlist);
 
1176             logger.debug("Json PolicyRefs Data:{}", jsonString);
 
1177         } catch (Exception e) {
 
1178             String error = "Error creating JsonNode for policyRefs Data";
 
1179             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
 
1181             throw new MsoAdapterException(error);
 
1183         // update parameters
 
1184         if (pFqdns != null && node != null) {
 
1185             StringBuilder buf = new StringBuilder();
 
1187             for (String pf : pFqdns) {
 
1188                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1189                     buf.append(sep).append(pf);
 
1193             String csl = buf.toString();
 
1194             stackParams.put("policy_refs", csl);
 
1195             stackParams.put("policy_refsdata", node);
 
1198         logger.debug("StackParams updated with policy refs");
 
1202     private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
 
1204         // update parameters
 
1205         if (rtFqdns != null) {
 
1206             StringBuilder buf = new StringBuilder();
 
1208             for (String rtf : rtFqdns) {
 
1209                 if (!commonUtils.isNullOrEmpty(rtf)) {
 
1210                     buf.append(sep).append(rtf);
 
1214             String csl = buf.toString();
 
1215             stackParams.put("route_table_refs", csl);
 
1218         logger.debug("StackParams updated with route_table refs");
 
1224      * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
 
1225      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
 
1226      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
 
1227      * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
 
1228      * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
 
1229      * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
 
1230      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
 
1231      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
 
1232      * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
 
1233      * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
 
1234      * "host_routes": null }
 
1236     private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
 
1237             throws MsoException {
 
1239         // Resource Property
 
1240         List<ContrailSubnet> cslist = new ArrayList<>();
 
1241         for (Subnet subnet : subnets) {
 
1242             logger.debug("Input Subnet:{}", subnet);
 
1243             ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
 
1244             logger.debug("Contrail Subnet:{}", cs);
 
1248         JsonNode node = null;
 
1250             ObjectMapper mapper = new ObjectMapper();
 
1251             node = mapper.convertValue(cslist, JsonNode.class);
 
1252             String jsonString = mapper.writeValueAsString(cslist);
 
1253             logger.debug("Json Subnet List:{}", jsonString);
 
1254         } catch (Exception e) {
 
1255             String error = "Error creating JsonNode from input subnets";
 
1256             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
 
1257             throw new MsoAdapterException(error);
 
1259         // update parameters
 
1261             stackParams.put("subnet_list", node);
 
1263         // Outputs - All subnets are in one ipam_subnets structure
 
1264         String outputTempl = "  subnet:\n" + "    description: Openstack subnet identifier\n"
 
1265                 + "    value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
 
1267         // append outputs in heatTemplate
 
1268         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1269         heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
 
1270         logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
 
1271         return heatTemplate;
 
1275     private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
 
1277         String resourceTempl = "  subnet_%subnetId%:\n" + "    type: OS::Neutron::Subnet\n" + "    properties:\n"
 
1278                 + "      name: %name%\n" + "      network_id: { get_resource: network }\n" + "      cidr: %cidr%\n";
 
1281          * make these optional + "      ip_version: %ipversion%\n" + "      enable_dhcp: %enabledhcp%\n" +
 
1282          * "      gateway_ip: %gatewayip%\n" + "      allocation_pools:\n" + "       - start: %poolstart%\n" +
 
1283          * "         end: %poolend%\n";
 
1287         String outputTempl = "  subnet_id_%subnetId%:\n" + "    description: Openstack subnet identifier\n"
 
1288                 + "    value: {get_resource: subnet_%subnetId%}\n";
 
1292         StringBuilder resourcesBuf = new StringBuilder();
 
1293         StringBuilder outputsBuf = new StringBuilder();
 
1294         for (Subnet subnet : subnets) {
 
1296             // build template for each subnet
 
1297             curR = resourceTempl;
 
1298             if (subnet.getSubnetId() != null) {
 
1299                 curR = curR.replace("%subnetId%", subnet.getSubnetId());
 
1301                 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
 
1302                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1303                 throw new MsoAdapterException(error);
 
1306             if (subnet.getSubnetName() != null) {
 
1307                 curR = curR.replace("%name%", subnet.getSubnetName());
 
1309                 curR = curR.replace("%name%", subnet.getSubnetId());
 
1312             if (subnet.getCidr() != null) {
 
1313                 curR = curR.replace("%cidr%", subnet.getCidr());
 
1315                 String error = "Missing Required cidr for subnet in HEAT Template";
 
1316                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1317                 throw new MsoAdapterException(error);
 
1320             if (subnet.getIpVersion() != null) {
 
1321                 curR = curR + "      ip_version: " + subnet.getIpVersion() + "\n";
 
1323             if (subnet.getEnableDHCP() != null) {
 
1324                 curR = curR + "      enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
 
1326             if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
 
1327                 curR = curR + "      gateway_ip: " + subnet.getGatewayIp() + "\n";
 
1330             if (subnet.getAllocationPools() != null) {
 
1331                 StringBuilder tempBuf = new StringBuilder();
 
1332                 tempBuf.append(curR);
 
1333                 tempBuf.append("      allocation_pools:\n");
 
1334                 for (Pool pool : subnet.getAllocationPools()) {
 
1335                     if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
 
1336                         tempBuf.append("       - start: ");
 
1337                         tempBuf.append(pool.getStart());
 
1338                         tempBuf.append("\n         end: ");
 
1339                         tempBuf.append(pool.getEnd());
 
1340                         tempBuf.append("\n");
 
1343                 curR = tempBuf.toString();
 
1346             resourcesBuf.append(curR);
 
1349             curO = curO.replace("%subnetId%", subnet.getSubnetId());
 
1351             outputsBuf.append(curO);
 
1353         // append resources and outputs in heatTemplate
 
1354         logger.debug("Tempate initial:{}", heatTemplate);
 
1355         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1356         heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
 
1357         int resourcesIdx = heatTemplate.indexOf("resources:");
 
1358         heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
 
1360         logger.debug("Template updated with all subnets:{}", heatTemplate);
 
1361         return heatTemplate;
 
1364     private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
 
1366         Map<String, String> sMap = new HashMap<>();
 
1369             Object obj = outputs.get(key);
 
1370             ObjectMapper mapper = new ObjectMapper();
 
1371             String jStr = mapper.writeValueAsString(obj);
 
1372             logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
 
1374             JsonNode rootNode = mapper.readTree(jStr);
 
1375             if (rootNode != null) {
 
1376                 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
 
1377                     logger.debug("Output Subnet Node {}", sNode);
 
1378                     String name = sNode.path("subnet_name").textValue();
 
1379                     String uuid = sNode.path("subnet_uuid").textValue();
 
1380                     String aaiId = name; // default
 
1381                     // try to find aaiId for name in input subnetList
 
1382                     if (subnets != null) {
 
1383                         for (Subnet subnet : subnets) {
 
1384                             if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
 
1385                                     && subnet.getSubnetName().equals(name)) {
 
1386                                 aaiId = subnet.getSubnetId();
 
1391                     sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
 
1394                 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
 
1395                         ErrorCode.DataError.getValue());
 
1397         } catch (Exception e) {
 
1398             logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
 
1399                     ErrorCode.DataError.getValue(), e);
 
1402         logger.debug("Return sMap {}", sMap);
 
1406     private static String insertStr(String template, String snippet, int index) {
 
1408         String updatedTemplate;
 
1410         logger.debug("Index:{} Snippet:{}", index, snippet);
 
1412         String templateBeg = template.substring(0, index);
 
1413         String templateEnd = template.substring(index);
 
1415         updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
 
1417         logger.debug("Template updated with a subnet:{}", updatedTemplate);
 
1418         return updatedTemplate;