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)
144 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
145 String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
146 Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
147 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
148 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
149 Holder<String> networkFqdn = new Holder<>();
150 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
151 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
152 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
156 public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
157 String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
158 String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
159 Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
160 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
161 Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
162 throws NetworkException {
163 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
164 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
165 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
169 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
170 * the specified cloud and tenant. The tenant must exist at the time this service is called.
172 * If a network with the same name already exists, this can be considered a success or failure, depending on the
173 * value of the 'failIfExists' parameter.
175 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
176 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
177 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
179 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
180 * multiple VLANs on the same physical network.
182 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
183 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
184 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
186 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
187 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
188 * created but the orchestration fails on a subsequent operation.
191 private void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
192 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
193 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
194 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
195 Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
196 Holder<NetworkRollback> rollback) throws NetworkException {
197 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
199 // Will capture execution time for metrics
200 long startTime = System.currentTimeMillis();
202 // Build a default rollback object (no actions performed)
203 NetworkRollback networkRollback = new NetworkRollback();
204 networkRollback.setCloudId(cloudSiteId);
205 networkRollback.setTenantId(tenantId);
206 networkRollback.setMsoRequest(msoRequest);
207 networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
209 // tenant query is not required here.
210 // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
211 // So this is just catching that error in a bit more obvious way up front.
213 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
214 if (!cloudSiteOpt.isPresent()) {
215 String error = String.format(
216 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
217 networkName, cloudSiteId, tenantId);
218 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
219 // Set the detailed error as the Exception 'message'
220 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
224 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
225 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
226 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
228 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
229 if (heatTemplate == null) {
230 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
231 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
232 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
235 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
237 // "Fix" the template if it has CR/LF (getting this from Oracle)
238 String template = heatTemplate.getHeatTemplate();
239 template = template.replaceAll("\r\n", "\n");
241 boolean os3template = false;
242 String os3nw = OS3_NW;
244 os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
246 if (template.contains(os3nw))
249 // First, look up to see if the Network already exists (by name).
250 // For HEAT orchestration of networks, the stack name will always match the network name
251 StackInfo heatStack = null;
253 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
254 } catch (MsoException me) {
255 me.addContext(CREATE_NETWORK_CONTEXT);
256 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ", MessageEnum.RA_QUERY_NETWORK_EXC,
257 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
258 throw new NetworkException(me);
261 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
262 // Stack exists. Return success or error depending on input directive
263 if (failIfExists != null && failIfExists) {
264 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
265 cloudSiteId, tenantId, heatStack.getCanonicalName());
266 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
268 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
270 // Populate the outputs from the existing stack.
271 networkId.value = heatStack.getCanonicalName();
272 Map<String, String> sMap = new HashMap<>();
273 if (heatStack.getOutputs() != null) {
274 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
275 rollback.value = networkRollback; // Default rollback - no updates performed
277 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
279 Map<String, Object> outputs = heatStack.getOutputs();
281 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
282 String key = entry.getKey();
283 if (key != null && key.startsWith("subnet")) {
284 if (os3template) // one subnet_id output
286 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
288 } else // multiples subnet_%aaid% outputs
290 String subnetUUId = (String) outputs.get(key);
291 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
296 subnetIdMap.value = sMap;
297 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
298 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
299 networkName, cloudSiteId, tenantId);
301 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
305 // Ready to deploy the new Network
306 // Build the common set of HEAT template parameters
307 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName, physicalNetworkName,
308 vlans, routeTargets, shared, external, os3template);
310 // Validate (and update) the input parameters against the DB definition
311 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
312 // and inputs were already validated.
314 stackParams = heat.validateStackParams(stackParams, heatTemplate);
315 } catch (IllegalArgumentException e) {
316 String error = "Create Network: Configuration Error: " + e.getMessage();
317 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
318 // Input parameters were not valid
319 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
322 if (subnets != null) {
325 template = mergeSubnetsAIC3(template, subnets, stackParams);
327 template = mergeSubnets(template, subnets);
329 } catch (MsoException me) {
330 me.addContext(CREATE_NETWORK_CONTEXT);
331 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
332 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
333 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
334 throw new NetworkException(me);
338 if (policyFqdns != null && !policyFqdns.isEmpty() && os3template) {
340 mergePolicyRefs(policyFqdns, stackParams);
341 } catch (MsoException me) {
342 me.addContext(CREATE_NETWORK_CONTEXT);
343 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
344 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
345 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
346 throw new NetworkException(me);
350 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
352 mergeRouteTableRefs(routeTableFqdns, stackParams);
353 } catch (MsoException me) {
354 me.addContext(CREATE_NETWORK_CONTEXT);
355 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
356 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
357 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
358 throw new NetworkException(me);
362 // Deploy the network stack
363 // Ignore MsoStackAlreadyExists exception because we already checked.
367 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template, stackParams,
368 true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue(), failIfExists);
369 } catch (MsoException me) {
370 me.addContext(CREATE_NETWORK_CONTEXT);
371 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
372 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
373 throw new NetworkException(me);
376 // Reach this point if createStack is successful.
378 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
379 // and the neutronNetworkId is the network UUID returned in stack outputs.
380 networkId.value = heatStack.getCanonicalName();
381 if (heatStack.getOutputs() != null) {
382 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
384 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
387 Map<String, Object> outputs = heatStack.getOutputs();
388 Map<String, String> sMap = new HashMap<>();
389 if (outputs != null) {
390 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
391 String key = entry.getKey();
392 if (key != null && key.startsWith("subnet")) {
393 if (os3template) // one subnet output expected
395 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
397 } else // multiples subnet_%aaid% outputs allowed
399 String subnetUUId = (String) outputs.get(key);
400 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
404 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
406 subnetIdMap.value = sMap;
408 rollback.value = networkRollback;
409 // Populate remaining rollback info and response parameters.
410 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
411 networkRollback.setNetworkCreated(true);
412 networkRollback.setNetworkType(networkType);
415 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
416 } catch (Exception e) {
417 logger.warn("Exception while updating infra active request", e);
420 logger.debug("Network {} successfully created via HEAT", networkName);
427 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
428 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
429 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
430 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
431 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
432 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
438 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
439 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
440 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
441 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
442 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
443 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
444 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
449 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
450 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
451 * remove a VLAN), but other properties may be updated as well.
453 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
454 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
455 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
457 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
458 * VLANs on the same physical network.
460 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
461 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
462 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
464 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
465 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
466 * a subsequent operation.
468 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
469 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
470 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
471 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
472 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
474 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
475 cloudSiteId, tenantId);
477 // Will capture execution time for metrics
478 long startTime = System.currentTimeMillis();
480 // Build a default rollback object (no actions performed)
481 NetworkRollback networkRollback = new NetworkRollback();
482 networkRollback.setCloudId(cloudSiteId);
483 networkRollback.setTenantId(tenantId);
484 networkRollback.setMsoRequest(msoRequest);
486 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
487 if (!cloudSiteOpt.isPresent()) {
488 String error = String.format(
489 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
490 networkName, cloudSiteId, tenantId);
491 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
492 // Set the detailed error as the Exception 'message'
493 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
498 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
499 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
500 String mode = networkResource.getOrchestrationMode();
501 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
503 // Use an MsoNeutronUtils for all Neutron commands
505 if (NEUTRON_MODE.equals(mode)) {
507 // Verify that the Network exists
508 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
509 NetworkInfo netInfo = null;
511 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
512 } catch (MsoException me) {
513 me.addContext(UPDATE_NETWORK_CONTEXT);
514 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
515 ErrorCode.BusinessProcessError.getValue(), networkId, cloudSiteId, tenantId, me);
516 throw new NetworkException(me);
519 if (netInfo == null) {
520 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
521 cloudSiteId, tenantId);
522 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
523 ErrorCode.BusinessProcessError.getValue(), error);
524 // Does not exist. Throw an exception (can't update a non-existent network)
525 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
527 long updateNetworkStarttime = System.currentTimeMillis();
529 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
530 physicalNetworkName, vlans);
531 } catch (MsoException me) {
532 me.addContext(UPDATE_NETWORK_CONTEXT);
533 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
534 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
535 throw new NetworkException(me);
538 // Add the network ID and previously queried vlans to the rollback object
539 networkRollback.setNetworkId(netInfo.getId());
540 networkRollback.setNeutronNetworkId(netInfo.getId());
541 networkRollback.setNetworkType(networkType);
542 // Save previous parameters
543 networkRollback.setNetworkName(netInfo.getName());
544 networkRollback.setPhysicalNetwork(netInfo.getProvider());
545 networkRollback.setVlans(netInfo.getVlans());
547 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
548 } else if ("HEAT".equals(mode)) {
550 // First, look up to see that the Network already exists.
551 // For Heat-based orchestration, the networkId is the network Stack ID.
552 StackInfo heatStack = null;
554 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
555 } catch (MsoException me) {
556 me.addContext(UPDATE_NETWORK_CONTEXT);
557 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
558 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
559 throw new NetworkException(me);
562 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
563 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
564 cloudSiteId, tenantId);
565 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
567 // Network stack does not exist. Return an error
568 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
571 // Get the previous parameters for rollback
572 Map<String, Object> heatParams = heatStack.getParameters();
574 String previousNetworkName = (String) heatParams.get("network_name");
575 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
577 List<Integer> previousVlans = new ArrayList<>();
578 String vlansParam = (String) heatParams.get(VLANS);
579 if (vlansParam != null) {
580 for (String vlan : vlansParam.split(",")) {
582 previousVlans.add(Integer.parseInt(vlan));
583 } catch (NumberFormatException e) {
584 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
585 ErrorCode.DataError.getValue(), vlansParam, e);
589 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
591 // Ready to deploy the updated Network via Heat
594 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
595 if (heatTemplate == null) {
596 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
597 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
599 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
602 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
604 // "Fix" the template if it has CR/LF (getting this from Oracle)
605 String template = heatTemplate.getHeatTemplate();
606 template = template.replaceAll("\r\n", "\n");
608 boolean os3template = false;
609 String os3nw = OS3_NW;
611 os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
613 if (template.contains(os3nw))
616 // Build the common set of HEAT template parameters
617 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
618 physicalNetworkName, vlans, routeTargets, shared, external, os3template);
620 // Validate (and update) the input parameters against the DB definition
621 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
623 stackParams = heat.validateStackParams(stackParams, heatTemplate);
624 } catch (IllegalArgumentException e) {
625 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
626 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
627 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
630 if (subnets != null) {
633 template = mergeSubnetsAIC3(template, subnets, stackParams);
635 template = mergeSubnets(template, subnets);
637 } catch (MsoException me) {
638 me.addContext(UPDATE_NETWORK_CONTEXT);
639 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
640 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
641 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
642 throw new NetworkException(me);
646 if (policyFqdns != null && os3template) {
648 mergePolicyRefs(policyFqdns, stackParams);
649 } catch (MsoException me) {
650 me.addContext(UPDATE_NETWORK_CONTEXT);
651 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
652 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
653 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
654 throw new NetworkException(me);
658 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
660 mergeRouteTableRefs(routeTableFqdns, stackParams);
661 } catch (MsoException me) {
662 me.addContext(UPDATE_NETWORK_CONTEXT);
663 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
664 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
665 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
666 throw new NetworkException(me);
670 // Update the network stack
671 // Ignore MsoStackNotFound exception because we already checked.
673 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
674 stackParams, true, heatTemplate.getTimeoutMinutes());
675 } catch (MsoException me) {
676 me.addContext(UPDATE_NETWORK_CONTEXT);
677 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
678 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
679 throw new NetworkException(me);
682 Map<String, Object> outputs = heatStack.getOutputs();
683 Map<String, String> sMap = new HashMap<>();
684 if (outputs != null) {
685 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
686 String key = entry.getKey();
687 if (key != null && key.startsWith("subnet")) {
688 if (os3template) // one subnet output expected
690 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
692 } else // multiples subnet_%aaid% outputs allowed
694 String subnetUUId = (String) outputs.get(key);
695 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
700 subnetIdMap.value = sMap;
702 // Reach this point if createStack is successful.
703 // Populate remaining rollback info and response parameters.
704 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
705 if (null != outputs) {
706 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
708 logger.debug("outputs is NULL");
710 networkRollback.setNetworkType(networkType);
711 // Save previous parameters
712 networkRollback.setNetworkName(previousNetworkName);
713 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
714 networkRollback.setVlans(previousVlans);
716 rollback.value = networkRollback;
718 logger.debug("Network {} successfully updated via HEAT", networkId);
724 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
725 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
726 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
727 // Retrieve the Network Resource definition
728 NetworkResource networkResource = null;
729 NetworkResourceCustomization networkCust = null;
730 CollectionNetworkResourceCustomization collectionNetworkCust = null;
731 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
732 if (!commonUtils.isNullOrEmpty(networkType)) {
733 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
736 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
737 if (networkCust == null) {
738 collectionNetworkCust =
739 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
742 if (networkCust != null) {
743 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
745 networkResource = networkCust.getNetworkResource();
746 } else if (collectionNetworkCust != null) {
747 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
748 networkResource = collectionNetworkCust.getNetworkResource();
750 if (networkResource == null) {
751 String error = String.format(
752 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
753 networkType, modelCustomizationUuid);
754 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
756 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
758 logger.debug(LOG_DEBUG_MSG, networkResource);
760 String mode = networkResource.getOrchestrationMode();
761 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
763 // All Networks are orchestrated via HEAT or Neutron
764 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
765 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
766 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
767 ErrorCode.DataError.getValue(), error);
768 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
771 MavenLikeVersioning osV = new MavenLikeVersioning();
772 osV.setVersion(cloudSite.getCloudVersion());
773 if ((osV.isMoreRecentThan(networkResource.getAicVersionMin())
774 || osV.isTheSameVersion(networkResource.getAicVersionMin())) // os
777 && (osV.isTheSameVersion(networkResource.getAicVersionMax())
778 || !(osV.isMoreRecentThan(networkResource.getAicVersionMax())))) // os <= max
780 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
781 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
782 cloudSite.getCloudVersion());
784 String error = String.format(
785 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
786 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
787 cloudSite.getCloudVersion());
788 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
789 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
792 // Validate the Network parameters.
794 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
795 if (!missing.isEmpty()) {
796 String error = "Create Network: Missing parameters: " + missing;
797 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
799 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
802 return networkResource;
806 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
807 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
808 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
809 throws NetworkException {
810 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
811 status, vlans, null, subnetIdMap);
815 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
816 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
817 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
818 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
819 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
820 status, null, routeTargets, subnetIdMap);
824 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
825 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
826 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
828 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
829 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
830 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
831 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
833 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
835 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
836 || commonUtils.isNullOrEmpty(networkNameOrId)) {
838 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
839 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
840 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
843 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
844 if (!cloudSiteOpt.isPresent()) {
845 String error = String.format(
846 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
847 networkNameOrId, cloudSiteId, tenantId);
848 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
849 // Set the detailed error as the Exception 'message'
850 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
853 // Use MsoNeutronUtils for all NEUTRON commands
855 String neutronId = null;
856 // Try Heat first, since networks may be named the same as the Heat stack
857 StackInfo heatStack = null;
859 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
860 } catch (MsoException me) {
861 me.addContext("QueryNetwork");
862 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
863 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
864 throw new NetworkException(me);
867 // Populate the outputs based on the returned Stack information
868 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
869 // Found it. Get the neutronNetworkId for further query
870 Map<String, String> sMap = new HashMap<>();
871 Map<String, Object> outputs = heatStack.getOutputs();
872 if (outputs != null) {
873 neutronId = (String) outputs.get(NETWORK_ID);
875 for (String key : outputs.keySet()) {
876 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
878 String subnetUUId = (String) outputs.get(key);
879 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
880 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
882 Map<String, String> map = getSubnetUUId(key, outputs, null);
888 subnetIdMap.value = sMap;
891 // Query directly against the Neutron Network for the details
892 // no RouteTargets available for ContrailV2 in neutron net-show
893 // networkId is heatStackId
895 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
896 if (netInfo != null) {
897 // Found. Populate the output elements
898 networkExists.value = Boolean.TRUE;
899 if (heatStack != null) {
900 networkId.value = heatStack.getCanonicalName();
902 networkId.value = netInfo.getId();
904 neutronNetworkId.value = netInfo.getId();
905 status.value = netInfo.getStatus();
907 vlans.value = netInfo.getVlans();
909 logger.debug("Network {}, ID = {}{}", networkNameOrId, networkId.value,
910 (",NeutronId = " + neutronNetworkId.value));
912 // Not found. Populate the status fields, leave the rest null
913 networkExists.value = Boolean.FALSE;
914 status.value = NetworkStatus.NOTFOUND;
915 neutronNetworkId.value = null;
917 vlans.value = new ArrayList<>();
919 logger.debug("Network {} not found", networkNameOrId);
921 } catch (MsoException me) {
922 me.addContext("QueryNetwork");
923 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
924 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
925 throw new NetworkException(me);
931 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
934 * If the network is not found, it is treated as a success.
936 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
937 * network orchestration mode for each network type is declared in its catalog definition.
939 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
940 * networkId should be the Neutron network UUID.
942 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
943 * will require manual fallout in the client.
946 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
947 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
948 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
949 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
950 || commonUtils.isNullOrEmpty(networkId)) {
951 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
952 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
953 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
956 // Retrieve the Network Resource definition
957 NetworkResource networkResource = null;
958 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
959 if (!commonUtils.isNullOrEmpty(networkType)) {
960 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
963 NetworkResourceCustomization nrc =
964 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
966 networkResource = nrc.getNetworkResource();
970 int timeoutMinutes = 118;
971 if (networkResource != null) {
972 logger.debug(LOG_DEBUG_MSG, networkResource.toString());
973 networkResource.getHeatTemplate().getTimeoutMinutes();
974 HeatTemplate heat = networkResource.getHeatTemplate();
975 if (heat != null && heat.getTimeoutMinutes() != null) {
976 if (heat.getTimeoutMinutes() < 118) {
977 timeoutMinutes = heat.getTimeoutMinutes();
983 StackInfo stack = heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, timeoutMinutes);
984 networkDeleted.value = stack.isOperationPerformed();
985 } catch (MsoException me) {
986 me.addContext("DeleteNetwork");
987 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
988 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
989 throw new NetworkException(me);
993 heat.updateResourceStatus(msoRequest.getRequestId(),
994 networkDeleted.value ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
995 } catch (Exception e) {
996 logger.warn("Exception while updating infra active request", e);
1001 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1002 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1003 * to undo the creation.
1005 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1009 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1010 if (rollback == null) {
1011 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1015 // Get the elements of the VnfRollback object for easier access
1016 String cloudSiteId = rollback.getCloudId();
1017 String tenantId = rollback.getTenantId();
1018 String networkId = rollback.getNetworkStackId();
1020 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1022 if (rollback.getNetworkCreated()) {
1024 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
1025 } catch (MsoException me) {
1026 me.addContext("RollbackNetwork");
1027 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1028 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
1029 cloudSiteId, tenantId, me);
1030 throw new NetworkException(me);
1036 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1037 List<Integer> vlans, List<RouteTarget> routeTargets) {
1039 StringBuilder missing = new StringBuilder();
1040 if (commonUtils.isNullOrEmpty(networkName)) {
1041 missing.append("networkName");
1045 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1046 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1047 missing.append(sep).append("physicalNetworkName");
1050 if (vlans == null || vlans.isEmpty()) {
1051 missing.append(sep).append(VLANS);
1055 return missing.toString();
1058 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1059 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1060 boolean os3template) {
1061 // Build the common set of HEAT template parameters
1062 Map<String, Object> stackParams = new HashMap<>();
1063 stackParams.put("network_name", networkName);
1065 if (neutronNetworkType == NetworkType.PROVIDER) {
1066 // For Provider type
1067 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1068 stackParams.put("vlan", vlans.get(0).toString());
1069 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1070 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1071 // It supports all ProviderNet properties except segmentation_id, and adds a
1072 // comma-separated-list of VLANs as a "segments" property.
1073 // Note that this does not match the Neutron definition of Multi-Provider network,
1074 // which contains a list of 'segments', each having physical_network, network_type,
1075 // and segmentation_id.
1076 StringBuilder buf = new StringBuilder();
1078 for (Integer vlan : vlans) {
1079 buf.append(sep).append(vlan.toString());
1082 String csl = buf.toString();
1084 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1085 stackParams.put(VLANS, csl);
1087 if (routeTargets != null) {
1089 String rtGlobal = "";
1090 String rtImport = "";
1091 String rtExport = "";
1093 for (RouteTarget rt : routeTargets) {
1094 boolean rtIsNull = false;
1096 String routeTarget = rt.getRouteTarget();
1097 String routeTargetRole = rt.getRouteTargetRole();
1098 logger.debug("Checking for an actually null route target: {}", rt);
1099 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1101 if (routeTargetRole == null || routeTargetRole.equals("")
1102 || routeTargetRole.equalsIgnoreCase("null"))
1108 logger.debug("Input RT:{}", rt);
1109 String role = rt.getRouteTargetRole();
1110 String rtValue = rt.getRouteTarget();
1112 if ("IMPORT".equalsIgnoreCase(role)) {
1113 sep = rtImport.isEmpty() ? "" : ",";
1114 rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1115 } else if ("EXPORT".equalsIgnoreCase(role)) {
1116 sep = rtExport.isEmpty() ? "" : ",";
1117 rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1118 } else // covers BOTH, empty etc
1120 sep = rtGlobal.isEmpty() ? "" : ",";
1121 rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1127 if (!rtImport.isEmpty()) {
1128 stackParams.put("route_targets_import", rtImport);
1130 if (!rtExport.isEmpty()) {
1131 stackParams.put("route_targets_export", rtExport);
1133 if (!rtGlobal.isEmpty()) {
1134 stackParams.put("route_targets", rtGlobal);
1137 if (commonUtils.isNullOrEmpty(shared)) {
1138 stackParams.put("shared", "False");
1140 stackParams.put("shared", shared);
1142 if (commonUtils.isNullOrEmpty(external)) {
1143 stackParams.put("external", "False");
1145 stackParams.put("external", external);
1153 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1154 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1155 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1156 * "network_policy_refs_data_sequence_minor": "0" } } ]
1158 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1159 // Resource Property
1160 List<ContrailPolicyRef> prlist = new ArrayList<>();
1163 if (pFqdns != null) {
1164 for (String pf : pFqdns) {
1165 if (!commonUtils.isNullOrEmpty(pf)) {
1166 ContrailPolicyRef pr = new ContrailPolicyRef();
1167 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1170 logger.debug("Contrail PolicyRefs Data:{}", pr);
1175 String error = "Null pFqdns at start of mergePolicyRefs";
1176 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1178 throw new MsoAdapterException(error);
1181 JsonNode node = null;
1183 ObjectMapper mapper = new ObjectMapper();
1184 node = mapper.convertValue(prlist, JsonNode.class);
1185 String jsonString = mapper.writeValueAsString(prlist);
1186 logger.debug("Json PolicyRefs Data:{}", jsonString);
1187 } catch (Exception e) {
1188 String error = "Error creating JsonNode for policyRefs Data";
1189 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1191 throw new MsoAdapterException(error);
1193 // update parameters
1194 if (pFqdns != null && node != null) {
1195 StringBuilder buf = new StringBuilder();
1197 for (String pf : pFqdns) {
1198 if (!commonUtils.isNullOrEmpty(pf)) {
1199 buf.append(sep).append(pf);
1203 String csl = buf.toString();
1204 stackParams.put("policy_refs", csl);
1205 stackParams.put("policy_refsdata", node);
1208 logger.debug("StackParams updated with policy refs");
1212 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1214 // update parameters
1215 if (rtFqdns != null) {
1216 StringBuilder buf = new StringBuilder();
1218 for (String rtf : rtFqdns) {
1219 if (!commonUtils.isNullOrEmpty(rtf)) {
1220 buf.append(sep).append(rtf);
1224 String csl = buf.toString();
1225 stackParams.put("route_table_refs", csl);
1228 logger.debug("StackParams updated with route_table refs");
1234 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1235 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1236 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1237 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1238 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1239 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1240 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1241 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1242 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1243 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1244 * "host_routes": null }
1246 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1247 throws MsoException {
1249 // Resource Property
1250 List<ContrailSubnet> cslist = new ArrayList<>();
1251 for (Subnet subnet : subnets) {
1252 logger.debug("Input Subnet:{}", subnet);
1253 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1254 logger.debug("Contrail Subnet:{}", cs);
1258 JsonNode node = null;
1260 ObjectMapper mapper = new ObjectMapper();
1261 node = mapper.convertValue(cslist, JsonNode.class);
1262 String jsonString = mapper.writeValueAsString(cslist);
1263 logger.debug("Json Subnet List:{}", jsonString);
1264 } catch (Exception e) {
1265 String error = "Error creating JsonNode from input subnets";
1266 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1267 throw new MsoAdapterException(error);
1269 // update parameters
1271 stackParams.put("subnet_list", node);
1273 // Outputs - All subnets are in one ipam_subnets structure
1274 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1275 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1277 // append outputs in heatTemplate
1278 int outputsIdx = heatTemplate.indexOf("outputs:");
1279 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1280 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1281 return heatTemplate;
1285 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1287 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1288 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1291 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1292 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1293 * " end: %poolend%\n";
1297 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1298 + " value: {get_resource: subnet_%subnetId%}\n";
1302 StringBuilder resourcesBuf = new StringBuilder();
1303 StringBuilder outputsBuf = new StringBuilder();
1304 for (Subnet subnet : subnets) {
1306 // build template for each subnet
1307 curR = resourceTempl;
1308 if (subnet.getSubnetId() != null) {
1309 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1311 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1312 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1313 throw new MsoAdapterException(error);
1316 if (subnet.getSubnetName() != null) {
1317 curR = curR.replace("%name%", subnet.getSubnetName());
1319 curR = curR.replace("%name%", subnet.getSubnetId());
1322 if (subnet.getCidr() != null) {
1323 curR = curR.replace("%cidr%", subnet.getCidr());
1325 String error = "Missing Required cidr 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.getIpVersion() != null) {
1331 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1333 if (subnet.getEnableDHCP() != null) {
1334 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1336 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1337 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1340 if (subnet.getAllocationPools() != null) {
1341 StringBuilder tempBuf = new StringBuilder();
1342 tempBuf.append(curR);
1343 tempBuf.append(" allocation_pools:\n");
1344 for (Pool pool : subnet.getAllocationPools()) {
1345 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1346 tempBuf.append(" - start: ");
1347 tempBuf.append(pool.getStart());
1348 tempBuf.append("\n end: ");
1349 tempBuf.append(pool.getEnd());
1350 tempBuf.append("\n");
1353 curR = tempBuf.toString();
1356 resourcesBuf.append(curR);
1359 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1361 outputsBuf.append(curO);
1363 // append resources and outputs in heatTemplate
1364 logger.debug("Tempate initial:{}", heatTemplate);
1365 int outputsIdx = heatTemplate.indexOf("outputs:");
1366 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1367 int resourcesIdx = heatTemplate.indexOf("resources:");
1368 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1370 logger.debug("Template updated with all subnets:{}", heatTemplate);
1371 return heatTemplate;
1374 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1376 Map<String, String> sMap = new HashMap<>();
1379 Object obj = outputs.get(key);
1380 ObjectMapper mapper = new ObjectMapper();
1381 String jStr = mapper.writeValueAsString(obj);
1382 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1384 JsonNode rootNode = mapper.readTree(jStr);
1385 if (rootNode != null) {
1386 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1387 logger.debug("Output Subnet Node {}", sNode);
1388 String name = sNode.path("subnet_name").textValue();
1389 String uuid = sNode.path("subnet_uuid").textValue();
1390 String aaiId = name; // default
1391 // try to find aaiId for name in input subnetList
1392 if (subnets != null) {
1393 for (Subnet subnet : subnets) {
1394 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1395 && subnet.getSubnetName().equals(name)) {
1396 aaiId = subnet.getSubnetId();
1401 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1404 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1405 ErrorCode.DataError.getValue());
1407 } catch (Exception e) {
1408 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1409 ErrorCode.DataError.getValue(), e);
1412 logger.debug("Return sMap {}", sMap);
1416 private static String insertStr(String template, String snippet, int index) {
1418 String updatedTemplate;
1420 logger.debug("Index:{} Snippet:{}", index, snippet);
1422 String templateBeg = template.substring(0, index);
1423 String templateEnd = template.substring(index);
1425 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1427 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1428 return updatedTemplate;