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 com.fasterxml.jackson.databind.JsonNode;
28 import com.fasterxml.jackson.databind.ObjectMapper;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
33 import java.util.Optional;
34 import javax.jws.WebService;
35 import javax.xml.ws.Holder;
36 import org.onap.so.logger.LoggingAnchor;
37 import org.onap.so.adapters.network.beans.ContrailPolicyRef;
38 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
39 import org.onap.so.adapters.network.beans.ContrailSubnet;
40 import org.onap.so.adapters.network.exceptions.NetworkException;
41 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
42 import org.onap.so.cloud.CloudConfig;
43 import org.onap.so.db.catalog.beans.CloudSite;
44 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
45 import org.onap.so.db.catalog.beans.HeatTemplate;
46 import org.onap.so.db.catalog.beans.NetworkResource;
47 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
48 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
49 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
50 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
51 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
52 import org.onap.so.entity.MsoRequest;
53 import org.onap.so.logger.ErrorCode;
54 import org.onap.so.logger.MessageEnum;
55 import org.onap.so.openstack.beans.HeatStatus;
56 import org.onap.so.openstack.beans.NetworkInfo;
57 import org.onap.so.openstack.beans.NetworkRollback;
58 import org.onap.so.openstack.beans.NetworkStatus;
59 import org.onap.so.openstack.beans.Pool;
60 import org.onap.so.openstack.beans.RouteTarget;
61 import org.onap.so.openstack.beans.StackInfo;
62 import org.onap.so.openstack.beans.Subnet;
63 import org.onap.so.openstack.exceptions.MsoAdapterException;
64 import org.onap.so.openstack.exceptions.MsoException;
65 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
66 import org.onap.so.openstack.utils.MsoCommonUtils;
67 import org.onap.so.openstack.utils.MsoHeatUtils;
68 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
69 import org.onap.so.openstack.utils.MsoNeutronUtils;
70 import org.onap.so.openstack.utils.MsoNeutronUtils.NetworkType;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73 import org.springframework.beans.factory.annotation.Autowired;
74 import org.springframework.core.env.Environment;
75 import org.springframework.stereotype.Component;
76 import org.springframework.transaction.annotation.Transactional;
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: {}";
96 private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
99 private CloudConfig cloudConfig;
101 private Environment environment;
103 private MsoNeutronUtils neutron;
105 private MsoHeatUtils heat;
107 private MsoHeatUtilsWithUpdate heatWithUpdate;
109 private MsoCommonUtils commonUtils;
112 private NetworkResourceCustomizationRepository networkCustomRepo;
115 private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
118 private NetworkResourceRepository networkResourceRepo;
120 public MsoNetworkAdapterImpl() {}
123 * Health Check web method. Does nothing but return to show the adapter is deployed.
126 public void healthCheck() {
127 logger.debug("Health check call in Network Adapter");
131 * Do not use this constructor or the msoPropertiesFactory will be NULL.
133 * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
137 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
138 String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
139 Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
140 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
141 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
142 Holder<String> networkFqdn = new Holder<>();
143 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
144 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
145 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
149 public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
150 String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
151 String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
152 Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
153 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
154 Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
155 throws NetworkException {
156 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
157 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
158 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
162 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
163 * the specified cloud and tenant. The tenant must exist at the time this service is called.
165 * If a network with the same name already exists, this can be considered a success or failure, depending on the
166 * value of the 'failIfExists' parameter.
168 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
169 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
170 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
172 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
173 * multiple VLANs on the same physical network.
175 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
176 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
177 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
179 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
180 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
181 * created but the orchestration fails on a subsequent operation.
184 private void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
185 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
186 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
187 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
188 Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
189 Holder<NetworkRollback> rollback) throws NetworkException {
190 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
192 // Will capture execution time for metrics
193 long startTime = System.currentTimeMillis();
195 // Build a default rollback object (no actions performed)
196 NetworkRollback networkRollback = new NetworkRollback();
197 networkRollback.setCloudId(cloudSiteId);
198 networkRollback.setTenantId(tenantId);
199 networkRollback.setMsoRequest(msoRequest);
200 networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
202 // tenant query is not required here.
203 // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
204 // So this is just catching that error in a bit more obvious way up front.
206 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
207 if (!cloudSiteOpt.isPresent()) {
208 String error = String.format(
209 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
210 networkName, cloudSiteId, tenantId);
211 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
212 // Set the detailed error as the Exception 'message'
213 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
217 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
218 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
219 String mode = networkResource.getOrchestrationMode();
220 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
222 if (NEUTRON_MODE.equals(mode)) {
224 // Use an MsoNeutronUtils for all neutron commands
226 // See if the Network already exists (by name)
227 NetworkInfo netInfo = null;
228 long queryNetworkStarttime = System.currentTimeMillis();
230 netInfo = neutron.queryNetwork(networkName, tenantId, cloudSiteId);
231 } catch (MsoException me) {
233 "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
234 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkName,
235 cloudSiteId, tenantId, me);
236 me.addContext(CREATE_NETWORK_CONTEXT);
237 throw new NetworkException(me);
240 if (netInfo != null) {
241 // Exists. If that's OK, return success with the network ID.
242 // Otherwise, return an exception.
243 if (failIfExists != null && failIfExists) {
244 String error = String.format("Create Nework: Network %s already exists in %s/%s with ID %s",
245 networkName, cloudSiteId, tenantId, netInfo.getId());
246 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
247 ErrorCode.DataError.getValue(), error);
248 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
250 // Populate the outputs from the existing network.
251 networkId.value = netInfo.getId();
252 neutronNetworkId.value = netInfo.getId();
253 rollback.value = networkRollback; // Default rollback - no updates performed
254 logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
255 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), netInfo.getStatus());
260 long createNetworkStarttime = System.currentTimeMillis();
262 netInfo = neutron.createNetwork(cloudSiteId, tenantId, neutronNetworkType, networkName,
263 physicalNetworkName, vlans);
264 } catch (MsoException me) {
265 me.addContext(CREATE_NETWORK_CONTEXT);
266 logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
267 ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
269 throw new NetworkException(me);
272 // Note: ignoring MsoNetworkAlreadyExists because we already checked.
274 // If reach this point, network creation is successful.
275 // Since directly created via Neutron, networkId tracked by MSO is the same
276 // as the neutron network ID.
277 networkId.value = netInfo.getId();
278 neutronNetworkId.value = netInfo.getId();
280 networkRollback.setNetworkCreated(true);
281 networkRollback.setNetworkId(netInfo.getId());
282 networkRollback.setNeutronNetworkId(netInfo.getId());
283 networkRollback.setNetworkType(networkType);
285 logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
286 } else if ("HEAT".equals(mode)) {
288 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
289 if (heatTemplate == null) {
290 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
291 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
293 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
296 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
298 // "Fix" the template if it has CR/LF (getting this from Oracle)
299 String template = heatTemplate.getHeatTemplate();
300 template = template.replaceAll("\r\n", "\n");
302 boolean aic3template = false;
303 String aic3nw = AIC3_NW;
305 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
307 if (template.contains(aic3nw))
310 // First, look up to see if the Network already exists (by name).
311 // For HEAT orchestration of networks, the stack name will always match the network name
312 StackInfo heatStack = null;
313 long queryNetworkStarttime = System.currentTimeMillis();
315 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
316 } catch (MsoException me) {
317 me.addContext(CREATE_NETWORK_CONTEXT);
318 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
319 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName, cloudSiteId,
321 throw new NetworkException(me);
324 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
325 // Stack exists. Return success or error depending on input directive
326 if (failIfExists != null && failIfExists) {
327 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
328 cloudSiteId, tenantId, heatStack.getCanonicalName());
329 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
330 ErrorCode.DataError.getValue(), error);
331 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
333 // Populate the outputs from the existing stack.
334 networkId.value = heatStack.getCanonicalName();
335 Map<String, String> sMap = new HashMap<>();
336 if (heatStack.getOutputs() != null) {
337 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
338 rollback.value = networkRollback; // Default rollback - no updates performed
340 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
342 Map<String, Object> outputs = heatStack.getOutputs();
344 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
345 String key = entry.getKey();
346 if (key != null && key.startsWith("subnet")) {
347 if (aic3template) // one subnet_id output
349 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
351 } else // multiples subnet_%aaid% outputs
353 String subnetUUId = (String) outputs.get(key);
354 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
359 subnetIdMap.value = sMap;
360 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
361 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
362 networkName, cloudSiteId, tenantId);
367 // Ready to deploy the new Network
368 // Build the common set of HEAT template parameters
369 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
370 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
372 // Validate (and update) the input parameters against the DB definition
373 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
374 // and inputs were already validated.
376 stackParams = heat.validateStackParams(stackParams, heatTemplate);
377 } catch (IllegalArgumentException e) {
378 String error = "Create Network: Configuration Error: " + e.getMessage();
379 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
380 // Input parameters were not valid
381 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
384 if (subnets != null) {
387 template = mergeSubnetsAIC3(template, subnets, stackParams);
389 template = mergeSubnets(template, subnets);
391 } catch (MsoException me) {
392 me.addContext(CREATE_NETWORK_CONTEXT);
393 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
394 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
395 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
396 throw new NetworkException(me);
400 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
402 mergePolicyRefs(policyFqdns, stackParams);
403 } catch (MsoException me) {
404 me.addContext(CREATE_NETWORK_CONTEXT);
405 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
406 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
407 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
408 throw new NetworkException(me);
412 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
414 mergeRouteTableRefs(routeTableFqdns, stackParams);
415 } catch (MsoException me) {
416 me.addContext(CREATE_NETWORK_CONTEXT);
417 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
418 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
419 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
420 throw new NetworkException(me);
424 // Deploy the network stack
425 // Ignore MsoStackAlreadyExists exception because we already checked.
429 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template,
430 stackParams, true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue());
431 } catch (MsoException me) {
432 me.addContext(CREATE_NETWORK_CONTEXT);
433 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
434 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
435 throw new NetworkException(me);
438 // Reach this point if createStack is successful.
440 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
441 // and the neutronNetworkId is the network UUID returned in stack outputs.
442 networkId.value = heatStack.getCanonicalName();
443 if (heatStack.getOutputs() != null) {
444 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
446 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
449 Map<String, Object> outputs = heatStack.getOutputs();
450 Map<String, String> sMap = new HashMap<>();
451 if (outputs != null) {
452 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
453 String key = entry.getKey();
454 if (key != null && key.startsWith("subnet")) {
455 if (aic3template) // one subnet output expected
457 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
459 } else // multiples subnet_%aaid% outputs allowed
461 String subnetUUId = (String) outputs.get(key);
462 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
466 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
468 subnetIdMap.value = sMap;
470 rollback.value = networkRollback;
471 // Populate remaining rollback info and response parameters.
472 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
473 networkRollback.setNetworkCreated(true);
474 networkRollback.setNetworkType(networkType);
476 logger.debug("Network {} successfully created via HEAT", networkName);
483 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
484 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
485 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
486 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
487 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
488 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
494 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
495 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
496 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
497 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
498 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
499 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
500 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
505 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
506 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
507 * remove a VLAN), but other properties may be updated as well.
509 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
510 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
511 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
513 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
514 * VLANs on the same physical network.
516 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
517 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
518 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
520 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
521 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
522 * a subsequent operation.
524 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
525 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
526 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
527 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
528 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
530 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
531 cloudSiteId, tenantId);
533 // Will capture execution time for metrics
534 long startTime = System.currentTimeMillis();
536 // Build a default rollback object (no actions performed)
537 NetworkRollback networkRollback = new NetworkRollback();
538 networkRollback.setCloudId(cloudSiteId);
539 networkRollback.setTenantId(tenantId);
540 networkRollback.setMsoRequest(msoRequest);
542 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
543 if (!cloudSiteOpt.isPresent()) {
544 String error = String.format(
545 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
546 networkName, cloudSiteId, tenantId);
547 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
548 // Set the detailed error as the Exception 'message'
549 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
554 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
555 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
556 String mode = networkResource.getOrchestrationMode();
557 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
559 // Use an MsoNeutronUtils for all Neutron commands
561 if (NEUTRON_MODE.equals(mode)) {
563 // Verify that the Network exists
564 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
565 NetworkInfo netInfo = null;
566 long queryNetworkStarttime = System.currentTimeMillis();
568 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
569 } catch (MsoException me) {
570 me.addContext(UPDATE_NETWORK_CONTEXT);
571 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
572 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
573 throw new NetworkException(me);
576 if (netInfo == null) {
577 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
578 cloudSiteId, tenantId);
579 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
580 ErrorCode.BusinessProcesssError.getValue(), error);
581 // Does not exist. Throw an exception (can't update a non-existent network)
582 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
584 long updateNetworkStarttime = System.currentTimeMillis();
586 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
587 physicalNetworkName, vlans);
588 } catch (MsoException me) {
589 me.addContext(UPDATE_NETWORK_CONTEXT);
590 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
591 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
592 throw new NetworkException(me);
595 // Add the network ID and previously queried vlans to the rollback object
596 networkRollback.setNetworkId(netInfo.getId());
597 networkRollback.setNeutronNetworkId(netInfo.getId());
598 networkRollback.setNetworkType(networkType);
599 // Save previous parameters
600 networkRollback.setNetworkName(netInfo.getName());
601 networkRollback.setPhysicalNetwork(netInfo.getProvider());
602 networkRollback.setVlans(netInfo.getVlans());
604 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
605 } else if ("HEAT".equals(mode)) {
607 // First, look up to see that the Network already exists.
608 // For Heat-based orchestration, the networkId is the network Stack ID.
609 StackInfo heatStack = null;
610 long queryStackStarttime = System.currentTimeMillis();
612 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
613 } catch (MsoException me) {
614 me.addContext(UPDATE_NETWORK_CONTEXT);
615 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
616 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
617 throw new NetworkException(me);
620 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
621 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
622 cloudSiteId, tenantId);
623 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
625 // Network stack does not exist. Return an error
626 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
629 // Get the previous parameters for rollback
630 Map<String, Object> heatParams = heatStack.getParameters();
632 String previousNetworkName = (String) heatParams.get("network_name");
633 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
635 List<Integer> previousVlans = new ArrayList<>();
636 String vlansParam = (String) heatParams.get(VLANS);
637 if (vlansParam != null) {
638 for (String vlan : vlansParam.split(",")) {
640 previousVlans.add(Integer.parseInt(vlan));
641 } catch (NumberFormatException e) {
642 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
643 ErrorCode.DataError.getValue(), vlansParam, e);
647 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
649 // Ready to deploy the updated Network via Heat
652 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
653 if (heatTemplate == null) {
654 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
655 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
657 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
660 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
662 // "Fix" the template if it has CR/LF (getting this from Oracle)
663 String template = heatTemplate.getHeatTemplate();
664 template = template.replaceAll("\r\n", "\n");
666 boolean aic3template = false;
667 String aic3nw = AIC3_NW;
669 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
671 if (template.contains(aic3nw))
674 // Build the common set of HEAT template parameters
675 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
676 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
678 // Validate (and update) the input parameters against the DB definition
679 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
681 stackParams = heat.validateStackParams(stackParams, heatTemplate);
682 } catch (IllegalArgumentException e) {
683 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
684 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
685 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
688 if (subnets != null) {
691 template = mergeSubnetsAIC3(template, subnets, stackParams);
693 template = mergeSubnets(template, subnets);
695 } catch (MsoException me) {
696 me.addContext(UPDATE_NETWORK_CONTEXT);
697 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
698 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
699 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
700 throw new NetworkException(me);
704 if (policyFqdns != null && aic3template) {
706 mergePolicyRefs(policyFqdns, stackParams);
707 } catch (MsoException me) {
708 me.addContext(UPDATE_NETWORK_CONTEXT);
709 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
710 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
711 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
712 throw new NetworkException(me);
716 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
718 mergeRouteTableRefs(routeTableFqdns, stackParams);
719 } catch (MsoException me) {
720 me.addContext(UPDATE_NETWORK_CONTEXT);
721 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
722 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
723 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
724 throw new NetworkException(me);
728 // Update the network stack
729 // Ignore MsoStackNotFound exception because we already checked.
730 long updateStackStarttime = System.currentTimeMillis();
732 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
733 stackParams, true, heatTemplate.getTimeoutMinutes());
734 } catch (MsoException me) {
735 me.addContext(UPDATE_NETWORK_CONTEXT);
736 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
737 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
738 throw new NetworkException(me);
741 Map<String, Object> outputs = heatStack.getOutputs();
742 Map<String, String> sMap = new HashMap<>();
743 if (outputs != null) {
744 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
745 String key = entry.getKey();
746 if (key != null && key.startsWith("subnet")) {
747 if (aic3template) // one subnet output expected
749 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
751 } else // multiples subnet_%aaid% outputs allowed
753 String subnetUUId = (String) outputs.get(key);
754 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
759 subnetIdMap.value = sMap;
761 // Reach this point if createStack is successful.
762 // Populate remaining rollback info and response parameters.
763 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
764 if (null != outputs) {
765 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
767 logger.debug("outputs is NULL");
769 networkRollback.setNetworkType(networkType);
770 // Save previous parameters
771 networkRollback.setNetworkName(previousNetworkName);
772 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
773 networkRollback.setVlans(previousVlans);
775 rollback.value = networkRollback;
777 logger.debug("Network {} successfully updated via HEAT", networkId);
783 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
784 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
785 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
786 // Retrieve the Network Resource definition
787 NetworkResource networkResource = null;
788 NetworkResourceCustomization networkCust = null;
789 CollectionNetworkResourceCustomization collectionNetworkCust = null;
790 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
791 if (!commonUtils.isNullOrEmpty(networkType)) {
792 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
795 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
796 if (networkCust == null) {
797 collectionNetworkCust =
798 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
801 if (networkCust != null) {
802 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
804 networkResource = networkCust.getNetworkResource();
805 } else if (collectionNetworkCust != null) {
806 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
807 networkResource = collectionNetworkCust.getNetworkResource();
809 if (networkResource == null) {
810 String error = String.format(
811 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
812 networkType, modelCustomizationUuid);
813 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
815 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
817 logger.debug(LOG_DEBUG_MSG, networkResource);
819 String mode = networkResource.getOrchestrationMode();
820 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
822 // All Networks are orchestrated via HEAT or Neutron
823 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
824 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
825 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
826 ErrorCode.DataError.getValue(), error);
827 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
830 MavenLikeVersioning aicV = new MavenLikeVersioning();
831 aicV.setVersion(cloudSite.getCloudVersion());
832 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
833 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
836 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
837 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
839 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
840 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
841 cloudSite.getCloudVersion());
843 String error = String.format(
844 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
845 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
846 cloudSite.getCloudVersion());
847 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
848 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
851 // Validate the Network parameters.
853 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
854 if (!missing.isEmpty()) {
855 String error = "Create Network: Missing parameters: " + missing;
856 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
858 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
861 return networkResource;
865 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
866 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
867 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
868 throws NetworkException {
869 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
870 status, vlans, null, subnetIdMap);
874 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
875 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
876 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
877 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
878 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
879 status, null, routeTargets, subnetIdMap);
883 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
884 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
885 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
887 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
888 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
889 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
890 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
892 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
894 // Will capture execution time for metrics
895 long startTime = System.currentTimeMillis();
897 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
898 || commonUtils.isNullOrEmpty(networkNameOrId)) {
900 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
901 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
902 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
905 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
906 if (!cloudSiteOpt.isPresent()) {
907 String error = String.format(
908 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
909 networkNameOrId, cloudSiteId, tenantId);
910 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
911 // Set the detailed error as the Exception 'message'
912 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
915 // Use MsoNeutronUtils for all NEUTRON commands
918 String neutronId = null;
919 // Try Heat first, since networks may be named the same as the Heat stack
920 StackInfo heatStack = null;
921 long queryStackStarttime = System.currentTimeMillis();
923 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
924 } catch (MsoException me) {
925 me.addContext("QueryNetwork");
926 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
927 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
928 throw new NetworkException(me);
931 // Populate the outputs based on the returned Stack information
932 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
933 // Found it. Get the neutronNetworkId for further query
934 Map<String, String> sMap = new HashMap<>();
935 Map<String, Object> outputs = heatStack.getOutputs();
937 if (outputs != null) {
938 neutronId = (String) outputs.get(NETWORK_ID);
940 for (String key : outputs.keySet()) {
941 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
943 String subnetUUId = (String) outputs.get(key);
944 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
945 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
947 Map<String, String> map = getSubnetUUId(key, outputs, null);
953 subnetIdMap.value = sMap;
955 // Input ID was not a Heat stack ID. Try it directly in Neutron
956 neutronId = networkNameOrId;
960 // Query directly against the Neutron Network for the details
961 // no RouteTargets available for ContrailV2 in neutron net-show
962 // networkId is heatStackId
963 long queryNetworkStarttime = System.currentTimeMillis();
965 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
966 if (netInfo != null) {
967 // Found. Populate the output elements
968 networkExists.value = Boolean.TRUE;
969 if ("HEAT".equals(mode) && heatStack != null) {
970 networkId.value = heatStack.getCanonicalName();
972 networkId.value = netInfo.getId();
974 neutronNetworkId.value = netInfo.getId();
975 status.value = netInfo.getStatus();
977 vlans.value = netInfo.getVlans();
979 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
980 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
982 // Not found. Populate the status fields, leave the rest null
983 networkExists.value = Boolean.FALSE;
984 status.value = NetworkStatus.NOTFOUND;
985 neutronNetworkId.value = null;
987 vlans.value = new ArrayList<>();
989 logger.debug("Network {} not found", networkNameOrId);
991 } catch (MsoException me) {
992 me.addContext("QueryNetwork");
993 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
994 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
995 throw new NetworkException(me);
1001 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
1004 * If the network is not found, it is treated as a success.
1006 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
1007 * network orchestration mode for each network type is declared in its catalog definition.
1009 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1010 * networkId should be the Neutron network UUID.
1012 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1013 * will require manual fallout in the client.
1016 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1017 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1019 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1021 // Will capture execution time for metrics
1022 long startTime = System.currentTimeMillis();
1025 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1026 || commonUtils.isNullOrEmpty(networkId)) {
1027 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1028 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1029 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1032 // Retrieve the Network Resource definition
1033 NetworkResource networkResource = null;
1035 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1036 if (!commonUtils.isNullOrEmpty(networkType)) {
1037 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1040 NetworkResourceCustomization nrc =
1041 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1043 networkResource = nrc.getNetworkResource();
1047 if (networkResource != null) {
1048 logger.debug(LOG_DEBUG_MSG, networkResource);
1050 mode = networkResource.getOrchestrationMode();
1053 if (NEUTRON_MODE.equals(mode)) {
1055 // Use MsoNeutronUtils for all NEUTRON commands
1056 long deleteNetworkStarttime = System.currentTimeMillis();
1058 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1059 // was not found. So don't bother to query first.
1060 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1061 networkDeleted.value = deleted;
1062 } catch (MsoException me) {
1063 me.addContext("DeleteNetwork");
1064 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1065 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1066 throw new NetworkException(me);
1068 } else { // DEFAULT to ("HEAT".equals (mode))
1069 long deleteStackStarttime = System.currentTimeMillis();
1072 // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack
1074 // So query first to report back if stack WAS deleted or just NOTOFUND
1075 StackInfo heatStack = null;
1076 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId);
1077 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
1078 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true);
1079 networkDeleted.value = true;
1081 networkDeleted.value = false;
1083 } catch (MsoException me) {
1084 me.addContext("DeleteNetwork");
1085 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1086 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1087 throw new NetworkException(me);
1092 // On success, nothing is returned.
1097 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1098 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1099 * to undo the creation.
1101 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1105 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1106 // Will capture execution time for metrics
1107 long startTime = System.currentTimeMillis();
1109 if (rollback == null) {
1110 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1114 // Get the elements of the VnfRollback object for easier access
1115 String cloudSiteId = rollback.getCloudId();
1116 String tenantId = rollback.getTenantId();
1117 String networkId = rollback.getNetworkStackId();
1118 String networkType = rollback.getNetworkType();
1119 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1121 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1124 // Retrieve the Network Resource definition
1125 NetworkResource networkResource = null;
1126 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1127 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1130 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1133 if (networkResource != null) {
1135 logger.debug(LOG_DEBUG_MSG, networkResource);
1137 mode = networkResource.getOrchestrationMode();
1140 if (rollback.getNetworkCreated()) {
1141 // Rolling back a newly created network, so delete it.
1142 if (NEUTRON_MODE.equals(mode)) {
1143 // Use MsoNeutronUtils for all NEUTRON commands
1144 long deleteNetworkStarttime = System.currentTimeMillis();
1146 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1147 // was not found. So don't bother to query first.
1148 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1149 } catch (MsoException me) {
1150 me.addContext("RollbackNetwork");
1151 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1152 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1153 cloudSiteId, tenantId, me);
1154 throw new NetworkException(me);
1156 } else { // DEFAULT to if ("HEAT".equals (mode))
1157 long deleteStackStarttime = System.currentTimeMillis();
1159 // The deleteStack function in MsoHeatUtils returns success if the stack
1160 // was not found. So don't bother to query first.
1161 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true);
1162 } catch (MsoException me) {
1163 me.addContext("RollbackNetwork");
1164 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1165 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1166 cloudSiteId, tenantId, me);
1167 throw new NetworkException(me);
1175 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1176 List<Integer> vlans, List<RouteTarget> routeTargets) {
1178 StringBuilder missing = new StringBuilder();
1179 if (commonUtils.isNullOrEmpty(networkName)) {
1180 missing.append("networkName");
1184 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1185 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1186 missing.append(sep).append("physicalNetworkName");
1189 if (vlans == null || vlans.isEmpty()) {
1190 missing.append(sep).append(VLANS);
1194 return missing.toString();
1197 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1198 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1199 boolean aic3template) {
1200 // Build the common set of HEAT template parameters
1201 Map<String, Object> stackParams = new HashMap<>();
1202 stackParams.put("network_name", networkName);
1204 if (neutronNetworkType == NetworkType.PROVIDER) {
1205 // For Provider type
1206 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1207 stackParams.put("vlan", vlans.get(0).toString());
1208 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1209 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1210 // It supports all ProviderNet properties except segmentation_id, and adds a
1211 // comma-separated-list of VLANs as a "segments" property.
1212 // Note that this does not match the Neutron definition of Multi-Provider network,
1213 // which contains a list of 'segments', each having physical_network, network_type,
1214 // and segmentation_id.
1215 StringBuilder buf = new StringBuilder();
1217 for (Integer vlan : vlans) {
1218 buf.append(sep).append(vlan.toString());
1221 String csl = buf.toString();
1223 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1224 stackParams.put(VLANS, csl);
1226 if (routeTargets != null) {
1228 String rtGlobal = "";
1229 String rtImport = "";
1230 String rtExport = "";
1232 for (RouteTarget rt : routeTargets) {
1233 boolean rtIsNull = false;
1235 String routeTarget = rt.getRouteTarget();
1236 String routeTargetRole = rt.getRouteTargetRole();
1237 logger.debug("Checking for an actually null route target: {}", rt);
1238 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1240 if (routeTargetRole == null || routeTargetRole.equals("")
1241 || routeTargetRole.equalsIgnoreCase("null"))
1247 logger.debug("Input RT:{}", rt);
1248 String role = rt.getRouteTargetRole();
1249 String rtValue = rt.getRouteTarget();
1251 if ("IMPORT".equalsIgnoreCase(role)) {
1252 sep = rtImport.isEmpty() ? "" : ",";
1253 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1254 } else if ("EXPORT".equalsIgnoreCase(role)) {
1255 sep = rtExport.isEmpty() ? "" : ",";
1256 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1257 } else // covers BOTH, empty etc
1259 sep = rtGlobal.isEmpty() ? "" : ",";
1260 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1266 if (!rtImport.isEmpty()) {
1267 stackParams.put("route_targets_import", rtImport);
1269 if (!rtExport.isEmpty()) {
1270 stackParams.put("route_targets_export", rtExport);
1272 if (!rtGlobal.isEmpty()) {
1273 stackParams.put("route_targets", rtGlobal);
1276 if (commonUtils.isNullOrEmpty(shared)) {
1277 stackParams.put("shared", "False");
1279 stackParams.put("shared", shared);
1281 if (commonUtils.isNullOrEmpty(external)) {
1282 stackParams.put("external", "False");
1284 stackParams.put("external", external);
1292 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1293 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1294 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1295 * "network_policy_refs_data_sequence_minor": "0" } } ]
1297 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1298 // Resource Property
1299 List<ContrailPolicyRef> prlist = new ArrayList<>();
1302 if (pFqdns != null) {
1303 for (String pf : pFqdns) {
1304 if (!commonUtils.isNullOrEmpty(pf)) {
1305 ContrailPolicyRef pr = new ContrailPolicyRef();
1306 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1309 logger.debug("Contrail PolicyRefs Data:{}", pr);
1314 String error = "Null pFqdns at start of mergePolicyRefs";
1315 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1317 throw new MsoAdapterException(error);
1320 JsonNode node = null;
1322 ObjectMapper mapper = new ObjectMapper();
1323 node = mapper.convertValue(prlist, JsonNode.class);
1324 String jsonString = mapper.writeValueAsString(prlist);
1325 logger.debug("Json PolicyRefs Data:{}", jsonString);
1326 } catch (Exception e) {
1327 String error = "Error creating JsonNode for policyRefs Data";
1328 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1330 throw new MsoAdapterException(error);
1332 // update parameters
1333 if (pFqdns != null && node != null) {
1334 StringBuilder buf = new StringBuilder();
1336 for (String pf : pFqdns) {
1337 if (!commonUtils.isNullOrEmpty(pf)) {
1338 buf.append(sep).append(pf);
1342 String csl = buf.toString();
1343 stackParams.put("policy_refs", csl);
1344 stackParams.put("policy_refsdata", node);
1347 logger.debug("StackParams updated with policy refs");
1351 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1353 // update parameters
1354 if (rtFqdns != null) {
1355 StringBuilder buf = new StringBuilder();
1357 for (String rtf : rtFqdns) {
1358 if (!commonUtils.isNullOrEmpty(rtf)) {
1359 buf.append(sep).append(rtf);
1363 String csl = buf.toString();
1364 stackParams.put("route_table_refs", csl);
1367 logger.debug("StackParams updated with route_table refs");
1373 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1374 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1375 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1376 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1377 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1378 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1379 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1380 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1381 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1382 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1383 * "host_routes": null }
1385 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1386 throws MsoException {
1388 // Resource Property
1389 List<ContrailSubnet> cslist = new ArrayList<>();
1390 for (Subnet subnet : subnets) {
1391 logger.debug("Input Subnet:{}", subnet);
1392 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1393 logger.debug("Contrail Subnet:{}", cs);
1397 JsonNode node = null;
1399 ObjectMapper mapper = new ObjectMapper();
1400 node = mapper.convertValue(cslist, JsonNode.class);
1401 String jsonString = mapper.writeValueAsString(cslist);
1402 logger.debug("Json Subnet List:{}", jsonString);
1403 } catch (Exception e) {
1404 String error = "Error creating JsonNode from input subnets";
1405 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1406 throw new MsoAdapterException(error);
1408 // update parameters
1410 stackParams.put("subnet_list", node);
1412 // Outputs - All subnets are in one ipam_subnets structure
1413 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1414 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1416 // append outputs in heatTemplate
1417 int outputsIdx = heatTemplate.indexOf("outputs:");
1418 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1419 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1420 return heatTemplate;
1424 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1426 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1427 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1430 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1431 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1432 * " end: %poolend%\n";
1436 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1437 + " value: {get_resource: subnet_%subnetId%}\n";
1441 StringBuilder resourcesBuf = new StringBuilder();
1442 StringBuilder outputsBuf = new StringBuilder();
1443 for (Subnet subnet : subnets) {
1445 // build template for each subnet
1446 curR = resourceTempl;
1447 if (subnet.getSubnetId() != null) {
1448 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1450 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1451 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1452 throw new MsoAdapterException(error);
1455 if (subnet.getSubnetName() != null) {
1456 curR = curR.replace("%name%", subnet.getSubnetName());
1458 curR = curR.replace("%name%", subnet.getSubnetId());
1461 if (subnet.getCidr() != null) {
1462 curR = curR.replace("%cidr%", subnet.getCidr());
1464 String error = "Missing Required cidr for subnet in HEAT Template";
1465 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1466 throw new MsoAdapterException(error);
1469 if (subnet.getIpVersion() != null) {
1470 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1472 if (subnet.getEnableDHCP() != null) {
1473 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1475 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1476 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1479 if (subnet.getAllocationPools() != null) {
1480 StringBuilder tempBuf = new StringBuilder();
1481 tempBuf.append(curR);
1482 tempBuf.append(" allocation_pools:\n");
1483 for (Pool pool : subnet.getAllocationPools()) {
1484 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1485 tempBuf.append(" - start: ");
1486 tempBuf.append(pool.getStart());
1487 tempBuf.append("\n end: ");
1488 tempBuf.append(pool.getEnd());
1489 tempBuf.append("\n");
1492 curR = tempBuf.toString();
1495 resourcesBuf.append(curR);
1498 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1500 outputsBuf.append(curO);
1502 // append resources and outputs in heatTemplate
1503 logger.debug("Tempate initial:{}", heatTemplate);
1504 int outputsIdx = heatTemplate.indexOf("outputs:");
1505 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1506 int resourcesIdx = heatTemplate.indexOf("resources:");
1507 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1509 logger.debug("Template updated with all subnets:{}", heatTemplate);
1510 return heatTemplate;
1513 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1515 Map<String, String> sMap = new HashMap<>();
1518 Object obj = outputs.get(key);
1519 ObjectMapper mapper = new ObjectMapper();
1520 String jStr = mapper.writeValueAsString(obj);
1521 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1523 JsonNode rootNode = mapper.readTree(jStr);
1524 if (rootNode != null) {
1525 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1526 logger.debug("Output Subnet Node {}", sNode);
1527 String name = sNode.path("subnet_name").textValue();
1528 String uuid = sNode.path("subnet_uuid").textValue();
1529 String aaiId = name; // default
1530 // try to find aaiId for name in input subnetList
1531 if (subnets != null) {
1532 for (Subnet subnet : subnets) {
1533 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1534 && subnet.getSubnetName().equals(name)) {
1535 aaiId = subnet.getSubnetId();
1540 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1543 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1544 ErrorCode.DataError.getValue());
1546 } catch (Exception e) {
1547 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1548 ErrorCode.DataError.getValue(), e);
1551 logger.debug("Return sMap {}", sMap);
1555 private static String insertStr(String template, String snippet, int index) {
1557 String updatedTemplate;
1559 logger.debug("Index:{} Snippet:{}", index, snippet);
1561 String templateBeg = template.substring(0, index);
1562 String templateEnd = template.substring(index);
1564 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1566 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1567 return updatedTemplate;