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;
314 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
315 } catch (MsoException me) {
316 me.addContext(CREATE_NETWORK_CONTEXT);
317 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
318 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName, cloudSiteId,
320 throw new NetworkException(me);
323 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
324 // Stack exists. Return success or error depending on input directive
325 if (failIfExists != null && failIfExists) {
326 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
327 cloudSiteId, tenantId, heatStack.getCanonicalName());
328 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
329 ErrorCode.DataError.getValue(), error);
330 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
332 // Populate the outputs from the existing stack.
333 networkId.value = heatStack.getCanonicalName();
334 Map<String, String> sMap = new HashMap<>();
335 if (heatStack.getOutputs() != null) {
336 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
337 rollback.value = networkRollback; // Default rollback - no updates performed
339 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
341 Map<String, Object> outputs = heatStack.getOutputs();
343 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
344 String key = entry.getKey();
345 if (key != null && key.startsWith("subnet")) {
346 if (aic3template) // one subnet_id output
348 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
350 } else // multiples subnet_%aaid% outputs
352 String subnetUUId = (String) outputs.get(key);
353 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
358 subnetIdMap.value = sMap;
359 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
360 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
361 networkName, cloudSiteId, tenantId);
366 // Ready to deploy the new Network
367 // Build the common set of HEAT template parameters
368 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
369 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
371 // Validate (and update) the input parameters against the DB definition
372 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
373 // and inputs were already validated.
375 stackParams = heat.validateStackParams(stackParams, heatTemplate);
376 } catch (IllegalArgumentException e) {
377 String error = "Create Network: Configuration Error: " + e.getMessage();
378 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
379 // Input parameters were not valid
380 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
383 if (subnets != null) {
386 template = mergeSubnetsAIC3(template, subnets, stackParams);
388 template = mergeSubnets(template, subnets);
390 } catch (MsoException me) {
391 me.addContext(CREATE_NETWORK_CONTEXT);
392 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
393 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
394 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
395 throw new NetworkException(me);
399 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
401 mergePolicyRefs(policyFqdns, stackParams);
402 } catch (MsoException me) {
403 me.addContext(CREATE_NETWORK_CONTEXT);
404 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
405 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
406 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
407 throw new NetworkException(me);
411 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
413 mergeRouteTableRefs(routeTableFqdns, stackParams);
414 } catch (MsoException me) {
415 me.addContext(CREATE_NETWORK_CONTEXT);
416 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
417 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
418 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
419 throw new NetworkException(me);
423 // Deploy the network stack
424 // Ignore MsoStackAlreadyExists exception because we already checked.
428 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template,
429 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;
567 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
568 } catch (MsoException me) {
569 me.addContext(UPDATE_NETWORK_CONTEXT);
570 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
571 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
572 throw new NetworkException(me);
575 if (netInfo == null) {
576 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
577 cloudSiteId, tenantId);
578 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
579 ErrorCode.BusinessProcesssError.getValue(), error);
580 // Does not exist. Throw an exception (can't update a non-existent network)
581 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
583 long updateNetworkStarttime = System.currentTimeMillis();
585 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
586 physicalNetworkName, vlans);
587 } catch (MsoException me) {
588 me.addContext(UPDATE_NETWORK_CONTEXT);
589 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
590 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
591 throw new NetworkException(me);
594 // Add the network ID and previously queried vlans to the rollback object
595 networkRollback.setNetworkId(netInfo.getId());
596 networkRollback.setNeutronNetworkId(netInfo.getId());
597 networkRollback.setNetworkType(networkType);
598 // Save previous parameters
599 networkRollback.setNetworkName(netInfo.getName());
600 networkRollback.setPhysicalNetwork(netInfo.getProvider());
601 networkRollback.setVlans(netInfo.getVlans());
603 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
604 } else if ("HEAT".equals(mode)) {
606 // First, look up to see that the Network already exists.
607 // For Heat-based orchestration, the networkId is the network Stack ID.
608 StackInfo heatStack = null;
610 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
611 } catch (MsoException me) {
612 me.addContext(UPDATE_NETWORK_CONTEXT);
613 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
614 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
615 throw new NetworkException(me);
618 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
619 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
620 cloudSiteId, tenantId);
621 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
623 // Network stack does not exist. Return an error
624 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
627 // Get the previous parameters for rollback
628 Map<String, Object> heatParams = heatStack.getParameters();
630 String previousNetworkName = (String) heatParams.get("network_name");
631 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
633 List<Integer> previousVlans = new ArrayList<>();
634 String vlansParam = (String) heatParams.get(VLANS);
635 if (vlansParam != null) {
636 for (String vlan : vlansParam.split(",")) {
638 previousVlans.add(Integer.parseInt(vlan));
639 } catch (NumberFormatException e) {
640 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
641 ErrorCode.DataError.getValue(), vlansParam, e);
645 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
647 // Ready to deploy the updated Network via Heat
650 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
651 if (heatTemplate == null) {
652 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
653 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
655 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
658 logger.debug("Got HEAT Template from DB: {}", heatTemplate);
660 // "Fix" the template if it has CR/LF (getting this from Oracle)
661 String template = heatTemplate.getHeatTemplate();
662 template = template.replaceAll("\r\n", "\n");
664 boolean aic3template = false;
665 String aic3nw = AIC3_NW;
667 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
669 if (template.contains(aic3nw))
672 // Build the common set of HEAT template parameters
673 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
674 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
676 // Validate (and update) the input parameters against the DB definition
677 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
679 stackParams = heat.validateStackParams(stackParams, heatTemplate);
680 } catch (IllegalArgumentException e) {
681 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
682 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
683 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
686 if (subnets != null) {
689 template = mergeSubnetsAIC3(template, subnets, stackParams);
691 template = mergeSubnets(template, subnets);
693 } catch (MsoException me) {
694 me.addContext(UPDATE_NETWORK_CONTEXT);
695 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
696 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
697 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
698 throw new NetworkException(me);
702 if (policyFqdns != null && aic3template) {
704 mergePolicyRefs(policyFqdns, stackParams);
705 } catch (MsoException me) {
706 me.addContext(UPDATE_NETWORK_CONTEXT);
707 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
708 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
709 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
710 throw new NetworkException(me);
714 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
716 mergeRouteTableRefs(routeTableFqdns, stackParams);
717 } catch (MsoException me) {
718 me.addContext(UPDATE_NETWORK_CONTEXT);
719 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
720 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
721 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
722 throw new NetworkException(me);
726 // Update the network stack
727 // Ignore MsoStackNotFound exception because we already checked.
729 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
730 stackParams, true, heatTemplate.getTimeoutMinutes());
731 } catch (MsoException me) {
732 me.addContext(UPDATE_NETWORK_CONTEXT);
733 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
734 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
735 throw new NetworkException(me);
738 Map<String, Object> outputs = heatStack.getOutputs();
739 Map<String, String> sMap = new HashMap<>();
740 if (outputs != null) {
741 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
742 String key = entry.getKey();
743 if (key != null && key.startsWith("subnet")) {
744 if (aic3template) // one subnet output expected
746 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
748 } else // multiples subnet_%aaid% outputs allowed
750 String subnetUUId = (String) outputs.get(key);
751 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
756 subnetIdMap.value = sMap;
758 // Reach this point if createStack is successful.
759 // Populate remaining rollback info and response parameters.
760 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
761 if (null != outputs) {
762 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
764 logger.debug("outputs is NULL");
766 networkRollback.setNetworkType(networkType);
767 // Save previous parameters
768 networkRollback.setNetworkName(previousNetworkName);
769 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
770 networkRollback.setVlans(previousVlans);
772 rollback.value = networkRollback;
774 logger.debug("Network {} successfully updated via HEAT", networkId);
780 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
781 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
782 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
783 // Retrieve the Network Resource definition
784 NetworkResource networkResource = null;
785 NetworkResourceCustomization networkCust = null;
786 CollectionNetworkResourceCustomization collectionNetworkCust = null;
787 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
788 if (!commonUtils.isNullOrEmpty(networkType)) {
789 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
792 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
793 if (networkCust == null) {
794 collectionNetworkCust =
795 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
798 if (networkCust != null) {
799 logger.debug("Got Network Customization definition from Catalog: {}", networkCust);
801 networkResource = networkCust.getNetworkResource();
802 } else if (collectionNetworkCust != null) {
803 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}", collectionNetworkCust);
804 networkResource = collectionNetworkCust.getNetworkResource();
806 if (networkResource == null) {
807 String error = String.format(
808 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
809 networkType, modelCustomizationUuid);
810 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
812 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
814 logger.debug(LOG_DEBUG_MSG, networkResource);
816 String mode = networkResource.getOrchestrationMode();
817 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
819 // All Networks are orchestrated via HEAT or Neutron
820 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
821 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
822 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
823 ErrorCode.DataError.getValue(), error);
824 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
827 MavenLikeVersioning aicV = new MavenLikeVersioning();
828 aicV.setVersion(cloudSite.getCloudVersion());
829 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
830 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
833 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
834 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
836 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
837 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
838 cloudSite.getCloudVersion());
840 String error = String.format(
841 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
842 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
843 cloudSite.getCloudVersion());
844 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
845 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
848 // Validate the Network parameters.
850 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
851 if (!missing.isEmpty()) {
852 String error = "Create Network: Missing parameters: " + missing;
853 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
855 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
858 return networkResource;
862 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
863 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
864 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
865 throws NetworkException {
866 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
867 status, vlans, null, subnetIdMap);
871 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
872 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
873 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
874 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
875 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
876 status, null, routeTargets, subnetIdMap);
880 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
881 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
882 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
884 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
885 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
886 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
887 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
889 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
891 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
892 || commonUtils.isNullOrEmpty(networkNameOrId)) {
894 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
895 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
896 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
899 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
900 if (!cloudSiteOpt.isPresent()) {
901 String error = String.format(
902 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
903 networkNameOrId, cloudSiteId, tenantId);
904 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
905 // Set the detailed error as the Exception 'message'
906 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
909 // Use MsoNeutronUtils for all NEUTRON commands
912 String neutronId = null;
913 // Try Heat first, since networks may be named the same as the Heat stack
914 StackInfo heatStack = null;
916 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
917 } catch (MsoException me) {
918 me.addContext("QueryNetwork");
919 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
920 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
921 throw new NetworkException(me);
924 // Populate the outputs based on the returned Stack information
925 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
926 // Found it. Get the neutronNetworkId for further query
927 Map<String, String> sMap = new HashMap<>();
928 Map<String, Object> outputs = heatStack.getOutputs();
930 if (outputs != null) {
931 neutronId = (String) outputs.get(NETWORK_ID);
933 for (String key : outputs.keySet()) {
934 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
936 String subnetUUId = (String) outputs.get(key);
937 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
938 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
940 Map<String, String> map = getSubnetUUId(key, outputs, null);
946 subnetIdMap.value = sMap;
948 // Input ID was not a Heat stack ID. Try it directly in Neutron
949 neutronId = networkNameOrId;
953 // Query directly against the Neutron Network for the details
954 // no RouteTargets available for ContrailV2 in neutron net-show
955 // networkId is heatStackId
957 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
958 if (netInfo != null) {
959 // Found. Populate the output elements
960 networkExists.value = Boolean.TRUE;
961 if ("HEAT".equals(mode) && heatStack != null) {
962 networkId.value = heatStack.getCanonicalName();
964 networkId.value = netInfo.getId();
966 neutronNetworkId.value = netInfo.getId();
967 status.value = netInfo.getStatus();
969 vlans.value = netInfo.getVlans();
971 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
972 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
974 // Not found. Populate the status fields, leave the rest null
975 networkExists.value = Boolean.FALSE;
976 status.value = NetworkStatus.NOTFOUND;
977 neutronNetworkId.value = null;
979 vlans.value = new ArrayList<>();
981 logger.debug("Network {} not found", networkNameOrId);
983 } catch (MsoException me) {
984 me.addContext("QueryNetwork");
985 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
986 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
987 throw new NetworkException(me);
993 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
996 * If the network is not found, it is treated as a success.
998 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
999 * network orchestration mode for each network type is declared in its catalog definition.
1001 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1002 * networkId should be the Neutron network UUID.
1004 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1005 * will require manual fallout in the client.
1008 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1009 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1010 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1011 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1012 || commonUtils.isNullOrEmpty(networkId)) {
1013 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1014 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1015 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1018 // Retrieve the Network Resource definition
1019 NetworkResource networkResource = null;
1021 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1022 if (!commonUtils.isNullOrEmpty(networkType)) {
1023 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1026 NetworkResourceCustomization nrc =
1027 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1029 networkResource = nrc.getNetworkResource();
1034 if (networkResource != null) {
1035 logger.debug(LOG_DEBUG_MSG, networkResource.toString());
1036 mode = networkResource.getOrchestrationMode();
1039 if (NEUTRON_MODE.equals(mode)) {
1041 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1042 networkDeleted.value = deleted;
1043 } catch (MsoException me) {
1044 me.addContext("DeleteNetwork");
1045 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1046 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1047 throw new NetworkException(me);
1051 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
1052 networkDeleted.value = true;
1053 } catch (MsoException me) {
1054 me.addContext("DeleteNetwork");
1055 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1056 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1057 throw new NetworkException(me);
1063 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1064 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1065 * to undo the creation.
1067 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1071 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1072 if (rollback == null) {
1073 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1077 // Get the elements of the VnfRollback object for easier access
1078 String cloudSiteId = rollback.getCloudId();
1079 String tenantId = rollback.getTenantId();
1080 String networkId = rollback.getNetworkStackId();
1081 String networkType = rollback.getNetworkType();
1082 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1084 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1085 // Retrieve the Network Resource definition
1086 NetworkResource networkResource = null;
1087 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1088 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1091 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1094 if (networkResource != null) {
1096 logger.debug(LOG_DEBUG_MSG, networkResource);
1098 mode = networkResource.getOrchestrationMode();
1101 if (rollback.getNetworkCreated()) {
1102 if (NEUTRON_MODE.equals(mode)) {
1104 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1105 } catch (MsoException me) {
1106 me.addContext("RollbackNetwork");
1107 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1108 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1109 cloudSiteId, tenantId, me);
1110 throw new NetworkException(me);
1114 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
1115 } catch (MsoException me) {
1116 me.addContext("RollbackNetwork");
1117 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1118 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1119 cloudSiteId, tenantId, me);
1120 throw new NetworkException(me);
1126 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1127 List<Integer> vlans, List<RouteTarget> routeTargets) {
1129 StringBuilder missing = new StringBuilder();
1130 if (commonUtils.isNullOrEmpty(networkName)) {
1131 missing.append("networkName");
1135 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1136 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1137 missing.append(sep).append("physicalNetworkName");
1140 if (vlans == null || vlans.isEmpty()) {
1141 missing.append(sep).append(VLANS);
1145 return missing.toString();
1148 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1149 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1150 boolean aic3template) {
1151 // Build the common set of HEAT template parameters
1152 Map<String, Object> stackParams = new HashMap<>();
1153 stackParams.put("network_name", networkName);
1155 if (neutronNetworkType == NetworkType.PROVIDER) {
1156 // For Provider type
1157 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1158 stackParams.put("vlan", vlans.get(0).toString());
1159 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1160 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1161 // It supports all ProviderNet properties except segmentation_id, and adds a
1162 // comma-separated-list of VLANs as a "segments" property.
1163 // Note that this does not match the Neutron definition of Multi-Provider network,
1164 // which contains a list of 'segments', each having physical_network, network_type,
1165 // and segmentation_id.
1166 StringBuilder buf = new StringBuilder();
1168 for (Integer vlan : vlans) {
1169 buf.append(sep).append(vlan.toString());
1172 String csl = buf.toString();
1174 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1175 stackParams.put(VLANS, csl);
1177 if (routeTargets != null) {
1179 String rtGlobal = "";
1180 String rtImport = "";
1181 String rtExport = "";
1183 for (RouteTarget rt : routeTargets) {
1184 boolean rtIsNull = false;
1186 String routeTarget = rt.getRouteTarget();
1187 String routeTargetRole = rt.getRouteTargetRole();
1188 logger.debug("Checking for an actually null route target: {}", rt);
1189 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1191 if (routeTargetRole == null || routeTargetRole.equals("")
1192 || routeTargetRole.equalsIgnoreCase("null"))
1198 logger.debug("Input RT:{}", rt);
1199 String role = rt.getRouteTargetRole();
1200 String rtValue = rt.getRouteTarget();
1202 if ("IMPORT".equalsIgnoreCase(role)) {
1203 sep = rtImport.isEmpty() ? "" : ",";
1204 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1205 } else if ("EXPORT".equalsIgnoreCase(role)) {
1206 sep = rtExport.isEmpty() ? "" : ",";
1207 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1208 } else // covers BOTH, empty etc
1210 sep = rtGlobal.isEmpty() ? "" : ",";
1211 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1217 if (!rtImport.isEmpty()) {
1218 stackParams.put("route_targets_import", rtImport);
1220 if (!rtExport.isEmpty()) {
1221 stackParams.put("route_targets_export", rtExport);
1223 if (!rtGlobal.isEmpty()) {
1224 stackParams.put("route_targets", rtGlobal);
1227 if (commonUtils.isNullOrEmpty(shared)) {
1228 stackParams.put("shared", "False");
1230 stackParams.put("shared", shared);
1232 if (commonUtils.isNullOrEmpty(external)) {
1233 stackParams.put("external", "False");
1235 stackParams.put("external", external);
1243 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1244 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1245 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1246 * "network_policy_refs_data_sequence_minor": "0" } } ]
1248 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1249 // Resource Property
1250 List<ContrailPolicyRef> prlist = new ArrayList<>();
1253 if (pFqdns != null) {
1254 for (String pf : pFqdns) {
1255 if (!commonUtils.isNullOrEmpty(pf)) {
1256 ContrailPolicyRef pr = new ContrailPolicyRef();
1257 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1260 logger.debug("Contrail PolicyRefs Data:{}", pr);
1265 String error = "Null pFqdns at start of mergePolicyRefs";
1266 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1268 throw new MsoAdapterException(error);
1271 JsonNode node = null;
1273 ObjectMapper mapper = new ObjectMapper();
1274 node = mapper.convertValue(prlist, JsonNode.class);
1275 String jsonString = mapper.writeValueAsString(prlist);
1276 logger.debug("Json PolicyRefs Data:{}", jsonString);
1277 } catch (Exception e) {
1278 String error = "Error creating JsonNode for policyRefs Data";
1279 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1281 throw new MsoAdapterException(error);
1283 // update parameters
1284 if (pFqdns != null && node != null) {
1285 StringBuilder buf = new StringBuilder();
1287 for (String pf : pFqdns) {
1288 if (!commonUtils.isNullOrEmpty(pf)) {
1289 buf.append(sep).append(pf);
1293 String csl = buf.toString();
1294 stackParams.put("policy_refs", csl);
1295 stackParams.put("policy_refsdata", node);
1298 logger.debug("StackParams updated with policy refs");
1302 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1304 // update parameters
1305 if (rtFqdns != null) {
1306 StringBuilder buf = new StringBuilder();
1308 for (String rtf : rtFqdns) {
1309 if (!commonUtils.isNullOrEmpty(rtf)) {
1310 buf.append(sep).append(rtf);
1314 String csl = buf.toString();
1315 stackParams.put("route_table_refs", csl);
1318 logger.debug("StackParams updated with route_table refs");
1324 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1325 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1326 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1327 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1328 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1329 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1330 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1331 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1332 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1333 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1334 * "host_routes": null }
1336 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1337 throws MsoException {
1339 // Resource Property
1340 List<ContrailSubnet> cslist = new ArrayList<>();
1341 for (Subnet subnet : subnets) {
1342 logger.debug("Input Subnet:{}", subnet);
1343 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1344 logger.debug("Contrail Subnet:{}", cs);
1348 JsonNode node = null;
1350 ObjectMapper mapper = new ObjectMapper();
1351 node = mapper.convertValue(cslist, JsonNode.class);
1352 String jsonString = mapper.writeValueAsString(cslist);
1353 logger.debug("Json Subnet List:{}", jsonString);
1354 } catch (Exception e) {
1355 String error = "Error creating JsonNode from input subnets";
1356 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1357 throw new MsoAdapterException(error);
1359 // update parameters
1361 stackParams.put("subnet_list", node);
1363 // Outputs - All subnets are in one ipam_subnets structure
1364 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1365 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1367 // append outputs in heatTemplate
1368 int outputsIdx = heatTemplate.indexOf("outputs:");
1369 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1370 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1371 return heatTemplate;
1375 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1377 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1378 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1381 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1382 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1383 * " end: %poolend%\n";
1387 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1388 + " value: {get_resource: subnet_%subnetId%}\n";
1392 StringBuilder resourcesBuf = new StringBuilder();
1393 StringBuilder outputsBuf = new StringBuilder();
1394 for (Subnet subnet : subnets) {
1396 // build template for each subnet
1397 curR = resourceTempl;
1398 if (subnet.getSubnetId() != null) {
1399 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1401 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1402 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1403 throw new MsoAdapterException(error);
1406 if (subnet.getSubnetName() != null) {
1407 curR = curR.replace("%name%", subnet.getSubnetName());
1409 curR = curR.replace("%name%", subnet.getSubnetId());
1412 if (subnet.getCidr() != null) {
1413 curR = curR.replace("%cidr%", subnet.getCidr());
1415 String error = "Missing Required cidr for subnet in HEAT Template";
1416 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1417 throw new MsoAdapterException(error);
1420 if (subnet.getIpVersion() != null) {
1421 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1423 if (subnet.getEnableDHCP() != null) {
1424 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1426 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1427 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1430 if (subnet.getAllocationPools() != null) {
1431 StringBuilder tempBuf = new StringBuilder();
1432 tempBuf.append(curR);
1433 tempBuf.append(" allocation_pools:\n");
1434 for (Pool pool : subnet.getAllocationPools()) {
1435 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1436 tempBuf.append(" - start: ");
1437 tempBuf.append(pool.getStart());
1438 tempBuf.append("\n end: ");
1439 tempBuf.append(pool.getEnd());
1440 tempBuf.append("\n");
1443 curR = tempBuf.toString();
1446 resourcesBuf.append(curR);
1449 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1451 outputsBuf.append(curO);
1453 // append resources and outputs in heatTemplate
1454 logger.debug("Tempate initial:{}", heatTemplate);
1455 int outputsIdx = heatTemplate.indexOf("outputs:");
1456 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1457 int resourcesIdx = heatTemplate.indexOf("resources:");
1458 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1460 logger.debug("Template updated with all subnets:{}", heatTemplate);
1461 return heatTemplate;
1464 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1466 Map<String, String> sMap = new HashMap<>();
1469 Object obj = outputs.get(key);
1470 ObjectMapper mapper = new ObjectMapper();
1471 String jStr = mapper.writeValueAsString(obj);
1472 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1474 JsonNode rootNode = mapper.readTree(jStr);
1475 if (rootNode != null) {
1476 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1477 logger.debug("Output Subnet Node {}", sNode);
1478 String name = sNode.path("subnet_name").textValue();
1479 String uuid = sNode.path("subnet_uuid").textValue();
1480 String aaiId = name; // default
1481 // try to find aaiId for name in input subnetList
1482 if (subnets != null) {
1483 for (Subnet subnet : subnets) {
1484 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1485 && subnet.getSubnetName().equals(name)) {
1486 aaiId = subnet.getSubnetId();
1491 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1494 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1495 ErrorCode.DataError.getValue());
1497 } catch (Exception e) {
1498 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1499 ErrorCode.DataError.getValue(), e);
1502 logger.debug("Return sMap {}", sMap);
1506 private static String insertStr(String template, String snippet, int index) {
1508 String updatedTemplate;
1510 logger.debug("Index:{} Snippet:{}", index, snippet);
1512 String templateBeg = template.substring(0, index);
1513 String templateEnd = template.substring(index);
1515 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1517 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1518 return updatedTemplate;