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);
102 private static final ObjectMapper mapper = new ObjectMapper();
105 private CloudConfig cloudConfig;
107 private Environment environment;
109 private MsoNeutronUtils neutron;
111 private MsoHeatUtils heat;
113 private MsoHeatUtilsWithUpdate heatWithUpdate;
115 private MsoCommonUtils commonUtils;
118 private NetworkResourceCustomizationRepository networkCustomRepo;
121 private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
124 private NetworkResourceRepository networkResourceRepo;
126 public MsoNetworkAdapterImpl() {}
129 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
130 * the specified cloud and tenant. The tenant must exist at the time this service is called.
132 * If a network with the same name already exists, this can be considered a success or failure, depending on the
133 * value of the 'failIfExists' parameter.
135 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
136 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
137 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
139 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
140 * multiple VLANs on the same physical network.
142 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
143 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
144 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
146 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
147 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
148 * created but the orchestration fails on a subsequent operation.
151 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
152 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
153 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
154 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> stackId,
155 MutableBoolean isOs3Nw) throws NetworkException {
156 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
158 // Will capture execution time for metrics
159 long startTime = System.currentTimeMillis();
161 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
162 if (!cloudSiteOpt.isPresent()) {
163 String error = String.format(
164 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
165 networkName, cloudSiteId, tenantId);
166 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
167 // Set the detailed error as the Exception 'message'
168 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
172 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
173 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
174 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
176 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
177 if (heatTemplate == null) {
178 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
179 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
180 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
183 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
185 // "Fix" the template if it has CR/LF (getting this from Oracle)
186 String template = heatTemplate.getHeatTemplate();
187 template = template.replaceAll("\r\n", "\n");
189 boolean os3template = false;
191 String os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
193 if (template.contains(os3nw))
196 isOs3Nw.setValue(os3template);
197 // First, look up to see if the Network already exists (by name).
198 // For HEAT orchestration of networks, the stack name will always match the network name
199 StackInfo heatStack = null;
201 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
202 } catch (MsoException me) {
203 me.addContext(CREATE_NETWORK_CONTEXT);
204 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ", MessageEnum.RA_QUERY_NETWORK_EXC,
205 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
206 throw new NetworkException(me);
209 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
210 // Stack exists. Return success or error depending on input directive
211 if (failIfExists != null && failIfExists) {
212 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
213 cloudSiteId, tenantId, heatStack.getCanonicalName());
214 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
216 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
218 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
219 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
220 networkName, cloudSiteId, tenantId);
222 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
226 // Ready to deploy the new Network
227 // Build the common set of HEAT template parameters
228 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName, physicalNetworkName,
229 vlans, routeTargets, shared, external, os3template);
231 // Validate (and update) the input parameters against the DB definition
232 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
233 // and inputs were already validated.
235 stackParams = heat.validateStackParams(stackParams, heatTemplate);
236 } catch (IllegalArgumentException e) {
237 String error = "Create Network: Configuration Error: " + e.getMessage();
238 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
239 // Input parameters were not valid
240 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
243 if (subnets != null) {
246 template = mergeSubnetsAIC3(template, subnets, stackParams);
248 template = mergeSubnets(template, subnets);
250 } catch (MsoException me) {
251 me.addContext(CREATE_NETWORK_CONTEXT);
252 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
253 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
254 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
255 throw new NetworkException(me);
259 if (policyFqdns != null && !policyFqdns.isEmpty() && os3template) {
261 mergePolicyRefs(policyFqdns, stackParams);
262 } catch (MsoException me) {
263 me.addContext(CREATE_NETWORK_CONTEXT);
264 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
265 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
266 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
267 throw new NetworkException(me);
271 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
273 mergeRouteTableRefs(routeTableFqdns, stackParams);
274 } catch (MsoException me) {
275 me.addContext(CREATE_NETWORK_CONTEXT);
276 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
277 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
278 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
279 throw new NetworkException(me);
283 // Deploy the network stack
284 // Ignore MsoStackAlreadyExists exception because we already checked.
288 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template, stackParams,
289 false, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue(), failIfExists);
290 } catch (MsoException me) {
291 me.addContext(CREATE_NETWORK_CONTEXT);
292 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
293 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
294 throw new NetworkException(me);
297 // Reach this point if createStack is successful.
298 stackId.value = heatStack.getCanonicalName();
301 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
302 } catch (Exception e) {
303 logger.warn("Exception while updating infra active request", e);
306 logger.debug("Network {} successfully created via HEAT", networkName);
312 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
313 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
314 * remove a VLAN), but other properties may be updated as well.
316 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
317 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
318 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
320 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
321 * VLANs on the same physical network.
323 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
324 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
325 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
327 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
328 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
329 * a subsequent operation.
331 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
332 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
333 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
334 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> stackId)
335 throws NetworkException {
337 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
338 cloudSiteId, tenantId);
340 // Will capture execution time for metrics
341 long startTime = System.currentTimeMillis();
343 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
344 if (!cloudSiteOpt.isPresent()) {
345 String error = String.format(
346 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
347 networkName, cloudSiteId, tenantId);
348 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
349 // Set the detailed error as the Exception 'message'
350 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
353 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
354 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
355 String mode = networkResource.getOrchestrationMode();
356 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
358 // Use an MsoNeutronUtils for all Neutron commands
360 if (NEUTRON_MODE.equals(mode)) {
362 // Verify that the Network exists
363 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
364 NetworkInfo netInfo = null;
366 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
367 } catch (MsoException me) {
368 me.addContext(UPDATE_NETWORK_CONTEXT);
369 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
370 ErrorCode.BusinessProcessError.getValue(), networkId, cloudSiteId, tenantId, me);
371 throw new NetworkException(me);
374 if (netInfo == null) {
375 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
376 cloudSiteId, tenantId);
377 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
378 ErrorCode.BusinessProcessError.getValue(), error);
379 // Does not exist. Throw an exception (can't update a non-existent network)
380 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
383 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
384 physicalNetworkName, vlans);
385 } catch (MsoException me) {
386 me.addContext(UPDATE_NETWORK_CONTEXT);
387 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
388 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
389 throw new NetworkException(me);
392 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
393 } else if ("HEAT".equals(mode)) {
395 // First, look up to see that the Network already exists.
396 // For Heat-based orchestration, the networkId is the network Stack ID.
397 StackInfo heatStack = null;
399 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
400 } catch (MsoException me) {
401 me.addContext(UPDATE_NETWORK_CONTEXT);
402 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
403 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
404 throw new NetworkException(me);
407 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
408 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
409 cloudSiteId, tenantId);
410 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
412 // Network stack does not exist. Return an error
413 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
416 // Get the previous parameters for rollback
417 Map<String, Object> heatParams = heatStack.getParameters();
419 List<Integer> previousVlans = new ArrayList<>();
420 String vlansParam = (String) heatParams.get(VLANS);
421 if (vlansParam != null) {
422 for (String vlan : vlansParam.split(",")) {
424 previousVlans.add(Integer.parseInt(vlan));
425 } catch (NumberFormatException e) {
426 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
427 ErrorCode.DataError.getValue(), vlansParam, e);
431 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
433 // Ready to deploy the updated Network via Heat
434 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
435 if (heatTemplate == null) {
436 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
437 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
439 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
442 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
444 // "Fix" the template if it has CR/LF (getting this from Oracle)
445 String template = heatTemplate.getHeatTemplate();
446 template = template.replaceAll("\r\n", "\n");
448 boolean os3template = false;
449 String os3nw = OS3_NW;
451 os3nw = environment.getProperty(OS3_NW_PROPERTY, OS3_NW);
453 if (template.contains(os3nw))
456 // Build the common set of HEAT template parameters
457 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
458 physicalNetworkName, vlans, routeTargets, shared, external, os3template);
460 // Validate (and update) the input parameters against the DB definition
461 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
463 stackParams = heat.validateStackParams(stackParams, heatTemplate);
464 } catch (IllegalArgumentException e) {
465 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
466 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
467 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
470 if (subnets != null) {
473 template = mergeSubnetsAIC3(template, subnets, stackParams);
475 template = mergeSubnets(template, subnets);
477 } catch (MsoException me) {
478 me.addContext(UPDATE_NETWORK_CONTEXT);
479 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
480 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
481 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
482 throw new NetworkException(me);
486 if (policyFqdns != null && os3template) {
488 mergePolicyRefs(policyFqdns, stackParams);
489 } catch (MsoException me) {
490 me.addContext(UPDATE_NETWORK_CONTEXT);
491 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
492 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
493 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
494 throw new NetworkException(me);
498 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && os3template) {
500 mergeRouteTableRefs(routeTableFqdns, stackParams);
501 } catch (MsoException me) {
502 me.addContext(UPDATE_NETWORK_CONTEXT);
503 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
504 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
505 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
506 throw new NetworkException(me);
510 // Update the network stack
511 // Ignore MsoStackNotFound exception because we already checked.
513 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
514 stackParams, false, heatTemplate.getTimeoutMinutes());
515 } catch (MsoException me) {
516 me.addContext(UPDATE_NETWORK_CONTEXT);
517 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
518 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
519 throw new NetworkException(me);
522 stackId.value = heatStack.getCanonicalName();
524 logger.debug("Network {} successfully updated via HEAT", networkId);
530 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
531 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
532 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
533 // Retrieve the Network Resource definition
534 NetworkResource networkResource = null;
535 NetworkResourceCustomization networkCust = null;
536 CollectionNetworkResourceCustomization collectionNetworkCust = null;
537 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
538 if (!commonUtils.isNullOrEmpty(networkType)) {
539 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
542 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
543 if (networkCust == null) {
544 collectionNetworkCust =
545 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
548 if (networkCust != null) {
549 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
551 networkResource = networkCust.getNetworkResource();
552 } else if (collectionNetworkCust != null) {
553 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
554 networkResource = collectionNetworkCust.getNetworkResource();
556 if (networkResource == null) {
557 String error = String.format(
558 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
559 networkType, modelCustomizationUuid);
560 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
562 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
564 logger.debug(LOG_DEBUG_MSG, networkResource);
566 String mode = networkResource.getOrchestrationMode();
567 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
569 // All Networks are orchestrated via HEAT or Neutron
570 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
571 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
572 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
573 ErrorCode.DataError.getValue(), error);
574 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
577 MavenLikeVersioning osV = new MavenLikeVersioning();
578 osV.setVersion(cloudSite.getCloudVersion());
579 if ((osV.isMoreRecentThan(networkResource.getAicVersionMin())
580 || osV.isTheSameVersion(networkResource.getAicVersionMin())) // os
583 && (osV.isTheSameVersion(networkResource.getAicVersionMax())
584 || !(osV.isMoreRecentThan(networkResource.getAicVersionMax())))) // os <= max
586 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
587 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
588 cloudSite.getCloudVersion());
590 String error = String.format(
591 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
592 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
593 cloudSite.getCloudVersion());
594 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
595 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
598 // Validate the Network parameters.
600 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
601 if (!missing.isEmpty()) {
602 String error = "Create Network: Missing parameters: " + missing;
603 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
605 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
608 return networkResource;
612 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
615 * If the network is not found, it is treated as a success.
617 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
618 * network orchestration mode for each network type is declared in its catalog definition.
620 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
621 * networkId should be the Neutron network UUID.
623 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
624 * will require manual fallout in the client.
626 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
627 String networkId, MsoRequest msoRequest) throws NetworkException {
628 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
629 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
630 || commonUtils.isNullOrEmpty(networkId)) {
631 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
632 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
633 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
636 int timeoutMinutes = heat.getNetworkHeatTimeoutValue(modelCustomizationUuid, networkType);
638 boolean networkDeleted = false;
640 StackInfo stack = heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, false, timeoutMinutes);
641 networkDeleted = stack.isOperationPerformed();
642 } catch (MsoException me) {
643 me.addContext("DeleteNetwork");
644 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
645 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
646 throw new NetworkException(me);
650 heat.updateResourceStatus(msoRequest.getRequestId(),
651 networkDeleted ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
652 } catch (Exception e) {
653 logger.warn("Exception while updating infra active request", e);
658 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
659 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
660 * to undo the creation.
662 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
665 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
666 if (rollback == null) {
667 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
671 // Get the elements of the VnfRollback object for easier access
672 String cloudSiteId = rollback.getCloudId();
673 String tenantId = rollback.getTenantId();
674 String networkId = rollback.getNetworkStackId();
676 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
678 if (rollback.getNetworkCreated()) {
680 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, false, 120);
681 } catch (MsoException me) {
682 me.addContext("RollbackNetwork");
683 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
684 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
685 cloudSiteId, tenantId, me);
686 throw new NetworkException(me);
692 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
693 List<Integer> vlans, List<RouteTarget> routeTargets) {
695 StringBuilder missing = new StringBuilder();
696 if (commonUtils.isNullOrEmpty(networkName)) {
697 missing.append("networkName");
701 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
702 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
703 missing.append(sep).append("physicalNetworkName");
706 if (vlans == null || vlans.isEmpty()) {
707 missing.append(sep).append(VLANS);
711 return missing.toString();
714 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
715 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
716 boolean os3template) {
717 // Build the common set of HEAT template parameters
718 Map<String, Object> stackParams = new HashMap<>();
719 stackParams.put("network_name", networkName);
721 if (neutronNetworkType == NetworkType.PROVIDER) {
723 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
724 stackParams.put("vlan", vlans.get(0).toString());
725 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
726 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
727 // It supports all ProviderNet properties except segmentation_id, and adds a
728 // comma-separated-list of VLANs as a "segments" property.
729 // Note that this does not match the Neutron definition of Multi-Provider network,
730 // which contains a list of 'segments', each having physical_network, network_type,
731 // and segmentation_id.
732 StringBuilder buf = new StringBuilder();
734 for (Integer vlan : vlans) {
735 buf.append(sep).append(vlan.toString());
738 String csl = buf.toString();
740 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
741 stackParams.put(VLANS, csl);
743 if (routeTargets != null) {
745 String rtGlobal = "";
746 String rtImport = "";
747 String rtExport = "";
749 for (RouteTarget rt : routeTargets) {
750 boolean rtIsNull = false;
752 String routeTarget = rt.getRouteTarget();
753 String routeTargetRole = rt.getRouteTargetRole();
754 logger.debug("Checking for an actually null route target: {}", rt);
755 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
757 if (routeTargetRole == null || routeTargetRole.equals("")
758 || routeTargetRole.equalsIgnoreCase("null"))
764 logger.debug("Input RT:{}", rt);
765 String role = rt.getRouteTargetRole();
766 String rtValue = rt.getRouteTarget();
768 if ("IMPORT".equalsIgnoreCase(role)) {
769 sep = rtImport.isEmpty() ? "" : ",";
770 rtImport = os3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
771 } else if ("EXPORT".equalsIgnoreCase(role)) {
772 sep = rtExport.isEmpty() ? "" : ",";
773 rtExport = os3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
774 } else // covers BOTH, empty etc
776 sep = rtGlobal.isEmpty() ? "" : ",";
777 rtGlobal = os3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
783 if (!rtImport.isEmpty()) {
784 stackParams.put("route_targets_import", rtImport);
786 if (!rtExport.isEmpty()) {
787 stackParams.put("route_targets_export", rtExport);
789 if (!rtGlobal.isEmpty()) {
790 stackParams.put("route_targets", rtGlobal);
793 if (commonUtils.isNullOrEmpty(shared)) {
794 stackParams.put("shared", "False");
796 stackParams.put("shared", shared);
798 if (commonUtils.isNullOrEmpty(external)) {
799 stackParams.put("external", "False");
801 stackParams.put("external", external);
809 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
810 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
811 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
812 * "network_policy_refs_data_sequence_minor": "0" } } ]
814 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
816 List<ContrailPolicyRef> prlist = new ArrayList<>();
819 if (pFqdns != null) {
820 for (String pf : pFqdns) {
821 if (!commonUtils.isNullOrEmpty(pf)) {
822 ContrailPolicyRef pr = new ContrailPolicyRef();
823 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
826 logger.debug("Contrail PolicyRefs Data:{}", pr);
831 String error = "Null pFqdns at start of mergePolicyRefs";
832 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
834 throw new MsoAdapterException(error);
837 JsonNode node = null;
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 node = mapper.convertValue(cslist, JsonNode.class);
916 String jsonString = mapper.writeValueAsString(cslist);
917 logger.debug("Json Subnet List:{}", jsonString);
918 } catch (Exception e) {
919 String error = "Error creating JsonNode from input subnets";
920 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
921 throw new MsoAdapterException(error);
925 stackParams.put("subnet_list", node);
927 // Outputs - All subnets are in one ipam_subnets structure
928 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
929 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
931 // append outputs in heatTemplate
932 int outputsIdx = heatTemplate.indexOf("outputs:");
933 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
934 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
939 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
941 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
942 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
945 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
946 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
947 * " end: %poolend%\n";
951 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
952 + " value: {get_resource: subnet_%subnetId%}\n";
956 StringBuilder resourcesBuf = new StringBuilder();
957 StringBuilder outputsBuf = new StringBuilder();
958 for (Subnet subnet : subnets) {
960 // build template for each subnet
961 curR = resourceTempl;
962 if (subnet.getSubnetId() != null) {
963 curR = curR.replace("%subnetId%", subnet.getSubnetId());
965 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
966 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
967 throw new MsoAdapterException(error);
970 if (subnet.getSubnetName() != null) {
971 curR = curR.replace("%name%", subnet.getSubnetName());
973 curR = curR.replace("%name%", subnet.getSubnetId());
976 if (subnet.getCidr() != null) {
977 curR = curR.replace("%cidr%", subnet.getCidr());
979 String error = "Missing Required cidr for subnet in HEAT Template";
980 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
981 throw new MsoAdapterException(error);
984 if (subnet.getIpVersion() != null) {
985 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
987 if (subnet.getEnableDHCP() != null) {
988 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
990 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
991 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
994 if (subnet.getAllocationPools() != null) {
995 StringBuilder tempBuf = new StringBuilder();
996 tempBuf.append(curR);
997 tempBuf.append(" allocation_pools:\n");
998 for (Pool pool : subnet.getAllocationPools()) {
999 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1000 tempBuf.append(" - start: ");
1001 tempBuf.append(pool.getStart());
1002 tempBuf.append("\n end: ");
1003 tempBuf.append(pool.getEnd());
1004 tempBuf.append("\n");
1007 curR = tempBuf.toString();
1010 resourcesBuf.append(curR);
1013 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1015 outputsBuf.append(curO);
1017 // append resources and outputs in heatTemplate
1018 logger.debug("Tempate initial:{}", heatTemplate);
1019 int outputsIdx = heatTemplate.indexOf("outputs:");
1020 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1021 int resourcesIdx = heatTemplate.indexOf("resources:");
1022 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1024 logger.debug("Template updated with all subnets:{}", heatTemplate);
1025 return heatTemplate;
1028 public Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1030 Map<String, String> sMap = new HashMap<>();
1033 Object obj = outputs.get(key);
1034 String jStr = mapper.writeValueAsString(obj);
1035 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1037 JsonNode rootNode = mapper.readTree(jStr);
1038 if (rootNode != null) {
1039 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1040 logger.debug("Output Subnet Node {}", sNode);
1041 String name = sNode.path("subnet_name").textValue();
1042 String uuid = sNode.path("subnet_uuid").textValue();
1043 String aaiId = name; // default
1044 // try to find aaiId for name in input subnetList
1045 if (subnets != null) {
1046 for (Subnet subnet : subnets) {
1047 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1048 && subnet.getSubnetName().equals(name)) {
1049 aaiId = subnet.getSubnetId();
1054 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1057 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1058 ErrorCode.DataError.getValue());
1060 } catch (Exception e) {
1061 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1062 ErrorCode.DataError.getValue(), e);
1065 logger.debug("Return sMap {}", sMap);
1069 private static String insertStr(String template, String snippet, int index) {
1071 String updatedTemplate;
1073 logger.debug("Index:{} Snippet:{}", index, snippet);
1075 String templateBeg = template.substring(0, index);
1076 String templateEnd = template.substring(index);
1078 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1080 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1081 return updatedTemplate;
1084 public Map<String, String> buildSubnetMap(Map<String, Object> outputs, List<Subnet> subnets, boolean os3template) {
1086 Map<String, String> sMap = new HashMap<>();
1087 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
1088 String key = entry.getKey();
1089 if (key != null && key.startsWith("subnet")) {
1090 if (os3template) // one subnet_id output
1092 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
1094 } else // multiples subnet_%aaid% outputs
1096 String subnetUUId = (String) outputs.get(key);
1097 sMap.put(key.substring("subnet_id_".length()), subnetUUId);