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.BusinessProcessError.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.BusinessProcessError.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.BusinessProcessError.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;
1020 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1021 if (!commonUtils.isNullOrEmpty(networkType)) {
1022 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1025 NetworkResourceCustomization nrc =
1026 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1028 networkResource = nrc.getNetworkResource();
1032 int timeoutMinutes = 118;
1034 if (networkResource != null) {
1035 logger.debug(LOG_DEBUG_MSG, networkResource.toString());
1036 mode = networkResource.getOrchestrationMode();
1037 networkResource.getHeatTemplate().getTimeoutMinutes();
1038 HeatTemplate heat = networkResource.getHeatTemplate();
1039 if (heat != null && heat.getTimeoutMinutes() != null) {
1040 if (heat.getTimeoutMinutes() < 118) {
1041 timeoutMinutes = heat.getTimeoutMinutes();
1046 if (NEUTRON_MODE.equals(mode)) {
1048 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1049 networkDeleted.value = deleted;
1050 } catch (MsoException me) {
1051 me.addContext("DeleteNetwork");
1052 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1053 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1054 throw new NetworkException(me);
1058 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, timeoutMinutes);
1059 networkDeleted.value = true;
1060 } catch (MsoException me) {
1061 me.addContext("DeleteNetwork");
1062 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1063 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1064 throw new NetworkException(me);
1070 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1071 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1072 * to undo the creation.
1074 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1078 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1079 if (rollback == null) {
1080 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1084 // Get the elements of the VnfRollback object for easier access
1085 String cloudSiteId = rollback.getCloudId();
1086 String tenantId = rollback.getTenantId();
1087 String networkId = rollback.getNetworkStackId();
1088 String networkType = rollback.getNetworkType();
1089 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1091 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1092 // Retrieve the Network Resource definition
1093 NetworkResource networkResource = null;
1094 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1095 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1098 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1101 if (networkResource != null) {
1103 logger.debug(LOG_DEBUG_MSG, networkResource);
1105 mode = networkResource.getOrchestrationMode();
1108 if (rollback.getNetworkCreated()) {
1109 if (NEUTRON_MODE.equals(mode)) {
1111 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1112 } catch (MsoException me) {
1113 me.addContext("RollbackNetwork");
1114 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1115 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
1116 cloudSiteId, tenantId, me);
1117 throw new NetworkException(me);
1121 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true, 120);
1122 } catch (MsoException me) {
1123 me.addContext("RollbackNetwork");
1124 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1125 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcessError.getValue(), networkId,
1126 cloudSiteId, tenantId, me);
1127 throw new NetworkException(me);
1133 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1134 List<Integer> vlans, List<RouteTarget> routeTargets) {
1136 StringBuilder missing = new StringBuilder();
1137 if (commonUtils.isNullOrEmpty(networkName)) {
1138 missing.append("networkName");
1142 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1143 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1144 missing.append(sep).append("physicalNetworkName");
1147 if (vlans == null || vlans.isEmpty()) {
1148 missing.append(sep).append(VLANS);
1152 return missing.toString();
1155 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1156 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1157 boolean aic3template) {
1158 // Build the common set of HEAT template parameters
1159 Map<String, Object> stackParams = new HashMap<>();
1160 stackParams.put("network_name", networkName);
1162 if (neutronNetworkType == NetworkType.PROVIDER) {
1163 // For Provider type
1164 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1165 stackParams.put("vlan", vlans.get(0).toString());
1166 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1167 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1168 // It supports all ProviderNet properties except segmentation_id, and adds a
1169 // comma-separated-list of VLANs as a "segments" property.
1170 // Note that this does not match the Neutron definition of Multi-Provider network,
1171 // which contains a list of 'segments', each having physical_network, network_type,
1172 // and segmentation_id.
1173 StringBuilder buf = new StringBuilder();
1175 for (Integer vlan : vlans) {
1176 buf.append(sep).append(vlan.toString());
1179 String csl = buf.toString();
1181 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1182 stackParams.put(VLANS, csl);
1184 if (routeTargets != null) {
1186 String rtGlobal = "";
1187 String rtImport = "";
1188 String rtExport = "";
1190 for (RouteTarget rt : routeTargets) {
1191 boolean rtIsNull = false;
1193 String routeTarget = rt.getRouteTarget();
1194 String routeTargetRole = rt.getRouteTargetRole();
1195 logger.debug("Checking for an actually null route target: {}", rt);
1196 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1198 if (routeTargetRole == null || routeTargetRole.equals("")
1199 || routeTargetRole.equalsIgnoreCase("null"))
1205 logger.debug("Input RT:{}", rt);
1206 String role = rt.getRouteTargetRole();
1207 String rtValue = rt.getRouteTarget();
1209 if ("IMPORT".equalsIgnoreCase(role)) {
1210 sep = rtImport.isEmpty() ? "" : ",";
1211 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1212 } else if ("EXPORT".equalsIgnoreCase(role)) {
1213 sep = rtExport.isEmpty() ? "" : ",";
1214 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1215 } else // covers BOTH, empty etc
1217 sep = rtGlobal.isEmpty() ? "" : ",";
1218 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1224 if (!rtImport.isEmpty()) {
1225 stackParams.put("route_targets_import", rtImport);
1227 if (!rtExport.isEmpty()) {
1228 stackParams.put("route_targets_export", rtExport);
1230 if (!rtGlobal.isEmpty()) {
1231 stackParams.put("route_targets", rtGlobal);
1234 if (commonUtils.isNullOrEmpty(shared)) {
1235 stackParams.put("shared", "False");
1237 stackParams.put("shared", shared);
1239 if (commonUtils.isNullOrEmpty(external)) {
1240 stackParams.put("external", "False");
1242 stackParams.put("external", external);
1250 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1251 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1252 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1253 * "network_policy_refs_data_sequence_minor": "0" } } ]
1255 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1256 // Resource Property
1257 List<ContrailPolicyRef> prlist = new ArrayList<>();
1260 if (pFqdns != null) {
1261 for (String pf : pFqdns) {
1262 if (!commonUtils.isNullOrEmpty(pf)) {
1263 ContrailPolicyRef pr = new ContrailPolicyRef();
1264 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1267 logger.debug("Contrail PolicyRefs Data:{}", pr);
1272 String error = "Null pFqdns at start of mergePolicyRefs";
1273 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1275 throw new MsoAdapterException(error);
1278 JsonNode node = null;
1280 ObjectMapper mapper = new ObjectMapper();
1281 node = mapper.convertValue(prlist, JsonNode.class);
1282 String jsonString = mapper.writeValueAsString(prlist);
1283 logger.debug("Json PolicyRefs Data:{}", jsonString);
1284 } catch (Exception e) {
1285 String error = "Error creating JsonNode for policyRefs Data";
1286 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcessError.getValue(),
1288 throw new MsoAdapterException(error);
1290 // update parameters
1291 if (pFqdns != null && node != null) {
1292 StringBuilder buf = new StringBuilder();
1294 for (String pf : pFqdns) {
1295 if (!commonUtils.isNullOrEmpty(pf)) {
1296 buf.append(sep).append(pf);
1300 String csl = buf.toString();
1301 stackParams.put("policy_refs", csl);
1302 stackParams.put("policy_refsdata", node);
1305 logger.debug("StackParams updated with policy refs");
1309 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1311 // update parameters
1312 if (rtFqdns != null) {
1313 StringBuilder buf = new StringBuilder();
1315 for (String rtf : rtFqdns) {
1316 if (!commonUtils.isNullOrEmpty(rtf)) {
1317 buf.append(sep).append(rtf);
1321 String csl = buf.toString();
1322 stackParams.put("route_table_refs", csl);
1325 logger.debug("StackParams updated with route_table refs");
1331 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1332 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1333 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1334 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1335 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1336 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1337 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1338 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1339 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1340 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1341 * "host_routes": null }
1343 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1344 throws MsoException {
1346 // Resource Property
1347 List<ContrailSubnet> cslist = new ArrayList<>();
1348 for (Subnet subnet : subnets) {
1349 logger.debug("Input Subnet:{}", subnet);
1350 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1351 logger.debug("Contrail Subnet:{}", cs);
1355 JsonNode node = null;
1357 ObjectMapper mapper = new ObjectMapper();
1358 node = mapper.convertValue(cslist, JsonNode.class);
1359 String jsonString = mapper.writeValueAsString(cslist);
1360 logger.debug("Json Subnet List:{}", jsonString);
1361 } catch (Exception e) {
1362 String error = "Error creating JsonNode from input subnets";
1363 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1364 throw new MsoAdapterException(error);
1366 // update parameters
1368 stackParams.put("subnet_list", node);
1370 // Outputs - All subnets are in one ipam_subnets structure
1371 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1372 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1374 // append outputs in heatTemplate
1375 int outputsIdx = heatTemplate.indexOf("outputs:");
1376 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1377 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1378 return heatTemplate;
1382 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1384 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1385 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1388 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1389 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1390 * " end: %poolend%\n";
1394 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1395 + " value: {get_resource: subnet_%subnetId%}\n";
1399 StringBuilder resourcesBuf = new StringBuilder();
1400 StringBuilder outputsBuf = new StringBuilder();
1401 for (Subnet subnet : subnets) {
1403 // build template for each subnet
1404 curR = resourceTempl;
1405 if (subnet.getSubnetId() != null) {
1406 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1408 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1409 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1410 throw new MsoAdapterException(error);
1413 if (subnet.getSubnetName() != null) {
1414 curR = curR.replace("%name%", subnet.getSubnetName());
1416 curR = curR.replace("%name%", subnet.getSubnetId());
1419 if (subnet.getCidr() != null) {
1420 curR = curR.replace("%cidr%", subnet.getCidr());
1422 String error = "Missing Required cidr for subnet in HEAT Template";
1423 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1424 throw new MsoAdapterException(error);
1427 if (subnet.getIpVersion() != null) {
1428 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1430 if (subnet.getEnableDHCP() != null) {
1431 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1433 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1434 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1437 if (subnet.getAllocationPools() != null) {
1438 StringBuilder tempBuf = new StringBuilder();
1439 tempBuf.append(curR);
1440 tempBuf.append(" allocation_pools:\n");
1441 for (Pool pool : subnet.getAllocationPools()) {
1442 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1443 tempBuf.append(" - start: ");
1444 tempBuf.append(pool.getStart());
1445 tempBuf.append("\n end: ");
1446 tempBuf.append(pool.getEnd());
1447 tempBuf.append("\n");
1450 curR = tempBuf.toString();
1453 resourcesBuf.append(curR);
1456 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1458 outputsBuf.append(curO);
1460 // append resources and outputs in heatTemplate
1461 logger.debug("Tempate initial:{}", heatTemplate);
1462 int outputsIdx = heatTemplate.indexOf("outputs:");
1463 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1464 int resourcesIdx = heatTemplate.indexOf("resources:");
1465 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1467 logger.debug("Template updated with all subnets:{}", heatTemplate);
1468 return heatTemplate;
1471 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1473 Map<String, String> sMap = new HashMap<>();
1476 Object obj = outputs.get(key);
1477 ObjectMapper mapper = new ObjectMapper();
1478 String jStr = mapper.writeValueAsString(obj);
1479 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1481 JsonNode rootNode = mapper.readTree(jStr);
1482 if (rootNode != null) {
1483 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1484 logger.debug("Output Subnet Node {}", sNode);
1485 String name = sNode.path("subnet_name").textValue();
1486 String uuid = sNode.path("subnet_uuid").textValue();
1487 String aaiId = name; // default
1488 // try to find aaiId for name in input subnetList
1489 if (subnets != null) {
1490 for (Subnet subnet : subnets) {
1491 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())
1492 && subnet.getSubnetName().equals(name)) {
1493 aaiId = subnet.getSubnetId();
1498 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1501 logger.error("{} {} null rootNode - cannot get subnet-uuids", MessageEnum.RA_MARSHING_ERROR,
1502 ErrorCode.DataError.getValue());
1504 } catch (Exception e) {
1505 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1506 ErrorCode.DataError.getValue(), e);
1509 logger.debug("Return sMap {}", sMap);
1513 private static String insertStr(String template, String snippet, int index) {
1515 String updatedTemplate;
1517 logger.debug("Index:{} Snippet:{}", index, snippet);
1519 String templateBeg = template.substring(0, index);
1520 String templateEnd = template.substring(index);
1522 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1524 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1525 return updatedTemplate;