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.so.logger.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 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";
 
  94     private static final String LOG_DEBUG_MSG = "Got Network definition from Catalog: {}";
 
  96     private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
 
  99     private CloudConfig cloudConfig;
 
 101     private Environment environment;
 
 103     private MsoNeutronUtils neutron;
 
 105     private MsoHeatUtils heat;
 
 107     private MsoHeatUtilsWithUpdate heatWithUpdate;
 
 109     private MsoCommonUtils commonUtils;
 
 112     private NetworkResourceCustomizationRepository networkCustomRepo;
 
 115     private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
 
 118     private NetworkResourceRepository networkResourceRepo;
 
 120     public MsoNetworkAdapterImpl() {}
 
 123      * Health Check web method. Does nothing but return to show the adapter is deployed.
 
 126     public void healthCheck() {
 
 127         logger.debug("Health check call in Network Adapter");
 
 131      * Do not use this constructor or the msoPropertiesFactory will be NULL.
 
 133      * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
 
 137     public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 138             String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
 
 139             Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
 
 140             MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 141             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 142         Holder<String> networkFqdn = new Holder<>();
 
 143         createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
 
 144                 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
 
 145                 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
 
 149     public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
 
 150             String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
 
 151             String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
 
 152             Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
 
 153             MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 154             Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
 
 155             throws NetworkException {
 
 156         createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
 
 157                 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
 
 158                 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
 
 162      * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
 
 163      * the specified cloud and tenant. The tenant must exist at the time this service is called.
 
 165      * If a network with the same name already exists, this can be considered a success or failure, depending on the
 
 166      * value of the 'failIfExists' parameter.
 
 168      * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
 
 169      * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
 
 170      * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
 
 172      * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
 
 173      * multiple VLANs on the same physical network.
 
 175      * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
 
 176      * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
 
 177      * support some subset of the same input parameters: network_name, physical_network, vlan(s).
 
 179      * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
 
 180      * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
 
 181      * created but the orchestration fails on a subsequent operation.
 
 184     private void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 185             String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
 
 186             String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
 
 187             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
 
 188             Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
 
 189             Holder<NetworkRollback> rollback) throws NetworkException {
 
 190         logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
 
 192         // Will capture execution time for metrics
 
 193         long startTime = System.currentTimeMillis();
 
 195         // Build a default rollback object (no actions performed)
 
 196         NetworkRollback networkRollback = new NetworkRollback();
 
 197         networkRollback.setCloudId(cloudSiteId);
 
 198         networkRollback.setTenantId(tenantId);
 
 199         networkRollback.setMsoRequest(msoRequest);
 
 200         networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
 
 202         // tenant query is not required here.
 
 203         // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
 
 204         // So this is just catching that error in a bit more obvious way up front.
 
 206         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 207         if (!cloudSiteOpt.isPresent()) {
 
 208             String error = String.format(
 
 209                     "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 210                     networkName, cloudSiteId, tenantId);
 
 211             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 212             // Set the detailed error as the Exception 'message'
 
 213             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 217         NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
 
 218                 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
 
 219         String mode = networkResource.getOrchestrationMode();
 
 220         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 222         if (NEUTRON_MODE.equals(mode)) {
 
 224             // Use an MsoNeutronUtils for all neutron commands
 
 226             // See if the Network already exists (by name)
 
 227             NetworkInfo netInfo = null;
 
 228             long queryNetworkStarttime = System.currentTimeMillis();
 
 230                 netInfo = neutron.queryNetwork(networkName, tenantId, cloudSiteId);
 
 231             } catch (MsoException me) {
 
 233                         "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
 
 234                         MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkName,
 
 235                         cloudSiteId, tenantId, me);
 
 236                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 237                 throw new NetworkException(me);
 
 240             if (netInfo != null) {
 
 241                 // Exists. If that's OK, return success with the network ID.
 
 242                 // Otherwise, return an exception.
 
 243                 if (failIfExists != null && failIfExists) {
 
 244                     String error = String.format("Create Nework: Network %s already exists in %s/%s with ID %s",
 
 245                             networkName, cloudSiteId, tenantId, netInfo.getId());
 
 246                     logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
 
 247                             ErrorCode.DataError.getValue(), error);
 
 248                     throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 250                     // Populate the outputs from the existing network.
 
 251                     networkId.value = netInfo.getId();
 
 252                     neutronNetworkId.value = netInfo.getId();
 
 253                     rollback.value = networkRollback; // Default rollback - no updates performed
 
 254                     logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
 
 255                             MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), netInfo.getStatus());
 
 260             long createNetworkStarttime = System.currentTimeMillis();
 
 262                 netInfo = neutron.createNetwork(cloudSiteId, tenantId, neutronNetworkType, networkName,
 
 263                         physicalNetworkName, vlans);
 
 264             } catch (MsoException me) {
 
 265                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 266                 logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
 
 267                         ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
 
 269                 throw new NetworkException(me);
 
 272             // Note: ignoring MsoNetworkAlreadyExists because we already checked.
 
 274             // If reach this point, network creation is successful.
 
 275             // Since directly created via Neutron, networkId tracked by MSO is the same
 
 276             // as the neutron network ID.
 
 277             networkId.value = netInfo.getId();
 
 278             neutronNetworkId.value = netInfo.getId();
 
 280             networkRollback.setNetworkCreated(true);
 
 281             networkRollback.setNetworkId(netInfo.getId());
 
 282             networkRollback.setNeutronNetworkId(netInfo.getId());
 
 283             networkRollback.setNetworkType(networkType);
 
 285             logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
 
 286         } else if ("HEAT".equals(mode)) {
 
 288             HeatTemplate heatTemplate = networkResource.getHeatTemplate();
 
 289             if (heatTemplate == null) {
 
 290                 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
 
 291                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 293                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 296             logger.debug("Got HEAT Template from DB: {}", heatTemplate);
 
 298             // "Fix" the template if it has CR/LF (getting this from Oracle)
 
 299             String template = heatTemplate.getHeatTemplate();
 
 300             template = template.replaceAll("\r\n", "\n");
 
 302             boolean aic3template = false;
 
 303             String aic3nw = AIC3_NW;
 
 305             aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
 
 307             if (template.contains(aic3nw))
 
 310             // First, look up to see if the Network already exists (by name).
 
 311             // For HEAT orchestration of networks, the stack name will always match the network name
 
 312             StackInfo heatStack = null;
 
 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(),
 
 431             } catch (MsoException me) {
 
 432                 me.addContext(CREATE_NETWORK_CONTEXT);
 
 433                 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
 
 434                         ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
 
 435                 throw new NetworkException(me);
 
 438             // Reach this point if createStack is successful.
 
 440             // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
 
 441             // and the neutronNetworkId is the network UUID returned in stack outputs.
 
 442             networkId.value = heatStack.getCanonicalName();
 
 443             if (heatStack.getOutputs() != null) {
 
 444                 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
 
 446                     networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
 
 449             Map<String, Object> outputs = heatStack.getOutputs();
 
 450             Map<String, String> sMap = new HashMap<>();
 
 451             if (outputs != null) {
 
 452                 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 453                     String key = entry.getKey();
 
 454                     if (key != null && key.startsWith("subnet")) {
 
 455                         if (aic3template) // one subnet output expected
 
 457                             Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 459                         } else // multiples subnet_%aaid% outputs allowed
 
 461                             String subnetUUId = (String) outputs.get(key);
 
 462                             sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 466                 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
 
 468             subnetIdMap.value = sMap;
 
 470             rollback.value = networkRollback;
 
 471             // Populate remaining rollback info and response parameters.
 
 472             networkRollback.setNetworkStackId(heatStack.getCanonicalName());
 
 473             networkRollback.setNetworkCreated(true);
 
 474             networkRollback.setNetworkType(networkType);
 
 476             logger.debug("Network {} successfully created via HEAT", networkName);
 
 483     public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 484             String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
 
 485             String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
 
 486             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 487         updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
 
 488                 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
 
 494     public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
 
 495             String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
 
 496             String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
 
 497             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
 
 498             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 499         updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
 
 500                 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
 
 505      * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
 
 506      * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
 
 507      * remove a VLAN), but other properties may be updated as well.
 
 509      * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
 
 510      * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
 
 511      * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
 
 513      * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
 
 514      * VLANs on the same physical network.
 
 516      * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
 
 517      * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
 
 518      * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
 
 520      * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
 
 521      * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
 
 522      * a subsequent operation.
 
 524     private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
 525             String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
 
 526             List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
 
 527             List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
 
 528             Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
 
 530         logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
 
 531                 cloudSiteId, tenantId);
 
 533         // Will capture execution time for metrics
 
 534         long startTime = System.currentTimeMillis();
 
 536         // Build a default rollback object (no actions performed)
 
 537         NetworkRollback networkRollback = new NetworkRollback();
 
 538         networkRollback.setCloudId(cloudSiteId);
 
 539         networkRollback.setTenantId(tenantId);
 
 540         networkRollback.setMsoRequest(msoRequest);
 
 542         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 543         if (!cloudSiteOpt.isPresent()) {
 
 544             String error = String.format(
 
 545                     "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 546                     networkName, cloudSiteId, tenantId);
 
 547             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 548             // Set the detailed error as the Exception 'message'
 
 549             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 554         NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
 
 555                 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
 
 556         String mode = networkResource.getOrchestrationMode();
 
 557         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 559         // Use an MsoNeutronUtils for all Neutron commands
 
 561         if (NEUTRON_MODE.equals(mode)) {
 
 563             // Verify that the Network exists
 
 564             // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
 
 565             NetworkInfo netInfo = null;
 
 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.BusinessProcessError.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.BusinessProcessError.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;
 
 610                 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
 
 611             } catch (MsoException me) {
 
 612                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 613                 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 614                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 615                 throw new NetworkException(me);
 
 618             if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
 
 619                 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
 
 620                         cloudSiteId, tenantId);
 
 621                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 623                 // Network stack does not exist. Return an error
 
 624                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 627             // Get the previous parameters for rollback
 
 628             Map<String, Object> heatParams = heatStack.getParameters();
 
 630             String previousNetworkName = (String) heatParams.get("network_name");
 
 631             String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
 
 633             List<Integer> previousVlans = new ArrayList<>();
 
 634             String vlansParam = (String) heatParams.get(VLANS);
 
 635             if (vlansParam != null) {
 
 636                 for (String vlan : vlansParam.split(",")) {
 
 638                         previousVlans.add(Integer.parseInt(vlan));
 
 639                     } catch (NumberFormatException e) {
 
 640                         logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
 
 641                                 ErrorCode.DataError.getValue(), vlansParam, e);
 
 645             logger.debug("Update Stack:  Previous VLANS: {}", previousVlans);
 
 647             // Ready to deploy the updated Network via Heat
 
 650             HeatTemplate heatTemplate = networkResource.getHeatTemplate();
 
 651             if (heatTemplate == null) {
 
 652                 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
 
 653                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
 
 655                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 658             logger.debug("Got HEAT Template from DB: {}", heatTemplate);
 
 660             // "Fix" the template if it has CR/LF (getting this from Oracle)
 
 661             String template = heatTemplate.getHeatTemplate();
 
 662             template = template.replaceAll("\r\n", "\n");
 
 664             boolean aic3template = false;
 
 665             String aic3nw = AIC3_NW;
 
 667             aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
 
 669             if (template.contains(aic3nw))
 
 672             // Build the common set of HEAT template parameters
 
 673             Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
 
 674                     physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
 
 676             // Validate (and update) the input parameters against the DB definition
 
 677             // Shouldn't happen unless DB config is wrong, since all networks use same inputs
 
 679                 stackParams = heat.validateStackParams(stackParams, heatTemplate);
 
 680             } catch (IllegalArgumentException e) {
 
 681                 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
 
 682                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 683                 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
 
 686             if (subnets != null) {
 
 689                         template = mergeSubnetsAIC3(template, subnets, stackParams);
 
 691                         template = mergeSubnets(template, subnets);
 
 693                 } catch (MsoException me) {
 
 694                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 695                     logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
 
 696                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 697                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 698                     throw new NetworkException(me);
 
 702             if (policyFqdns != null && aic3template) {
 
 704                     mergePolicyRefs(policyFqdns, stackParams);
 
 705                 } catch (MsoException me) {
 
 706                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 707                     logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
 
 708                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 709                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 710                     throw new NetworkException(me);
 
 714             if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
 
 716                     mergeRouteTableRefs(routeTableFqdns, stackParams);
 
 717                 } catch (MsoException me) {
 
 718                     me.addContext(UPDATE_NETWORK_CONTEXT);
 
 719                     logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
 
 720                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
 
 721                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
 
 722                     throw new NetworkException(me);
 
 726             // Update the network stack
 
 727             // Ignore MsoStackNotFound exception because we already checked.
 
 729                 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
 
 730                         stackParams, true, heatTemplate.getTimeoutMinutes());
 
 731             } catch (MsoException me) {
 
 732                 me.addContext(UPDATE_NETWORK_CONTEXT);
 
 733                 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
 
 734                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
 735                 throw new NetworkException(me);
 
 738             Map<String, Object> outputs = heatStack.getOutputs();
 
 739             Map<String, String> sMap = new HashMap<>();
 
 740             if (outputs != null) {
 
 741                 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
 
 742                     String key = entry.getKey();
 
 743                     if (key != null && key.startsWith("subnet")) {
 
 744                         if (aic3template) // one subnet output expected
 
 746                             Map<String, String> map = getSubnetUUId(key, outputs, subnets);
 
 748                         } else // multiples subnet_%aaid% outputs allowed
 
 750                             String subnetUUId = (String) outputs.get(key);
 
 751                             sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 756             subnetIdMap.value = sMap;
 
 758             // Reach this point if createStack is successful.
 
 759             // Populate remaining rollback info and response parameters.
 
 760             networkRollback.setNetworkStackId(heatStack.getCanonicalName());
 
 761             if (null != outputs) {
 
 762                 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
 
 764                 logger.debug("outputs is NULL");
 
 766             networkRollback.setNetworkType(networkType);
 
 767             // Save previous parameters
 
 768             networkRollback.setNetworkName(previousNetworkName);
 
 769             networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
 
 770             networkRollback.setVlans(previousVlans);
 
 772             rollback.value = networkRollback;
 
 774             logger.debug("Network {} successfully updated via HEAT", networkId);
 
 780     private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
 
 781             String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
 
 782             String cloudSiteId, CloudSite cloudSite) throws NetworkException {
 
 783         // Retrieve the Network Resource definition
 
 784         NetworkResource networkResource = null;
 
 785         NetworkResourceCustomization networkCust = null;
 
 786         CollectionNetworkResourceCustomization collectionNetworkCust = null;
 
 787         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
 788             if (!commonUtils.isNullOrEmpty(networkType)) {
 
 789                 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
 
 792             networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 793             if (networkCust == null) {
 
 794                 collectionNetworkCust =
 
 795                         collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
 798         if (networkCust != null) {
 
 799             logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
 
 801             networkResource = networkCust.getNetworkResource();
 
 802         } else if (collectionNetworkCust != null) {
 
 803             logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
 
 804             networkResource = collectionNetworkCust.getNetworkResource();
 
 806         if (networkResource == null) {
 
 807             String error = String.format(
 
 808                     "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
 
 809                     networkType, modelCustomizationUuid);
 
 810             logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
 
 812             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 814         logger.debug(LOG_DEBUG_MSG, networkResource);
 
 816         String mode = networkResource.getOrchestrationMode();
 
 817         NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
 
 819         // All Networks are orchestrated via HEAT or Neutron
 
 820         if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
 
 821             String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
 
 822             logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
 
 823                     ErrorCode.DataError.getValue(), error);
 
 824             throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
 
 827         MavenLikeVersioning aicV = new MavenLikeVersioning();
 
 828         aicV.setVersion(cloudSite.getCloudVersion());
 
 829         if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
 
 830                 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
 
 833                 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
 
 834                         || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
 
 836             logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
 
 837                     networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
 
 838                     cloudSite.getCloudVersion());
 
 840             String error = String.format(
 
 841                     "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
 
 842                     networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
 
 843                     cloudSite.getCloudVersion());
 
 844             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 845             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 848         // Validate the Network parameters.
 
 850                 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
 
 851         if (!missing.isEmpty()) {
 
 852             String error = "Create Network: Missing parameters: " + missing;
 
 853             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
 855             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 858         return networkResource;
 
 862     public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 863             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 864             Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
 
 865             throws NetworkException {
 
 866         queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
 
 867                 status, vlans, null, subnetIdMap);
 
 871     public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 872             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 873             Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
 
 874             Holder<Map<String, String>> subnetIdMap) throws NetworkException {
 
 875         queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
 
 876                 status, null, routeTargets, subnetIdMap);
 
 880      * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
 
 881      * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
 
 882      * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
 
 884     private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
 
 885             Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
 
 886             Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
 
 887             Holder<Map<String, String>> subnetIdMap) throws NetworkException {
 
 889         logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
 
 891         if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
 
 892                 || commonUtils.isNullOrEmpty(networkNameOrId)) {
 
 894             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
 
 895             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
 896             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 899         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
 
 900         if (!cloudSiteOpt.isPresent()) {
 
 901             String error = String.format(
 
 902                     "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
 
 903                     networkNameOrId, cloudSiteId, tenantId);
 
 904             logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
 
 905             // Set the detailed error as the Exception 'message'
 
 906             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
 909         // Use MsoNeutronUtils for all NEUTRON commands
 
 912         String neutronId = null;
 
 913         // Try Heat first, since networks may be named the same as the Heat stack
 
 914         StackInfo heatStack = null;
 
 916             heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
 
 917         } catch (MsoException me) {
 
 918             me.addContext("QueryNetwork");
 
 919             logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 920                     ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
 
 921             throw new NetworkException(me);
 
 924         // Populate the outputs based on the returned Stack information
 
 925         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
 
 926             // Found it. Get the neutronNetworkId for further query
 
 927             Map<String, String> sMap = new HashMap<>();
 
 928             Map<String, Object> outputs = heatStack.getOutputs();
 
 930             if (outputs != null) {
 
 931                 neutronId = (String) outputs.get(NETWORK_ID);
 
 933                 for (String key : outputs.keySet()) {
 
 934                     if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
 
 936                         String subnetUUId = (String) outputs.get(key);
 
 937                         sMap.put(key.substring("subnet_id_".length()), subnetUUId);
 
 938                     } else if (key != null && key.startsWith("subnet")) // one subnet output expected
 
 940                         Map<String, String> map = getSubnetUUId(key, outputs, null);
 
 946             subnetIdMap.value = sMap;
 
 948             // Input ID was not a Heat stack ID. Try it directly in Neutron
 
 949             neutronId = networkNameOrId;
 
 953         // Query directly against the Neutron Network for the details
 
 954         // no RouteTargets available for ContrailV2 in neutron net-show
 
 955         // networkId is heatStackId
 
 957             NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
 
 958             if (netInfo != null) {
 
 959                 // Found. Populate the output elements
 
 960                 networkExists.value = Boolean.TRUE;
 
 961                 if ("HEAT".equals(mode) && heatStack != null) {
 
 962                     networkId.value = heatStack.getCanonicalName();
 
 964                     networkId.value = netInfo.getId();
 
 966                 neutronNetworkId.value = netInfo.getId();
 
 967                 status.value = netInfo.getStatus();
 
 969                     vlans.value = netInfo.getVlans();
 
 971                 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
 
 972                         ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
 
 974                 // Not found. Populate the status fields, leave the rest null
 
 975                 networkExists.value = Boolean.FALSE;
 
 976                 status.value = NetworkStatus.NOTFOUND;
 
 977                 neutronNetworkId.value = null;
 
 979                     vlans.value = new ArrayList<>();
 
 981                 logger.debug("Network {} not found", networkNameOrId);
 
 983         } catch (MsoException me) {
 
 984             me.addContext("QueryNetwork");
 
 985             logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
 
 986                     ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
 
 987             throw new NetworkException(me);
 
 993      * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
 
 996      * If the network is not found, it is treated as a success.
 
 998      * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
 
 999      * network orchestration mode for each network type is declared in its catalog definition.
 
1001      * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
 
1002      * networkId should be the Neutron network UUID.
 
1004      * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
 
1005      * will require manual fallout in the client.
 
1008     public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
 
1009             String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
 
1010         logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
 
1011         if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
 
1012                 || commonUtils.isNullOrEmpty(networkId)) {
 
1013             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
 
1014             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1015             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
 
1018         // Retrieve the Network Resource definition
 
1019         NetworkResource networkResource = null;
 
1020         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
1021             if (!commonUtils.isNullOrEmpty(networkType)) {
 
1022                 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
 
1025             NetworkResourceCustomization nrc =
 
1026                     networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
 
1028                 networkResource = nrc.getNetworkResource();
 
1032         int timeoutMinutes = 118;
 
1034         if (networkResource != null) {
 
1035             logger.debug(LOG_DEBUG_MSG, networkResource.toString());
 
1036             mode = networkResource.getOrchestrationMode();
 
1037             networkResource.getHeatTemplate().getTimeoutMinutes();
 
1038             HeatTemplate heat = networkResource.getHeatTemplate();
 
1039             if (heat != null && heat.getTimeoutMinutes() != null) {
 
1040                 if (heat.getTimeoutMinutes() < 118) {
 
1041                     timeoutMinutes = heat.getTimeoutMinutes();
 
1046         if (NEUTRON_MODE.equals(mode)) {
 
1048                 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
 
1049                 networkDeleted.value = deleted;
 
1050             } catch (MsoException me) {
 
1051                 me.addContext("DeleteNetwork");
 
1052                 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
 
1053                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
1054                 throw new NetworkException(me);
 
1058                 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, timeoutMinutes);
 
1059                 networkDeleted.value = true;
 
1060             } catch (MsoException me) {
 
1061                 me.addContext("DeleteNetwork");
 
1062                 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
 
1063                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
 
1064                 throw new NetworkException(me);
 
1070      * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
 
1071      * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
 
1072      * to undo the creation.
 
1074      * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
 
1078     public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
 
1079         if (rollback == null) {
 
1080             logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
 
1084         // Get the elements of the VnfRollback object for easier access
 
1085         String cloudSiteId = rollback.getCloudId();
 
1086         String tenantId = rollback.getTenantId();
 
1087         String networkId = rollback.getNetworkStackId();
 
1088         String networkType = rollback.getNetworkType();
 
1089         String modelCustomizationUuid = rollback.getModelCustomizationUuid();
 
1091         logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
 
1092         // Retrieve the Network Resource definition
 
1093         NetworkResource networkResource = null;
 
1094         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
 
1095             networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
 
1098                     networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
 
1101         if (networkResource != null) {
 
1103             logger.debug(LOG_DEBUG_MSG, networkResource);
 
1105             mode = networkResource.getOrchestrationMode();
 
1108         if (rollback.getNetworkCreated()) {
 
1109             if (NEUTRON_MODE.equals(mode)) {
 
1111                     neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
 
1112                 } catch (MsoException me) {
 
1113                     me.addContext("RollbackNetwork");
 
1114                     logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
 
1115                             MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
 
1116                             cloudSiteId, tenantId, me);
 
1117                     throw new NetworkException(me);
 
1121                     heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
 
1122                 } catch (MsoException me) {
 
1123                     me.addContext("RollbackNetwork");
 
1124                     logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
 
1125                             MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
 
1126                             cloudSiteId, tenantId, me);
 
1127                     throw new NetworkException(me);
 
1133     private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
 
1134             List<Integer> vlans, List<RouteTarget> routeTargets) {
 
1136         StringBuilder missing = new StringBuilder();
 
1137         if (commonUtils.isNullOrEmpty(networkName)) {
 
1138             missing.append("networkName");
 
1142         if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1143             if (commonUtils.isNullOrEmpty(physicalNetwork)) {
 
1144                 missing.append(sep).append("physicalNetworkName");
 
1147             if (vlans == null || vlans.isEmpty()) {
 
1148                 missing.append(sep).append(VLANS);
 
1152         return missing.toString();
 
1155     private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
 
1156             String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
 
1157             boolean aic3template) {
 
1158         // Build the common set of HEAT template parameters
 
1159         Map<String, Object> stackParams = new HashMap<>();
 
1160         stackParams.put("network_name", networkName);
 
1162         if (neutronNetworkType == NetworkType.PROVIDER) {
 
1163             // For Provider type
 
1164             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1165             stackParams.put("vlan", vlans.get(0).toString());
 
1166         } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
 
1167             // For Multi-provider, PO supports a custom resource extension of ProviderNet.
 
1168             // It supports all ProviderNet properties except segmentation_id, and adds a
 
1169             // comma-separated-list of VLANs as a "segments" property.
 
1170             // Note that this does not match the Neutron definition of Multi-Provider network,
 
1171             // which contains a list of 'segments', each having physical_network, network_type,
 
1172             // and segmentation_id.
 
1173             StringBuilder buf = new StringBuilder();
 
1175             for (Integer vlan : vlans) {
 
1176                 buf.append(sep).append(vlan.toString());
 
1179             String csl = buf.toString();
 
1181             stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
 
1182             stackParams.put(VLANS, csl);
 
1184         if (routeTargets != null) {
 
1186             String rtGlobal = "";
 
1187             String rtImport = "";
 
1188             String rtExport = "";
 
1190             for (RouteTarget rt : routeTargets) {
 
1191                 boolean rtIsNull = false;
 
1193                     String routeTarget = rt.getRouteTarget();
 
1194                     String routeTargetRole = rt.getRouteTargetRole();
 
1195                     logger.debug("Checking for an actually null route target: {}", rt);
 
1196                     if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
 
1198                     if (routeTargetRole == null || routeTargetRole.equals("")
 
1199                             || routeTargetRole.equalsIgnoreCase("null"))
 
1205                     logger.debug("Input RT:{}", rt);
 
1206                     String role = rt.getRouteTargetRole();
 
1207                     String rtValue = rt.getRouteTarget();
 
1209                     if ("IMPORT".equalsIgnoreCase(role)) {
 
1210                         sep = rtImport.isEmpty() ? "" : ",";
 
1211                         rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
 
1212                     } else if ("EXPORT".equalsIgnoreCase(role)) {
 
1213                         sep = rtExport.isEmpty() ? "" : ",";
 
1214                         rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
 
1215                     } else // covers BOTH, empty etc
 
1217                         sep = rtGlobal.isEmpty() ? "" : ",";
 
1218                         rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
 
1224             if (!rtImport.isEmpty()) {
 
1225                 stackParams.put("route_targets_import", rtImport);
 
1227             if (!rtExport.isEmpty()) {
 
1228                 stackParams.put("route_targets_export", rtExport);
 
1230             if (!rtGlobal.isEmpty()) {
 
1231                 stackParams.put("route_targets", rtGlobal);
 
1234         if (commonUtils.isNullOrEmpty(shared)) {
 
1235             stackParams.put("shared", "False");
 
1237             stackParams.put("shared", shared);
 
1239         if (commonUtils.isNullOrEmpty(external)) {
 
1240             stackParams.put("external", "False");
 
1242             stackParams.put("external", external);
 
1250      * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
 
1251      * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
 
1252      * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
 
1253      * "network_policy_refs_data_sequence_minor": "0" } } ]
 
1255     private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
 
1256         // Resource Property
 
1257         List<ContrailPolicyRef> prlist = new ArrayList<>();
 
1260         if (pFqdns != null) {
 
1261             for (String pf : pFqdns) {
 
1262                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1263                     ContrailPolicyRef pr = new ContrailPolicyRef();
 
1264                     ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
 
1267                     logger.debug("Contrail PolicyRefs Data:{}", pr);
 
1272             String error = "Null pFqdns at start of mergePolicyRefs";
 
1273             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
 
1275             throw new MsoAdapterException(error);
 
1278         JsonNode node = null;
 
1280             ObjectMapper mapper = new ObjectMapper();
 
1281             node = mapper.convertValue(prlist, JsonNode.class);
 
1282             String jsonString = mapper.writeValueAsString(prlist);
 
1283             logger.debug("Json PolicyRefs Data:{}", jsonString);
 
1284         } catch (Exception e) {
 
1285             String error = "Error creating JsonNode for policyRefs Data";
 
1286             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
 
1288             throw new MsoAdapterException(error);
 
1290         // update parameters
 
1291         if (pFqdns != null && node != null) {
 
1292             StringBuilder buf = new StringBuilder();
 
1294             for (String pf : pFqdns) {
 
1295                 if (!commonUtils.isNullOrEmpty(pf)) {
 
1296                     buf.append(sep).append(pf);
 
1300             String csl = buf.toString();
 
1301             stackParams.put("policy_refs", csl);
 
1302             stackParams.put("policy_refsdata", node);
 
1305         logger.debug("StackParams updated with policy refs");
 
1309     private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
 
1311         // update parameters
 
1312         if (rtFqdns != null) {
 
1313             StringBuilder buf = new StringBuilder();
 
1315             for (String rtf : rtFqdns) {
 
1316                 if (!commonUtils.isNullOrEmpty(rtf)) {
 
1317                     buf.append(sep).append(rtf);
 
1321             String csl = buf.toString();
 
1322             stackParams.put("route_table_refs", csl);
 
1325         logger.debug("StackParams updated with route_table refs");
 
1331      * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
 
1332      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
 
1333      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
 
1334      * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
 
1335      * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
 
1336      * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
 
1337      * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
 
1338      * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
 
1339      * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
 
1340      * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
 
1341      * "host_routes": null }
 
1343     private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
 
1344             throws MsoException {
 
1346         // Resource Property
 
1347         List<ContrailSubnet> cslist = new ArrayList<>();
 
1348         for (Subnet subnet : subnets) {
 
1349             logger.debug("Input Subnet:{}", subnet);
 
1350             ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
 
1351             logger.debug("Contrail Subnet:{}", cs);
 
1355         JsonNode node = null;
 
1357             ObjectMapper mapper = new ObjectMapper();
 
1358             node = mapper.convertValue(cslist, JsonNode.class);
 
1359             String jsonString = mapper.writeValueAsString(cslist);
 
1360             logger.debug("Json Subnet List:{}", jsonString);
 
1361         } catch (Exception e) {
 
1362             String error = "Error creating JsonNode from input subnets";
 
1363             logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
 
1364             throw new MsoAdapterException(error);
 
1366         // update parameters
 
1368             stackParams.put("subnet_list", node);
 
1370         // Outputs - All subnets are in one ipam_subnets structure
 
1371         String outputTempl = "  subnet:\n" + "    description: Openstack subnet identifier\n"
 
1372                 + "    value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
 
1374         // append outputs in heatTemplate
 
1375         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1376         heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
 
1377         logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
 
1378         return heatTemplate;
 
1382     private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
 
1384         String resourceTempl = "  subnet_%subnetId%:\n" + "    type: OS::Neutron::Subnet\n" + "    properties:\n"
 
1385                 + "      name: %name%\n" + "      network_id: { get_resource: network }\n" + "      cidr: %cidr%\n";
 
1388          * make these optional + "      ip_version: %ipversion%\n" + "      enable_dhcp: %enabledhcp%\n" +
 
1389          * "      gateway_ip: %gatewayip%\n" + "      allocation_pools:\n" + "       - start: %poolstart%\n" +
 
1390          * "         end: %poolend%\n";
 
1394         String outputTempl = "  subnet_id_%subnetId%:\n" + "    description: Openstack subnet identifier\n"
 
1395                 + "    value: {get_resource: subnet_%subnetId%}\n";
 
1399         StringBuilder resourcesBuf = new StringBuilder();
 
1400         StringBuilder outputsBuf = new StringBuilder();
 
1401         for (Subnet subnet : subnets) {
 
1403             // build template for each subnet
 
1404             curR = resourceTempl;
 
1405             if (subnet.getSubnetId() != null) {
 
1406                 curR = curR.replace("%subnetId%", subnet.getSubnetId());
 
1408                 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
 
1409                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1410                 throw new MsoAdapterException(error);
 
1413             if (subnet.getSubnetName() != null) {
 
1414                 curR = curR.replace("%name%", subnet.getSubnetName());
 
1416                 curR = curR.replace("%name%", subnet.getSubnetId());
 
1419             if (subnet.getCidr() != null) {
 
1420                 curR = curR.replace("%cidr%", subnet.getCidr());
 
1422                 String error = "Missing Required cidr for subnet in HEAT Template";
 
1423                 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
 
1424                 throw new MsoAdapterException(error);
 
1427             if (subnet.getIpVersion() != null) {
 
1428                 curR = curR + "      ip_version: " + subnet.getIpVersion() + "\n";
 
1430             if (subnet.getEnableDHCP() != null) {
 
1431                 curR = curR + "      enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
 
1433             if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
 
1434                 curR = curR + "      gateway_ip: " + subnet.getGatewayIp() + "\n";
 
1437             if (subnet.getAllocationPools() != null) {
 
1438                 StringBuilder tempBuf = new StringBuilder();
 
1439                 tempBuf.append(curR);
 
1440                 tempBuf.append("      allocation_pools:\n");
 
1441                 for (Pool pool : subnet.getAllocationPools()) {
 
1442                     if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
 
1443                         tempBuf.append("       - start: ");
 
1444                         tempBuf.append(pool.getStart());
 
1445                         tempBuf.append("\n         end: ");
 
1446                         tempBuf.append(pool.getEnd());
 
1447                         tempBuf.append("\n");
 
1450                 curR = tempBuf.toString();
 
1453             resourcesBuf.append(curR);
 
1456             curO = curO.replace("%subnetId%", subnet.getSubnetId());
 
1458             outputsBuf.append(curO);
 
1460         // append resources and outputs in heatTemplate
 
1461         logger.debug("Tempate initial:{}", heatTemplate);
 
1462         int outputsIdx = heatTemplate.indexOf("outputs:");
 
1463         heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
 
1464         int resourcesIdx = heatTemplate.indexOf("resources:");
 
1465         heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
 
1467         logger.debug("Template updated with all subnets:{}", heatTemplate);
 
1468         return heatTemplate;
 
1471     private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
 
1473         Map<String, String> sMap = new HashMap<>();
 
1476             Object obj = outputs.get(key);
 
1477             ObjectMapper mapper = new ObjectMapper();
 
1478             String jStr = mapper.writeValueAsString(obj);
 
1479             logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
 
1481             JsonNode rootNode = mapper.readTree(jStr);
 
1482             if (rootNode != null) {
 
1483                 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
 
1484                     logger.debug("Output Subnet Node {}", sNode);
 
1485                     String name = sNode.path("subnet_name").textValue();
 
1486                     String uuid = sNode.path("subnet_uuid").textValue();
 
1487                     String aaiId = name; // default
 
1488                     // try to find aaiId for name in input subnetList
 
1489                     if (subnets != null) {
 
1490                         for (Subnet subnet : subnets) {
 
1491                             if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
 
1492                                     && subnet.getSubnetName().equals(name)) {
 
1493                                 aaiId = subnet.getSubnetId();
 
1498                     sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
 
1501                 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
 
1502                         ErrorCode.DataError.getValue());
 
1504         } catch (Exception e) {
 
1505             logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
 
1506                     ErrorCode.DataError.getValue(), e);
 
1509         logger.debug("Return sMap {}", sMap);
 
1513     private static String insertStr(String template, String snippet, int index) {
 
1515         String updatedTemplate;
 
1517         logger.debug("Index:{} Snippet:{}", index, snippet);
 
1519         String templateBeg = template.substring(0, index);
 
1520         String templateEnd = template.substring(index);
 
1522         updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
 
1524         logger.debug("Template updated with a subnet:{}", updatedTemplate);
 
1525         return updatedTemplate;