2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7 * ================================================================================
8 * Modifications Copyright (C) 2018 IBM.
9 * Modifications Copyright (c) 2019 Samsung
10 * ================================================================================
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 * ============LICENSE_END=========================================================
25 package org.onap.so.adapters.network;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
31 import java.util.Optional;
32 import javax.jws.WebService;
33 import javax.xml.ws.Holder;
34 import org.onap.so.adapters.network.beans.ContrailPolicyRef;
35 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
36 import org.onap.so.adapters.network.beans.ContrailSubnet;
37 import org.onap.so.adapters.network.exceptions.NetworkException;
38 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
39 import org.onap.so.cloud.CloudConfig;
40 import org.onap.so.db.catalog.beans.CloudSite;
41 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
42 import org.onap.so.db.catalog.beans.HeatTemplate;
43 import org.onap.so.db.catalog.beans.NetworkResource;
44 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
45 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
46 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
47 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
48 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
49 import org.onap.so.entity.MsoRequest;
50 import org.onap.logging.filter.base.ErrorCode;
51 import org.onap.so.logger.LoggingAnchor;
52 import org.onap.so.logger.MessageEnum;
53 import org.onap.so.openstack.beans.HeatStatus;
54 import org.onap.so.openstack.beans.NetworkInfo;
55 import org.onap.so.openstack.beans.NetworkRollback;
56 import org.onap.so.openstack.beans.NetworkStatus;
57 import org.onap.so.openstack.beans.Pool;
58 import org.onap.so.openstack.beans.RouteTarget;
59 import org.onap.so.openstack.beans.StackInfo;
60 import org.onap.so.openstack.beans.Subnet;
61 import org.onap.so.openstack.exceptions.MsoAdapterException;
62 import org.onap.so.openstack.exceptions.MsoException;
63 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
64 import org.onap.so.openstack.utils.MsoCommonUtils;
65 import org.onap.so.openstack.utils.MsoHeatUtils;
66 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
67 import org.onap.so.openstack.utils.MsoNeutronUtils;
68 import org.onap.so.openstack.utils.MsoNeutronUtils.NetworkType;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71 import org.springframework.beans.factory.annotation.Autowired;
72 import org.springframework.core.env.Environment;
73 import org.springframework.stereotype.Component;
74 import org.springframework.transaction.annotation.Transactional;
75 import com.fasterxml.jackson.databind.JsonNode;
76 import com.fasterxml.jackson.databind.ObjectMapper;
80 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.onap.so.adapters.network.MsoNetworkAdapter",
81 targetNamespace = "http://org.onap.so/network")
82 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
84 private static final String OS3_NW_PROPERTY = "org.onap.so.adapters.network.aic3nw";
85 private static final String OS3_NW = "OS::ContrailV2::VirtualNetwork";
86 private static final String VLANS = "vlans";
87 private static final String PHYSICAL_NETWORK = "physical_network";
88 private static final String UPDATE_NETWORK_CONTEXT = "UpdateNetwork";
89 private static final String NETWORK_ID = "network_id";
90 private static final String NETWORK_FQDN = "network_fqdn";
91 private static final String CREATE_NETWORK_CONTEXT = "CreateNetwork";
92 private static final String NEUTRON_MODE = "NEUTRON";
93 private static final String CLOUD_OWNER = "CloudOwner";
94 private static final String LOG_DEBUG_MSG = "Got Network definition from Catalog: {}";
95 private static final String NETWORK_EXIST_STATUS_MESSAGE =
96 "The network was found to already exist, thus no new network was created in the cloud via this request";
97 private static final String NETWORK_CREATED_STATUS_MESSAGE =
98 "The new network was successfully created in the cloud";
99 private static final String NETWORK_NOT_EXIST_STATUS_MESSAGE =
100 "The network was not found, thus no network was deleted in the cloud via this request";
101 private static final String NETWORK_DELETED_STATUS_MESSAGE = "The network was successfully deleted in the cloud";
103 private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
106 private CloudConfig cloudConfig;
108 private Environment environment;
110 private MsoNeutronUtils neutron;
112 private MsoHeatUtils heat;
114 private MsoHeatUtilsWithUpdate heatWithUpdate;
116 private MsoCommonUtils commonUtils;
119 private NetworkResourceCustomizationRepository networkCustomRepo;
122 private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
125 private NetworkResourceRepository networkResourceRepo;
127 public MsoNetworkAdapterImpl() {}
130 * Health Check web method. Does nothing but return to show the adapter is deployed.
133 public void healthCheck() {
134 logger.debug("Health check call in Network Adapter");
138 * Do not use this constructor or the msoPropertiesFactory will be NULL.
140 * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
145 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
146 String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
147 Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
148 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
149 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
150 Holder<String> networkFqdn = new Holder<>();
151 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
152 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
153 neutronNetworkId, networkFqdn, subnetIdMap, rollback, true);
158 public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
159 String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
160 String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
161 Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
162 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
163 Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
164 throws NetworkException {
165 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
166 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
167 neutronNetworkId, networkFqdn, subnetIdMap, rollback, true);
171 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
172 * the specified cloud and tenant. The tenant must exist at the time this service is called.
174 * If a network with the same name already exists, this can be considered a success or failure, depending on the
175 * value of the 'failIfExists' parameter.
177 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
178 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
179 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
181 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
182 * multiple VLANs on the same physical network.
184 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
185 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
186 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
188 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
189 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
190 * created but the orchestration fails on a subsequent operation.
193 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
194 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
195 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
196 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
197 Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
198 Holder<NetworkRollback> rollback, Boolean pollForCompletion) throws NetworkException {
199 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
201 // Will capture execution time for metrics
202 long startTime = System.currentTimeMillis();
204 // Build a default rollback object (no actions performed)
205 NetworkRollback networkRollback = new NetworkRollback();
206 networkRollback.setCloudId(cloudSiteId);
207 networkRollback.setTenantId(tenantId);
208 networkRollback.setMsoRequest(msoRequest);
209 networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
211 // tenant query is not required here.
212 // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
213 // So this is just catching that error in a bit more obvious way up front.
215 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
216 if (!cloudSiteOpt.isPresent()) {
217 String error = String.format(
218 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
219 networkName, cloudSiteId, tenantId);
220 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
221 // Set the detailed error as the Exception 'message'
222 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
226 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
227 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
228 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
230 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
231 if (heatTemplate == null) {
232 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
233 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
234 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
237 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
239 // "Fix" the template if it has CR/LF (getting this from Oracle)
240 String template = heatTemplate.getHeatTemplate();
241 template = template.replaceAll("\r\n", "\n");
243 boolean os3template = false;
244 String os3nw = OS3_NW;
246 os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
248 if (template.contains(os3nw))
251 // First, look up to see if the Network already exists (by name).
252 // For HEAT orchestration of networks, the stack name will always match the network name
253 StackInfo heatStack = null;
255 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
256 } catch (MsoException me) {
257 me.addContext(CREATE_NETWORK_CONTEXT);
258 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ", MessageEnum.RA_QUERY_NETWORK_EXC,
259 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
260 throw new NetworkException(me);
263 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
264 // Stack exists. Return success or error depending on input directive
265 if (failIfExists != null && failIfExists) {
266 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
267 cloudSiteId, tenantId, heatStack.getCanonicalName());
268 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
270 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
272 // Populate the outputs from the existing stack.
273 networkId.value = heatStack.getCanonicalName();
274 Map<String, String> sMap = new HashMap<>();
275 if (heatStack.getOutputs() != null) {
276 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
277 rollback.value = networkRollback; // Default rollback - no updates performed
279 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
281 Map<String, Object> outputs = heatStack.getOutputs();
283 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
284 String key = entry.getKey();
285 if (key != null && key.startsWith("subnet")) {
286 if (os3template) // one subnet_id output
288 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
290 } else // multiples subnet_%aaid% outputs
292 String subnetUUId = (String) outputs.get(key);
293 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
298 subnetIdMap.value = sMap;
299 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
300 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
301 networkName, cloudSiteId, tenantId);
303 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
307 // Ready to deploy the new Network
308 // Build the common set of HEAT template parameters
309 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName, physicalNetworkName,
310 vlans, routeTargets, shared, external, os3template);
312 // Validate (and update) the input parameters against the DB definition
313 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
314 // and inputs were already validated.
316 stackParams = heat.validateStackParams(stackParams, heatTemplate);
317 } catch (IllegalArgumentException e) {
318 String error = "Create Network: Configuration Error: " + e.getMessage();
319 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
320 // Input parameters were not valid
321 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
324 if (subnets != null) {
327 template = mergeSubnetsAIC3(template, subnets, stackParams);
329 template = mergeSubnets(template, subnets);
331 } catch (MsoException me) {
332 me.addContext(CREATE_NETWORK_CONTEXT);
333 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
334 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
335 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
336 throw new NetworkException(me);
340 if (policyFqdns != null && !policyFqdns.isEmpty() && os3template) {
342 mergePolicyRefs(policyFqdns, stackParams);
343 } catch (MsoException me) {
344 me.addContext(CREATE_NETWORK_CONTEXT);
345 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
346 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
347 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
348 throw new NetworkException(me);
352 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
354 mergeRouteTableRefs(routeTableFqdns, stackParams);
355 } catch (MsoException me) {
356 me.addContext(CREATE_NETWORK_CONTEXT);
357 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
358 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
359 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
360 throw new NetworkException(me);
364 // Deploy the network stack
365 // Ignore MsoStackAlreadyExists exception because we already checked.
369 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template, stackParams,
370 pollForCompletion, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue(),
372 } catch (MsoException me) {
373 me.addContext(CREATE_NETWORK_CONTEXT);
374 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
375 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
376 throw new NetworkException(me);
379 // Reach this point if createStack is successful.
381 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
382 // and the neutronNetworkId is the network UUID returned in stack outputs.
383 networkId.value = heatStack.getCanonicalName();
384 if (heatStack.getOutputs() != null) {
385 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
387 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
390 Map<String, Object> outputs = heatStack.getOutputs();
391 Map<String, String> sMap = new HashMap<>();
392 if (outputs != null) {
393 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
394 String key = entry.getKey();
395 if (key != null && key.startsWith("subnet")) {
396 if (os3template) // one subnet output expected
398 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
400 } else // multiples subnet_%aaid% outputs allowed
402 String subnetUUId = (String) outputs.get(key);
403 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
407 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
409 subnetIdMap.value = sMap;
411 rollback.value = networkRollback;
412 // Populate remaining rollback info and response parameters.
413 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
414 networkRollback.setNetworkCreated(true);
415 networkRollback.setNetworkType(networkType);
418 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
419 } catch (Exception e) {
420 logger.warn("Exception while updating infra active request", e);
423 logger.debug("Network {} successfully created via HEAT", networkName);
431 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
432 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
433 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
434 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
435 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
436 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
443 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
444 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
445 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
446 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
447 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
448 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
449 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
454 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
455 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
456 * remove a VLAN), but other properties may be updated as well.
458 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
459 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
460 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
462 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
463 * VLANs on the same physical network.
465 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
466 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
467 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
469 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
470 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
471 * a subsequent operation.
473 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
474 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
475 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
476 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
477 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
479 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
480 cloudSiteId, tenantId);
482 // Will capture execution time for metrics
483 long startTime = System.currentTimeMillis();
485 // Build a default rollback object (no actions performed)
486 NetworkRollback networkRollback = new NetworkRollback();
487 networkRollback.setCloudId(cloudSiteId);
488 networkRollback.setTenantId(tenantId);
489 networkRollback.setMsoRequest(msoRequest);
491 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
492 if (!cloudSiteOpt.isPresent()) {
493 String error = String.format(
494 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
495 networkName, cloudSiteId, tenantId);
496 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
497 // Set the detailed error as the Exception 'message'
498 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
503 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
504 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
505 String mode = networkResource.getOrchestrationMode();
506 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
508 // Use an MsoNeutronUtils for all Neutron commands
510 if (NEUTRON_MODE.equals(mode)) {
512 // Verify that the Network exists
513 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
514 NetworkInfo netInfo = null;
516 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
517 } catch (MsoException me) {
518 me.addContext(UPDATE_NETWORK_CONTEXT);
519 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
520 ErrorCode.BusinessProcessError.getValue(), networkId, cloudSiteId, tenantId, me);
521 throw new NetworkException(me);
524 if (netInfo == null) {
525 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
526 cloudSiteId, tenantId);
527 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
528 ErrorCode.BusinessProcessError.getValue(), error);
529 // Does not exist. Throw an exception (can't update a non-existent network)
530 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
533 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
534 physicalNetworkName, vlans);
535 } catch (MsoException me) {
536 me.addContext(UPDATE_NETWORK_CONTEXT);
537 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
538 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
539 throw new NetworkException(me);
542 // Add the network ID and previously queried vlans to the rollback object
543 networkRollback.setNetworkId(netInfo.getId());
544 networkRollback.setNeutronNetworkId(netInfo.getId());
545 networkRollback.setNetworkType(networkType);
546 // Save previous parameters
547 networkRollback.setNetworkName(netInfo.getName());
548 networkRollback.setPhysicalNetwork(netInfo.getProvider());
549 networkRollback.setVlans(netInfo.getVlans());
551 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
552 } else if ("HEAT".equals(mode)) {
554 // First, look up to see that the Network already exists.
555 // For Heat-based orchestration, the networkId is the network Stack ID.
556 StackInfo heatStack = null;
558 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
559 } catch (MsoException me) {
560 me.addContext(UPDATE_NETWORK_CONTEXT);
561 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
562 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
563 throw new NetworkException(me);
566 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
567 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
568 cloudSiteId, tenantId);
569 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
571 // Network stack does not exist. Return an error
572 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
575 // Get the previous parameters for rollback
576 Map<String, Object> heatParams = heatStack.getParameters();
578 String previousNetworkName = (String) heatParams.get("network_name");
579 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
581 List<Integer> previousVlans = new ArrayList<>();
582 String vlansParam = (String) heatParams.get(VLANS);
583 if (vlansParam != null) {
584 for (String vlan : vlansParam.split(",")) {
586 previousVlans.add(Integer.parseInt(vlan));
587 } catch (NumberFormatException e) {
588 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
589 ErrorCode.DataError.getValue(), vlansParam, e);
593 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
595 // Ready to deploy the updated Network via Heat
598 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
599 if (heatTemplate == null) {
600 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
601 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
603 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
606 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
608 // "Fix" the template if it has CR/LF (getting this from Oracle)
609 String template = heatTemplate.getHeatTemplate();
610 template = template.replaceAll("\r\n", "\n");
612 boolean os3template = false;
613 String os3nw = OS3_NW;
615 os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
617 if (template.contains(os3nw))
620 // Build the common set of HEAT template parameters
621 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
622 physicalNetworkName, vlans, routeTargets, shared, external, os3template);
624 // Validate (and update) the input parameters against the DB definition
625 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
627 stackParams = heat.validateStackParams(stackParams, heatTemplate);
628 } catch (IllegalArgumentException e) {
629 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
630 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
631 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
634 if (subnets != null) {
637 template = mergeSubnetsAIC3(template, subnets, stackParams);
639 template = mergeSubnets(template, subnets);
641 } catch (MsoException me) {
642 me.addContext(UPDATE_NETWORK_CONTEXT);
643 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
644 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
645 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
646 throw new NetworkException(me);
650 if (policyFqdns != null && os3template) {
652 mergePolicyRefs(policyFqdns, stackParams);
653 } catch (MsoException me) {
654 me.addContext(UPDATE_NETWORK_CONTEXT);
655 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
656 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
657 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
658 throw new NetworkException(me);
662 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
664 mergeRouteTableRefs(routeTableFqdns, stackParams);
665 } catch (MsoException me) {
666 me.addContext(UPDATE_NETWORK_CONTEXT);
667 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
668 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
669 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
670 throw new NetworkException(me);
674 // Update the network stack
675 // Ignore MsoStackNotFound exception because we already checked.
677 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
678 stackParams, false, heatTemplate.getTimeoutMinutes());
679 } catch (MsoException me) {
680 me.addContext(UPDATE_NETWORK_CONTEXT);
681 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
682 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
683 throw new NetworkException(me);
686 Map<String, Object> outputs = heatStack.getOutputs();
687 Map<String, String> sMap = new HashMap<>();
688 if (outputs != null) {
689 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
690 String key = entry.getKey();
691 if (key != null && key.startsWith("subnet")) {
692 if (os3template) // one subnet output expected
694 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
696 } else // multiples subnet_%aaid% outputs allowed
698 String subnetUUId = (String) outputs.get(key);
699 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
704 subnetIdMap.value = sMap;
706 // Reach this point if createStack is successful.
707 // Populate remaining rollback info and response parameters.
708 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
709 if (null != outputs) {
710 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
712 logger.debug("outputs is NULL");
714 networkRollback.setNetworkType(networkType);
715 // Save previous parameters
716 networkRollback.setNetworkName(previousNetworkName);
717 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
718 networkRollback.setVlans(previousVlans);
720 rollback.value = networkRollback;
722 logger.debug("Network {} successfully updated via HEAT", networkId);
728 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
729 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
730 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
731 // Retrieve the Network Resource definition
732 NetworkResource networkResource = null;
733 NetworkResourceCustomization networkCust = null;
734 CollectionNetworkResourceCustomization collectionNetworkCust = null;
735 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
736 if (!commonUtils.isNullOrEmpty(networkType)) {
737 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
740 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
741 if (networkCust == null) {
742 collectionNetworkCust =
743 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
746 if (networkCust != null) {
747 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
749 networkResource = networkCust.getNetworkResource();
750 } else if (collectionNetworkCust != null) {
751 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
752 networkResource = collectionNetworkCust.getNetworkResource();
754 if (networkResource == null) {
755 String error = String.format(
756 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
757 networkType, modelCustomizationUuid);
758 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
760 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
762 logger.debug(LOG_DEBUG_MSG, networkResource);
764 String mode = networkResource.getOrchestrationMode();
765 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
767 // All Networks are orchestrated via HEAT or Neutron
768 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
769 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
770 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
771 ErrorCode.DataError.getValue(), error);
772 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
775 MavenLikeVersioning osV = new MavenLikeVersioning();
776 osV.setVersion(cloudSite.getCloudVersion());
777 if ((osV.isMoreRecentThan(networkResource.getAicVersionMin())
778 || osV.isTheSameVersion(networkResource.getAicVersionMin())) // os
781 && (osV.isTheSameVersion(networkResource.getAicVersionMax())
782 || !(osV.isMoreRecentThan(networkResource.getAicVersionMax())))) // os <= max
784 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
785 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
786 cloudSite.getCloudVersion());
788 String error = String.format(
789 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
790 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
791 cloudSite.getCloudVersion());
792 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
793 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
796 // Validate the Network parameters.
798 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
799 if (!missing.isEmpty()) {
800 String error = "Create Network: Missing parameters: " + missing;
801 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
803 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
806 return networkResource;
810 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
811 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
812 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
813 throws NetworkException {
814 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
815 status, vlans, null, subnetIdMap);
819 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
820 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
821 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
822 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
823 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
824 status, null, routeTargets, subnetIdMap);
828 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
829 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
830 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
832 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
833 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
834 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
835 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
837 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
839 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
840 || commonUtils.isNullOrEmpty(networkNameOrId)) {
842 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
843 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
844 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
847 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
848 if (!cloudSiteOpt.isPresent()) {
849 String error = String.format(
850 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
851 networkNameOrId, cloudSiteId, tenantId);
852 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
853 // Set the detailed error as the Exception 'message'
854 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
857 // Use MsoNeutronUtils for all NEUTRON commands
859 String neutronId = null;
860 // Try Heat first, since networks may be named the same as the Heat stack
861 StackInfo heatStack = null;
863 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
864 } catch (MsoException me) {
865 me.addContext("QueryNetwork");
866 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
867 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
868 throw new NetworkException(me);
871 // Populate the outputs based on the returned Stack information
872 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
873 // Found it. Get the neutronNetworkId for further query
874 Map<String, String> sMap = new HashMap<>();
875 Map<String, Object> outputs = heatStack.getOutputs();
876 if (outputs != null) {
877 neutronId = (String) outputs.get(NETWORK_ID);
879 for (String key : outputs.keySet()) {
880 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
882 String subnetUUId = (String) outputs.get(key);
883 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
884 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
886 Map<String, String> map = getSubnetUUId(key, outputs, null);
892 subnetIdMap.value = sMap;
895 // Query directly against the Neutron Network for the details
896 // no RouteTargets available for ContrailV2 in neutron net-show
897 // networkId is heatStackId
899 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
900 if (netInfo != null) {
901 // Found. Populate the output elements
902 networkExists.value = Boolean.TRUE;
903 if (heatStack != null) {
904 networkId.value = heatStack.getCanonicalName();
906 networkId.value = netInfo.getId();
908 neutronNetworkId.value = netInfo.getId();
909 status.value = netInfo.getStatus();
911 vlans.value = netInfo.getVlans();
913 logger.debug("Network {}, ID = {}{}", networkNameOrId, networkId.value,
914 (",NeutronId = " + neutronNetworkId.value));
916 // Not found. Populate the status fields, leave the rest null
917 networkExists.value = Boolean.FALSE;
918 status.value = NetworkStatus.NOTFOUND;
919 neutronNetworkId.value = null;
921 vlans.value = new ArrayList<>();
923 logger.debug("Network {} not found", networkNameOrId);
925 } catch (MsoException me) {
926 me.addContext("QueryNetwork");
927 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
928 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
929 throw new NetworkException(me);
935 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
938 * If the network is not found, it is treated as a success.
940 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
941 * network orchestration mode for each network type is declared in its catalog definition.
943 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
944 * networkId should be the Neutron network UUID.
946 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
947 * will require manual fallout in the client.
950 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
951 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted, Boolean pollForCompletion)
952 throws NetworkException {
953 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
954 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
955 || commonUtils.isNullOrEmpty(networkId)) {
956 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
957 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
958 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
961 if (pollForCompletion == null) {
962 pollForCompletion = true;
965 int timeoutMinutes = heat.getNetworkHeatTimeoutValue(modelCustomizationUuid, networkType);
969 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, timeoutMinutes);
970 networkDeleted.value = stack.isOperationPerformed();
971 } catch (MsoException me) {
972 me.addContext("DeleteNetwork");
973 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
974 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
975 throw new NetworkException(me);
979 heat.updateResourceStatus(msoRequest.getRequestId(),
980 networkDeleted.value ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
981 } catch (Exception e) {
982 logger.warn("Exception while updating infra active request", e);
987 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
988 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
989 * to undo the creation.
991 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
995 public void rollbackNetwork(NetworkRollback rollback, Boolean pollForCompletion) throws NetworkException {
996 if (rollback == null) {
997 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1001 if (pollForCompletion == null) {
1002 pollForCompletion = true;
1005 // Get the elements of the VnfRollback object for easier access
1006 String cloudSiteId = rollback.getCloudId();
1007 String tenantId = rollback.getTenantId();
1008 String networkId = rollback.getNetworkStackId();
1010 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1012 if (rollback.getNetworkCreated()) {
1014 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, 120);
1015 } catch (MsoException me) {
1016 me.addContext("RollbackNetwork");
1017 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1018 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
1019 cloudSiteId, tenantId, me);
1020 throw new NetworkException(me);
1026 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1027 List<Integer> vlans, List<RouteTarget> routeTargets) {
1029 StringBuilder missing = new StringBuilder();
1030 if (commonUtils.isNullOrEmpty(networkName)) {
1031 missing.append("networkName");
1035 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1036 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1037 missing.append(sep).append("physicalNetworkName");
1040 if (vlans == null || vlans.isEmpty()) {
1041 missing.append(sep).append(VLANS);
1045 return missing.toString();
1048 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1049 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1050 boolean os3template) {
1051 // Build the common set of HEAT template parameters
1052 Map<String, Object> stackParams = new HashMap<>();
1053 stackParams.put("network_name", networkName);
1055 if (neutronNetworkType == NetworkType.PROVIDER) {
1056 // For Provider type
1057 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1058 stackParams.put("vlan", vlans.get(0).toString());
1059 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1060 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1061 // It supports all ProviderNet properties except segmentation_id, and adds a
1062 // comma-separated-list of VLANs as a "segments" property.
1063 // Note that this does not match the Neutron definition of Multi-Provider network,
1064 // which contains a list of 'segments', each having physical_network, network_type,
1065 // and segmentation_id.
1066 StringBuilder buf = new StringBuilder();
1068 for (Integer vlan : vlans) {
1069 buf.append(sep).append(vlan.toString());
1072 String csl = buf.toString();
1074 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1075 stackParams.put(VLANS, csl);
1077 if (routeTargets != null) {
1079 String rtGlobal = "";
1080 String rtImport = "";
1081 String rtExport = "";
1083 for (RouteTarget rt : routeTargets) {
1084 boolean rtIsNull = false;
1086 String routeTarget = rt.getRouteTarget();
1087 String routeTargetRole = rt.getRouteTargetRole();
1088 logger.debug("Checking for an actually null route target: {}", rt);
1089 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1091 if (routeTargetRole == null || routeTargetRole.equals("")
1092 || routeTargetRole.equalsIgnoreCase("null"))
1098 logger.debug("Input RT:{}", rt);
1099 String role = rt.getRouteTargetRole();
1100 String rtValue = rt.getRouteTarget();
1102 if ("IMPORT".equalsIgnoreCase(role)) {
1103 sep = rtImport.isEmpty() ? "" : ",";
1104 rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1105 } else if ("EXPORT".equalsIgnoreCase(role)) {
1106 sep = rtExport.isEmpty() ? "" : ",";
1107 rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1108 } else // covers BOTH, empty etc
1110 sep = rtGlobal.isEmpty() ? "" : ",";
1111 rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1117 if (!rtImport.isEmpty()) {
1118 stackParams.put("route_targets_import", rtImport);
1120 if (!rtExport.isEmpty()) {
1121 stackParams.put("route_targets_export", rtExport);
1123 if (!rtGlobal.isEmpty()) {
1124 stackParams.put("route_targets", rtGlobal);
1127 if (commonUtils.isNullOrEmpty(shared)) {
1128 stackParams.put("shared", "False");
1130 stackParams.put("shared", shared);
1132 if (commonUtils.isNullOrEmpty(external)) {
1133 stackParams.put("external", "False");
1135 stackParams.put("external", external);
1143 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1144 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1145 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1146 * "network_policy_refs_data_sequence_minor": "0" } } ]
1148 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1149 // Resource Property
1150 List<ContrailPolicyRef> prlist = new ArrayList<>();
1153 if (pFqdns != null) {
1154 for (String pf : pFqdns) {
1155 if (!commonUtils.isNullOrEmpty(pf)) {
1156 ContrailPolicyRef pr = new ContrailPolicyRef();
1157 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1160 logger.debug("Contrail PolicyRefs Data:{}", pr);
1165 String error = "Null pFqdns at start of mergePolicyRefs";
1166 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1168 throw new MsoAdapterException(error);
1171 JsonNode node = null;
1173 ObjectMapper mapper = new ObjectMapper();
1174 node = mapper.convertValue(prlist, JsonNode.class);
1175 String jsonString = mapper.writeValueAsString(prlist);
1176 logger.debug("Json PolicyRefs Data:{}", jsonString);
1177 } catch (Exception e) {
1178 String error = "Error creating JsonNode for policyRefs Data";
1179 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1181 throw new MsoAdapterException(error);
1183 // update parameters
1184 if (pFqdns != null && node != null) {
1185 StringBuilder buf = new StringBuilder();
1187 for (String pf : pFqdns) {
1188 if (!commonUtils.isNullOrEmpty(pf)) {
1189 buf.append(sep).append(pf);
1193 String csl = buf.toString();
1194 stackParams.put("policy_refs", csl);
1195 stackParams.put("policy_refsdata", node);
1198 logger.debug("StackParams updated with policy refs");
1202 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1204 // update parameters
1205 if (rtFqdns != null) {
1206 StringBuilder buf = new StringBuilder();
1208 for (String rtf : rtFqdns) {
1209 if (!commonUtils.isNullOrEmpty(rtf)) {
1210 buf.append(sep).append(rtf);
1214 String csl = buf.toString();
1215 stackParams.put("route_table_refs", csl);
1218 logger.debug("StackParams updated with route_table refs");
1224 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1225 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1226 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1227 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1228 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1229 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1230 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1231 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1232 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1233 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1234 * "host_routes": null }
1236 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1237 throws MsoException {
1239 // Resource Property
1240 List<ContrailSubnet> cslist = new ArrayList<>();
1241 for (Subnet subnet : subnets) {
1242 logger.debug("Input Subnet:{}", subnet);
1243 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1244 logger.debug("Contrail Subnet:{}", cs);
1248 JsonNode node = null;
1250 ObjectMapper mapper = new ObjectMapper();
1251 node = mapper.convertValue(cslist, JsonNode.class);
1252 String jsonString = mapper.writeValueAsString(cslist);
1253 logger.debug("Json Subnet List:{}", jsonString);
1254 } catch (Exception e) {
1255 String error = "Error creating JsonNode from input subnets";
1256 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1257 throw new MsoAdapterException(error);
1259 // update parameters
1261 stackParams.put("subnet_list", node);
1263 // Outputs - All subnets are in one ipam_subnets structure
1264 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1265 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1267 // append outputs in heatTemplate
1268 int outputsIdx = heatTemplate.indexOf("outputs:");
1269 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1270 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1271 return heatTemplate;
1275 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1277 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1278 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1281 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1282 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1283 * " end: %poolend%\n";
1287 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1288 + " value: {get_resource: subnet_%subnetId%}\n";
1292 StringBuilder resourcesBuf = new StringBuilder();
1293 StringBuilder outputsBuf = new StringBuilder();
1294 for (Subnet subnet : subnets) {
1296 // build template for each subnet
1297 curR = resourceTempl;
1298 if (subnet.getSubnetId() != null) {
1299 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1301 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1302 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1303 throw new MsoAdapterException(error);
1306 if (subnet.getSubnetName() != null) {
1307 curR = curR.replace("%name%", subnet.getSubnetName());
1309 curR = curR.replace("%name%", subnet.getSubnetId());
1312 if (subnet.getCidr() != null) {
1313 curR = curR.replace("%cidr%", subnet.getCidr());
1315 String error = "Missing Required cidr for subnet in HEAT Template";
1316 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1317 throw new MsoAdapterException(error);
1320 if (subnet.getIpVersion() != null) {
1321 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1323 if (subnet.getEnableDHCP() != null) {
1324 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1326 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1327 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1330 if (subnet.getAllocationPools() != null) {
1331 StringBuilder tempBuf = new StringBuilder();
1332 tempBuf.append(curR);
1333 tempBuf.append(" allocation_pools:\n");
1334 for (Pool pool : subnet.getAllocationPools()) {
1335 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1336 tempBuf.append(" - start: ");
1337 tempBuf.append(pool.getStart());
1338 tempBuf.append("\n end: ");
1339 tempBuf.append(pool.getEnd());
1340 tempBuf.append("\n");
1343 curR = tempBuf.toString();
1346 resourcesBuf.append(curR);
1349 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1351 outputsBuf.append(curO);
1353 // append resources and outputs in heatTemplate
1354 logger.debug("Tempate initial:{}", heatTemplate);
1355 int outputsIdx = heatTemplate.indexOf("outputs:");
1356 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1357 int resourcesIdx = heatTemplate.indexOf("resources:");
1358 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1360 logger.debug("Template updated with all subnets:{}", heatTemplate);
1361 return heatTemplate;
1364 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1366 Map<String, String> sMap = new HashMap<>();
1369 Object obj = outputs.get(key);
1370 ObjectMapper mapper = new ObjectMapper();
1371 String jStr = mapper.writeValueAsString(obj);
1372 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1374 JsonNode rootNode = mapper.readTree(jStr);
1375 if (rootNode != null) {
1376 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1377 logger.debug("Output Subnet Node {}", sNode);
1378 String name = sNode.path("subnet_name").textValue();
1379 String uuid = sNode.path("subnet_uuid").textValue();
1380 String aaiId = name; // default
1381 // try to find aaiId for name in input subnetList
1382 if (subnets != null) {
1383 for (Subnet subnet : subnets) {
1384 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1385 && subnet.getSubnetName().equals(name)) {
1386 aaiId = subnet.getSubnetId();
1391 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1394 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1395 ErrorCode.DataError.getValue());
1397 } catch (Exception e) {
1398 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1399 ErrorCode.DataError.getValue(), e);
1402 logger.debug("Return sMap {}", sMap);
1406 private static String insertStr(String template, String snippet, int index) {
1408 String updatedTemplate;
1410 logger.debug("Index:{} Snippet:{}", index, snippet);
1412 String templateBeg = template.substring(0, index);
1413 String templateEnd = template.substring(index);
1415 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1417 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1418 return updatedTemplate;