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 AIC3_NW_PROPERTY = "org.onap.so.adapters.network.aic3nw";
85 private static final String AIC3_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 as 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 String mode = networkResource.getOrchestrationMode();
227 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
229 if (NEUTRON_MODE.equals(mode)) {
231 // Use an MsoNeutronUtils for all neutron commands
233 // See if the Network already exists (by name)
234 NetworkInfo netInfo = null;
236 netInfo = neutron.queryNetwork(networkName, tenantId, cloudSiteId);
237 } catch (MsoException me) {
239 "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
240 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkName,
241 cloudSiteId, tenantId, me);
242 me.addContext(CREATE_NETWORK_CONTEXT);
243 throw new NetworkException(me);
246 if (netInfo != null) {
247 // Exists. If that's OK, return success with the network ID.
248 // Otherwise, return an exception.
249 if (failIfExists != null && failIfExists) {
250 String error = String.format("Create Nework: Network %s already exists in %s/%s with ID %s",
251 networkName, cloudSiteId, tenantId, netInfo.getId());
252 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
253 ErrorCode.DataError.getValue(), error);
254 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
256 // Populate the outputs from the existing network.
257 networkId.value = netInfo.getId();
258 neutronNetworkId.value = netInfo.getId();
259 rollback.value = networkRollback; // Default rollback - no updates performed
260 logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
261 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), netInfo.getStatus());
263 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
268 netInfo = neutron.createNetwork(cloudSiteId, tenantId, neutronNetworkType, networkName,
269 physicalNetworkName, vlans);
270 } catch (MsoException me) {
271 me.addContext(CREATE_NETWORK_CONTEXT);
272 logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
273 ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
275 throw new NetworkException(me);
278 // Note: ignoring MsoNetworkAlreadyExists because we already checked.
280 // If reach this point, network creation is successful.
281 // Since directly created via Neutron, networkId tracked by MSO is the same
282 // as the neutron network ID.
283 networkId.value = netInfo.getId();
284 neutronNetworkId.value = netInfo.getId();
286 networkRollback.setNetworkCreated(true);
287 networkRollback.setNetworkId(netInfo.getId());
288 networkRollback.setNeutronNetworkId(netInfo.getId());
289 networkRollback.setNetworkType(networkType);
291 logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
292 } else if ("HEAT".equals(mode)) {
294 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
295 if (heatTemplate == null) {
296 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
297 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
299 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
302 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
304 // "Fix" the template if it has CR/LF (getting this from Oracle)
305 String template = heatTemplate.getHeatTemplate();
306 template = template.replaceAll("\r\n", "\n");
308 boolean aic3template = false;
309 String aic3nw = AIC3_NW;
311 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
313 if (template.contains(aic3nw))
316 // First, look up to see if the Network already exists (by name).
317 // For HEAT orchestration of networks, the stack name will always match the network name
318 StackInfo heatStack = null;
320 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
321 } catch (MsoException me) {
322 me.addContext(CREATE_NETWORK_CONTEXT);
323 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
324 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName, cloudSiteId,
326 throw new NetworkException(me);
329 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
330 // Stack exists. Return success or error depending on input directive
331 if (failIfExists != null && failIfExists) {
332 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
333 cloudSiteId, tenantId, heatStack.getCanonicalName());
334 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
335 ErrorCode.DataError.getValue(), error);
336 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
338 // Populate the outputs from the existing stack.
339 networkId.value = heatStack.getCanonicalName();
340 Map<String, String> sMap = new HashMap<>();
341 if (heatStack.getOutputs() != null) {
342 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
343 rollback.value = networkRollback; // Default rollback - no updates performed
345 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
347 Map<String, Object> outputs = heatStack.getOutputs();
349 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
350 String key = entry.getKey();
351 if (key != null && key.startsWith("subnet")) {
352 if (aic3template) // one subnet_id output
354 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
356 } else // multiples subnet_%aaid% outputs
358 String subnetUUId = (String) outputs.get(key);
359 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
364 subnetIdMap.value = sMap;
365 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
366 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
367 networkName, cloudSiteId, tenantId);
369 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_EXIST_STATUS_MESSAGE);
373 // Ready to deploy the new Network
374 // Build the common set of HEAT template parameters
375 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
376 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
378 // Validate (and update) the input parameters against the DB definition
379 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
380 // and inputs were already validated.
382 stackParams = heat.validateStackParams(stackParams, heatTemplate);
383 } catch (IllegalArgumentException e) {
384 String error = "Create Network: Configuration Error: " + e.getMessage();
385 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
386 // Input parameters were not valid
387 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
390 if (subnets != null) {
393 template = mergeSubnetsAIC3(template, subnets, stackParams);
395 template = mergeSubnets(template, subnets);
397 } catch (MsoException me) {
398 me.addContext(CREATE_NETWORK_CONTEXT);
399 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
400 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
401 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
402 throw new NetworkException(me);
406 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
408 mergePolicyRefs(policyFqdns, stackParams);
409 } catch (MsoException me) {
410 me.addContext(CREATE_NETWORK_CONTEXT);
411 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
412 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
413 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
414 throw new NetworkException(me);
418 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
420 mergeRouteTableRefs(routeTableFqdns, stackParams);
421 } catch (MsoException me) {
422 me.addContext(CREATE_NETWORK_CONTEXT);
423 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
424 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
425 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
426 throw new NetworkException(me);
430 // Deploy the network stack
431 // Ignore MsoStackAlreadyExists exception because we already checked.
435 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template,
436 stackParams, true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue(),
438 } catch (MsoException me) {
439 me.addContext(CREATE_NETWORK_CONTEXT);
440 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
441 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
442 throw new NetworkException(me);
445 // Reach this point if createStack is successful.
447 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
448 // and the neutronNetworkId is the network UUID returned in stack outputs.
449 networkId.value = heatStack.getCanonicalName();
450 if (heatStack.getOutputs() != null) {
451 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
453 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
456 Map<String, Object> outputs = heatStack.getOutputs();
457 Map<String, String> sMap = new HashMap<>();
458 if (outputs != null) {
459 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
460 String key = entry.getKey();
461 if (key != null && key.startsWith("subnet")) {
462 if (aic3template) // one subnet output expected
464 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
466 } else // multiples subnet_%aaid% outputs allowed
468 String subnetUUId = (String) outputs.get(key);
469 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
473 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
475 subnetIdMap.value = sMap;
477 rollback.value = networkRollback;
478 // Populate remaining rollback info and response parameters.
479 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
480 networkRollback.setNetworkCreated(true);
481 networkRollback.setNetworkType(networkType);
484 heat.updateResourceStatus(msoRequest.getRequestId(), NETWORK_CREATED_STATUS_MESSAGE);
485 } catch (Exception e) {
486 logger.warn("Exception while updating infra active request", e);
489 logger.debug("Network {} successfully created via HEAT", networkName);
496 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
497 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
498 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
499 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
500 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
501 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
507 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
508 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
509 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
510 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
511 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
512 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
513 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
518 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
519 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
520 * remove a VLAN), but other properties may be updated as well.
522 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
523 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
524 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
526 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
527 * VLANs on the same physical network.
529 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
530 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
531 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
533 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
534 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
535 * a subsequent operation.
537 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
538 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
539 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
540 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
541 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
543 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
544 cloudSiteId, tenantId);
546 // Will capture execution time for metrics
547 long startTime = System.currentTimeMillis();
549 // Build a default rollback object (no actions performed)
550 NetworkRollback networkRollback = new NetworkRollback();
551 networkRollback.setCloudId(cloudSiteId);
552 networkRollback.setTenantId(tenantId);
553 networkRollback.setMsoRequest(msoRequest);
555 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
556 if (!cloudSiteOpt.isPresent()) {
557 String error = String.format(
558 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
559 networkName, cloudSiteId, tenantId);
560 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
561 // Set the detailed error as the Exception 'message'
562 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
567 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
568 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
569 String mode = networkResource.getOrchestrationMode();
570 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
572 // Use an MsoNeutronUtils for all Neutron commands
574 if (NEUTRON_MODE.equals(mode)) {
576 // Verify that the Network exists
577 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
578 NetworkInfo netInfo = null;
580 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
581 } catch (MsoException me) {
582 me.addContext(UPDATE_NETWORK_CONTEXT);
583 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
584 ErrorCode.BusinessProcessError.getValue(), networkId, cloudSiteId, tenantId, me);
585 throw new NetworkException(me);
588 if (netInfo == null) {
589 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
590 cloudSiteId, tenantId);
591 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
592 ErrorCode.BusinessProcessError.getValue(), error);
593 // Does not exist. Throw an exception (can't update a non-existent network)
594 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
596 long updateNetworkStarttime = System.currentTimeMillis();
598 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
599 physicalNetworkName, vlans);
600 } catch (MsoException me) {
601 me.addContext(UPDATE_NETWORK_CONTEXT);
602 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
603 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
604 throw new NetworkException(me);
607 // Add the network ID and previously queried vlans to the rollback object
608 networkRollback.setNetworkId(netInfo.getId());
609 networkRollback.setNeutronNetworkId(netInfo.getId());
610 networkRollback.setNetworkType(networkType);
611 // Save previous parameters
612 networkRollback.setNetworkName(netInfo.getName());
613 networkRollback.setPhysicalNetwork(netInfo.getProvider());
614 networkRollback.setVlans(netInfo.getVlans());
616 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
617 } else if ("HEAT".equals(mode)) {
619 // First, look up to see that the Network already exists.
620 // For Heat-based orchestration, the networkId is the network Stack ID.
621 StackInfo heatStack = null;
623 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
624 } catch (MsoException me) {
625 me.addContext(UPDATE_NETWORK_CONTEXT);
626 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
627 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
628 throw new NetworkException(me);
631 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
632 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
633 cloudSiteId, tenantId);
634 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
636 // Network stack does not exist. Return an error
637 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
640 // Get the previous parameters for rollback
641 Map<String, Object> heatParams = heatStack.getParameters();
643 String previousNetworkName = (String) heatParams.get("network_name");
644 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
646 List<Integer> previousVlans = new ArrayList<>();
647 String vlansParam = (String) heatParams.get(VLANS);
648 if (vlansParam != null) {
649 for (String vlan : vlansParam.split(",")) {
651 previousVlans.add(Integer.parseInt(vlan));
652 } catch (NumberFormatException e) {
653 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
654 ErrorCode.DataError.getValue(), vlansParam, e);
658 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
660 // Ready to deploy the updated Network via Heat
663 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
664 if (heatTemplate == null) {
665 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
666 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
668 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
671 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
673 // "Fix" the template if it has CR/LF (getting this from Oracle)
674 String template = heatTemplate.getHeatTemplate();
675 template = template.replaceAll("\r\n", "\n");
677 boolean aic3template = false;
678 String aic3nw = AIC3_NW;
680 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
682 if (template.contains(aic3nw))
685 // Build the common set of HEAT template parameters
686 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
687 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
689 // Validate (and update) the input parameters against the DB definition
690 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
692 stackParams = heat.validateStackParams(stackParams, heatTemplate);
693 } catch (IllegalArgumentException e) {
694 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
695 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
696 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
699 if (subnets != null) {
702 template = mergeSubnetsAIC3(template, subnets, stackParams);
704 template = mergeSubnets(template, subnets);
706 } catch (MsoException me) {
707 me.addContext(UPDATE_NETWORK_CONTEXT);
708 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
709 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
710 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
711 throw new NetworkException(me);
715 if (policyFqdns != null && aic3template) {
717 mergePolicyRefs(policyFqdns, stackParams);
718 } catch (MsoException me) {
719 me.addContext(UPDATE_NETWORK_CONTEXT);
720 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
721 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
722 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
723 throw new NetworkException(me);
727 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
729 mergeRouteTableRefs(routeTableFqdns, stackParams);
730 } catch (MsoException me) {
731 me.addContext(UPDATE_NETWORK_CONTEXT);
732 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
733 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
734 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
735 throw new NetworkException(me);
739 // Update the network stack
740 // Ignore MsoStackNotFound exception because we already checked.
742 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
743 stackParams, true, heatTemplate.getTimeoutMinutes());
744 } catch (MsoException me) {
745 me.addContext(UPDATE_NETWORK_CONTEXT);
746 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
747 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
748 throw new NetworkException(me);
751 Map<String, Object> outputs = heatStack.getOutputs();
752 Map<String, String> sMap = new HashMap<>();
753 if (outputs != null) {
754 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
755 String key = entry.getKey();
756 if (key != null && key.startsWith("subnet")) {
757 if (aic3template) // one subnet output expected
759 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
761 } else // multiples subnet_%aaid% outputs allowed
763 String subnetUUId = (String) outputs.get(key);
764 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
769 subnetIdMap.value = sMap;
771 // Reach this point if createStack is successful.
772 // Populate remaining rollback info and response parameters.
773 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
774 if (null != outputs) {
775 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
777 logger.debug("outputs is NULL");
779 networkRollback.setNetworkType(networkType);
780 // Save previous parameters
781 networkRollback.setNetworkName(previousNetworkName);
782 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
783 networkRollback.setVlans(previousVlans);
785 rollback.value = networkRollback;
787 logger.debug("Network {} successfully updated via HEAT", networkId);
793 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
794 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
795 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
796 // Retrieve the Network Resource definition
797 NetworkResource networkResource = null;
798 NetworkResourceCustomization networkCust = null;
799 CollectionNetworkResourceCustomization collectionNetworkCust = null;
800 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
801 if (!commonUtils.isNullOrEmpty(networkType)) {
802 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
805 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
806 if (networkCust == null) {
807 collectionNetworkCust =
808 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
811 if (networkCust != null) {
812 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
814 networkResource = networkCust.getNetworkResource();
815 } else if (collectionNetworkCust != null) {
816 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
817 networkResource = collectionNetworkCust.getNetworkResource();
819 if (networkResource == null) {
820 String error = String.format(
821 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
822 networkType, modelCustomizationUuid);
823 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
825 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
827 logger.debug(LOG_DEBUG_MSG, networkResource);
829 String mode = networkResource.getOrchestrationMode();
830 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
832 // All Networks are orchestrated via HEAT or Neutron
833 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
834 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
835 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
836 ErrorCode.DataError.getValue(), error);
837 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
840 MavenLikeVersioning aicV = new MavenLikeVersioning();
841 aicV.setVersion(cloudSite.getCloudVersion());
842 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
843 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
846 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
847 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
849 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
850 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
851 cloudSite.getCloudVersion());
853 String error = String.format(
854 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
855 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
856 cloudSite.getCloudVersion());
857 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
858 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
861 // Validate the Network parameters.
863 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
864 if (!missing.isEmpty()) {
865 String error = "Create Network: Missing parameters: " + missing;
866 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
868 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
871 return networkResource;
875 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
876 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
877 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
878 throws NetworkException {
879 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
880 status, vlans, null, subnetIdMap);
884 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
885 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
886 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
887 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
888 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
889 status, null, routeTargets, subnetIdMap);
893 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
894 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
895 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
897 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
898 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
899 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
900 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
902 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
904 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
905 || commonUtils.isNullOrEmpty(networkNameOrId)) {
907 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
908 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
909 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
912 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
913 if (!cloudSiteOpt.isPresent()) {
914 String error = String.format(
915 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
916 networkNameOrId, cloudSiteId, tenantId);
917 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
918 // Set the detailed error as the Exception 'message'
919 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
922 // Use MsoNeutronUtils for all NEUTRON commands
925 String neutronId = null;
926 // Try Heat first, since networks may be named the same as the Heat stack
927 StackInfo heatStack = null;
929 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
930 } catch (MsoException me) {
931 me.addContext("QueryNetwork");
932 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
933 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
934 throw new NetworkException(me);
937 // Populate the outputs based on the returned Stack information
938 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
939 // Found it. Get the neutronNetworkId for further query
940 Map<String, String> sMap = new HashMap<>();
941 Map<String, Object> outputs = heatStack.getOutputs();
943 if (outputs != null) {
944 neutronId = (String) outputs.get(NETWORK_ID);
946 for (String key : outputs.keySet()) {
947 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
949 String subnetUUId = (String) outputs.get(key);
950 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
951 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
953 Map<String, String> map = getSubnetUUId(key, outputs, null);
959 subnetIdMap.value = sMap;
961 // Input ID was not a Heat stack ID. Try it directly in Neutron
962 neutronId = networkNameOrId;
966 // Query directly against the Neutron Network for the details
967 // no RouteTargets available for ContrailV2 in neutron net-show
968 // networkId is heatStackId
970 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
971 if (netInfo != null) {
972 // Found. Populate the output elements
973 networkExists.value = Boolean.TRUE;
974 if ("HEAT".equals(mode) && heatStack != null) {
975 networkId.value = heatStack.getCanonicalName();
977 networkId.value = netInfo.getId();
979 neutronNetworkId.value = netInfo.getId();
980 status.value = netInfo.getStatus();
982 vlans.value = netInfo.getVlans();
984 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
985 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
987 // Not found. Populate the status fields, leave the rest null
988 networkExists.value = Boolean.FALSE;
989 status.value = NetworkStatus.NOTFOUND;
990 neutronNetworkId.value = null;
992 vlans.value = new ArrayList<>();
994 logger.debug("Network {} not found", networkNameOrId);
996 } catch (MsoException me) {
997 me.addContext("QueryNetwork");
998 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
999 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
1000 throw new NetworkException(me);
1006 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
1009 * If the network is not found, it is treated as a success.
1011 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
1012 * network orchestration mode for each network type is declared in its catalog definition.
1014 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1015 * networkId should be the Neutron network UUID.
1017 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1018 * will require manual fallout in the client.
1021 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1022 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1023 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1024 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1025 || commonUtils.isNullOrEmpty(networkId)) {
1026 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1027 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1028 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1031 // Retrieve the Network Resource definition
1032 NetworkResource networkResource = null;
1033 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1034 if (!commonUtils.isNullOrEmpty(networkType)) {
1035 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1038 NetworkResourceCustomization nrc =
1039 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1041 networkResource = nrc.getNetworkResource();
1045 int timeoutMinutes = 118;
1047 if (networkResource != null) {
1048 logger.debug(LOG_DEBUG_MSG, networkResource.toString());
1049 mode = networkResource.getOrchestrationMode();
1050 networkResource.getHeatTemplate().getTimeoutMinutes();
1051 HeatTemplate heat = networkResource.getHeatTemplate();
1052 if (heat != null && heat.getTimeoutMinutes() != null) {
1053 if (heat.getTimeoutMinutes() < 118) {
1054 timeoutMinutes = heat.getTimeoutMinutes();
1059 if (NEUTRON_MODE.equals(mode)) {
1061 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1062 networkDeleted.value = deleted;
1063 } catch (MsoException me) {
1064 me.addContext("DeleteNetwork");
1065 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1066 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1067 throw new NetworkException(me);
1071 StackInfo stack = heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, timeoutMinutes);
1072 networkDeleted.value = stack.isOperationPerformed();
1073 } catch (MsoException me) {
1074 me.addContext("DeleteNetwork");
1075 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1076 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1077 throw new NetworkException(me);
1081 heat.updateResourceStatus(msoRequest.getRequestId(),
1082 networkDeleted.value ? NETWORK_DELETED_STATUS_MESSAGE : NETWORK_NOT_EXIST_STATUS_MESSAGE);
1083 } catch (Exception e) {
1084 logger.warn("Exception while updating infra active request", e);
1089 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1090 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1091 * to undo the creation.
1093 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1097 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1098 if (rollback == null) {
1099 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1103 // Get the elements of the VnfRollback object for easier access
1104 String cloudSiteId = rollback.getCloudId();
1105 String tenantId = rollback.getTenantId();
1106 String networkId = rollback.getNetworkStackId();
1107 String networkType = rollback.getNetworkType();
1108 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1110 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1111 // Retrieve the Network Resource definition
1112 NetworkResource networkResource = null;
1113 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1114 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1117 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1120 if (networkResource != null) {
1122 logger.debug(LOG_DEBUG_MSG, networkResource);
1124 mode = networkResource.getOrchestrationMode();
1127 if (rollback.getNetworkCreated()) {
1128 if (NEUTRON_MODE.equals(mode)) {
1130 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1131 } catch (MsoException me) {
1132 me.addContext("RollbackNetwork");
1133 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1134 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
1135 cloudSiteId, tenantId, me);
1136 throw new NetworkException(me);
1140 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
1141 } catch (MsoException me) {
1142 me.addContext("RollbackNetwork");
1143 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1144 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
1145 cloudSiteId, tenantId, me);
1146 throw new NetworkException(me);
1152 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1153 List<Integer> vlans, List<RouteTarget> routeTargets) {
1155 StringBuilder missing = new StringBuilder();
1156 if (commonUtils.isNullOrEmpty(networkName)) {
1157 missing.append("networkName");
1161 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1162 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1163 missing.append(sep).append("physicalNetworkName");
1166 if (vlans == null || vlans.isEmpty()) {
1167 missing.append(sep).append(VLANS);
1171 return missing.toString();
1174 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1175 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1176 boolean aic3template) {
1177 // Build the common set of HEAT template parameters
1178 Map<String, Object> stackParams = new HashMap<>();
1179 stackParams.put("network_name", networkName);
1181 if (neutronNetworkType == NetworkType.PROVIDER) {
1182 // For Provider type
1183 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1184 stackParams.put("vlan", vlans.get(0).toString());
1185 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1186 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1187 // It supports all ProviderNet properties except segmentation_id, and adds a
1188 // comma-separated-list of VLANs as a "segments" property.
1189 // Note that this does not match the Neutron definition of Multi-Provider network,
1190 // which contains a list of 'segments', each having physical_network, network_type,
1191 // and segmentation_id.
1192 StringBuilder buf = new StringBuilder();
1194 for (Integer vlan : vlans) {
1195 buf.append(sep).append(vlan.toString());
1198 String csl = buf.toString();
1200 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1201 stackParams.put(VLANS, csl);
1203 if (routeTargets != null) {
1205 String rtGlobal = "";
1206 String rtImport = "";
1207 String rtExport = "";
1209 for (RouteTarget rt : routeTargets) {
1210 boolean rtIsNull = false;
1212 String routeTarget = rt.getRouteTarget();
1213 String routeTargetRole = rt.getRouteTargetRole();
1214 logger.debug("Checking for an actually null route target: {}", rt);
1215 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1217 if (routeTargetRole == null || routeTargetRole.equals("")
1218 || routeTargetRole.equalsIgnoreCase("null"))
1224 logger.debug("Input RT:{}", rt);
1225 String role = rt.getRouteTargetRole();
1226 String rtValue = rt.getRouteTarget();
1228 if ("IMPORT".equalsIgnoreCase(role)) {
1229 sep = rtImport.isEmpty() ? "" : ",";
1230 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1231 } else if ("EXPORT".equalsIgnoreCase(role)) {
1232 sep = rtExport.isEmpty() ? "" : ",";
1233 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1234 } else // covers BOTH, empty etc
1236 sep = rtGlobal.isEmpty() ? "" : ",";
1237 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1243 if (!rtImport.isEmpty()) {
1244 stackParams.put("route_targets_import", rtImport);
1246 if (!rtExport.isEmpty()) {
1247 stackParams.put("route_targets_export", rtExport);
1249 if (!rtGlobal.isEmpty()) {
1250 stackParams.put("route_targets", rtGlobal);
1253 if (commonUtils.isNullOrEmpty(shared)) {
1254 stackParams.put("shared", "False");
1256 stackParams.put("shared", shared);
1258 if (commonUtils.isNullOrEmpty(external)) {
1259 stackParams.put("external", "False");
1261 stackParams.put("external", external);
1269 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1270 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1271 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1272 * "network_policy_refs_data_sequence_minor": "0" } } ]
1274 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1275 // Resource Property
1276 List<ContrailPolicyRef> prlist = new ArrayList<>();
1279 if (pFqdns != null) {
1280 for (String pf : pFqdns) {
1281 if (!commonUtils.isNullOrEmpty(pf)) {
1282 ContrailPolicyRef pr = new ContrailPolicyRef();
1283 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1286 logger.debug("Contrail PolicyRefs Data:{}", pr);
1291 String error = "Null pFqdns at start of mergePolicyRefs";
1292 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1294 throw new MsoAdapterException(error);
1297 JsonNode node = null;
1299 ObjectMapper mapper = new ObjectMapper();
1300 node = mapper.convertValue(prlist, JsonNode.class);
1301 String jsonString = mapper.writeValueAsString(prlist);
1302 logger.debug("Json PolicyRefs Data:{}", jsonString);
1303 } catch (Exception e) {
1304 String error = "Error creating JsonNode for policyRefs Data";
1305 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1307 throw new MsoAdapterException(error);
1309 // update parameters
1310 if (pFqdns != null && node != null) {
1311 StringBuilder buf = new StringBuilder();
1313 for (String pf : pFqdns) {
1314 if (!commonUtils.isNullOrEmpty(pf)) {
1315 buf.append(sep).append(pf);
1319 String csl = buf.toString();
1320 stackParams.put("policy_refs", csl);
1321 stackParams.put("policy_refsdata", node);
1324 logger.debug("StackParams updated with policy refs");
1328 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1330 // update parameters
1331 if (rtFqdns != null) {
1332 StringBuilder buf = new StringBuilder();
1334 for (String rtf : rtFqdns) {
1335 if (!commonUtils.isNullOrEmpty(rtf)) {
1336 buf.append(sep).append(rtf);
1340 String csl = buf.toString();
1341 stackParams.put("route_table_refs", csl);
1344 logger.debug("StackParams updated with route_table refs");
1350 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1351 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1352 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1353 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1354 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1355 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1356 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1357 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1358 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1359 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1360 * "host_routes": null }
1362 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1363 throws MsoException {
1365 // Resource Property
1366 List<ContrailSubnet> cslist = new ArrayList<>();
1367 for (Subnet subnet : subnets) {
1368 logger.debug("Input Subnet:{}", subnet);
1369 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1370 logger.debug("Contrail Subnet:{}", cs);
1374 JsonNode node = null;
1376 ObjectMapper mapper = new ObjectMapper();
1377 node = mapper.convertValue(cslist, JsonNode.class);
1378 String jsonString = mapper.writeValueAsString(cslist);
1379 logger.debug("Json Subnet List:{}", jsonString);
1380 } catch (Exception e) {
1381 String error = "Error creating JsonNode from input subnets";
1382 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1383 throw new MsoAdapterException(error);
1385 // update parameters
1387 stackParams.put("subnet_list", node);
1389 // Outputs - All subnets are in one ipam_subnets structure
1390 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1391 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1393 // append outputs in heatTemplate
1394 int outputsIdx = heatTemplate.indexOf("outputs:");
1395 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1396 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1397 return heatTemplate;
1401 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1403 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1404 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1407 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1408 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1409 * " end: %poolend%\n";
1413 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1414 + " value: {get_resource: subnet_%subnetId%}\n";
1418 StringBuilder resourcesBuf = new StringBuilder();
1419 StringBuilder outputsBuf = new StringBuilder();
1420 for (Subnet subnet : subnets) {
1422 // build template for each subnet
1423 curR = resourceTempl;
1424 if (subnet.getSubnetId() != null) {
1425 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1427 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1428 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1429 throw new MsoAdapterException(error);
1432 if (subnet.getSubnetName() != null) {
1433 curR = curR.replace("%name%", subnet.getSubnetName());
1435 curR = curR.replace("%name%", subnet.getSubnetId());
1438 if (subnet.getCidr() != null) {
1439 curR = curR.replace("%cidr%", subnet.getCidr());
1441 String error = "Missing Required cidr for subnet in HEAT Template";
1442 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1443 throw new MsoAdapterException(error);
1446 if (subnet.getIpVersion() != null) {
1447 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1449 if (subnet.getEnableDHCP() != null) {
1450 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1452 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1453 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1456 if (subnet.getAllocationPools() != null) {
1457 StringBuilder tempBuf = new StringBuilder();
1458 tempBuf.append(curR);
1459 tempBuf.append(" allocation_pools:\n");
1460 for (Pool pool : subnet.getAllocationPools()) {
1461 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1462 tempBuf.append(" - start: ");
1463 tempBuf.append(pool.getStart());
1464 tempBuf.append("\n end: ");
1465 tempBuf.append(pool.getEnd());
1466 tempBuf.append("\n");
1469 curR = tempBuf.toString();
1472 resourcesBuf.append(curR);
1475 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1477 outputsBuf.append(curO);
1479 // append resources and outputs in heatTemplate
1480 logger.debug("Tempate initial:{}", heatTemplate);
1481 int outputsIdx = heatTemplate.indexOf("outputs:");
1482 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1483 int resourcesIdx = heatTemplate.indexOf("resources:");
1484 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1486 logger.debug("Template updated with all subnets:{}", heatTemplate);
1487 return heatTemplate;
1490 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1492 Map<String, String> sMap = new HashMap<>();
1495 Object obj = outputs.get(key);
1496 ObjectMapper mapper = new ObjectMapper();
1497 String jStr = mapper.writeValueAsString(obj);
1498 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1500 JsonNode rootNode = mapper.readTree(jStr);
1501 if (rootNode != null) {
1502 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1503 logger.debug("Output Subnet Node {}", sNode);
1504 String name = sNode.path("subnet_name").textValue();
1505 String uuid = sNode.path("subnet_uuid").textValue();
1506 String aaiId = name; // default
1507 // try to find aaiId for name in input subnetList
1508 if (subnets != null) {
1509 for (Subnet subnet : subnets) {
1510 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1511 && subnet.getSubnetName().equals(name)) {
1512 aaiId = subnet.getSubnetId();
1517 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1520 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1521 ErrorCode.DataError.getValue());
1523 } catch (Exception e) {
1524 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1525 ErrorCode.DataError.getValue(), e);
1528 logger.debug("Return sMap {}", sMap);
1532 private static String insertStr(String template, String snippet, int index) {
1534 String updatedTemplate;
1536 logger.debug("Index:{} Snippet:{}", index, snippet);
1538 String templateBeg = template.substring(0, index);
1539 String templateEnd = template.substring(index);
1541 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1543 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1544 return updatedTemplate;