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.apache.commons.lang3.mutable.MutableBoolean;
35 import org.onap.logging.filter.base.ErrorCode;
36 import org.onap.so.adapters.network.beans.ContrailPolicyRef;
37 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
38 import org.onap.so.adapters.network.beans.ContrailSubnet;
39 import org.onap.so.adapters.network.exceptions.NetworkException;
40 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
41 import org.onap.so.cloud.CloudConfig;
42 import org.onap.so.db.catalog.beans.CloudSite;
43 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
44 import org.onap.so.db.catalog.beans.HeatTemplate;
45 import org.onap.so.db.catalog.beans.NetworkResource;
46 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
47 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
48 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
49 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
50 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
51 import org.onap.so.entity.MsoRequest;
52 import org.onap.so.logger.LoggingAnchor;
53 import org.onap.so.logger.MessageEnum;
54 import org.onap.so.openstack.beans.HeatStatus;
55 import org.onap.so.openstack.beans.NetworkInfo;
56 import org.onap.so.openstack.beans.NetworkRollback;
57 import org.onap.so.openstack.beans.NetworkStatus;
58 import org.onap.so.openstack.beans.Pool;
59 import org.onap.so.openstack.beans.RouteTarget;
60 import org.onap.so.openstack.beans.StackInfo;
61 import org.onap.so.openstack.beans.Subnet;
62 import org.onap.so.openstack.exceptions.MsoAdapterException;
63 import org.onap.so.openstack.exceptions.MsoException;
64 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
65 import org.onap.so.openstack.utils.MsoCommonUtils;
66 import org.onap.so.openstack.utils.MsoHeatUtils;
67 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
68 import org.onap.so.openstack.utils.MsoNeutronUtils;
69 import org.onap.so.openstack.utils.MsoNeutronUtils.NetworkType;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.beans.factory.annotation.Autowired;
73 import org.springframework.core.env.Environment;
74 import org.springframework.stereotype.Component;
75 import org.springframework.transaction.annotation.Transactional;
76 import com.fasterxml.jackson.databind.JsonNode;
77 import com.fasterxml.jackson.databind.ObjectMapper;
81 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.onap.so.adapters.network.MsoNetworkAdapter",
82 targetNamespace = "http://org.onap.so/network")
83 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
85 private static final String OS3_NW_PROPERTY = "org.onap.so.adapters.network.aic3nw";
86 private static final String OS3_NW = "OS::ContrailV2::VirtualNetwork";
87 private static final String VLANS = "vlans";
88 private static final String PHYSICAL_NETWORK = "physical_network";
89 private static final String UPDATE_NETWORK_CONTEXT = "UpdateNetwork";
90 private static final String NETWORK_ID = "network_id";
91 private static final String NETWORK_FQDN = "network_fqdn";
92 private static final String CREATE_NETWORK_CONTEXT = "CreateNetwork";
93 private static final String NEUTRON_MODE = "NEUTRON";
94 private static final String CLOUD_OWNER = "CloudOwner";
95 private static final String LOG_DEBUG_MSG = "Got Network definition from Catalog: {}";
96 private static final String NETWORK_EXIST_STATUS_MESSAGE =
97 "The network was found to already exist, thus no new network was created in the cloud via this request";
98 private static final String NETWORK_CREATED_STATUS_MESSAGE =
99 "The new network was successfully created in the cloud";
100 private static final String NETWORK_NOT_EXIST_STATUS_MESSAGE =
101 "The network was not found, thus no network was deleted in the cloud via this request";
102 private static final String NETWORK_DELETED_STATUS_MESSAGE = "The network was successfully deleted in the cloud";
104 private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
107 private CloudConfig cloudConfig;
109 private Environment environment;
111 private MsoNeutronUtils neutron;
113 private MsoHeatUtils heat;
115 private MsoHeatUtilsWithUpdate heatWithUpdate;
117 private MsoCommonUtils commonUtils;
120 private NetworkResourceCustomizationRepository networkCustomRepo;
123 private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
126 private NetworkResourceRepository networkResourceRepo;
128 public MsoNetworkAdapterImpl() {}
131 * Health Check web method. Does nothing but return to show the adapter is deployed.
134 public void healthCheck() {
135 logger.debug("Health check call in Network Adapter");
139 * Do not use this constructor or the msoPropertiesFactory will be NULL.
141 * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
146 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
147 String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
148 Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
149 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
150 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
151 Holder<String> networkFqdn = new Holder<>();
152 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
153 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
154 neutronNetworkId, networkFqdn, subnetIdMap, rollback, true, new MutableBoolean());
159 public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
160 String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
161 String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
162 Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
163 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
164 Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
165 throws NetworkException {
166 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
167 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
168 neutronNetworkId, networkFqdn, subnetIdMap, rollback, true, new MutableBoolean());
172 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
173 * the specified cloud and tenant. The tenant must exist at the time this service is called.
175 * If a network with the same name already exists, this can be considered a success or failure, depending on the
176 * value of the 'failIfExists' parameter.
178 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
179 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
180 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
182 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
183 * multiple VLANs on the same physical network.
185 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
186 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
187 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
189 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
190 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
191 * created but the orchestration fails on a subsequent operation.
194 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
195 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
196 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
197 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
198 Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
199 Holder<NetworkRollback> rollback, Boolean pollForCompletion, MutableBoolean isOs3Nw)
200 throws NetworkException {
201 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
203 // Will capture execution time for metrics
204 long startTime = System.currentTimeMillis();
206 // Build a default rollback object (no actions performed) //TODO remove
207 NetworkRollback networkRollback = new NetworkRollback();
208 networkRollback.setCloudId(cloudSiteId);
209 networkRollback.setTenantId(tenantId);
210 networkRollback.setMsoRequest(msoRequest);
211 networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
213 // tenant query is not required here.
214 // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
215 // So this is just catching that error in a bit more obvious way up front.
217 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
218 if (!cloudSiteOpt.isPresent()) {
219 String error = String.format(
220 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
221 networkName, cloudSiteId, tenantId);
222 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
223 // Set the detailed error as the Exception 'message'
224 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
228 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
229 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
230 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
232 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
233 if (heatTemplate == null) {
234 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
235 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
236 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
239 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
241 // "Fix" the template if it has CR/LF (getting this from Oracle)
242 String template = heatTemplate.getHeatTemplate();
243 template = template.replaceAll("\r\n", "\n");
245 boolean os3template = false;
247 String os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
249 if (template.contains(os3nw))
252 isOs3Nw.setValue(os3template);
253 // First, look up to see if the Network already exists (by name).
254 // For HEAT orchestration of networks, the stack name will always match the network name
255 StackInfo heatStack = null;
257 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
258 } catch (MsoException me) {
259 me.addContext(CREATE_NETWORK_CONTEXT);
260 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ", MessageEnum.RA_QUERY_NETWORK_EXC,
261 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
262 throw new NetworkException(me);
265 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
266 // Stack exists. Return success or error depending on input directive
267 if (failIfExists != null && failIfExists) {
268 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
269 cloudSiteId, tenantId, heatStack.getCanonicalName());
270 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
272 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
274 // Populate the outputs from the existing stack.
275 networkId.value = heatStack.getCanonicalName();
276 Map<String, String> sMap = new HashMap<>();
277 if (heatStack.getOutputs() != null) {
278 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
279 rollback.value = networkRollback; // Default rollback - no updates performed
281 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
283 Map<String, Object> outputs = heatStack.getOutputs();
285 sMap = buildSubnetMap(outputs, subnets, os3template);
287 subnetIdMap.value = sMap;
288 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
289 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
290 networkName, cloudSiteId, tenantId);
292 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
296 // Ready to deploy the new Network
297 // Build the common set of HEAT template parameters
298 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName, physicalNetworkName,
299 vlans, routeTargets, shared, external, os3template);
301 // Validate (and update) the input parameters against the DB definition
302 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
303 // and inputs were already validated.
305 stackParams = heat.validateStackParams(stackParams, heatTemplate);
306 } catch (IllegalArgumentException e) {
307 String error = "Create Network: Configuration Error: " + e.getMessage();
308 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
309 // Input parameters were not valid
310 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
313 if (subnets != null) {
316 template = mergeSubnetsAIC3(template, subnets, stackParams);
318 template = mergeSubnets(template, subnets);
320 } catch (MsoException me) {
321 me.addContext(CREATE_NETWORK_CONTEXT);
322 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
323 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
324 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
325 throw new NetworkException(me);
329 if (policyFqdns != null && !policyFqdns.isEmpty() && os3template) {
331 mergePolicyRefs(policyFqdns, stackParams);
332 } catch (MsoException me) {
333 me.addContext(CREATE_NETWORK_CONTEXT);
334 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
335 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
336 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
337 throw new NetworkException(me);
341 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
343 mergeRouteTableRefs(routeTableFqdns, stackParams);
344 } catch (MsoException me) {
345 me.addContext(CREATE_NETWORK_CONTEXT);
346 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
347 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
348 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
349 throw new NetworkException(me);
353 // Deploy the network stack
354 // Ignore MsoStackAlreadyExists exception because we already checked.
358 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template, stackParams,
359 pollForCompletion, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue(),
361 } catch (MsoException me) {
362 me.addContext(CREATE_NETWORK_CONTEXT);
363 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
364 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
365 throw new NetworkException(me);
368 // Reach this point if createStack is successful.
370 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
371 // and the neutronNetworkId is the network UUID returned in stack outputs.
372 networkId.value = heatStack.getCanonicalName();
373 if (heatStack.getOutputs() != null) {
374 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
376 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
379 Map<String, Object> outputs = heatStack.getOutputs();
380 Map<String, String> sMap = new HashMap<>();
381 if (outputs != null) {
382 sMap = buildSubnetMap(outputs, subnets, os3template);
383 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
385 subnetIdMap.value = sMap;
387 rollback.value = networkRollback;
388 // Populate remaining rollback info and response parameters.
389 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
390 networkRollback.setNetworkCreated(true);
391 networkRollback.setNetworkType(networkType);
394 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
395 } catch (Exception e) {
396 logger.warn("Exception while updating infra active request", e);
399 logger.debug("Network {} successfully created via HEAT", networkName);
407 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
408 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
409 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
410 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
411 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
412 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
419 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
420 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
421 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
422 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
423 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
424 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
425 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
430 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
431 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
432 * remove a VLAN), but other properties may be updated as well.
434 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
435 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
436 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
438 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
439 * VLANs on the same physical network.
441 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
442 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
443 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
445 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
446 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
447 * a subsequent operation.
449 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
450 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
451 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
452 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
453 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
455 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
456 cloudSiteId, tenantId);
458 // Will capture execution time for metrics
459 long startTime = System.currentTimeMillis();
461 // Build a default rollback object (no actions performed)
462 NetworkRollback networkRollback = new NetworkRollback();
463 networkRollback.setCloudId(cloudSiteId);
464 networkRollback.setTenantId(tenantId);
465 networkRollback.setMsoRequest(msoRequest);
467 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
468 if (!cloudSiteOpt.isPresent()) {
469 String error = String.format(
470 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
471 networkName, cloudSiteId, tenantId);
472 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
473 // Set the detailed error as the Exception 'message'
474 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
479 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
480 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
481 String mode = networkResource.getOrchestrationMode();
482 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
484 // Use an MsoNeutronUtils for all Neutron commands
486 if (NEUTRON_MODE.equals(mode)) {
488 // Verify that the Network exists
489 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
490 NetworkInfo netInfo = null;
492 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
493 } catch (MsoException me) {
494 me.addContext(UPDATE_NETWORK_CONTEXT);
495 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
496 ErrorCode.BusinessProcessError.getValue(), networkId, cloudSiteId, tenantId, me);
497 throw new NetworkException(me);
500 if (netInfo == null) {
501 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
502 cloudSiteId, tenantId);
503 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
504 ErrorCode.BusinessProcessError.getValue(), error);
505 // Does not exist. Throw an exception (can't update a non-existent network)
506 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
509 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
510 physicalNetworkName, vlans);
511 } catch (MsoException me) {
512 me.addContext(UPDATE_NETWORK_CONTEXT);
513 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
514 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
515 throw new NetworkException(me);
518 // Add the network ID and previously queried vlans to the rollback object
519 networkRollback.setNetworkId(netInfo.getId());
520 networkRollback.setNeutronNetworkId(netInfo.getId());
521 networkRollback.setNetworkType(networkType);
522 // Save previous parameters
523 networkRollback.setNetworkName(netInfo.getName());
524 networkRollback.setPhysicalNetwork(netInfo.getProvider());
525 networkRollback.setVlans(netInfo.getVlans());
527 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
528 } else if ("HEAT".equals(mode)) {
530 // First, look up to see that the Network already exists.
531 // For Heat-based orchestration, the networkId is the network Stack ID.
532 StackInfo heatStack = null;
534 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
535 } catch (MsoException me) {
536 me.addContext(UPDATE_NETWORK_CONTEXT);
537 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
538 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
539 throw new NetworkException(me);
542 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
543 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
544 cloudSiteId, tenantId);
545 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
547 // Network stack does not exist. Return an error
548 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
551 // Get the previous parameters for rollback
552 Map<String, Object> heatParams = heatStack.getParameters();
554 String previousNetworkName = (String) heatParams.get("network_name");
555 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
557 List<Integer> previousVlans = new ArrayList<>();
558 String vlansParam = (String) heatParams.get(VLANS);
559 if (vlansParam != null) {
560 for (String vlan : vlansParam.split(",")) {
562 previousVlans.add(Integer.parseInt(vlan));
563 } catch (NumberFormatException e) {
564 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
565 ErrorCode.DataError.getValue(), vlansParam, e);
569 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
571 // Ready to deploy the updated Network via Heat
574 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
575 if (heatTemplate == null) {
576 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
577 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
579 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
582 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
584 // "Fix" the template if it has CR/LF (getting this from Oracle)
585 String template = heatTemplate.getHeatTemplate();
586 template = template.replaceAll("\r\n", "\n");
588 boolean os3template = false;
589 String os3nw = OS3_NW;
591 os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
593 if (template.contains(os3nw))
596 // Build the common set of HEAT template parameters
597 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
598 physicalNetworkName, vlans, routeTargets, shared, external, os3template);
600 // Validate (and update) the input parameters against the DB definition
601 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
603 stackParams = heat.validateStackParams(stackParams, heatTemplate);
604 } catch (IllegalArgumentException e) {
605 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
606 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
607 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
610 if (subnets != null) {
613 template = mergeSubnetsAIC3(template, subnets, stackParams);
615 template = mergeSubnets(template, subnets);
617 } catch (MsoException me) {
618 me.addContext(UPDATE_NETWORK_CONTEXT);
619 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
620 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
621 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
622 throw new NetworkException(me);
626 if (policyFqdns != null && os3template) {
628 mergePolicyRefs(policyFqdns, stackParams);
629 } catch (MsoException me) {
630 me.addContext(UPDATE_NETWORK_CONTEXT);
631 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
632 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
633 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
634 throw new NetworkException(me);
638 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
640 mergeRouteTableRefs(routeTableFqdns, stackParams);
641 } catch (MsoException me) {
642 me.addContext(UPDATE_NETWORK_CONTEXT);
643 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
644 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
645 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
646 throw new NetworkException(me);
650 // Update the network stack
651 // Ignore MsoStackNotFound exception because we already checked.
653 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
654 stackParams, false, heatTemplate.getTimeoutMinutes());
655 } catch (MsoException me) {
656 me.addContext(UPDATE_NETWORK_CONTEXT);
657 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
658 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
659 throw new NetworkException(me);
662 Map<String, Object> outputs = heatStack.getOutputs();
663 Map<String, String> sMap = new HashMap<>();
664 if (outputs != null) {
665 sMap = buildSubnetMap(outputs, subnets, os3template);
667 subnetIdMap.value = sMap;
669 // Reach this point if createStack is successful.
670 // Populate remaining rollback info and response parameters.
671 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
672 if (null != outputs) {
673 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
675 logger.debug("outputs is NULL");
677 networkRollback.setNetworkType(networkType);
678 // Save previous parameters
679 networkRollback.setNetworkName(previousNetworkName);
680 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
681 networkRollback.setVlans(previousVlans);
683 rollback.value = networkRollback;
685 logger.debug("Network {} successfully updated via HEAT", networkId);
691 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
692 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
693 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
694 // Retrieve the Network Resource definition
695 NetworkResource networkResource = null;
696 NetworkResourceCustomization networkCust = null;
697 CollectionNetworkResourceCustomization collectionNetworkCust = null;
698 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
699 if (!commonUtils.isNullOrEmpty(networkType)) {
700 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
703 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
704 if (networkCust == null) {
705 collectionNetworkCust =
706 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
709 if (networkCust != null) {
710 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
712 networkResource = networkCust.getNetworkResource();
713 } else if (collectionNetworkCust != null) {
714 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
715 networkResource = collectionNetworkCust.getNetworkResource();
717 if (networkResource == null) {
718 String error = String.format(
719 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
720 networkType, modelCustomizationUuid);
721 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
723 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
725 logger.debug(LOG_DEBUG_MSG, networkResource);
727 String mode = networkResource.getOrchestrationMode();
728 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
730 // All Networks are orchestrated via HEAT or Neutron
731 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
732 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
733 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
734 ErrorCode.DataError.getValue(), error);
735 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
738 MavenLikeVersioning osV = new MavenLikeVersioning();
739 osV.setVersion(cloudSite.getCloudVersion());
740 if ((osV.isMoreRecentThan(networkResource.getAicVersionMin())
741 || osV.isTheSameVersion(networkResource.getAicVersionMin())) // os
744 && (osV.isTheSameVersion(networkResource.getAicVersionMax())
745 || !(osV.isMoreRecentThan(networkResource.getAicVersionMax())))) // os <= max
747 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
748 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
749 cloudSite.getCloudVersion());
751 String error = String.format(
752 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
753 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
754 cloudSite.getCloudVersion());
755 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
756 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
759 // Validate the Network parameters.
761 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
762 if (!missing.isEmpty()) {
763 String error = "Create Network: Missing parameters: " + missing;
764 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
766 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
769 return networkResource;
773 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
774 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
775 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
776 throws NetworkException {
777 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
778 status, vlans, null, subnetIdMap);
782 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
783 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
784 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
785 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
786 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
787 status, null, routeTargets, subnetIdMap);
791 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
792 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
793 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
795 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
796 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
797 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
798 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
800 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
802 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
803 || commonUtils.isNullOrEmpty(networkNameOrId)) {
805 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
806 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
807 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
810 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
811 if (!cloudSiteOpt.isPresent()) {
812 String error = String.format(
813 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
814 networkNameOrId, cloudSiteId, tenantId);
815 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
816 // Set the detailed error as the Exception 'message'
817 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
820 // Use MsoNeutronUtils for all NEUTRON commands
822 String neutronId = null;
823 // Try Heat first, since networks may be named the same as the Heat stack
824 StackInfo heatStack = null;
826 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
827 } catch (MsoException me) {
828 me.addContext("QueryNetwork");
829 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
830 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
831 throw new NetworkException(me);
834 // Populate the outputs based on the returned Stack information
835 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
836 // Found it. Get the neutronNetworkId for further query
837 Map<String, String> sMap = new HashMap<>();
838 Map<String, Object> outputs = heatStack.getOutputs();
839 if (outputs != null) {
840 neutronId = (String) outputs.get(NETWORK_ID);
842 for (String key : outputs.keySet()) {
843 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
845 String subnetUUId = (String) outputs.get(key);
846 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
847 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
849 Map<String, String> map = getSubnetUUId(key, outputs, null);
855 subnetIdMap.value = sMap;
858 // Query directly against the Neutron Network for the details
859 // no RouteTargets available for ContrailV2 in neutron net-show
860 // networkId is heatStackId
862 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
863 if (netInfo != null) {
864 // Found. Populate the output elements
865 networkExists.value = Boolean.TRUE;
866 if (heatStack != null) {
867 networkId.value = heatStack.getCanonicalName();
869 networkId.value = netInfo.getId();
871 neutronNetworkId.value = netInfo.getId();
872 status.value = netInfo.getStatus();
874 vlans.value = netInfo.getVlans();
876 logger.debug("Network {}, ID = {}{}", networkNameOrId, networkId.value,
877 (",NeutronId = " + neutronNetworkId.value));
879 // Not found. Populate the status fields, leave the rest null
880 networkExists.value = Boolean.FALSE;
881 status.value = NetworkStatus.NOTFOUND;
882 neutronNetworkId.value = null;
884 vlans.value = new ArrayList<>();
886 logger.debug("Network {} not found", networkNameOrId);
888 } catch (MsoException me) {
889 me.addContext("QueryNetwork");
890 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
891 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
892 throw new NetworkException(me);
898 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
901 * If the network is not found, it is treated as a success.
903 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
904 * network orchestration mode for each network type is declared in its catalog definition.
906 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
907 * networkId should be the Neutron network UUID.
909 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
910 * will require manual fallout in the client.
913 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
914 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted, Boolean pollForCompletion)
915 throws NetworkException {
916 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
917 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
918 || commonUtils.isNullOrEmpty(networkId)) {
919 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
920 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
921 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
924 if (pollForCompletion == null) {
925 pollForCompletion = true;
928 int timeoutMinutes = heat.getNetworkHeatTimeoutValue(modelCustomizationUuid, networkType);
932 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, timeoutMinutes);
933 networkDeleted.value = stack.isOperationPerformed();
934 } catch (MsoException me) {
935 me.addContext("DeleteNetwork");
936 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
937 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
938 throw new NetworkException(me);
942 heat.updateResourceStatus(msoRequest.getRequestId(),
943 networkDeleted.value ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
944 } catch (Exception e) {
945 logger.warn("Exception while updating infra active request", e);
950 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
951 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
952 * to undo the creation.
954 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
958 public void rollbackNetwork(NetworkRollback rollback, Boolean pollForCompletion) throws NetworkException {
959 if (rollback == null) {
960 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
964 if (pollForCompletion == null) {
965 pollForCompletion = true;
968 // Get the elements of the VnfRollback object for easier access
969 String cloudSiteId = rollback.getCloudId();
970 String tenantId = rollback.getTenantId();
971 String networkId = rollback.getNetworkStackId();
973 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
975 if (rollback.getNetworkCreated()) {
977 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, 120);
978 } catch (MsoException me) {
979 me.addContext("RollbackNetwork");
980 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
981 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
982 cloudSiteId, tenantId, me);
983 throw new NetworkException(me);
989 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
990 List<Integer> vlans, List<RouteTarget> routeTargets) {
992 StringBuilder missing = new StringBuilder();
993 if (commonUtils.isNullOrEmpty(networkName)) {
994 missing.append("networkName");
998 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
999 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1000 missing.append(sep).append("physicalNetworkName");
1003 if (vlans == null || vlans.isEmpty()) {
1004 missing.append(sep).append(VLANS);
1008 return missing.toString();
1011 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1012 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1013 boolean os3template) {
1014 // Build the common set of HEAT template parameters
1015 Map<String, Object> stackParams = new HashMap<>();
1016 stackParams.put("network_name", networkName);
1018 if (neutronNetworkType == NetworkType.PROVIDER) {
1019 // For Provider type
1020 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1021 stackParams.put("vlan", vlans.get(0).toString());
1022 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1023 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1024 // It supports all ProviderNet properties except segmentation_id, and adds a
1025 // comma-separated-list of VLANs as a "segments" property.
1026 // Note that this does not match the Neutron definition of Multi-Provider network,
1027 // which contains a list of 'segments', each having physical_network, network_type,
1028 // and segmentation_id.
1029 StringBuilder buf = new StringBuilder();
1031 for (Integer vlan : vlans) {
1032 buf.append(sep).append(vlan.toString());
1035 String csl = buf.toString();
1037 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1038 stackParams.put(VLANS, csl);
1040 if (routeTargets != null) {
1042 String rtGlobal = "";
1043 String rtImport = "";
1044 String rtExport = "";
1046 for (RouteTarget rt : routeTargets) {
1047 boolean rtIsNull = false;
1049 String routeTarget = rt.getRouteTarget();
1050 String routeTargetRole = rt.getRouteTargetRole();
1051 logger.debug("Checking for an actually null route target: {}", rt);
1052 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1054 if (routeTargetRole == null || routeTargetRole.equals("")
1055 || routeTargetRole.equalsIgnoreCase("null"))
1061 logger.debug("Input RT:{}", rt);
1062 String role = rt.getRouteTargetRole();
1063 String rtValue = rt.getRouteTarget();
1065 if ("IMPORT".equalsIgnoreCase(role)) {
1066 sep = rtImport.isEmpty() ? "" : ",";
1067 rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1068 } else if ("EXPORT".equalsIgnoreCase(role)) {
1069 sep = rtExport.isEmpty() ? "" : ",";
1070 rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1071 } else // covers BOTH, empty etc
1073 sep = rtGlobal.isEmpty() ? "" : ",";
1074 rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1080 if (!rtImport.isEmpty()) {
1081 stackParams.put("route_targets_import", rtImport);
1083 if (!rtExport.isEmpty()) {
1084 stackParams.put("route_targets_export", rtExport);
1086 if (!rtGlobal.isEmpty()) {
1087 stackParams.put("route_targets", rtGlobal);
1090 if (commonUtils.isNullOrEmpty(shared)) {
1091 stackParams.put("shared", "False");
1093 stackParams.put("shared", shared);
1095 if (commonUtils.isNullOrEmpty(external)) {
1096 stackParams.put("external", "False");
1098 stackParams.put("external", external);
1106 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1107 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1108 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1109 * "network_policy_refs_data_sequence_minor": "0" } } ]
1111 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1112 // Resource Property
1113 List<ContrailPolicyRef> prlist = new ArrayList<>();
1116 if (pFqdns != null) {
1117 for (String pf : pFqdns) {
1118 if (!commonUtils.isNullOrEmpty(pf)) {
1119 ContrailPolicyRef pr = new ContrailPolicyRef();
1120 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1123 logger.debug("Contrail PolicyRefs Data:{}", pr);
1128 String error = "Null pFqdns at start of mergePolicyRefs";
1129 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1131 throw new MsoAdapterException(error);
1134 JsonNode node = null;
1136 ObjectMapper mapper = new ObjectMapper();
1137 node = mapper.convertValue(prlist, JsonNode.class);
1138 String jsonString = mapper.writeValueAsString(prlist);
1139 logger.debug("Json PolicyRefs Data:{}", jsonString);
1140 } catch (Exception e) {
1141 String error = "Error creating JsonNode for policyRefs Data";
1142 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1144 throw new MsoAdapterException(error);
1146 // update parameters
1147 if (pFqdns != null && node != null) {
1148 StringBuilder buf = new StringBuilder();
1150 for (String pf : pFqdns) {
1151 if (!commonUtils.isNullOrEmpty(pf)) {
1152 buf.append(sep).append(pf);
1156 String csl = buf.toString();
1157 stackParams.put("policy_refs", csl);
1158 stackParams.put("policy_refsdata", node);
1161 logger.debug("StackParams updated with policy refs");
1165 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1167 // update parameters
1168 if (rtFqdns != null) {
1169 StringBuilder buf = new StringBuilder();
1171 for (String rtf : rtFqdns) {
1172 if (!commonUtils.isNullOrEmpty(rtf)) {
1173 buf.append(sep).append(rtf);
1177 String csl = buf.toString();
1178 stackParams.put("route_table_refs", csl);
1181 logger.debug("StackParams updated with route_table refs");
1187 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1188 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1189 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1190 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1191 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1192 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1193 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1194 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1195 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1196 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1197 * "host_routes": null }
1199 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1200 throws MsoException {
1202 // Resource Property
1203 List<ContrailSubnet> cslist = new ArrayList<>();
1204 for (Subnet subnet : subnets) {
1205 logger.debug("Input Subnet:{}", subnet);
1206 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1207 logger.debug("Contrail Subnet:{}", cs);
1211 JsonNode node = null;
1213 ObjectMapper mapper = new ObjectMapper();
1214 node = mapper.convertValue(cslist, JsonNode.class);
1215 String jsonString = mapper.writeValueAsString(cslist);
1216 logger.debug("Json Subnet List:{}", jsonString);
1217 } catch (Exception e) {
1218 String error = "Error creating JsonNode from input subnets";
1219 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1220 throw new MsoAdapterException(error);
1222 // update parameters
1224 stackParams.put("subnet_list", node);
1226 // Outputs - All subnets are in one ipam_subnets structure
1227 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1228 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1230 // append outputs in heatTemplate
1231 int outputsIdx = heatTemplate.indexOf("outputs:");
1232 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1233 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1234 return heatTemplate;
1238 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1240 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1241 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1244 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1245 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1246 * " end: %poolend%\n";
1250 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1251 + " value: {get_resource: subnet_%subnetId%}\n";
1255 StringBuilder resourcesBuf = new StringBuilder();
1256 StringBuilder outputsBuf = new StringBuilder();
1257 for (Subnet subnet : subnets) {
1259 // build template for each subnet
1260 curR = resourceTempl;
1261 if (subnet.getSubnetId() != null) {
1262 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1264 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1265 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1266 throw new MsoAdapterException(error);
1269 if (subnet.getSubnetName() != null) {
1270 curR = curR.replace("%name%", subnet.getSubnetName());
1272 curR = curR.replace("%name%", subnet.getSubnetId());
1275 if (subnet.getCidr() != null) {
1276 curR = curR.replace("%cidr%", subnet.getCidr());
1278 String error = "Missing Required cidr for subnet in HEAT Template";
1279 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1280 throw new MsoAdapterException(error);
1283 if (subnet.getIpVersion() != null) {
1284 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1286 if (subnet.getEnableDHCP() != null) {
1287 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1289 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1290 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1293 if (subnet.getAllocationPools() != null) {
1294 StringBuilder tempBuf = new StringBuilder();
1295 tempBuf.append(curR);
1296 tempBuf.append(" allocation_pools:\n");
1297 for (Pool pool : subnet.getAllocationPools()) {
1298 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1299 tempBuf.append(" - start: ");
1300 tempBuf.append(pool.getStart());
1301 tempBuf.append("\n end: ");
1302 tempBuf.append(pool.getEnd());
1303 tempBuf.append("\n");
1306 curR = tempBuf.toString();
1309 resourcesBuf.append(curR);
1312 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1314 outputsBuf.append(curO);
1316 // append resources and outputs in heatTemplate
1317 logger.debug("Tempate initial:{}", heatTemplate);
1318 int outputsIdx = heatTemplate.indexOf("outputs:");
1319 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1320 int resourcesIdx = heatTemplate.indexOf("resources:");
1321 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1323 logger.debug("Template updated with all subnets:{}", heatTemplate);
1324 return heatTemplate;
1328 public Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1330 Map<String, String> sMap = new HashMap<>();
1333 Object obj = outputs.get(key);
1334 ObjectMapper mapper = new ObjectMapper();
1335 String jStr = mapper.writeValueAsString(obj);
1336 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1338 JsonNode rootNode = mapper.readTree(jStr);
1339 if (rootNode != null) {
1340 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1341 logger.debug("Output Subnet Node {}", sNode);
1342 String name = sNode.path("subnet_name").textValue();
1343 String uuid = sNode.path("subnet_uuid").textValue();
1344 String aaiId = name; // default
1345 // try to find aaiId for name in input subnetList
1346 if (subnets != null) {
1347 for (Subnet subnet : subnets) {
1348 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1349 && subnet.getSubnetName().equals(name)) {
1350 aaiId = subnet.getSubnetId();
1355 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1358 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1359 ErrorCode.DataError.getValue());
1361 } catch (Exception e) {
1362 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1363 ErrorCode.DataError.getValue(), e);
1366 logger.debug("Return sMap {}", sMap);
1370 private static String insertStr(String template, String snippet, int index) {
1372 String updatedTemplate;
1374 logger.debug("Index:{} Snippet:{}", index, snippet);
1376 String templateBeg = template.substring(0, index);
1377 String templateEnd = template.substring(index);
1379 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1381 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1382 return updatedTemplate;
1385 public Map<String, String> buildSubnetMap(Map<String, Object> outputs, List<Subnet> subnets, boolean os3template) {
1387 Map<String, String> sMap = new HashMap<>();
1388 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
1389 String key = entry.getKey();
1390 if (key != null && key.startsWith("subnet")) {
1391 if (os3template) // one subnet_id output
1393 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
1395 } else // multiples subnet_%aaid% outputs
1397 String subnetUUId = (String) outputs.get(key);
1398 sMap.put(key.substring("subnet_id_".length()), subnetUUId);