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 // Retrieve the Network Resource definition
966 NetworkResource networkResource = null;
967 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
968 if (!commonUtils.isNullOrEmpty(networkType)) {
969 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
972 NetworkResourceCustomization nrc =
973 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
975 networkResource = nrc.getNetworkResource();
979 int timeoutMinutes = 118;
980 if (networkResource != null) {
981 logger.debug(LOG_DEBUG_MSG, networkResource.toString());
982 networkResource.getHeatTemplate().getTimeoutMinutes();
983 HeatTemplate heat = networkResource.getHeatTemplate();
984 if (heat != null && heat.getTimeoutMinutes() != null) {
985 if (heat.getTimeoutMinutes() < 118) {
986 timeoutMinutes = heat.getTimeoutMinutes();
993 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, timeoutMinutes);
994 networkDeleted.value = stack.isOperationPerformed();
995 } catch (MsoException me) {
996 me.addContext("DeleteNetwork");
997 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
998 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
999 throw new NetworkException(me);
1003 heat.updateResourceStatus(msoRequest.getRequestId(),
1004 networkDeleted.value ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
1005 } catch (Exception e) {
1006 logger.warn("Exception while updating infra active request", e);
1011 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1012 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1013 * to undo the creation.
1015 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1019 public void rollbackNetwork(NetworkRollback rollback, Boolean pollForCompletion) throws NetworkException {
1020 if (rollback == null) {
1021 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1025 if (pollForCompletion == null) {
1026 pollForCompletion = true;
1029 // Get the elements of the VnfRollback object for easier access
1030 String cloudSiteId = rollback.getCloudId();
1031 String tenantId = rollback.getTenantId();
1032 String networkId = rollback.getNetworkStackId();
1034 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1036 if (rollback.getNetworkCreated()) {
1038 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, pollForCompletion, 120);
1039 } catch (MsoException me) {
1040 me.addContext("RollbackNetwork");
1041 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1042 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
1043 cloudSiteId, tenantId, me);
1044 throw new NetworkException(me);
1050 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1051 List<Integer> vlans, List<RouteTarget> routeTargets) {
1053 StringBuilder missing = new StringBuilder();
1054 if (commonUtils.isNullOrEmpty(networkName)) {
1055 missing.append("networkName");
1059 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1060 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1061 missing.append(sep).append("physicalNetworkName");
1064 if (vlans == null || vlans.isEmpty()) {
1065 missing.append(sep).append(VLANS);
1069 return missing.toString();
1072 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1073 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1074 boolean os3template) {
1075 // Build the common set of HEAT template parameters
1076 Map<String, Object> stackParams = new HashMap<>();
1077 stackParams.put("network_name", networkName);
1079 if (neutronNetworkType == NetworkType.PROVIDER) {
1080 // For Provider type
1081 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1082 stackParams.put("vlan", vlans.get(0).toString());
1083 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1084 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1085 // It supports all ProviderNet properties except segmentation_id, and adds a
1086 // comma-separated-list of VLANs as a "segments" property.
1087 // Note that this does not match the Neutron definition of Multi-Provider network,
1088 // which contains a list of 'segments', each having physical_network, network_type,
1089 // and segmentation_id.
1090 StringBuilder buf = new StringBuilder();
1092 for (Integer vlan : vlans) {
1093 buf.append(sep).append(vlan.toString());
1096 String csl = buf.toString();
1098 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1099 stackParams.put(VLANS, csl);
1101 if (routeTargets != null) {
1103 String rtGlobal = "";
1104 String rtImport = "";
1105 String rtExport = "";
1107 for (RouteTarget rt : routeTargets) {
1108 boolean rtIsNull = false;
1110 String routeTarget = rt.getRouteTarget();
1111 String routeTargetRole = rt.getRouteTargetRole();
1112 logger.debug("Checking for an actually null route target: {}", rt);
1113 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1115 if (routeTargetRole == null || routeTargetRole.equals("")
1116 || routeTargetRole.equalsIgnoreCase("null"))
1122 logger.debug("Input RT:{}", rt);
1123 String role = rt.getRouteTargetRole();
1124 String rtValue = rt.getRouteTarget();
1126 if ("IMPORT".equalsIgnoreCase(role)) {
1127 sep = rtImport.isEmpty() ? "" : ",";
1128 rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1129 } else if ("EXPORT".equalsIgnoreCase(role)) {
1130 sep = rtExport.isEmpty() ? "" : ",";
1131 rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1132 } else // covers BOTH, empty etc
1134 sep = rtGlobal.isEmpty() ? "" : ",";
1135 rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1141 if (!rtImport.isEmpty()) {
1142 stackParams.put("route_targets_import", rtImport);
1144 if (!rtExport.isEmpty()) {
1145 stackParams.put("route_targets_export", rtExport);
1147 if (!rtGlobal.isEmpty()) {
1148 stackParams.put("route_targets", rtGlobal);
1151 if (commonUtils.isNullOrEmpty(shared)) {
1152 stackParams.put("shared", "False");
1154 stackParams.put("shared", shared);
1156 if (commonUtils.isNullOrEmpty(external)) {
1157 stackParams.put("external", "False");
1159 stackParams.put("external", external);
1167 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1168 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1169 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1170 * "network_policy_refs_data_sequence_minor": "0" } } ]
1172 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1173 // Resource Property
1174 List<ContrailPolicyRef> prlist = new ArrayList<>();
1177 if (pFqdns != null) {
1178 for (String pf : pFqdns) {
1179 if (!commonUtils.isNullOrEmpty(pf)) {
1180 ContrailPolicyRef pr = new ContrailPolicyRef();
1181 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1184 logger.debug("Contrail PolicyRefs Data:{}", pr);
1189 String error = "Null pFqdns at start of mergePolicyRefs";
1190 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1192 throw new MsoAdapterException(error);
1195 JsonNode node = null;
1197 ObjectMapper mapper = new ObjectMapper();
1198 node = mapper.convertValue(prlist, JsonNode.class);
1199 String jsonString = mapper.writeValueAsString(prlist);
1200 logger.debug("Json PolicyRefs Data:{}", jsonString);
1201 } catch (Exception e) {
1202 String error = "Error creating JsonNode for policyRefs Data";
1203 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1205 throw new MsoAdapterException(error);
1207 // update parameters
1208 if (pFqdns != null && node != null) {
1209 StringBuilder buf = new StringBuilder();
1211 for (String pf : pFqdns) {
1212 if (!commonUtils.isNullOrEmpty(pf)) {
1213 buf.append(sep).append(pf);
1217 String csl = buf.toString();
1218 stackParams.put("policy_refs", csl);
1219 stackParams.put("policy_refsdata", node);
1222 logger.debug("StackParams updated with policy refs");
1226 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1228 // update parameters
1229 if (rtFqdns != null) {
1230 StringBuilder buf = new StringBuilder();
1232 for (String rtf : rtFqdns) {
1233 if (!commonUtils.isNullOrEmpty(rtf)) {
1234 buf.append(sep).append(rtf);
1238 String csl = buf.toString();
1239 stackParams.put("route_table_refs", csl);
1242 logger.debug("StackParams updated with route_table refs");
1248 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1249 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1250 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1251 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1252 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1253 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1254 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1255 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1256 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1257 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1258 * "host_routes": null }
1260 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1261 throws MsoException {
1263 // Resource Property
1264 List<ContrailSubnet> cslist = new ArrayList<>();
1265 for (Subnet subnet : subnets) {
1266 logger.debug("Input Subnet:{}", subnet);
1267 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1268 logger.debug("Contrail Subnet:{}", cs);
1272 JsonNode node = null;
1274 ObjectMapper mapper = new ObjectMapper();
1275 node = mapper.convertValue(cslist, JsonNode.class);
1276 String jsonString = mapper.writeValueAsString(cslist);
1277 logger.debug("Json Subnet List:{}", jsonString);
1278 } catch (Exception e) {
1279 String error = "Error creating JsonNode from input subnets";
1280 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1281 throw new MsoAdapterException(error);
1283 // update parameters
1285 stackParams.put("subnet_list", node);
1287 // Outputs - All subnets are in one ipam_subnets structure
1288 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1289 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1291 // append outputs in heatTemplate
1292 int outputsIdx = heatTemplate.indexOf("outputs:");
1293 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1294 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1295 return heatTemplate;
1299 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1301 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1302 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1305 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1306 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1307 * " end: %poolend%\n";
1311 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1312 + " value: {get_resource: subnet_%subnetId%}\n";
1316 StringBuilder resourcesBuf = new StringBuilder();
1317 StringBuilder outputsBuf = new StringBuilder();
1318 for (Subnet subnet : subnets) {
1320 // build template for each subnet
1321 curR = resourceTempl;
1322 if (subnet.getSubnetId() != null) {
1323 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1325 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1326 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1327 throw new MsoAdapterException(error);
1330 if (subnet.getSubnetName() != null) {
1331 curR = curR.replace("%name%", subnet.getSubnetName());
1333 curR = curR.replace("%name%", subnet.getSubnetId());
1336 if (subnet.getCidr() != null) {
1337 curR = curR.replace("%cidr%", subnet.getCidr());
1339 String error = "Missing Required cidr for subnet in HEAT Template";
1340 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1341 throw new MsoAdapterException(error);
1344 if (subnet.getIpVersion() != null) {
1345 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1347 if (subnet.getEnableDHCP() != null) {
1348 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1350 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1351 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1354 if (subnet.getAllocationPools() != null) {
1355 StringBuilder tempBuf = new StringBuilder();
1356 tempBuf.append(curR);
1357 tempBuf.append(" allocation_pools:\n");
1358 for (Pool pool : subnet.getAllocationPools()) {
1359 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1360 tempBuf.append(" - start: ");
1361 tempBuf.append(pool.getStart());
1362 tempBuf.append("\n end: ");
1363 tempBuf.append(pool.getEnd());
1364 tempBuf.append("\n");
1367 curR = tempBuf.toString();
1370 resourcesBuf.append(curR);
1373 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1375 outputsBuf.append(curO);
1377 // append resources and outputs in heatTemplate
1378 logger.debug("Tempate initial:{}", heatTemplate);
1379 int outputsIdx = heatTemplate.indexOf("outputs:");
1380 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1381 int resourcesIdx = heatTemplate.indexOf("resources:");
1382 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1384 logger.debug("Template updated with all subnets:{}", heatTemplate);
1385 return heatTemplate;
1388 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1390 Map<String, String> sMap = new HashMap<>();
1393 Object obj = outputs.get(key);
1394 ObjectMapper mapper = new ObjectMapper();
1395 String jStr = mapper.writeValueAsString(obj);
1396 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1398 JsonNode rootNode = mapper.readTree(jStr);
1399 if (rootNode != null) {
1400 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1401 logger.debug("Output Subnet Node {}", sNode);
1402 String name = sNode.path("subnet_name").textValue();
1403 String uuid = sNode.path("subnet_uuid").textValue();
1404 String aaiId = name; // default
1405 // try to find aaiId for name in input subnetList
1406 if (subnets != null) {
1407 for (Subnet subnet : subnets) {
1408 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1409 && subnet.getSubnetName().equals(name)) {
1410 aaiId = subnet.getSubnetId();
1415 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1418 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1419 ErrorCode.DataError.getValue());
1421 } catch (Exception e) {
1422 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1423 ErrorCode.DataError.getValue(), e);
1426 logger.debug("Return sMap {}", sMap);
1430 private static String insertStr(String template, String snippet, int index) {
1432 String updatedTemplate;
1434 logger.debug("Index:{} Snippet:{}", index, snippet);
1436 String templateBeg = template.substring(0, index);
1437 String templateEnd = template.substring(index);
1439 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1441 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1442 return updatedTemplate;