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.so.logger.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: {}";
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(),
432 } catch (MsoException me) {
433 me.addContext(CREATE_NETWORK_CONTEXT);
434 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
435 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
436 throw new NetworkException(me);
439 // Reach this point if createStack is successful.
441 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
442 // and the neutronNetworkId is the network UUID returned in stack outputs.
443 networkId.value = heatStack.getCanonicalName();
444 if (heatStack.getOutputs() != null) {
445 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
447 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
450 Map<String, Object> outputs = heatStack.getOutputs();
451 Map<String, String> sMap = new HashMap<>();
452 if (outputs != null) {
453 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
454 String key = entry.getKey();
455 if (key != null && key.startsWith("subnet")) {
456 if (aic3template) // one subnet output expected
458 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
460 } else // multiples subnet_%aaid% outputs allowed
462 String subnetUUId = (String) outputs.get(key);
463 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
467 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
469 subnetIdMap.value = sMap;
471 rollback.value = networkRollback;
472 // Populate remaining rollback info and response parameters.
473 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
474 networkRollback.setNetworkCreated(true);
475 networkRollback.setNetworkType(networkType);
477 logger.debug("Network {} successfully created via HEAT", networkName);
484 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
485 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
486 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
487 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
488 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
489 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
495 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
496 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
497 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
498 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
499 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
500 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
501 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
506 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
507 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
508 * remove a VLAN), but other properties may be updated as well.
510 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
511 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
512 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
514 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
515 * VLANs on the same physical network.
517 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
518 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
519 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
521 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
522 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
523 * a subsequent operation.
525 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
526 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
527 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
528 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
529 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
531 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
532 cloudSiteId, tenantId);
534 // Will capture execution time for metrics
535 long startTime = System.currentTimeMillis();
537 // Build a default rollback object (no actions performed)
538 NetworkRollback networkRollback = new NetworkRollback();
539 networkRollback.setCloudId(cloudSiteId);
540 networkRollback.setTenantId(tenantId);
541 networkRollback.setMsoRequest(msoRequest);
543 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
544 if (!cloudSiteOpt.isPresent()) {
545 String error = String.format(
546 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
547 networkName, cloudSiteId, tenantId);
548 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
549 // Set the detailed error as the Exception 'message'
550 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
555 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
556 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
557 String mode = networkResource.getOrchestrationMode();
558 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
560 // Use an MsoNeutronUtils for all Neutron commands
562 if (NEUTRON_MODE.equals(mode)) {
564 // Verify that the Network exists
565 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
566 NetworkInfo netInfo = null;
567 long queryNetworkStarttime = System.currentTimeMillis();
569 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
570 } catch (MsoException me) {
571 me.addContext(UPDATE_NETWORK_CONTEXT);
572 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
573 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
574 throw new NetworkException(me);
577 if (netInfo == null) {
578 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
579 cloudSiteId, tenantId);
580 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
581 ErrorCode.BusinessProcesssError.getValue(), error);
582 // Does not exist. Throw an exception (can't update a non-existent network)
583 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
585 long updateNetworkStarttime = System.currentTimeMillis();
587 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
588 physicalNetworkName, vlans);
589 } catch (MsoException me) {
590 me.addContext(UPDATE_NETWORK_CONTEXT);
591 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
592 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
593 throw new NetworkException(me);
596 // Add the network ID and previously queried vlans to the rollback object
597 networkRollback.setNetworkId(netInfo.getId());
598 networkRollback.setNeutronNetworkId(netInfo.getId());
599 networkRollback.setNetworkType(networkType);
600 // Save previous parameters
601 networkRollback.setNetworkName(netInfo.getName());
602 networkRollback.setPhysicalNetwork(netInfo.getProvider());
603 networkRollback.setVlans(netInfo.getVlans());
605 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
606 } else if ("HEAT".equals(mode)) {
608 // First, look up to see that the Network already exists.
609 // For Heat-based orchestration, the networkId is the network Stack ID.
610 StackInfo heatStack = null;
611 long queryStackStarttime = System.currentTimeMillis();
613 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
614 } catch (MsoException me) {
615 me.addContext(UPDATE_NETWORK_CONTEXT);
616 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
617 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
618 throw new NetworkException(me);
621 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
622 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
623 cloudSiteId, tenantId);
624 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
626 // Network stack does not exist. Return an error
627 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
630 // Get the previous parameters for rollback
631 Map<String, Object> heatParams = heatStack.getParameters();
633 String previousNetworkName = (String) heatParams.get("network_name");
634 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
636 List<Integer> previousVlans = new ArrayList<>();
637 String vlansParam = (String) heatParams.get(VLANS);
638 if (vlansParam != null) {
639 for (String vlan : vlansParam.split(",")) {
641 previousVlans.add(Integer.parseInt(vlan));
642 } catch (NumberFormatException e) {
643 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
644 ErrorCode.DataError.getValue(), vlansParam, e);
648 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
650 // Ready to deploy the updated Network via Heat
653 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
654 if (heatTemplate == null) {
655 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
656 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
658 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
661 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
663 // "Fix" the template if it has CR/LF (getting this from Oracle)
664 String template = heatTemplate.getHeatTemplate();
665 template = template.replaceAll("\r\n", "\n");
667 boolean aic3template = false;
668 String aic3nw = AIC3_NW;
670 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
672 if (template.contains(aic3nw))
675 // Build the common set of HEAT template parameters
676 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
677 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
679 // Validate (and update) the input parameters against the DB definition
680 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
682 stackParams = heat.validateStackParams(stackParams, heatTemplate);
683 } catch (IllegalArgumentException e) {
684 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
685 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
686 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
689 if (subnets != null) {
692 template = mergeSubnetsAIC3(template, subnets, stackParams);
694 template = mergeSubnets(template, subnets);
696 } catch (MsoException me) {
697 me.addContext(UPDATE_NETWORK_CONTEXT);
698 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
699 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
700 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
701 throw new NetworkException(me);
705 if (policyFqdns != null && aic3template) {
707 mergePolicyRefs(policyFqdns, stackParams);
708 } catch (MsoException me) {
709 me.addContext(UPDATE_NETWORK_CONTEXT);
710 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
711 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
712 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
713 throw new NetworkException(me);
717 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
719 mergeRouteTableRefs(routeTableFqdns, stackParams);
720 } catch (MsoException me) {
721 me.addContext(UPDATE_NETWORK_CONTEXT);
722 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
723 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
724 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
725 throw new NetworkException(me);
729 // Update the network stack
730 // Ignore MsoStackNotFound exception because we already checked.
731 long updateStackStarttime = System.currentTimeMillis();
733 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
734 stackParams, true, heatTemplate.getTimeoutMinutes());
735 } catch (MsoException me) {
736 me.addContext(UPDATE_NETWORK_CONTEXT);
737 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
738 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
739 throw new NetworkException(me);
742 Map<String, Object> outputs = heatStack.getOutputs();
743 Map<String, String> sMap = new HashMap<>();
744 if (outputs != null) {
745 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
746 String key = entry.getKey();
747 if (key != null && key.startsWith("subnet")) {
748 if (aic3template) // one subnet output expected
750 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
752 } else // multiples subnet_%aaid% outputs allowed
754 String subnetUUId = (String) outputs.get(key);
755 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
760 subnetIdMap.value = sMap;
762 // Reach this point if createStack is successful.
763 // Populate remaining rollback info and response parameters.
764 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
765 if (null != outputs) {
766 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
768 logger.debug("outputs is NULL");
770 networkRollback.setNetworkType(networkType);
771 // Save previous parameters
772 networkRollback.setNetworkName(previousNetworkName);
773 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
774 networkRollback.setVlans(previousVlans);
776 rollback.value = networkRollback;
778 logger.debug("Network {} successfully updated via HEAT", networkId);
784 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
785 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
786 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
787 // Retrieve the Network Resource definition
788 NetworkResource networkResource = null;
789 NetworkResourceCustomization networkCust = null;
790 CollectionNetworkResourceCustomization collectionNetworkCust = null;
791 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
792 if (!commonUtils.isNullOrEmpty(networkType)) {
793 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
796 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
797 if (networkCust == null) {
798 collectionNetworkCust =
799 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
802 if (networkCust != null) {
803 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
805 networkResource = networkCust.getNetworkResource();
806 } else if (collectionNetworkCust != null) {
807 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
808 networkResource = collectionNetworkCust.getNetworkResource();
810 if (networkResource == null) {
811 String error = String.format(
812 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
813 networkType, modelCustomizationUuid);
814 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
816 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
818 logger.debug(LOG_DEBUG_MSG, networkResource);
820 String mode = networkResource.getOrchestrationMode();
821 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
823 // All Networks are orchestrated via HEAT or Neutron
824 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
825 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
826 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
827 ErrorCode.DataError.getValue(), error);
828 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
831 MavenLikeVersioning aicV = new MavenLikeVersioning();
832 aicV.setVersion(cloudSite.getCloudVersion());
833 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
834 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
837 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
838 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
840 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
841 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
842 cloudSite.getCloudVersion());
844 String error = String.format(
845 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
846 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
847 cloudSite.getCloudVersion());
848 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
849 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
852 // Validate the Network parameters.
854 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
855 if (!missing.isEmpty()) {
856 String error = "Create Network: Missing parameters: " + missing;
857 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
859 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
862 return networkResource;
866 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
867 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
868 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
869 throws NetworkException {
870 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
871 status, vlans, null, subnetIdMap);
875 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
876 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
877 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
878 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
879 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
880 status, null, routeTargets, subnetIdMap);
884 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
885 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
886 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
888 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
889 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
890 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
891 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
893 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
895 // Will capture execution time for metrics
896 long startTime = System.currentTimeMillis();
898 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
899 || commonUtils.isNullOrEmpty(networkNameOrId)) {
901 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
902 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
903 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
906 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
907 if (!cloudSiteOpt.isPresent()) {
908 String error = String.format(
909 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
910 networkNameOrId, cloudSiteId, tenantId);
911 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
912 // Set the detailed error as the Exception 'message'
913 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
916 // Use MsoNeutronUtils for all NEUTRON commands
919 String neutronId = null;
920 // Try Heat first, since networks may be named the same as the Heat stack
921 StackInfo heatStack = null;
922 long queryStackStarttime = System.currentTimeMillis();
924 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
925 } catch (MsoException me) {
926 me.addContext("QueryNetwork");
927 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
928 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
929 throw new NetworkException(me);
932 // Populate the outputs based on the returned Stack information
933 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
934 // Found it. Get the neutronNetworkId for further query
935 Map<String, String> sMap = new HashMap<>();
936 Map<String, Object> outputs = heatStack.getOutputs();
938 if (outputs != null) {
939 neutronId = (String) outputs.get(NETWORK_ID);
941 for (String key : outputs.keySet()) {
942 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
944 String subnetUUId = (String) outputs.get(key);
945 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
946 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
948 Map<String, String> map = getSubnetUUId(key, outputs, null);
954 subnetIdMap.value = sMap;
956 // Input ID was not a Heat stack ID. Try it directly in Neutron
957 neutronId = networkNameOrId;
961 // Query directly against the Neutron Network for the details
962 // no RouteTargets available for ContrailV2 in neutron net-show
963 // networkId is heatStackId
964 long queryNetworkStarttime = System.currentTimeMillis();
966 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
967 if (netInfo != null) {
968 // Found. Populate the output elements
969 networkExists.value = Boolean.TRUE;
970 if ("HEAT".equals(mode) && heatStack != null) {
971 networkId.value = heatStack.getCanonicalName();
973 networkId.value = netInfo.getId();
975 neutronNetworkId.value = netInfo.getId();
976 status.value = netInfo.getStatus();
978 vlans.value = netInfo.getVlans();
980 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
981 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
983 // Not found. Populate the status fields, leave the rest null
984 networkExists.value = Boolean.FALSE;
985 status.value = NetworkStatus.NOTFOUND;
986 neutronNetworkId.value = null;
988 vlans.value = new ArrayList<>();
990 logger.debug("Network {} not found", networkNameOrId);
992 } catch (MsoException me) {
993 me.addContext("QueryNetwork");
994 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
995 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
996 throw new NetworkException(me);
1002 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
1005 * If the network is not found, it is treated as a success.
1007 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
1008 * network orchestration mode for each network type is declared in its catalog definition.
1010 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1011 * networkId should be the Neutron network UUID.
1013 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1014 * will require manual fallout in the client.
1017 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1018 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1019 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1020 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1021 || commonUtils.isNullOrEmpty(networkId)) {
1022 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1023 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1024 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1027 // Retrieve the Network Resource definition
1028 NetworkResource networkResource = null;
1030 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1031 if (!commonUtils.isNullOrEmpty(networkType)) {
1032 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1035 NetworkResourceCustomization nrc =
1036 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1038 networkResource = nrc.getNetworkResource();
1043 if (networkResource != null) {
1044 logger.debug(LOG_DEBUG_MSG, networkResource.toString());
1045 mode = networkResource.getOrchestrationMode();
1048 if (NEUTRON_MODE.equals(mode)) {
1050 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1051 networkDeleted.value = deleted;
1052 } catch (MsoException me) {
1053 me.addContext("DeleteNetwork");
1054 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1055 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1056 throw new NetworkException(me);
1060 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
1061 networkDeleted.value = true;
1062 } catch (MsoException me) {
1063 me.addContext("DeleteNetwork");
1064 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1065 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1066 throw new NetworkException(me);
1072 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1073 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1074 * to undo the creation.
1076 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1080 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1081 if (rollback == null) {
1082 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1086 // Get the elements of the VnfRollback object for easier access
1087 String cloudSiteId = rollback.getCloudId();
1088 String tenantId = rollback.getTenantId();
1089 String networkId = rollback.getNetworkStackId();
1090 String networkType = rollback.getNetworkType();
1091 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1093 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1094 // Retrieve the Network Resource definition
1095 NetworkResource networkResource = null;
1096 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1097 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1100 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1103 if (networkResource != null) {
1105 logger.debug(LOG_DEBUG_MSG, networkResource);
1107 mode = networkResource.getOrchestrationMode();
1110 if (rollback.getNetworkCreated()) {
1111 if (NEUTRON_MODE.equals(mode)) {
1113 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1114 } catch (MsoException me) {
1115 me.addContext("RollbackNetwork");
1116 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1117 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1118 cloudSiteId, tenantId, me);
1119 throw new NetworkException(me);
1123 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
1124 } catch (MsoException me) {
1125 me.addContext("RollbackNetwork");
1126 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1127 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1128 cloudSiteId, tenantId, me);
1129 throw new NetworkException(me);
1135 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1136 List<Integer> vlans, List<RouteTarget> routeTargets) {
1138 StringBuilder missing = new StringBuilder();
1139 if (commonUtils.isNullOrEmpty(networkName)) {
1140 missing.append("networkName");
1144 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1145 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1146 missing.append(sep).append("physicalNetworkName");
1149 if (vlans == null || vlans.isEmpty()) {
1150 missing.append(sep).append(VLANS);
1154 return missing.toString();
1157 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1158 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1159 boolean aic3template) {
1160 // Build the common set of HEAT template parameters
1161 Map<String, Object> stackParams = new HashMap<>();
1162 stackParams.put("network_name", networkName);
1164 if (neutronNetworkType == NetworkType.PROVIDER) {
1165 // For Provider type
1166 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1167 stackParams.put("vlan", vlans.get(0).toString());
1168 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1169 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1170 // It supports all ProviderNet properties except segmentation_id, and adds a
1171 // comma-separated-list of VLANs as a "segments" property.
1172 // Note that this does not match the Neutron definition of Multi-Provider network,
1173 // which contains a list of 'segments', each having physical_network, network_type,
1174 // and segmentation_id.
1175 StringBuilder buf = new StringBuilder();
1177 for (Integer vlan : vlans) {
1178 buf.append(sep).append(vlan.toString());
1181 String csl = buf.toString();
1183 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1184 stackParams.put(VLANS, csl);
1186 if (routeTargets != null) {
1188 String rtGlobal = "";
1189 String rtImport = "";
1190 String rtExport = "";
1192 for (RouteTarget rt : routeTargets) {
1193 boolean rtIsNull = false;
1195 String routeTarget = rt.getRouteTarget();
1196 String routeTargetRole = rt.getRouteTargetRole();
1197 logger.debug("Checking for an actually null route target: {}", rt);
1198 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1200 if (routeTargetRole == null || routeTargetRole.equals("")
1201 || routeTargetRole.equalsIgnoreCase("null"))
1207 logger.debug("Input RT:{}", rt);
1208 String role = rt.getRouteTargetRole();
1209 String rtValue = rt.getRouteTarget();
1211 if ("IMPORT".equalsIgnoreCase(role)) {
1212 sep = rtImport.isEmpty() ? "" : ",";
1213 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1214 } else if ("EXPORT".equalsIgnoreCase(role)) {
1215 sep = rtExport.isEmpty() ? "" : ",";
1216 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1217 } else // covers BOTH, empty etc
1219 sep = rtGlobal.isEmpty() ? "" : ",";
1220 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1226 if (!rtImport.isEmpty()) {
1227 stackParams.put("route_targets_import", rtImport);
1229 if (!rtExport.isEmpty()) {
1230 stackParams.put("route_targets_export", rtExport);
1232 if (!rtGlobal.isEmpty()) {
1233 stackParams.put("route_targets", rtGlobal);
1236 if (commonUtils.isNullOrEmpty(shared)) {
1237 stackParams.put("shared", "False");
1239 stackParams.put("shared", shared);
1241 if (commonUtils.isNullOrEmpty(external)) {
1242 stackParams.put("external", "False");
1244 stackParams.put("external", external);
1252 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1253 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1254 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1255 * "network_policy_refs_data_sequence_minor": "0" } } ]
1257 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1258 // Resource Property
1259 List<ContrailPolicyRef> prlist = new ArrayList<>();
1262 if (pFqdns != null) {
1263 for (String pf : pFqdns) {
1264 if (!commonUtils.isNullOrEmpty(pf)) {
1265 ContrailPolicyRef pr = new ContrailPolicyRef();
1266 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1269 logger.debug("Contrail PolicyRefs Data:{}", pr);
1274 String error = "Null pFqdns at start of mergePolicyRefs";
1275 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1277 throw new MsoAdapterException(error);
1280 JsonNode node = null;
1282 ObjectMapper mapper = new ObjectMapper();
1283 node = mapper.convertValue(prlist, JsonNode.class);
1284 String jsonString = mapper.writeValueAsString(prlist);
1285 logger.debug("Json PolicyRefs Data:{}", jsonString);
1286 } catch (Exception e) {
1287 String error = "Error creating JsonNode for policyRefs Data";
1288 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1290 throw new MsoAdapterException(error);
1292 // update parameters
1293 if (pFqdns != null && node != null) {
1294 StringBuilder buf = new StringBuilder();
1296 for (String pf : pFqdns) {
1297 if (!commonUtils.isNullOrEmpty(pf)) {
1298 buf.append(sep).append(pf);
1302 String csl = buf.toString();
1303 stackParams.put("policy_refs", csl);
1304 stackParams.put("policy_refsdata", node);
1307 logger.debug("StackParams updated with policy refs");
1311 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1313 // update parameters
1314 if (rtFqdns != null) {
1315 StringBuilder buf = new StringBuilder();
1317 for (String rtf : rtFqdns) {
1318 if (!commonUtils.isNullOrEmpty(rtf)) {
1319 buf.append(sep).append(rtf);
1323 String csl = buf.toString();
1324 stackParams.put("route_table_refs", csl);
1327 logger.debug("StackParams updated with route_table refs");
1333 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1334 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1335 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1336 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1337 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1338 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1339 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1340 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1341 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1342 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1343 * "host_routes": null }
1345 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1346 throws MsoException {
1348 // Resource Property
1349 List<ContrailSubnet> cslist = new ArrayList<>();
1350 for (Subnet subnet : subnets) {
1351 logger.debug("Input Subnet:{}", subnet);
1352 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1353 logger.debug("Contrail Subnet:{}", cs);
1357 JsonNode node = null;
1359 ObjectMapper mapper = new ObjectMapper();
1360 node = mapper.convertValue(cslist, JsonNode.class);
1361 String jsonString = mapper.writeValueAsString(cslist);
1362 logger.debug("Json Subnet List:{}", jsonString);
1363 } catch (Exception e) {
1364 String error = "Error creating JsonNode from input subnets";
1365 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1366 throw new MsoAdapterException(error);
1368 // update parameters
1370 stackParams.put("subnet_list", node);
1372 // Outputs - All subnets are in one ipam_subnets structure
1373 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1374 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1376 // append outputs in heatTemplate
1377 int outputsIdx = heatTemplate.indexOf("outputs:");
1378 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1379 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1380 return heatTemplate;
1384 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1386 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1387 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1390 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1391 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1392 * " end: %poolend%\n";
1396 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1397 + " value: {get_resource: subnet_%subnetId%}\n";
1401 StringBuilder resourcesBuf = new StringBuilder();
1402 StringBuilder outputsBuf = new StringBuilder();
1403 for (Subnet subnet : subnets) {
1405 // build template for each subnet
1406 curR = resourceTempl;
1407 if (subnet.getSubnetId() != null) {
1408 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1410 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1411 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1412 throw new MsoAdapterException(error);
1415 if (subnet.getSubnetName() != null) {
1416 curR = curR.replace("%name%", subnet.getSubnetName());
1418 curR = curR.replace("%name%", subnet.getSubnetId());
1421 if (subnet.getCidr() != null) {
1422 curR = curR.replace("%cidr%", subnet.getCidr());
1424 String error = "Missing Required cidr for subnet in HEAT Template";
1425 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1426 throw new MsoAdapterException(error);
1429 if (subnet.getIpVersion() != null) {
1430 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1432 if (subnet.getEnableDHCP() != null) {
1433 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1435 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1436 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1439 if (subnet.getAllocationPools() != null) {
1440 StringBuilder tempBuf = new StringBuilder();
1441 tempBuf.append(curR);
1442 tempBuf.append(" allocation_pools:\n");
1443 for (Pool pool : subnet.getAllocationPools()) {
1444 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1445 tempBuf.append(" - start: ");
1446 tempBuf.append(pool.getStart());
1447 tempBuf.append("\n end: ");
1448 tempBuf.append(pool.getEnd());
1449 tempBuf.append("\n");
1452 curR = tempBuf.toString();
1455 resourcesBuf.append(curR);
1458 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1460 outputsBuf.append(curO);
1462 // append resources and outputs in heatTemplate
1463 logger.debug("Tempate initial:{}", heatTemplate);
1464 int outputsIdx = heatTemplate.indexOf("outputs:");
1465 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1466 int resourcesIdx = heatTemplate.indexOf("resources:");
1467 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1469 logger.debug("Template updated with all subnets:{}", heatTemplate);
1470 return heatTemplate;
1473 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1475 Map<String, String> sMap = new HashMap<>();
1478 Object obj = outputs.get(key);
1479 ObjectMapper mapper = new ObjectMapper();
1480 String jStr = mapper.writeValueAsString(obj);
1481 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1483 JsonNode rootNode = mapper.readTree(jStr);
1484 if (rootNode != null) {
1485 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1486 logger.debug("Output Subnet Node {}", sNode);
1487 String name = sNode.path("subnet_name").textValue();
1488 String uuid = sNode.path("subnet_uuid").textValue();
1489 String aaiId = name; // default
1490 // try to find aaiId for name in input subnetList
1491 if (subnets != null) {
1492 for (Subnet subnet : subnets) {
1493 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1494 && subnet.getSubnetName().equals(name)) {
1495 aaiId = subnet.getSubnetId();
1500 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1503 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1504 ErrorCode.DataError.getValue());
1506 } catch (Exception e) {
1507 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1508 ErrorCode.DataError.getValue(), e);
1511 logger.debug("Return sMap {}", sMap);
1515 private static String insertStr(String template, String snippet, int index) {
1517 String updatedTemplate;
1519 logger.debug("Index:{} Snippet:{}", index, snippet);
1521 String templateBeg = template.substring(0, index);
1522 String templateEnd = template.substring(index);
1524 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1526 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1527 return updatedTemplate;