2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7 * ================================================================================
8 * Modifications Copyright (C) 2018 IBM.
9 * Modifications Copyright (c) 2019 Samsung
10 * ================================================================================
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 * ============LICENSE_END=========================================================
25 package org.onap.so.adapters.network;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
31 import java.util.Optional;
32 import javax.jws.WebService;
33 import javax.xml.ws.Holder;
34 import org.apache.commons.lang3.mutable.MutableBoolean;
35 import org.onap.logging.filter.base.ErrorCode;
36 import org.onap.so.adapters.network.beans.ContrailPolicyRef;
37 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
38 import org.onap.so.adapters.network.beans.ContrailSubnet;
39 import org.onap.so.adapters.network.exceptions.NetworkException;
40 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
41 import org.onap.so.cloud.CloudConfig;
42 import org.onap.so.db.catalog.beans.CloudSite;
43 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
44 import org.onap.so.db.catalog.beans.HeatTemplate;
45 import org.onap.so.db.catalog.beans.NetworkResource;
46 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
47 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
48 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
49 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
50 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
51 import org.onap.so.entity.MsoRequest;
52 import org.onap.so.logger.LoggingAnchor;
53 import org.onap.so.logger.MessageEnum;
54 import org.onap.so.openstack.beans.HeatStatus;
55 import org.onap.so.openstack.beans.NetworkInfo;
56 import org.onap.so.openstack.beans.NetworkRollback;
57 import org.onap.so.openstack.beans.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 {
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 CREATE_NETWORK_CONTEXT = "CreateNetwork";
90 private static final String NEUTRON_MODE = "NEUTRON";
91 private static final String CLOUD_OWNER = "CloudOwner";
92 private static final String LOG_DEBUG_MSG = "Got Network definition from Catalog: {}";
93 private static final String NETWORK_EXIST_STATUS_MESSAGE =
94 "The network was found to already exist, thus no new network was created in the cloud via this request";
95 private static final String NETWORK_CREATED_STATUS_MESSAGE =
96 "The new network was successfully created in the cloud";
97 private static final String NETWORK_NOT_EXIST_STATUS_MESSAGE =
98 "The network was not found, thus no network was deleted in the cloud via this request";
99 private static final String NETWORK_DELETED_STATUS_MESSAGE = "The network was successfully deleted in the cloud";
101 private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
104 private CloudConfig cloudConfig;
106 private Environment environment;
108 private MsoNeutronUtils neutron;
110 private MsoHeatUtils heat;
112 private MsoHeatUtilsWithUpdate heatWithUpdate;
114 private MsoCommonUtils commonUtils;
117 private NetworkResourceCustomizationRepository networkCustomRepo;
120 private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
123 private NetworkResourceRepository networkResourceRepo;
125 public MsoNetworkAdapterImpl() {}
128 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
129 * the specified cloud and tenant. The tenant must exist at the time this service is called.
131 * If a network with the same name already exists, this can be considered a success or failure, depending on the
132 * value of the 'failIfExists' parameter.
134 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
135 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
136 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
138 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
139 * multiple VLANs on the same physical network.
141 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
142 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
143 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
145 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
146 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
147 * created but the orchestration fails on a subsequent operation.
150 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
151 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
152 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
153 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> stackId,
154 MutableBoolean isOs3Nw) throws NetworkException {
155 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
157 // Will capture execution time for metrics
158 long startTime = System.currentTimeMillis();
160 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
161 if (!cloudSiteOpt.isPresent()) {
162 String error = String.format(
163 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
164 networkName, cloudSiteId, tenantId);
165 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
166 // Set the detailed error as the Exception 'message'
167 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
171 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
172 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
173 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
175 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
176 if (heatTemplate == null) {
177 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
178 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
179 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
182 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
184 // "Fix" the template if it has CR/LF (getting this from Oracle)
185 String template = heatTemplate.getHeatTemplate();
186 template = template.replaceAll("\r\n", "\n");
188 boolean os3template = false;
190 String os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
192 if (template.contains(os3nw))
195 isOs3Nw.setValue(os3template);
196 // First, look up to see if the Network already exists (by name).
197 // For HEAT orchestration of networks, the stack name will always match the network name
198 StackInfo heatStack = null;
200 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
201 } catch (MsoException me) {
202 me.addContext(CREATE_NETWORK_CONTEXT);
203 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ", MessageEnum.RA_QUERY_NETWORK_EXC,
204 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
205 throw new NetworkException(me);
208 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
209 // Stack exists. Return success or error depending on input directive
210 if (failIfExists != null && failIfExists) {
211 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
212 cloudSiteId, tenantId, heatStack.getCanonicalName());
213 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
215 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
217 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
218 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
219 networkName, cloudSiteId, tenantId);
221 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
225 // Ready to deploy the new Network
226 // Build the common set of HEAT template parameters
227 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName, physicalNetworkName,
228 vlans, routeTargets, shared, external, os3template);
230 // Validate (and update) the input parameters against the DB definition
231 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
232 // and inputs were already validated.
234 stackParams = heat.validateStackParams(stackParams, heatTemplate);
235 } catch (IllegalArgumentException e) {
236 String error = "Create Network: Configuration Error: " + e.getMessage();
237 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
238 // Input parameters were not valid
239 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
242 if (subnets != null) {
245 template = mergeSubnetsAIC3(template, subnets, stackParams);
247 template = mergeSubnets(template, subnets);
249 } catch (MsoException me) {
250 me.addContext(CREATE_NETWORK_CONTEXT);
251 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
252 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
253 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
254 throw new NetworkException(me);
258 if (policyFqdns != null && !policyFqdns.isEmpty() && os3template) {
260 mergePolicyRefs(policyFqdns, stackParams);
261 } catch (MsoException me) {
262 me.addContext(CREATE_NETWORK_CONTEXT);
263 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
264 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
265 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
266 throw new NetworkException(me);
270 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
272 mergeRouteTableRefs(routeTableFqdns, stackParams);
273 } catch (MsoException me) {
274 me.addContext(CREATE_NETWORK_CONTEXT);
275 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
276 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
277 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
278 throw new NetworkException(me);
282 // Deploy the network stack
283 // Ignore MsoStackAlreadyExists exception because we already checked.
287 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template, stackParams,
288 false, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue(), failIfExists);
289 } catch (MsoException me) {
290 me.addContext(CREATE_NETWORK_CONTEXT);
291 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
292 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
293 throw new NetworkException(me);
296 // Reach this point if createStack is successful.
297 stackId.value = heatStack.getCanonicalName();
300 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
301 } catch (Exception e) {
302 logger.warn("Exception while updating infra active request", e);
305 logger.debug("Network {} successfully created via HEAT", networkName);
311 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
312 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
313 * remove a VLAN), but other properties may be updated as well.
315 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
316 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
317 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
319 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
320 * VLANs on the same physical network.
322 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
323 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
324 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
326 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
327 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
328 * a subsequent operation.
330 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
331 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
332 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
333 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> stackId)
334 throws NetworkException {
336 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
337 cloudSiteId, tenantId);
339 // Will capture execution time for metrics
340 long startTime = System.currentTimeMillis();
342 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
343 if (!cloudSiteOpt.isPresent()) {
344 String error = String.format(
345 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
346 networkName, cloudSiteId, tenantId);
347 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
348 // Set the detailed error as the Exception 'message'
349 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
352 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
353 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
354 String mode = networkResource.getOrchestrationMode();
355 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
357 // Use an MsoNeutronUtils for all Neutron commands
359 if (NEUTRON_MODE.equals(mode)) {
361 // Verify that the Network exists
362 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
363 NetworkInfo netInfo = null;
365 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
366 } catch (MsoException me) {
367 me.addContext(UPDATE_NETWORK_CONTEXT);
368 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
369 ErrorCode.BusinessProcessError.getValue(), networkId, cloudSiteId, tenantId, me);
370 throw new NetworkException(me);
373 if (netInfo == null) {
374 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
375 cloudSiteId, tenantId);
376 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
377 ErrorCode.BusinessProcessError.getValue(), error);
378 // Does not exist. Throw an exception (can't update a non-existent network)
379 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
382 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
383 physicalNetworkName, vlans);
384 } catch (MsoException me) {
385 me.addContext(UPDATE_NETWORK_CONTEXT);
386 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
387 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
388 throw new NetworkException(me);
391 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
392 } else if ("HEAT".equals(mode)) {
394 // First, look up to see that the Network already exists.
395 // For Heat-based orchestration, the networkId is the network Stack ID.
396 StackInfo heatStack = null;
398 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
399 } catch (MsoException me) {
400 me.addContext(UPDATE_NETWORK_CONTEXT);
401 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
402 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
403 throw new NetworkException(me);
406 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
407 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
408 cloudSiteId, tenantId);
409 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
411 // Network stack does not exist. Return an error
412 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
415 // Get the previous parameters for rollback
416 Map<String, Object> heatParams = heatStack.getParameters();
418 List<Integer> previousVlans = new ArrayList<>();
419 String vlansParam = (String) heatParams.get(VLANS);
420 if (vlansParam != null) {
421 for (String vlan : vlansParam.split(",")) {
423 previousVlans.add(Integer.parseInt(vlan));
424 } catch (NumberFormatException e) {
425 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
426 ErrorCode.DataError.getValue(), vlansParam, e);
430 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
432 // Ready to deploy the updated Network via Heat
433 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
434 if (heatTemplate == null) {
435 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
436 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
438 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
441 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
443 // "Fix" the template if it has CR/LF (getting this from Oracle)
444 String template = heatTemplate.getHeatTemplate();
445 template = template.replaceAll("\r\n", "\n");
447 boolean os3template = false;
448 String os3nw = OS3_NW;
450 os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
452 if (template.contains(os3nw))
455 // Build the common set of HEAT template parameters
456 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
457 physicalNetworkName, vlans, routeTargets, shared, external, os3template);
459 // Validate (and update) the input parameters against the DB definition
460 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
462 stackParams = heat.validateStackParams(stackParams, heatTemplate);
463 } catch (IllegalArgumentException e) {
464 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
465 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
466 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
469 if (subnets != null) {
472 template = mergeSubnetsAIC3(template, subnets, stackParams);
474 template = mergeSubnets(template, subnets);
476 } catch (MsoException me) {
477 me.addContext(UPDATE_NETWORK_CONTEXT);
478 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
479 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
480 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
481 throw new NetworkException(me);
485 if (policyFqdns != null && os3template) {
487 mergePolicyRefs(policyFqdns, stackParams);
488 } catch (MsoException me) {
489 me.addContext(UPDATE_NETWORK_CONTEXT);
490 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
491 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
492 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
493 throw new NetworkException(me);
497 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
499 mergeRouteTableRefs(routeTableFqdns, stackParams);
500 } catch (MsoException me) {
501 me.addContext(UPDATE_NETWORK_CONTEXT);
502 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
503 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
504 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
505 throw new NetworkException(me);
509 // Update the network stack
510 // Ignore MsoStackNotFound exception because we already checked.
512 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
513 stackParams, false, heatTemplate.getTimeoutMinutes());
514 } catch (MsoException me) {
515 me.addContext(UPDATE_NETWORK_CONTEXT);
516 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
517 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
518 throw new NetworkException(me);
521 stackId.value = heatStack.getCanonicalName();
523 logger.debug("Network {} successfully updated via HEAT", networkId);
529 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
530 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
531 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
532 // Retrieve the Network Resource definition
533 NetworkResource networkResource = null;
534 NetworkResourceCustomization networkCust = null;
535 CollectionNetworkResourceCustomization collectionNetworkCust = null;
536 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
537 if (!commonUtils.isNullOrEmpty(networkType)) {
538 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
541 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
542 if (networkCust == null) {
543 collectionNetworkCust =
544 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
547 if (networkCust != null) {
548 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
550 networkResource = networkCust.getNetworkResource();
551 } else if (collectionNetworkCust != null) {
552 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
553 networkResource = collectionNetworkCust.getNetworkResource();
555 if (networkResource == null) {
556 String error = String.format(
557 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
558 networkType, modelCustomizationUuid);
559 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
561 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
563 logger.debug(LOG_DEBUG_MSG, networkResource);
565 String mode = networkResource.getOrchestrationMode();
566 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
568 // All Networks are orchestrated via HEAT or Neutron
569 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
570 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
571 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
572 ErrorCode.DataError.getValue(), error);
573 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
576 MavenLikeVersioning osV = new MavenLikeVersioning();
577 osV.setVersion(cloudSite.getCloudVersion());
578 if ((osV.isMoreRecentThan(networkResource.getAicVersionMin())
579 || osV.isTheSameVersion(networkResource.getAicVersionMin())) // os
582 && (osV.isTheSameVersion(networkResource.getAicVersionMax())
583 || !(osV.isMoreRecentThan(networkResource.getAicVersionMax())))) // os <= max
585 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
586 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
587 cloudSite.getCloudVersion());
589 String error = String.format(
590 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
591 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
592 cloudSite.getCloudVersion());
593 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
594 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
597 // Validate the Network parameters.
599 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
600 if (!missing.isEmpty()) {
601 String error = "Create Network: Missing parameters: " + missing;
602 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
604 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
607 return networkResource;
611 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
614 * If the network is not found, it is treated as a success.
616 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
617 * network orchestration mode for each network type is declared in its catalog definition.
619 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
620 * networkId should be the Neutron network UUID.
622 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
623 * will require manual fallout in the client.
625 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
626 String networkId, MsoRequest msoRequest) throws NetworkException {
627 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
628 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
629 || commonUtils.isNullOrEmpty(networkId)) {
630 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
631 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
632 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
635 int timeoutMinutes = heat.getNetworkHeatTimeoutValue(modelCustomizationUuid, networkType);
637 boolean networkDeleted = false;
639 StackInfo stack = heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, false, timeoutMinutes);
640 networkDeleted = stack.isOperationPerformed();
641 } catch (MsoException me) {
642 me.addContext("DeleteNetwork");
643 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
644 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
645 throw new NetworkException(me);
649 heat.updateResourceStatus(msoRequest.getRequestId(),
650 networkDeleted ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
651 } catch (Exception e) {
652 logger.warn("Exception while updating infra active request", e);
657 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
658 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
659 * to undo the creation.
661 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
664 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
665 if (rollback == null) {
666 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
670 // Get the elements of the VnfRollback object for easier access
671 String cloudSiteId = rollback.getCloudId();
672 String tenantId = rollback.getTenantId();
673 String networkId = rollback.getNetworkStackId();
675 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
677 if (rollback.getNetworkCreated()) {
679 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, false, 120);
680 } catch (MsoException me) {
681 me.addContext("RollbackNetwork");
682 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
683 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
684 cloudSiteId, tenantId, me);
685 throw new NetworkException(me);
691 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
692 List<Integer> vlans, List<RouteTarget> routeTargets) {
694 StringBuilder missing = new StringBuilder();
695 if (commonUtils.isNullOrEmpty(networkName)) {
696 missing.append("networkName");
700 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
701 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
702 missing.append(sep).append("physicalNetworkName");
705 if (vlans == null || vlans.isEmpty()) {
706 missing.append(sep).append(VLANS);
710 return missing.toString();
713 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
714 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
715 boolean os3template) {
716 // Build the common set of HEAT template parameters
717 Map<String, Object> stackParams = new HashMap<>();
718 stackParams.put("network_name", networkName);
720 if (neutronNetworkType == NetworkType.PROVIDER) {
722 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
723 stackParams.put("vlan", vlans.get(0).toString());
724 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
725 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
726 // It supports all ProviderNet properties except segmentation_id, and adds a
727 // comma-separated-list of VLANs as a "segments" property.
728 // Note that this does not match the Neutron definition of Multi-Provider network,
729 // which contains a list of 'segments', each having physical_network, network_type,
730 // and segmentation_id.
731 StringBuilder buf = new StringBuilder();
733 for (Integer vlan : vlans) {
734 buf.append(sep).append(vlan.toString());
737 String csl = buf.toString();
739 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
740 stackParams.put(VLANS, csl);
742 if (routeTargets != null) {
744 String rtGlobal = "";
745 String rtImport = "";
746 String rtExport = "";
748 for (RouteTarget rt : routeTargets) {
749 boolean rtIsNull = false;
751 String routeTarget = rt.getRouteTarget();
752 String routeTargetRole = rt.getRouteTargetRole();
753 logger.debug("Checking for an actually null route target: {}", rt);
754 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
756 if (routeTargetRole == null || routeTargetRole.equals("")
757 || routeTargetRole.equalsIgnoreCase("null"))
763 logger.debug("Input RT:{}", rt);
764 String role = rt.getRouteTargetRole();
765 String rtValue = rt.getRouteTarget();
767 if ("IMPORT".equalsIgnoreCase(role)) {
768 sep = rtImport.isEmpty() ? "" : ",";
769 rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
770 } else if ("EXPORT".equalsIgnoreCase(role)) {
771 sep = rtExport.isEmpty() ? "" : ",";
772 rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
773 } else // covers BOTH, empty etc
775 sep = rtGlobal.isEmpty() ? "" : ",";
776 rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
782 if (!rtImport.isEmpty()) {
783 stackParams.put("route_targets_import", rtImport);
785 if (!rtExport.isEmpty()) {
786 stackParams.put("route_targets_export", rtExport);
788 if (!rtGlobal.isEmpty()) {
789 stackParams.put("route_targets", rtGlobal);
792 if (commonUtils.isNullOrEmpty(shared)) {
793 stackParams.put("shared", "False");
795 stackParams.put("shared", shared);
797 if (commonUtils.isNullOrEmpty(external)) {
798 stackParams.put("external", "False");
800 stackParams.put("external", external);
808 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
809 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
810 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
811 * "network_policy_refs_data_sequence_minor": "0" } } ]
813 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
815 List<ContrailPolicyRef> prlist = new ArrayList<>();
818 if (pFqdns != null) {
819 for (String pf : pFqdns) {
820 if (!commonUtils.isNullOrEmpty(pf)) {
821 ContrailPolicyRef pr = new ContrailPolicyRef();
822 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
825 logger.debug("Contrail PolicyRefs Data:{}", pr);
830 String error = "Null pFqdns at start of mergePolicyRefs";
831 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
833 throw new MsoAdapterException(error);
836 JsonNode node = null;
838 ObjectMapper mapper = new ObjectMapper();
839 node = mapper.convertValue(prlist, JsonNode.class);
840 String jsonString = mapper.writeValueAsString(prlist);
841 logger.debug("Json PolicyRefs Data:{}", jsonString);
842 } catch (Exception e) {
843 String error = "Error creating JsonNode for policyRefs Data";
844 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
846 throw new MsoAdapterException(error);
849 if (pFqdns != null && node != null) {
850 StringBuilder buf = new StringBuilder();
852 for (String pf : pFqdns) {
853 if (!commonUtils.isNullOrEmpty(pf)) {
854 buf.append(sep).append(pf);
858 String csl = buf.toString();
859 stackParams.put("policy_refs", csl);
860 stackParams.put("policy_refsdata", node);
863 logger.debug("StackParams updated with policy refs");
867 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
870 if (rtFqdns != null) {
871 StringBuilder buf = new StringBuilder();
873 for (String rtf : rtFqdns) {
874 if (!commonUtils.isNullOrEmpty(rtf)) {
875 buf.append(sep).append(rtf);
879 String csl = buf.toString();
880 stackParams.put("route_table_refs", csl);
883 logger.debug("StackParams updated with route_table refs");
889 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
890 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
891 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
892 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
893 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
894 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
895 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
896 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
897 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
898 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
899 * "host_routes": null }
901 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
902 throws MsoException {
905 List<ContrailSubnet> cslist = new ArrayList<>();
906 for (Subnet subnet : subnets) {
907 logger.debug("Input Subnet:{}", subnet);
908 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
909 logger.debug("Contrail Subnet:{}", cs);
913 JsonNode node = null;
915 ObjectMapper mapper = new ObjectMapper();
916 node = mapper.convertValue(cslist, JsonNode.class);
917 String jsonString = mapper.writeValueAsString(cslist);
918 logger.debug("Json Subnet List:{}", jsonString);
919 } catch (Exception e) {
920 String error = "Error creating JsonNode from input subnets";
921 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
922 throw new MsoAdapterException(error);
926 stackParams.put("subnet_list", node);
928 // Outputs - All subnets are in one ipam_subnets structure
929 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
930 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
932 // append outputs in heatTemplate
933 int outputsIdx = heatTemplate.indexOf("outputs:");
934 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
935 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
940 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
942 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
943 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
946 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
947 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
948 * " end: %poolend%\n";
952 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
953 + " value: {get_resource: subnet_%subnetId%}\n";
957 StringBuilder resourcesBuf = new StringBuilder();
958 StringBuilder outputsBuf = new StringBuilder();
959 for (Subnet subnet : subnets) {
961 // build template for each subnet
962 curR = resourceTempl;
963 if (subnet.getSubnetId() != null) {
964 curR = curR.replace("%subnetId%", subnet.getSubnetId());
966 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
967 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
968 throw new MsoAdapterException(error);
971 if (subnet.getSubnetName() != null) {
972 curR = curR.replace("%name%", subnet.getSubnetName());
974 curR = curR.replace("%name%", subnet.getSubnetId());
977 if (subnet.getCidr() != null) {
978 curR = curR.replace("%cidr%", subnet.getCidr());
980 String error = "Missing Required cidr for subnet in HEAT Template";
981 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
982 throw new MsoAdapterException(error);
985 if (subnet.getIpVersion() != null) {
986 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
988 if (subnet.getEnableDHCP() != null) {
989 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
991 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
992 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
995 if (subnet.getAllocationPools() != null) {
996 StringBuilder tempBuf = new StringBuilder();
997 tempBuf.append(curR);
998 tempBuf.append(" allocation_pools:\n");
999 for (Pool pool : subnet.getAllocationPools()) {
1000 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1001 tempBuf.append(" - start: ");
1002 tempBuf.append(pool.getStart());
1003 tempBuf.append("\n end: ");
1004 tempBuf.append(pool.getEnd());
1005 tempBuf.append("\n");
1008 curR = tempBuf.toString();
1011 resourcesBuf.append(curR);
1014 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1016 outputsBuf.append(curO);
1018 // append resources and outputs in heatTemplate
1019 logger.debug("Tempate initial:{}", heatTemplate);
1020 int outputsIdx = heatTemplate.indexOf("outputs:");
1021 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1022 int resourcesIdx = heatTemplate.indexOf("resources:");
1023 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1025 logger.debug("Template updated with all subnets:{}", heatTemplate);
1026 return heatTemplate;
1029 public Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1031 Map<String, String> sMap = new HashMap<>();
1034 Object obj = outputs.get(key);
1035 ObjectMapper mapper = new ObjectMapper();
1036 String jStr = mapper.writeValueAsString(obj);
1037 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1039 JsonNode rootNode = mapper.readTree(jStr);
1040 if (rootNode != null) {
1041 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1042 logger.debug("Output Subnet Node {}", sNode);
1043 String name = sNode.path("subnet_name").textValue();
1044 String uuid = sNode.path("subnet_uuid").textValue();
1045 String aaiId = name; // default
1046 // try to find aaiId for name in input subnetList
1047 if (subnets != null) {
1048 for (Subnet subnet : subnets) {
1049 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1050 && subnet.getSubnetName().equals(name)) {
1051 aaiId = subnet.getSubnetId();
1056 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1059 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1060 ErrorCode.DataError.getValue());
1062 } catch (Exception e) {
1063 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1064 ErrorCode.DataError.getValue(), e);
1067 logger.debug("Return sMap {}", sMap);
1071 private static String insertStr(String template, String snippet, int index) {
1073 String updatedTemplate;
1075 logger.debug("Index:{} Snippet:{}", index, snippet);
1077 String templateBeg = template.substring(0, index);
1078 String templateEnd = template.substring(index);
1080 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1082 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1083 return updatedTemplate;
1086 public Map<String, String> buildSubnetMap(Map<String, Object> outputs, List<Subnet> subnets, boolean os3template) {
1088 Map<String, String> sMap = new HashMap<>();
1089 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
1090 String key = entry.getKey();
1091 if (key != null && key.startsWith("subnet")) {
1092 if (os3template) // one subnet_id output
1094 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
1096 } else // multiples subnet_%aaid% outputs
1098 String subnetUUId = (String) outputs.get(key);
1099 sMap.put(key.substring("subnet_id_".length()), subnetUUId);