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 com.fasterxml.jackson.databind.JsonNode;
 
  28 import com.fasterxml.jackson.databind.ObjectMapper;
 
  29 import java.util.ArrayList;
 
  30 import java.util.HashMap;
 
  31 import java.util.List;
 
  33 import java.util.Optional;
 
  34 import javax.jws.WebService;
 
  35 import javax.xml.ws.Holder;
 
  36 import org.onap.so.logger.LoggingAnchor;
 
  37 import org.onap.so.adapters.network.beans.ContrailPolicyRef;
 
  38 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
 
  39 import org.onap.so.adapters.network.beans.ContrailSubnet;
 
  40 import org.onap.so.adapters.network.exceptions.NetworkException;
 
  41 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
 
  42 import org.onap.so.cloud.CloudConfig;
 
  43 import org.onap.so.db.catalog.beans.CloudSite;
 
  44 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
 
  45 import org.onap.so.db.catalog.beans.HeatTemplate;
 
  46 import org.onap.so.db.catalog.beans.NetworkResource;
 
  47 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
 
  48 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
 
  49 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
 
  50 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
 
  51 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
 
  52 import org.onap.so.entity.MsoRequest;
 
  53 import org.onap.so.logger.ErrorCode;
 
  54 import org.onap.so.logger.MessageEnum;
 
  55 import org.onap.so.openstack.beans.HeatStatus;
 
  56 import org.onap.so.openstack.beans.NetworkInfo;
 
  57 import org.onap.so.openstack.beans.NetworkRollback;
 
  58 import org.onap.so.openstack.beans.NetworkStatus;
 
  59 import org.onap.so.openstack.beans.Pool;
 
  60 import org.onap.so.openstack.beans.RouteTarget;
 
  61 import org.onap.so.openstack.beans.StackInfo;
 
  62 import org.onap.so.openstack.beans.Subnet;
 
  63 import org.onap.so.openstack.exceptions.MsoAdapterException;
 
  64 import org.onap.so.openstack.exceptions.MsoException;
 
  65 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
 
  66 import org.onap.so.openstack.utils.MsoCommonUtils;
 
  67 import org.onap.so.openstack.utils.MsoHeatUtils;
 
  68 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
 
  69 import org.onap.so.openstack.utils.MsoNeutronUtils;
 
  70 import org.onap.so.openstack.utils.MsoNeutronUtils.NetworkType;
 
  71 import org.slf4j.Logger;
 
  72 import org.slf4j.LoggerFactory;
 
  73 import org.springframework.beans.factory.annotation.Autowired;
 
  74 import org.springframework.core.env.Environment;
 
  75 import org.springframework.stereotype.Component;
 
  76 import org.springframework.transaction.annotation.Transactional;
 
  80 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.onap.so.adapters.network.MsoNetworkAdapter",
 
  81         targetNamespace = "http://org.onap.so/network")
 
  82 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
 
  84     private static final String AIC3_NW_PROPERTY = "org.onap.so.adapters.network.aic3nw";
 
  85     private static final String AIC3_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";
 
  95     private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
 
  98     private CloudConfig cloudConfig;
 
 100     private Environment environment;
 
 102     private MsoNeutronUtils neutron;
 
 104     private MsoHeatUtils heat;
 
 106     private MsoHeatUtilsWithUpdate heatWithUpdate;
 
 108     private MsoCommonUtils commonUtils;
 
 111     private NetworkResourceCustomizationRepository networkCustomRepo;
 
 114     private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
 
 117     private NetworkResourceRepository networkResourceRepo;
 
 119     public MsoNetworkAdapterImpl() {}
 
 122      * Health Check web method. Does nothing but return to show the adapter is deployed.
 
 125     public void healthCheck() {
 
 126         logger.debug("Health check call in Network Adapter");
 
 130      * Do not use this constructor or the msoPropertiesFactory will be NULL.
 
 132      * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
 
 136     public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 137             String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
 
 138             Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
 
 139             MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 140             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 141         Holder<String> networkFqdn = new Holder<>();
 
 142         createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
 
 143                 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
 
 144                 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
 
 148     public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
 
 149             String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
 
 150             String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
 
 151             Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
 
 152             MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 153             Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
 
 154             throws NetworkException {
 
 155         createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
 
 156                 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
 
 157                 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
 
 161      * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
 
 162      * the specified cloud and tenant. The tenant must exist at the time this service is called.
 
 164      * If a network with the same name already exists, this can be considered a success or failure, depending on the
 
 165      * value of the 'failIfExists' parameter.
 
 167      * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
 
 168      * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
 
 169      * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
 
 171      * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
 
 172      * multiple VLANs on the same physical network.
 
 174      * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
 
 175      * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
 
 176      * support some subset of the same input parameters: network_name, physical_network, vlan(s).
 
 178      * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
 
 179      * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
 
 180      * created but the orchestration fails on a subsequent operation.
 
 183     private void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 184             String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
 
 185             String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
 
 186             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
 
 187             Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
 
 188             Holder<NetworkRollback> rollback) throws NetworkException {
 
 189         logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
 
 191         // Will capture execution time for metrics
 
 192         long startTime = System.currentTimeMillis();
 
 194         // Build a default rollback object (no actions performed)
 
 195         NetworkRollback networkRollback = new NetworkRollback();
 
 196         networkRollback.setCloudId(cloudSiteId);
 
 197         networkRollback.setTenantId(tenantId);
 
 198         networkRollback.setMsoRequest(msoRequest);
 
 199         networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
 
 201         // tenant query is not required here.
 
 202         // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
 
 203         // So this is just catching that error in a bit more obvious way up front.
 
 205         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 206         if (!cloudSiteOpt.isPresent()) {
 
 207             String error = String.format(
 
 208                     "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 209                     networkName, cloudSiteId, tenantId);
 
 210             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 211             // Set the detailed error as the Exception 'message'
 
 212             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 216         NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
 
 217                 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
 
 218         String mode = networkResource.getOrchestrationMode();
 
 219         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 221         if (NEUTRON_MODE.equals(mode)) {
 
 223             // Use an MsoNeutronUtils for all neutron commands
 
 225             // See if the Network already exists (by name)
 
 226             NetworkInfo netInfo = null;
 
 227             long queryNetworkStarttime = System.currentTimeMillis();
 
 229                 netInfo = neutron.queryNetwork(networkName, tenantId, cloudSiteId);
 
 230             } catch (MsoException me) {
 
 232                         "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
 
 233                         MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkName,
 
 234                         cloudSiteId, tenantId, me);
 
 235                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 236                 throw new NetworkException(me);
 
 239             if (netInfo != null) {
 
 240                 // Exists. If that's OK, return success with the network ID.
 
 241                 // Otherwise, return an exception.
 
 242                 if (failIfExists != null && failIfExists) {
 
 243                     String error = String.format("Create Nework: Network %s already exists in %s/%s with ID %s",
 
 244                             networkName, cloudSiteId, tenantId, netInfo.getId());
 
 245                     logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
 
 246                             ErrorCode.DataError.getValue(), error);
 
 247                     throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 249                     // Populate the outputs from the existing network.
 
 250                     networkId.value = netInfo.getId();
 
 251                     neutronNetworkId.value = netInfo.getId();
 
 252                     rollback.value = networkRollback; // Default rollback - no updates performed
 
 253                     logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
 
 254                             MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), netInfo.getStatus());
 
 259             long createNetworkStarttime = System.currentTimeMillis();
 
 261                 netInfo = neutron.createNetwork(cloudSiteId, tenantId, neutronNetworkType, networkName,
 
 262                         physicalNetworkName, vlans);
 
 263             } catch (MsoException me) {
 
 264                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 265                 logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
 
 266                         ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
 
 268                 throw new NetworkException(me);
 
 271             // Note: ignoring MsoNetworkAlreadyExists because we already checked.
 
 273             // If reach this point, network creation is successful.
 
 274             // Since directly created via Neutron, networkId tracked by MSO is the same
 
 275             // as the neutron network ID.
 
 276             networkId.value = netInfo.getId();
 
 277             neutronNetworkId.value = netInfo.getId();
 
 279             networkRollback.setNetworkCreated(true);
 
 280             networkRollback.setNetworkId(netInfo.getId());
 
 281             networkRollback.setNeutronNetworkId(netInfo.getId());
 
 282             networkRollback.setNetworkType(networkType);
 
 284             logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
 
 285         } else if ("HEAT".equals(mode)) {
 
 287             HeatTemplate heatTemplate = networkResource.getHeatTemplate();
 
 288             if (heatTemplate == null) {
 
 289                 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
 
 290                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 292                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 295             logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
 
 297             // "Fix" the template if it has CR/LF (getting this from Oracle)
 
 298             String template = heatTemplate.getHeatTemplate();
 
 299             template = template.replaceAll("\r\n", "\n");
 
 301             boolean aic3template = false;
 
 302             String aic3nw = AIC3_NW;
 
 304             aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
 
 306             if (template.contains(aic3nw))
 
 309             // First, look up to see if the Network already exists (by name).
 
 310             // For HEAT orchestration of networks, the stack name will always match the network name
 
 311             StackInfo heatStack = null;
 
 312             long queryNetworkStarttime = System.currentTimeMillis();
 
 314                 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
 
 315             } catch (MsoException me) {
 
 316                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 317                 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
 
 318                         MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName, cloudSiteId,
 
 320                 throw new NetworkException(me);
 
 323             if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
 
 324                 // Stack exists. Return success or error depending on input directive
 
 325                 if (failIfExists != null && failIfExists) {
 
 326                     String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
 
 327                             cloudSiteId, tenantId, heatStack.getCanonicalName());
 
 328                     logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
 
 329                             ErrorCode.DataError.getValue(), error);
 
 330                     throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 332                     // Populate the outputs from the existing stack.
 
 333                     networkId.value = heatStack.getCanonicalName();
 
 334                     Map<String, String> sMap = new HashMap<>();
 
 335                     if (heatStack.getOutputs() != null) {
 
 336                         neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
 
 337                         rollback.value = networkRollback; // Default rollback - no updates performed
 
 339                             networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
 
 341                         Map<String, Object> outputs = heatStack.getOutputs();
 
 343                         for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 344                             String key = entry.getKey();
 
 345                             if (key != null && key.startsWith("subnet")) {
 
 346                                 if (aic3template) // one subnet_id output
 
 348                                     Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 350                                 } else // multiples subnet_%aaid% outputs
 
 352                                     String subnetUUId = (String) outputs.get(key);
 
 353                                     sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 358                     subnetIdMap.value = sMap;
 
 359                     logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
 
 360                             MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
 
 361                             networkName, cloudSiteId, tenantId);
 
 366             // Ready to deploy the new Network
 
 367             // Build the common set of HEAT template parameters
 
 368             Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
 
 369                     physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
 
 371             // Validate (and update) the input parameters against the DB definition
 
 372             // Shouldn't happen unless DB config is wrong, since all networks use same inputs
 
 373             // and inputs were already validated.
 
 375                 stackParams = heat.validateStackParams(stackParams, heatTemplate);
 
 376             } catch (IllegalArgumentException e) {
 
 377                 String error = "Create Network: Configuration Error: " + e.getMessage();
 
 378                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
 
 379                 // Input parameters were not valid
 
 380                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 383             if (subnets != null) {
 
 386                         template = mergeSubnetsAIC3(template, subnets, stackParams);
 
 388                         template = mergeSubnets(template, subnets);
 
 390                 } catch (MsoException me) {
 
 391                     me.addContext(CREATE_NETWORK_CONTEXT);
 
 392                     logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
 
 393                             MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
 
 394                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 395                     throw new NetworkException(me);
 
 399             if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
 
 401                     mergePolicyRefs(policyFqdns, stackParams);
 
 402                 } catch (MsoException me) {
 
 403                     me.addContext(CREATE_NETWORK_CONTEXT);
 
 404                     logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
 
 405                             MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
 
 406                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 407                     throw new NetworkException(me);
 
 411             if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
 
 413                     mergeRouteTableRefs(routeTableFqdns, stackParams);
 
 414                 } catch (MsoException me) {
 
 415                     me.addContext(CREATE_NETWORK_CONTEXT);
 
 416                     logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
 
 417                             MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
 
 418                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 419                     throw new NetworkException(me);
 
 423             // Deploy the network stack
 
 424             // Ignore MsoStackAlreadyExists exception because we already checked.
 
 428                 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template,
 
 429                         stackParams, true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue());
 
 430             } catch (MsoException me) {
 
 431                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 432                 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
 
 433                         ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
 
 434                 throw new NetworkException(me);
 
 437             // Reach this point if createStack is successful.
 
 439             // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
 
 440             // and the neutronNetworkId is the network UUID returned in stack outputs.
 
 441             networkId.value = heatStack.getCanonicalName();
 
 442             if (heatStack.getOutputs() != null) {
 
 443                 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
 
 445                     networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
 
 448             Map<String, Object> outputs = heatStack.getOutputs();
 
 449             Map<String, String> sMap = new HashMap<>();
 
 450             if (outputs != null) {
 
 451                 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 452                     String key = entry.getKey();
 
 453                     if (key != null && key.startsWith("subnet")) {
 
 454                         if (aic3template) // one subnet output expected
 
 456                             Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 458                         } else // multiples subnet_%aaid% outputs allowed
 
 460                             String subnetUUId = (String) outputs.get(key);
 
 461                             sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 465                 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
 
 467             subnetIdMap.value = sMap;
 
 469             rollback.value = networkRollback;
 
 470             // Populate remaining rollback info and response parameters.
 
 471             networkRollback.setNetworkStackId(heatStack.getCanonicalName());
 
 472             networkRollback.setNetworkCreated(true);
 
 473             networkRollback.setNetworkType(networkType);
 
 475             logger.debug("Network {} successfully created via HEAT", networkName);
 
 482     public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 483             String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
 
 484             String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
 
 485             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 486         updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
 
 487                 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
 
 493     public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
 
 494             String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
 
 495             String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
 
 496             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
 
 497             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 498         updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
 
 499                 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
 
 504      * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
 
 505      * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
 
 506      * remove a VLAN), but other properties may be updated as well.
 
 508      * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
 
 509      * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
 
 510      * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
 
 512      * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
 
 513      * VLANs on the same physical network.
 
 515      * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
 
 516      * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
 
 517      * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
 
 519      * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
 
 520      * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
 
 521      * a subsequent operation.
 
 523     private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 524             String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
 
 525             List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
 
 526             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
 
 527             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 529         logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
 
 530                 cloudSiteId, tenantId);
 
 532         // Will capture execution time for metrics
 
 533         long startTime = System.currentTimeMillis();
 
 535         // Build a default rollback object (no actions performed)
 
 536         NetworkRollback networkRollback = new NetworkRollback();
 
 537         networkRollback.setCloudId(cloudSiteId);
 
 538         networkRollback.setTenantId(tenantId);
 
 539         networkRollback.setMsoRequest(msoRequest);
 
 541         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 542         if (!cloudSiteOpt.isPresent()) {
 
 543             String error = String.format(
 
 544                     "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 545                     networkName, cloudSiteId, tenantId);
 
 546             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 547             // Set the detailed error as the Exception 'message'
 
 548             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 553         NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
 
 554                 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
 
 555         String mode = networkResource.getOrchestrationMode();
 
 556         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 558         // Use an MsoNeutronUtils for all Neutron commands
 
 560         if (NEUTRON_MODE.equals(mode)) {
 
 562             // Verify that the Network exists
 
 563             // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
 
 564             NetworkInfo netInfo = null;
 
 565             long queryNetworkStarttime = System.currentTimeMillis();
 
 567                 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
 
 568             } catch (MsoException me) {
 
 569                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 570                 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 571                         ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 572                 throw new NetworkException(me);
 
 575             if (netInfo == null) {
 
 576                 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
 
 577                         cloudSiteId, tenantId);
 
 578                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
 
 579                         ErrorCode.BusinessProcesssError.getValue(), error);
 
 580                 // Does not exist. Throw an exception (can't update a non-existent network)
 
 581                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 583             long updateNetworkStarttime = System.currentTimeMillis();
 
 585                 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
 
 586                         physicalNetworkName, vlans);
 
 587             } catch (MsoException me) {
 
 588                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 589                 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
 
 590                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 591                 throw new NetworkException(me);
 
 594             // Add the network ID and previously queried vlans to the rollback object
 
 595             networkRollback.setNetworkId(netInfo.getId());
 
 596             networkRollback.setNeutronNetworkId(netInfo.getId());
 
 597             networkRollback.setNetworkType(networkType);
 
 598             // Save previous parameters
 
 599             networkRollback.setNetworkName(netInfo.getName());
 
 600             networkRollback.setPhysicalNetwork(netInfo.getProvider());
 
 601             networkRollback.setVlans(netInfo.getVlans());
 
 603             logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
 
 604         } else if ("HEAT".equals(mode)) {
 
 606             // First, look up to see that the Network already exists.
 
 607             // For Heat-based orchestration, the networkId is the network Stack ID.
 
 608             StackInfo heatStack = null;
 
 609             long queryStackStarttime = System.currentTimeMillis();
 
 611                 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
 
 612             } catch (MsoException me) {
 
 613                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 614                 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 615                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 616                 throw new NetworkException(me);
 
 619             if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
 
 620                 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
 
 621                         cloudSiteId, tenantId);
 
 622                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 624                 // Network stack does not exist. Return an error
 
 625                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 628             // Get the previous parameters for rollback
 
 629             Map<String, Object> heatParams = heatStack.getParameters();
 
 631             String previousNetworkName = (String) heatParams.get("network_name");
 
 632             String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
 
 634             List<Integer> previousVlans = new ArrayList<>();
 
 635             String vlansParam = (String) heatParams.get(VLANS);
 
 636             if (vlansParam != null) {
 
 637                 for (String vlan : vlansParam.split(",")) {
 
 639                         previousVlans.add(Integer.parseInt(vlan));
 
 640                     } catch (NumberFormatException e) {
 
 641                         logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
 
 642                                 ErrorCode.DataError.getValue(), vlansParam, e);
 
 646             logger.debug("Update Stack:  Previous VLANS: {}", previousVlans);
 
 648             // Ready to deploy the updated Network via Heat
 
 651             HeatTemplate heatTemplate = networkResource.getHeatTemplate();
 
 652             if (heatTemplate == null) {
 
 653                 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
 
 654                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 656                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 659             logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
 
 661             // "Fix" the template if it has CR/LF (getting this from Oracle)
 
 662             String template = heatTemplate.getHeatTemplate();
 
 663             template = template.replaceAll("\r\n", "\n");
 
 665             boolean aic3template = false;
 
 666             String aic3nw = AIC3_NW;
 
 668             aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
 
 670             if (template.contains(aic3nw))
 
 673             // Build the common set of HEAT template parameters
 
 674             Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
 
 675                     physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
 
 677             // Validate (and update) the input parameters against the DB definition
 
 678             // Shouldn't happen unless DB config is wrong, since all networks use same inputs
 
 680                 stackParams = heat.validateStackParams(stackParams, heatTemplate);
 
 681             } catch (IllegalArgumentException e) {
 
 682                 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
 
 683                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 684                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
 
 687             if (subnets != null) {
 
 690                         template = mergeSubnetsAIC3(template, subnets, stackParams);
 
 692                         template = mergeSubnets(template, subnets);
 
 694                 } catch (MsoException me) {
 
 695                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 696                     logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
 
 697                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 698                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 699                     throw new NetworkException(me);
 
 703             if (policyFqdns != null && aic3template) {
 
 705                     mergePolicyRefs(policyFqdns, stackParams);
 
 706                 } catch (MsoException me) {
 
 707                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 708                     logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
 
 709                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 710                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 711                     throw new NetworkException(me);
 
 715             if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
 
 717                     mergeRouteTableRefs(routeTableFqdns, stackParams);
 
 718                 } catch (MsoException me) {
 
 719                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 720                     logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
 
 721                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 722                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 723                     throw new NetworkException(me);
 
 727             // Update the network stack
 
 728             // Ignore MsoStackNotFound exception because we already checked.
 
 729             long updateStackStarttime = System.currentTimeMillis();
 
 731                 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
 
 732                         stackParams, true, heatTemplate.getTimeoutMinutes());
 
 733             } catch (MsoException me) {
 
 734                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 735                 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
 
 736                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 737                 throw new NetworkException(me);
 
 740             Map<String, Object> outputs = heatStack.getOutputs();
 
 741             Map<String, String> sMap = new HashMap<>();
 
 742             if (outputs != null) {
 
 743                 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 744                     String key = entry.getKey();
 
 745                     if (key != null && key.startsWith("subnet")) {
 
 746                         if (aic3template) // one subnet output expected
 
 748                             Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 750                         } else // multiples subnet_%aaid% outputs allowed
 
 752                             String subnetUUId = (String) outputs.get(key);
 
 753                             sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 758             subnetIdMap.value = sMap;
 
 760             // Reach this point if createStack is successful.
 
 761             // Populate remaining rollback info and response parameters.
 
 762             networkRollback.setNetworkStackId(heatStack.getCanonicalName());
 
 763             if (null != outputs) {
 
 764                 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
 
 766                 logger.debug("outputs is NULL");
 
 768             networkRollback.setNetworkType(networkType);
 
 769             // Save previous parameters
 
 770             networkRollback.setNetworkName(previousNetworkName);
 
 771             networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
 
 772             networkRollback.setVlans(previousVlans);
 
 774             rollback.value = networkRollback;
 
 776             logger.debug("Network {} successfully updated via HEAT", networkId);
 
 782     private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
 
 783             String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
 
 784             String cloudSiteId, CloudSite cloudSite) throws NetworkException {
 
 785         // Retrieve the Network Resource definition
 
 786         NetworkResource networkResource = null;
 
 787         NetworkResourceCustomization networkCust = null;
 
 788         CollectionNetworkResourceCustomization collectionNetworkCust = null;
 
 789         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
 790             if (!commonUtils.isNullOrEmpty(networkType)) {
 
 791                 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
 
 794             networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 795             if (networkCust == null) {
 
 796                 collectionNetworkCust =
 
 797                         collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 800         if (networkCust != null) {
 
 801             logger.debug("Got Network Customization definition from Catalog: {}", networkCust.toString());
 
 803             networkResource = networkCust.getNetworkResource();
 
 804         } else if (collectionNetworkCust != null) {
 
 805             logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}",
 
 806                     collectionNetworkCust.toString());
 
 807             networkResource = collectionNetworkCust.getNetworkResource();
 
 809         if (networkResource == null) {
 
 810             String error = String.format(
 
 811                     "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
 
 812                     networkType, modelCustomizationUuid);
 
 813             logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
 
 815             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 817         logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
 
 819         String mode = networkResource.getOrchestrationMode();
 
 820         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 822         // All Networks are orchestrated via HEAT or Neutron
 
 823         if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
 
 824             String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
 
 825             logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
 
 826                     ErrorCode.DataError.getValue(), error);
 
 827             throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 830         MavenLikeVersioning aicV = new MavenLikeVersioning();
 
 831         aicV.setVersion(cloudSite.getCloudVersion());
 
 832         if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
 
 833                 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
 
 836                 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
 
 837                         || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
 
 839             logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
 
 840                     networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
 
 841                     cloudSite.getCloudVersion());
 
 843             String error = String.format(
 
 844                     "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
 
 845                     networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
 
 846                     cloudSite.getCloudVersion());
 
 847             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 848             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 851         // Validate the Network parameters.
 
 853                 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
 
 854         if (!missing.isEmpty()) {
 
 855             String error = "Create Network: Missing parameters: " + missing;
 
 856             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
 858             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 861         return networkResource;
 
 865     public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 866             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 867             Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
 
 868             throws NetworkException {
 
 869         queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
 
 870                 status, vlans, null, subnetIdMap);
 
 874     public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 875             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 876             Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
 
 877             Holder<Map<String, String>> subnetIdMap) throws NetworkException {
 
 878         queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
 
 879                 status, null, routeTargets, subnetIdMap);
 
 883      * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
 
 884      * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
 
 885      * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
 
 887     private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 888             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 889             Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
 
 890             Holder<Map<String, String>> subnetIdMap) throws NetworkException {
 
 892         logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
 
 894         // Will capture execution time for metrics
 
 895         long startTime = System.currentTimeMillis();
 
 897         if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
 
 898                 || commonUtils.isNullOrEmpty(networkNameOrId)) {
 
 900             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
 
 901             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
 902             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 905         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 906         if (!cloudSiteOpt.isPresent()) {
 
 907             String error = String.format(
 
 908                     "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 909                     networkNameOrId, cloudSiteId, tenantId);
 
 910             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 911             // Set the detailed error as the Exception 'message'
 
 912             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 915         // Use MsoNeutronUtils for all NEUTRON commands
 
 918         String neutronId = null;
 
 919         // Try Heat first, since networks may be named the same as the Heat stack
 
 920         StackInfo heatStack = null;
 
 921         long queryStackStarttime = System.currentTimeMillis();
 
 923             heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
 
 924         } catch (MsoException me) {
 
 925             me.addContext("QueryNetwork");
 
 926             logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 927                     ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
 
 928             throw new NetworkException(me);
 
 931         // Populate the outputs based on the returned Stack information
 
 932         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
 
 933             // Found it. Get the neutronNetworkId for further query
 
 934             Map<String, String> sMap = new HashMap<>();
 
 935             Map<String, Object> outputs = heatStack.getOutputs();
 
 937             if (outputs != null) {
 
 938                 neutronId = (String) outputs.get(NETWORK_ID);
 
 940                 for (String key : outputs.keySet()) {
 
 941                     if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
 
 943                         String subnetUUId = (String) outputs.get(key);
 
 944                         sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 945                     } else if (key != null && key.startsWith("subnet")) // one subnet output expected
 
 947                         Map<String, String> map = getSubnetUUId(key, outputs, null);
 
 953             subnetIdMap.value = sMap;
 
 955             // Input ID was not a Heat stack ID. Try it directly in Neutron
 
 956             neutronId = networkNameOrId;
 
 960         // Query directly against the Neutron Network for the details
 
 961         // no RouteTargets available for ContrailV2 in neutron net-show
 
 962         // networkId is heatStackId
 
 963         long queryNetworkStarttime = System.currentTimeMillis();
 
 965             NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
 
 966             if (netInfo != null) {
 
 967                 // Found. Populate the output elements
 
 968                 networkExists.value = Boolean.TRUE;
 
 969                 if ("HEAT".equals(mode) && heatStack != null) {
 
 970                     networkId.value = heatStack.getCanonicalName();
 
 972                     networkId.value = netInfo.getId();
 
 974                 neutronNetworkId.value = netInfo.getId();
 
 975                 status.value = netInfo.getStatus();
 
 977                     vlans.value = netInfo.getVlans();
 
 979                 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
 
 980                         ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
 
 982                 // Not found. Populate the status fields, leave the rest null
 
 983                 networkExists.value = Boolean.FALSE;
 
 984                 status.value = NetworkStatus.NOTFOUND;
 
 985                 neutronNetworkId.value = null;
 
 987                     vlans.value = new ArrayList<>();
 
 989                 logger.debug("Network {} not found", networkNameOrId);
 
 991         } catch (MsoException me) {
 
 992             me.addContext("QueryNetwork");
 
 993             logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 994                     ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
 
 995             throw new NetworkException(me);
 
1001      * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
 
1004      * If the network is not found, it is treated as a success.
 
1006      * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
 
1007      * network orchestration mode for each network type is declared in its catalog definition.
 
1009      * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
 
1010      * networkId should be the Neutron network UUID.
 
1012      * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
 
1013      * will require manual fallout in the client.
 
1016     public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
1017             String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
 
1019         logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
 
1021         // Will capture execution time for metrics
 
1022         long startTime = System.currentTimeMillis();
 
1025         if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
 
1026                 || commonUtils.isNullOrEmpty(networkId)) {
 
1027             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
 
1028             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1029             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
1032         // Retrieve the Network Resource definition
 
1033         NetworkResource networkResource = null;
 
1035         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
1036             if (!commonUtils.isNullOrEmpty(networkType)) {
 
1037                 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
 
1040             NetworkResourceCustomization nrc =
 
1041                     networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
1043                 networkResource = nrc.getNetworkResource();
 
1047         if (networkResource != null) {
 
1048             logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
 
1050             mode = networkResource.getOrchestrationMode();
 
1053         if (NEUTRON_MODE.equals(mode)) {
 
1055             // Use MsoNeutronUtils for all NEUTRON commands
 
1056             long deleteNetworkStarttime = System.currentTimeMillis();
 
1058                 // The deleteNetwork function in MsoNeutronUtils returns success if the network
 
1059                 // was not found. So don't bother to query first.
 
1060                 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
 
1061                 networkDeleted.value = deleted;
 
1062             } catch (MsoException me) {
 
1063                 me.addContext("DeleteNetwork");
 
1064                 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
 
1065                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
1066                 throw new NetworkException(me);
 
1068         } else { // DEFAULT to ("HEAT".equals (mode))
 
1069             long deleteStackStarttime = System.currentTimeMillis();
 
1072                 // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack
 
1074                 // So query first to report back if stack WAS deleted or just NOTOFUND
 
1075                 StackInfo heatStack = null;
 
1076                 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId);
 
1077                 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
 
1078                     heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true);
 
1079                     networkDeleted.value = true;
 
1081                     networkDeleted.value = false;
 
1083             } catch (MsoException me) {
 
1084                 me.addContext("DeleteNetwork");
 
1085                 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
 
1086                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
1087                 throw new NetworkException(me);
 
1092         // On success, nothing is returned.
 
1097      * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
 
1098      * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
 
1099      * to undo the creation.
 
1101      * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
 
1105     public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
 
1106         // Will capture execution time for metrics
 
1107         long startTime = System.currentTimeMillis();
 
1109         if (rollback == null) {
 
1110             logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
 
1114         // Get the elements of the VnfRollback object for easier access
 
1115         String cloudSiteId = rollback.getCloudId();
 
1116         String tenantId = rollback.getTenantId();
 
1117         String networkId = rollback.getNetworkStackId();
 
1118         String networkType = rollback.getNetworkType();
 
1119         String modelCustomizationUuid = rollback.getModelCustomizationUuid();
 
1121         logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
 
1124         // Retrieve the Network Resource definition
 
1125         NetworkResource networkResource = null;
 
1126         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
1127             networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
 
1130                     networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
 
1133         if (networkResource != null) {
 
1135             logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
 
1137             mode = networkResource.getOrchestrationMode();
 
1140         if (rollback.getNetworkCreated()) {
 
1141             // Rolling back a newly created network, so delete it.
 
1142             if (NEUTRON_MODE.equals(mode)) {
 
1143                 // Use MsoNeutronUtils for all NEUTRON commands
 
1144                 long deleteNetworkStarttime = System.currentTimeMillis();
 
1146                     // The deleteNetwork function in MsoNeutronUtils returns success if the network
 
1147                     // was not found. So don't bother to query first.
 
1148                     neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
 
1149                 } catch (MsoException me) {
 
1150                     me.addContext("RollbackNetwork");
 
1151                     logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
 
1152                             MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
 
1153                             cloudSiteId, tenantId, me);
 
1154                     throw new NetworkException(me);
 
1156             } else { // DEFAULT to if ("HEAT".equals (mode))
 
1157                 long deleteStackStarttime = System.currentTimeMillis();
 
1159                     // The deleteStack function in MsoHeatUtils returns success if the stack
 
1160                     // was not found. So don't bother to query first.
 
1161                     heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true);
 
1162                 } catch (MsoException me) {
 
1163                     me.addContext("RollbackNetwork");
 
1164                     logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
 
1165                             MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
 
1166                             cloudSiteId, tenantId, me);
 
1167                     throw new NetworkException(me);
 
1175     private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
 
1176             List<Integer> vlans, List<RouteTarget> routeTargets) {
 
1178         StringBuilder missing = new StringBuilder();
 
1179         if (commonUtils.isNullOrEmpty(networkName)) {
 
1180             missing.append("networkName");
 
1184         if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1185             if (commonUtils.isNullOrEmpty(physicalNetwork)) {
 
1186                 missing.append(sep).append("physicalNetworkName");
 
1189             if (vlans == null || vlans.isEmpty()) {
 
1190                 missing.append(sep).append(VLANS);
 
1194         return missing.toString();
 
1197     private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
 
1198             String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
 
1199             boolean aic3template) {
 
1200         // Build the common set of HEAT template parameters
 
1201         Map<String, Object> stackParams = new HashMap<>();
 
1202         stackParams.put("network_name", networkName);
 
1204         if (neutronNetworkType == NetworkType.PROVIDER) {
 
1205             // For Provider type
 
1206             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1207             stackParams.put("vlan", vlans.get(0).toString());
 
1208         } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1209             // For Multi-provider, PO supports a custom resource extension of ProviderNet.
 
1210             // It supports all ProviderNet properties except segmentation_id, and adds a
 
1211             // comma-separated-list of VLANs as a "segments" property.
 
1212             // Note that this does not match the Neutron definition of Multi-Provider network,
 
1213             // which contains a list of 'segments', each having physical_network, network_type,
 
1214             // and segmentation_id.
 
1215             StringBuilder buf = new StringBuilder();
 
1217             for (Integer vlan : vlans) {
 
1218                 buf.append(sep).append(vlan.toString());
 
1221             String csl = buf.toString();
 
1223             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1224             stackParams.put(VLANS, csl);
 
1226         if (routeTargets != null) {
 
1228             String rtGlobal = "";
 
1229             String rtImport = "";
 
1230             String rtExport = "";
 
1232             for (RouteTarget rt : routeTargets) {
 
1233                 boolean rtIsNull = false;
 
1235                     String routeTarget = rt.getRouteTarget();
 
1236                     String routeTargetRole = rt.getRouteTargetRole();
 
1237                     logger.debug("Checking for an actually null route target: {}", rt);
 
1238                     if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
 
1240                     if (routeTargetRole == null || routeTargetRole.equals("")
 
1241                             || routeTargetRole.equalsIgnoreCase("null"))
 
1247                     logger.debug("Input RT:{}", rt);
 
1248                     String role = rt.getRouteTargetRole();
 
1249                     String rtValue = rt.getRouteTarget();
 
1251                     if ("IMPORT".equalsIgnoreCase(role)) {
 
1252                         sep = rtImport.isEmpty() ? "" : ",";
 
1253                         rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
 
1254                     } else if ("EXPORT".equalsIgnoreCase(role)) {
 
1255                         sep = rtExport.isEmpty() ? "" : ",";
 
1256                         rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
 
1257                     } else // covers BOTH, empty etc
 
1259                         sep = rtGlobal.isEmpty() ? "" : ",";
 
1260                         rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
 
1266             if (!rtImport.isEmpty()) {
 
1267                 stackParams.put("route_targets_import", rtImport);
 
1269             if (!rtExport.isEmpty()) {
 
1270                 stackParams.put("route_targets_export", rtExport);
 
1272             if (!rtGlobal.isEmpty()) {
 
1273                 stackParams.put("route_targets", rtGlobal);
 
1276         if (commonUtils.isNullOrEmpty(shared)) {
 
1277             stackParams.put("shared", "False");
 
1279             stackParams.put("shared", shared);
 
1281         if (commonUtils.isNullOrEmpty(external)) {
 
1282             stackParams.put("external", "False");
 
1284             stackParams.put("external", external);
 
1292      * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
 
1293      * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
 
1294      * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
 
1295      * "network_policy_refs_data_sequence_minor": "0" } } ]
 
1297     private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
 
1298         // Resource Property
 
1299         List<ContrailPolicyRef> prlist = new ArrayList<>();
 
1302         if (pFqdns != null) {
 
1303             for (String pf : pFqdns) {
 
1304                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1305                     ContrailPolicyRef pr = new ContrailPolicyRef();
 
1306                     ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
 
1309                     logger.debug("Contrail PolicyRefs Data:{}", pr);
 
1314             String error = "Null pFqdns at start of mergePolicyRefs";
 
1315             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
 
1317             throw new MsoAdapterException(error);
 
1320         JsonNode node = null;
 
1322             ObjectMapper mapper = new ObjectMapper();
 
1323             node = mapper.convertValue(prlist, JsonNode.class);
 
1324             String jsonString = mapper.writeValueAsString(prlist);
 
1325             logger.debug("Json PolicyRefs Data:{}", jsonString);
 
1326         } catch (Exception e) {
 
1327             String error = "Error creating JsonNode for policyRefs Data";
 
1328             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
 
1330             throw new MsoAdapterException(error);
 
1332         // update parameters
 
1333         if (pFqdns != null && node != null) {
 
1334             StringBuilder buf = new StringBuilder();
 
1336             for (String pf : pFqdns) {
 
1337                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1338                     buf.append(sep).append(pf);
 
1342             String csl = buf.toString();
 
1343             stackParams.put("policy_refs", csl);
 
1344             stackParams.put("policy_refsdata", node);
 
1347         logger.debug("StackParams updated with policy refs");
 
1351     private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
 
1353         // update parameters
 
1354         if (rtFqdns != null) {
 
1355             StringBuilder buf = new StringBuilder();
 
1357             for (String rtf : rtFqdns) {
 
1358                 if (!commonUtils.isNullOrEmpty(rtf)) {
 
1359                     buf.append(sep).append(rtf);
 
1363             String csl = buf.toString();
 
1364             stackParams.put("route_table_refs", csl);
 
1367         logger.debug("StackParams updated with route_table refs");
 
1373      * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
 
1374      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
 
1375      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
 
1376      * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
 
1377      * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
 
1378      * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
 
1379      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
 
1380      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
 
1381      * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
 
1382      * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
 
1383      * "host_routes": null }
 
1385     private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
 
1386             throws MsoException {
 
1388         // Resource Property
 
1389         List<ContrailSubnet> cslist = new ArrayList<>();
 
1390         for (Subnet subnet : subnets) {
 
1391             logger.debug("Input Subnet:{}", subnet.toString());
 
1392             ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
 
1393             logger.debug("Contrail Subnet:{}", cs.toString());
 
1397         JsonNode node = null;
 
1399             ObjectMapper mapper = new ObjectMapper();
 
1400             node = mapper.convertValue(cslist, JsonNode.class);
 
1401             String jsonString = mapper.writeValueAsString(cslist);
 
1402             logger.debug("Json Subnet List:{}", jsonString);
 
1403         } catch (Exception e) {
 
1404             String error = "Error creating JsonNode from input subnets";
 
1405             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
 
1406             throw new MsoAdapterException(error);
 
1408         // update parameters
 
1410             stackParams.put("subnet_list", node);
 
1412         // Outputs - All subnets are in one ipam_subnets structure
 
1413         String outputTempl = "  subnet:\n" + "    description: Openstack subnet identifier\n"
 
1414                 + "    value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
 
1416         // append outputs in heatTemplate
 
1417         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1418         heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
 
1419         logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
 
1420         return heatTemplate;
 
1424     private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
 
1426         String resourceTempl = "  subnet_%subnetId%:\n" + "    type: OS::Neutron::Subnet\n" + "    properties:\n"
 
1427                 + "      name: %name%\n" + "      network_id: { get_resource: network }\n" + "      cidr: %cidr%\n";
 
1430          * make these optional + "      ip_version: %ipversion%\n" + "      enable_dhcp: %enabledhcp%\n" +
 
1431          * "      gateway_ip: %gatewayip%\n" + "      allocation_pools:\n" + "       - start: %poolstart%\n" +
 
1432          * "         end: %poolend%\n";
 
1436         String outputTempl = "  subnet_id_%subnetId%:\n" + "    description: Openstack subnet identifier\n"
 
1437                 + "    value: {get_resource: subnet_%subnetId%}\n";
 
1441         StringBuilder resourcesBuf = new StringBuilder();
 
1442         StringBuilder outputsBuf = new StringBuilder();
 
1443         for (Subnet subnet : subnets) {
 
1445             // build template for each subnet
 
1446             curR = resourceTempl;
 
1447             if (subnet.getSubnetId() != null) {
 
1448                 curR = curR.replace("%subnetId%", subnet.getSubnetId());
 
1450                 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
 
1451                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1452                 throw new MsoAdapterException(error);
 
1455             if (subnet.getSubnetName() != null) {
 
1456                 curR = curR.replace("%name%", subnet.getSubnetName());
 
1458                 curR = curR.replace("%name%", subnet.getSubnetId());
 
1461             if (subnet.getCidr() != null) {
 
1462                 curR = curR.replace("%cidr%", subnet.getCidr());
 
1464                 String error = "Missing Required cidr for subnet in HEAT Template";
 
1465                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1466                 throw new MsoAdapterException(error);
 
1469             if (subnet.getIpVersion() != null) {
 
1470                 curR = curR + "      ip_version: " + subnet.getIpVersion() + "\n";
 
1472             if (subnet.getEnableDHCP() != null) {
 
1473                 curR = curR + "      enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
 
1475             if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
 
1476                 curR = curR + "      gateway_ip: " + subnet.getGatewayIp() + "\n";
 
1479             if (subnet.getAllocationPools() != null) {
 
1480                 StringBuilder tempBuf = new StringBuilder();
 
1481                 tempBuf.append(curR);
 
1482                 tempBuf.append("      allocation_pools:\n");
 
1483                 for (Pool pool : subnet.getAllocationPools()) {
 
1484                     if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
 
1485                         tempBuf.append("       - start: ");
 
1486                         tempBuf.append(pool.getStart());
 
1487                         tempBuf.append("\n         end: ");
 
1488                         tempBuf.append(pool.getEnd());
 
1489                         tempBuf.append("\n");
 
1492                 curR = tempBuf.toString();
 
1495             resourcesBuf.append(curR);
 
1498             curO = curO.replace("%subnetId%", subnet.getSubnetId());
 
1500             outputsBuf.append(curO);
 
1502         // append resources and outputs in heatTemplate
 
1503         logger.debug("Tempate initial:{}", heatTemplate);
 
1504         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1505         heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
 
1506         int resourcesIdx = heatTemplate.indexOf("resources:");
 
1507         heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
 
1509         logger.debug("Template updated with all subnets:{}", heatTemplate);
 
1510         return heatTemplate;
 
1513     private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
 
1515         Map<String, String> sMap = new HashMap<>();
 
1518             Object obj = outputs.get(key);
 
1519             ObjectMapper mapper = new ObjectMapper();
 
1520             String jStr = mapper.writeValueAsString(obj);
 
1521             logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
 
1523             JsonNode rootNode = mapper.readTree(jStr);
 
1524             if (rootNode != null) {
 
1525                 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
 
1526                     logger.debug("Output Subnet Node {}", sNode.toString());
 
1527                     String name = sNode.path("subnet_name").textValue();
 
1528                     String uuid = sNode.path("subnet_uuid").textValue();
 
1529                     String aaiId = name; // default
 
1530                     // try to find aaiId for name in input subnetList
 
1531                     if (subnets != null) {
 
1532                         for (Subnet subnet : subnets) {
 
1533                             if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())) {
 
1534                                 if (subnet.getSubnetName().equals(name)) {
 
1535                                     aaiId = subnet.getSubnetId();
 
1541                     sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
 
1544                 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
 
1545                         ErrorCode.DataError.getValue());
 
1547         } catch (Exception e) {
 
1548             logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
 
1549                     ErrorCode.DataError.getValue(), e);
 
1552         logger.debug("Return sMap {}", sMap.toString());
 
1556     private static String insertStr(String template, String snippet, int index) {
 
1558         String updatedTemplate;
 
1560         logger.debug("Index:{} Snippet:{}", index, snippet);
 
1562         String templateBeg = template.substring(0, index);
 
1563         String templateEnd = template.substring(index);
 
1565         updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
 
1567         logger.debug("Template updated with a subnet:{}", updatedTemplate);
 
1568         return updatedTemplate;