2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7 * ================================================================================
8 * Modifications Copyright (C) 2018 IBM.
9 * Modifications Copyright (c) 2019 Samsung
10 * ================================================================================
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 * ============LICENSE_END=========================================================
25 package org.onap.so.adapters.network;
27 import com.fasterxml.jackson.databind.JsonNode;
28 import com.fasterxml.jackson.databind.ObjectMapper;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
33 import java.util.Optional;
34 import javax.jws.WebService;
35 import javax.xml.ws.Holder;
36 import org.onap.so.logger.LoggingAnchor;
37 import org.onap.so.adapters.network.beans.ContrailPolicyRef;
38 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
39 import org.onap.so.adapters.network.beans.ContrailSubnet;
40 import org.onap.so.adapters.network.exceptions.NetworkException;
41 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
42 import org.onap.so.cloud.CloudConfig;
43 import org.onap.so.db.catalog.beans.CloudSite;
44 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
45 import org.onap.so.db.catalog.beans.HeatTemplate;
46 import org.onap.so.db.catalog.beans.NetworkResource;
47 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
48 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
49 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
50 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
51 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
52 import org.onap.so.entity.MsoRequest;
53 import org.onap.so.logger.ErrorCode;
54 import org.onap.so.logger.MessageEnum;
55 import org.onap.so.openstack.beans.HeatStatus;
56 import org.onap.so.openstack.beans.NetworkInfo;
57 import org.onap.so.openstack.beans.NetworkRollback;
58 import org.onap.so.openstack.beans.NetworkStatus;
59 import org.onap.so.openstack.beans.Pool;
60 import org.onap.so.openstack.beans.RouteTarget;
61 import org.onap.so.openstack.beans.StackInfo;
62 import org.onap.so.openstack.beans.Subnet;
63 import org.onap.so.openstack.exceptions.MsoAdapterException;
64 import org.onap.so.openstack.exceptions.MsoException;
65 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
66 import org.onap.so.openstack.utils.MsoCommonUtils;
67 import org.onap.so.openstack.utils.MsoHeatUtils;
68 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
69 import org.onap.so.openstack.utils.MsoNeutronUtils;
70 import org.onap.so.openstack.utils.MsoNeutronUtils.NetworkType;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73 import org.springframework.beans.factory.annotation.Autowired;
74 import org.springframework.core.env.Environment;
75 import org.springframework.stereotype.Component;
76 import org.springframework.transaction.annotation.Transactional;
80 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.onap.so.adapters.network.MsoNetworkAdapter",
81 targetNamespace = "http://org.onap.so/network")
82 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
84 private static final String AIC3_NW_PROPERTY = "org.onap.so.adapters.network.aic3nw";
85 private static final String AIC3_NW = "OS::ContrailV2::VirtualNetwork";
86 private static final String VLANS = "vlans";
87 private static final String PHYSICAL_NETWORK = "physical_network";
88 private static final String UPDATE_NETWORK_CONTEXT = "UpdateNetwork";
89 private static final String NETWORK_ID = "network_id";
90 private static final String NETWORK_FQDN = "network_fqdn";
91 private static final String CREATE_NETWORK_CONTEXT = "CreateNetwork";
92 private static final String NEUTRON_MODE = "NEUTRON";
94 private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
97 private CloudConfig cloudConfig;
99 private Environment environment;
101 private MsoNeutronUtils neutron;
103 private MsoHeatUtils heat;
105 private MsoHeatUtilsWithUpdate heatWithUpdate;
107 private MsoCommonUtils commonUtils;
110 private NetworkResourceCustomizationRepository networkCustomRepo;
113 private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
116 private NetworkResourceRepository networkResourceRepo;
118 public MsoNetworkAdapterImpl() {}
121 * Health Check web method. Does nothing but return to show the adapter is deployed.
124 public void healthCheck() {
125 logger.debug("Health check call in Network Adapter");
129 * Do not use this constructor or the msoPropertiesFactory will be NULL.
131 * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
135 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
136 String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
137 Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
138 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
139 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
140 Holder<String> networkFqdn = new Holder<>();
141 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
142 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
143 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
147 public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
148 String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
149 String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
150 Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
151 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
152 Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
153 throws NetworkException {
154 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
155 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
156 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
160 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
161 * the specified cloud and tenant. The tenant must exist at the time this service is called.
163 * If a network with the same name already exists, this can be considered a success or failure, depending on the
164 * value of the 'failIfExists' parameter.
166 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
167 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
168 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
170 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
171 * multiple VLANs on the same physical network.
173 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
174 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
175 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
177 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
178 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
179 * created but the orchestration fails on a subsequent operation.
182 private void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
183 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
184 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
185 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
186 Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
187 Holder<NetworkRollback> rollback) throws NetworkException {
188 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
190 // Will capture execution time for metrics
191 long startTime = System.currentTimeMillis();
193 // Build a default rollback object (no actions performed)
194 NetworkRollback networkRollback = new NetworkRollback();
195 networkRollback.setCloudId(cloudSiteId);
196 networkRollback.setTenantId(tenantId);
197 networkRollback.setMsoRequest(msoRequest);
198 networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
200 // tenant query is not required here.
201 // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
202 // So this is just catching that error in a bit more obvious way up front.
204 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
205 if (!cloudSiteOpt.isPresent()) {
206 String error = String.format(
207 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
208 networkName, cloudSiteId, tenantId);
209 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
210 // Set the detailed error as the Exception 'message'
211 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
215 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
216 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
217 String mode = networkResource.getOrchestrationMode();
218 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
220 if (NEUTRON_MODE.equals(mode)) {
222 // Use an MsoNeutronUtils for all neutron commands
224 // See if the Network already exists (by name)
225 NetworkInfo netInfo = null;
226 long queryNetworkStarttime = System.currentTimeMillis();
228 netInfo = neutron.queryNetwork(networkName, tenantId, cloudSiteId);
229 } catch (MsoException me) {
231 "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
232 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkName,
233 cloudSiteId, tenantId, me);
234 me.addContext(CREATE_NETWORK_CONTEXT);
235 throw new NetworkException(me);
238 if (netInfo != null) {
239 // Exists. If that's OK, return success with the network ID.
240 // Otherwise, return an exception.
241 if (failIfExists != null && failIfExists) {
242 String error = String.format("Create Nework: Network %s already exists in %s/%s with ID %s",
243 networkName, cloudSiteId, tenantId, netInfo.getId());
244 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
245 ErrorCode.DataError.getValue(), error);
246 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
248 // Populate the outputs from the existing network.
249 networkId.value = netInfo.getId();
250 neutronNetworkId.value = netInfo.getId();
251 rollback.value = networkRollback; // Default rollback - no updates performed
252 logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
253 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), netInfo.getStatus());
258 long createNetworkStarttime = System.currentTimeMillis();
260 netInfo = neutron.createNetwork(cloudSiteId, tenantId, neutronNetworkType, networkName,
261 physicalNetworkName, vlans);
262 } catch (MsoException me) {
263 me.addContext(CREATE_NETWORK_CONTEXT);
264 logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
265 ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
267 throw new NetworkException(me);
270 // Note: ignoring MsoNetworkAlreadyExists because we already checked.
272 // If reach this point, network creation is successful.
273 // Since directly created via Neutron, networkId tracked by MSO is the same
274 // as the neutron network ID.
275 networkId.value = netInfo.getId();
276 neutronNetworkId.value = netInfo.getId();
278 networkRollback.setNetworkCreated(true);
279 networkRollback.setNetworkId(netInfo.getId());
280 networkRollback.setNeutronNetworkId(netInfo.getId());
281 networkRollback.setNetworkType(networkType);
283 logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
284 } else if ("HEAT".equals(mode)) {
286 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
287 if (heatTemplate == null) {
288 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
289 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
291 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
294 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
296 // "Fix" the template if it has CR/LF (getting this from Oracle)
297 String template = heatTemplate.getHeatTemplate();
298 template = template.replaceAll("\r\n", "\n");
300 boolean aic3template = false;
301 String aic3nw = AIC3_NW;
303 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
305 if (template.contains(aic3nw))
308 // First, look up to see if the Network already exists (by name).
309 // For HEAT orchestration of networks, the stack name will always match the network name
310 StackInfo heatStack = null;
311 long queryNetworkStarttime = System.currentTimeMillis();
313 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkName);
314 } catch (MsoException me) {
315 me.addContext(CREATE_NETWORK_CONTEXT);
316 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
317 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName, cloudSiteId,
319 throw new NetworkException(me);
322 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
323 // Stack exists. Return success or error depending on input directive
324 if (failIfExists != null && failIfExists) {
325 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
326 cloudSiteId, tenantId, heatStack.getCanonicalName());
327 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
328 ErrorCode.DataError.getValue(), error);
329 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
331 // Populate the outputs from the existing stack.
332 networkId.value = heatStack.getCanonicalName();
333 Map<String, String> sMap = new HashMap<>();
334 if (heatStack.getOutputs() != null) {
335 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
336 rollback.value = networkRollback; // Default rollback - no updates performed
338 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
340 Map<String, Object> outputs = heatStack.getOutputs();
342 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
343 String key = entry.getKey();
344 if (key != null && key.startsWith("subnet")) {
345 if (aic3template) // one subnet_id output
347 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
349 } else // multiples subnet_%aaid% outputs
351 String subnetUUId = (String) outputs.get(key);
352 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
357 subnetIdMap.value = sMap;
358 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
359 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
360 networkName, cloudSiteId, tenantId);
365 // Ready to deploy the new Network
366 // Build the common set of HEAT template parameters
367 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
368 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
370 // Validate (and update) the input parameters against the DB definition
371 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
372 // and inputs were already validated.
374 stackParams = heat.validateStackParams(stackParams, heatTemplate);
375 } catch (IllegalArgumentException e) {
376 String error = "Create Network: Configuration Error: " + e.getMessage();
377 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
378 // Input parameters were not valid
379 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
382 if (subnets != null) {
385 template = mergeSubnetsAIC3(template, subnets, stackParams);
387 template = mergeSubnets(template, subnets);
389 } catch (MsoException me) {
390 me.addContext(CREATE_NETWORK_CONTEXT);
391 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
392 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
393 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
394 throw new NetworkException(me);
398 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
400 mergePolicyRefs(policyFqdns, stackParams);
401 } catch (MsoException me) {
402 me.addContext(CREATE_NETWORK_CONTEXT);
403 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
404 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
405 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
406 throw new NetworkException(me);
410 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
412 mergeRouteTableRefs(routeTableFqdns, stackParams);
413 } catch (MsoException me) {
414 me.addContext(CREATE_NETWORK_CONTEXT);
415 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
416 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
417 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
418 throw new NetworkException(me);
422 // Deploy the network stack
423 // Ignore MsoStackAlreadyExists exception because we already checked.
427 heatStack = heat.createStack(cloudSiteId, "CloudOwner", tenantId, networkName, null, template,
428 stackParams, true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue());
429 } catch (MsoException me) {
430 me.addContext(CREATE_NETWORK_CONTEXT);
431 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
432 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
433 throw new NetworkException(me);
436 // Reach this point if createStack is successful.
438 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
439 // and the neutronNetworkId is the network UUID returned in stack outputs.
440 networkId.value = heatStack.getCanonicalName();
441 if (heatStack.getOutputs() != null) {
442 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
444 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
447 Map<String, Object> outputs = heatStack.getOutputs();
448 Map<String, String> sMap = new HashMap<>();
449 if (outputs != null) {
450 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
451 String key = entry.getKey();
452 if (key != null && key.startsWith("subnet")) {
453 if (aic3template) // one subnet output expected
455 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
457 } else // multiples subnet_%aaid% outputs allowed
459 String subnetUUId = (String) outputs.get(key);
460 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
465 subnetIdMap.value = sMap;
467 rollback.value = networkRollback;
468 // Populate remaining rollback info and response parameters.
469 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
470 networkRollback.setNeutronNetworkId((String) heatStack.getOutputs().get(NETWORK_ID));
471 networkRollback.setNetworkCreated(true);
472 networkRollback.setNetworkType(networkType);
474 logger.debug("Network {} successfully created via HEAT", networkName);
481 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
482 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
483 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
484 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
485 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
486 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
492 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
493 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
494 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
495 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
496 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
497 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
498 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
503 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
504 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
505 * remove a VLAN), but other properties may be updated as well.
507 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
508 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
509 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
511 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
512 * VLANs on the same physical network.
514 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
515 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
516 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
518 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
519 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
520 * a subsequent operation.
522 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
523 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
524 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
525 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
526 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
528 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
529 cloudSiteId, tenantId);
531 // Will capture execution time for metrics
532 long startTime = System.currentTimeMillis();
534 // Build a default rollback object (no actions performed)
535 NetworkRollback networkRollback = new NetworkRollback();
536 networkRollback.setCloudId(cloudSiteId);
537 networkRollback.setTenantId(tenantId);
538 networkRollback.setMsoRequest(msoRequest);
540 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
541 if (!cloudSiteOpt.isPresent()) {
542 String error = String.format(
543 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
544 networkName, cloudSiteId, tenantId);
545 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
546 // Set the detailed error as the Exception 'message'
547 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
552 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
553 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
554 String mode = networkResource.getOrchestrationMode();
555 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
557 // Use an MsoNeutronUtils for all Neutron commands
559 if (NEUTRON_MODE.equals(mode)) {
561 // Verify that the Network exists
562 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
563 NetworkInfo netInfo = null;
564 long queryNetworkStarttime = System.currentTimeMillis();
566 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
567 } catch (MsoException me) {
568 me.addContext(UPDATE_NETWORK_CONTEXT);
569 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
570 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
571 throw new NetworkException(me);
574 if (netInfo == null) {
575 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
576 cloudSiteId, tenantId);
577 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
578 ErrorCode.BusinessProcesssError.getValue(), error);
579 // Does not exist. Throw an exception (can't update a non-existent network)
580 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
582 long updateNetworkStarttime = System.currentTimeMillis();
584 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
585 physicalNetworkName, vlans);
586 } catch (MsoException me) {
587 me.addContext(UPDATE_NETWORK_CONTEXT);
588 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
589 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
590 throw new NetworkException(me);
593 // Add the network ID and previously queried vlans to the rollback object
594 networkRollback.setNetworkId(netInfo.getId());
595 networkRollback.setNeutronNetworkId(netInfo.getId());
596 networkRollback.setNetworkType(networkType);
597 // Save previous parameters
598 networkRollback.setNetworkName(netInfo.getName());
599 networkRollback.setPhysicalNetwork(netInfo.getProvider());
600 networkRollback.setVlans(netInfo.getVlans());
602 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
603 } else if ("HEAT".equals(mode)) {
605 // First, look up to see that the Network already exists.
606 // For Heat-based orchestration, the networkId is the network Stack ID.
607 StackInfo heatStack = null;
608 long queryStackStarttime = System.currentTimeMillis();
610 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", 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.toString());
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.
728 long updateStackStarttime = System.currentTimeMillis();
730 heatStack = heatWithUpdate.updateStack(cloudSiteId, "CloudOwner", tenantId, networkId, template,
731 stackParams, true, heatTemplate.getTimeoutMinutes());
732 } catch (MsoException me) {
733 me.addContext(UPDATE_NETWORK_CONTEXT);
734 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
735 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
736 throw new NetworkException(me);
739 Map<String, Object> outputs = heatStack.getOutputs();
740 Map<String, String> sMap = new HashMap<>();
741 if (outputs != null) {
742 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
743 String key = entry.getKey();
744 if (key != null && key.startsWith("subnet")) {
745 if (aic3template) // one subnet output expected
747 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
749 } else // multiples subnet_%aaid% outputs allowed
751 String subnetUUId = (String) outputs.get(key);
752 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
757 subnetIdMap.value = sMap;
759 // Reach this point if createStack is successful.
760 // Populate remaining rollback info and response parameters.
761 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
762 if (null != outputs) {
763 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
765 logger.debug("outputs is NULL");
767 networkRollback.setNetworkType(networkType);
768 // Save previous parameters
769 networkRollback.setNetworkName(previousNetworkName);
770 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
771 networkRollback.setVlans(previousVlans);
773 rollback.value = networkRollback;
775 logger.debug("Network {} successfully updated via HEAT", networkId);
781 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
782 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
783 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
784 // Retrieve the Network Resource definition
785 NetworkResource networkResource = null;
786 NetworkResourceCustomization networkCust = null;
787 CollectionNetworkResourceCustomization collectionNetworkCust = null;
788 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
789 if (!commonUtils.isNullOrEmpty(networkType)) {
790 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
793 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
794 if (networkCust == null) {
795 collectionNetworkCust =
796 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
799 if (networkCust != null) {
800 logger.debug("Got Network Customization definition from Catalog: {}", networkCust.toString());
802 networkResource = networkCust.getNetworkResource();
803 } else if (collectionNetworkCust != null) {
804 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}",
805 collectionNetworkCust.toString());
806 networkResource = collectionNetworkCust.getNetworkResource();
808 if (networkResource == null) {
809 String error = String.format(
810 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
811 networkType, modelCustomizationUuid);
812 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
814 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
816 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
818 String mode = networkResource.getOrchestrationMode();
819 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
821 // All Networks are orchestrated via HEAT or Neutron
822 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
823 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
824 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
825 ErrorCode.DataError.getValue(), error);
826 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
829 MavenLikeVersioning aicV = new MavenLikeVersioning();
830 aicV.setVersion(cloudSite.getCloudVersion());
831 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
832 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
835 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
836 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
838 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
839 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
840 cloudSite.getCloudVersion());
842 String error = String.format(
843 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
844 networkType, networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(),
845 cloudSiteId, cloudSite.getCloudVersion());
846 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
847 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
850 // Validate the Network parameters.
852 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
853 if (!missing.isEmpty()) {
854 String error = "Create Network: Missing parameters: " + missing;
855 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
857 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
860 return networkResource;
864 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
865 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
866 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
867 throws NetworkException {
868 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
869 status, vlans, null, subnetIdMap);
873 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
874 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
875 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
876 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
877 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
878 status, null, routeTargets, subnetIdMap);
882 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
883 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
884 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
886 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
887 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
888 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
889 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
891 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
893 // Will capture execution time for metrics
894 long startTime = System.currentTimeMillis();
896 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
897 || commonUtils.isNullOrEmpty(networkNameOrId)) {
899 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
900 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
901 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
904 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
905 if (!cloudSiteOpt.isPresent()) {
906 String error = String.format(
907 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
908 networkNameOrId, cloudSiteId, tenantId);
909 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
910 // Set the detailed error as the Exception 'message'
911 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
914 // Use MsoNeutronUtils for all NEUTRON commands
918 // Try Heat first, since networks may be named the same as the Heat stack
919 StackInfo heatStack = null;
920 long queryStackStarttime = System.currentTimeMillis();
922 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkNameOrId);
923 } catch (MsoException me) {
924 me.addContext("QueryNetwork");
925 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
926 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
927 throw new NetworkException(me);
930 // Populate the outputs based on the returned Stack information
931 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
932 // Found it. Get the neutronNetworkId for further query
933 Map<String, Object> outputs = heatStack.getOutputs();
934 neutronId = (String) outputs.get(NETWORK_ID);
937 Map<String, String> sMap = new HashMap<>();
938 if (outputs != null) {
939 for (String key : outputs.keySet()) {
940 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
942 String subnetUUId = (String) outputs.get(key);
943 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
944 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
946 Map<String, String> map = getSubnetUUId(key, outputs, null);
952 subnetIdMap.value = sMap;
954 // Input ID was not a Heat stack ID. Try it directly in Neutron
955 neutronId = networkNameOrId;
959 // Query directly against the Neutron Network for the details
960 // no RouteTargets available for ContrailV2 in neutron net-show
961 // networkId is heatStackId
962 long queryNetworkStarttime = System.currentTimeMillis();
964 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
965 if (netInfo != null) {
966 // Found. Populate the output elements
967 networkExists.value = Boolean.TRUE;
968 if ("HEAT".equals(mode) && heatStack != null) {
969 networkId.value = heatStack.getCanonicalName();
971 networkId.value = netInfo.getId();
973 neutronNetworkId.value = netInfo.getId();
974 status.value = netInfo.getStatus();
976 vlans.value = netInfo.getVlans();
978 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
979 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
981 // Not found. Populate the status fields, leave the rest null
982 networkExists.value = Boolean.FALSE;
983 status.value = NetworkStatus.NOTFOUND;
984 neutronNetworkId.value = null;
986 vlans.value = new ArrayList<>();
988 logger.debug("Network {} not found", networkNameOrId);
990 } catch (MsoException me) {
991 me.addContext("QueryNetwork");
992 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
993 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
994 throw new NetworkException(me);
1000 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
1003 * If the network is not found, it is treated as a success.
1005 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
1006 * network orchestration mode for each network type is declared in its catalog definition.
1008 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1009 * networkId should be the Neutron network UUID.
1011 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1012 * will require manual fallout in the client.
1015 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1016 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1018 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1020 // Will capture execution time for metrics
1021 long startTime = System.currentTimeMillis();
1024 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1025 || commonUtils.isNullOrEmpty(networkId)) {
1026 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1027 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1028 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1031 // Retrieve the Network Resource definition
1032 NetworkResource networkResource = null;
1034 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1035 if (!commonUtils.isNullOrEmpty(networkType)) {
1036 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1039 NetworkResourceCustomization nrc =
1040 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1042 networkResource = nrc.getNetworkResource();
1046 if (networkResource != null) {
1047 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1049 mode = networkResource.getOrchestrationMode();
1052 if (NEUTRON_MODE.equals(mode)) {
1054 // Use MsoNeutronUtils for all NEUTRON commands
1055 long deleteNetworkStarttime = System.currentTimeMillis();
1057 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1058 // was not found. So don't bother to query first.
1059 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1060 networkDeleted.value = deleted;
1061 } catch (MsoException me) {
1062 me.addContext("DeleteNetwork");
1063 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1064 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1065 throw new NetworkException(me);
1067 } else { // DEFAULT to ("HEAT".equals (mode))
1068 long deleteStackStarttime = System.currentTimeMillis();
1071 // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack
1073 // So query first to report back if stack WAS deleted or just NOTOFUND
1074 StackInfo heatStack = null;
1075 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkId);
1076 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
1077 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1078 networkDeleted.value = true;
1080 networkDeleted.value = false;
1082 } catch (MsoException me) {
1083 me.addContext("DeleteNetwork");
1084 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1085 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1086 throw new NetworkException(me);
1091 // On success, nothing is returned.
1096 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1097 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1098 * to undo the creation.
1100 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1104 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1105 // Will capture execution time for metrics
1106 long startTime = System.currentTimeMillis();
1108 if (rollback == null) {
1109 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1113 // Get the elements of the VnfRollback object for easier access
1114 String cloudSiteId = rollback.getCloudId();
1115 String tenantId = rollback.getTenantId();
1116 String networkId = rollback.getNetworkStackId();
1117 String networkType = rollback.getNetworkType();
1118 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1120 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1123 // Retrieve the Network Resource definition
1124 NetworkResource networkResource = null;
1125 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1126 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1129 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1132 if (networkResource != null) {
1134 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1136 mode = networkResource.getOrchestrationMode();
1139 if (rollback.getNetworkCreated()) {
1140 // Rolling back a newly created network, so delete it.
1141 if (NEUTRON_MODE.equals(mode)) {
1142 // Use MsoNeutronUtils for all NEUTRON commands
1143 long deleteNetworkStarttime = System.currentTimeMillis();
1145 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1146 // was not found. So don't bother to query first.
1147 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1148 } catch (MsoException me) {
1149 me.addContext("RollbackNetwork");
1150 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1151 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1152 cloudSiteId, tenantId, me);
1153 throw new NetworkException(me);
1155 } else { // DEFAULT to if ("HEAT".equals (mode))
1156 long deleteStackStarttime = System.currentTimeMillis();
1158 // The deleteStack function in MsoHeatUtils returns success if the stack
1159 // was not found. So don't bother to query first.
1160 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1161 } catch (MsoException me) {
1162 me.addContext("RollbackNetwork");
1163 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1164 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1165 cloudSiteId, tenantId, me);
1166 throw new NetworkException(me);
1174 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1175 List<Integer> vlans, List<RouteTarget> routeTargets) {
1177 StringBuilder missing = new StringBuilder();
1178 if (commonUtils.isNullOrEmpty(networkName)) {
1179 missing.append("networkName");
1183 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1184 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1185 missing.append(sep).append("physicalNetworkName");
1188 if (vlans == null || vlans.isEmpty()) {
1189 missing.append(sep).append(VLANS);
1193 return missing.toString();
1196 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1197 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1198 boolean aic3template) {
1199 // Build the common set of HEAT template parameters
1200 Map<String, Object> stackParams = new HashMap<>();
1201 stackParams.put("network_name", networkName);
1203 if (neutronNetworkType == NetworkType.PROVIDER) {
1204 // For Provider type
1205 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1206 stackParams.put("vlan", vlans.get(0).toString());
1207 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1208 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1209 // It supports all ProviderNet properties except segmentation_id, and adds a
1210 // comma-separated-list of VLANs as a "segments" property.
1211 // Note that this does not match the Neutron definition of Multi-Provider network,
1212 // which contains a list of 'segments', each having physical_network, network_type,
1213 // and segmentation_id.
1214 StringBuilder buf = new StringBuilder();
1216 for (Integer vlan : vlans) {
1217 buf.append(sep).append(vlan.toString());
1220 String csl = buf.toString();
1222 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1223 stackParams.put(VLANS, csl);
1225 if (routeTargets != null) {
1227 String rtGlobal = "";
1228 String rtImport = "";
1229 String rtExport = "";
1231 for (RouteTarget rt : routeTargets) {
1232 boolean rtIsNull = false;
1234 String routeTarget = rt.getRouteTarget();
1235 String routeTargetRole = rt.getRouteTargetRole();
1236 logger.debug("Checking for an actually null route target: {}", rt);
1237 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1239 if (routeTargetRole == null || routeTargetRole.equals("")
1240 || routeTargetRole.equalsIgnoreCase("null"))
1246 logger.debug("Input RT:{}", rt);
1247 String role = rt.getRouteTargetRole();
1248 String rtValue = rt.getRouteTarget();
1250 if ("IMPORT".equalsIgnoreCase(role)) {
1251 sep = rtImport.isEmpty() ? "" : ",";
1252 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1253 } else if ("EXPORT".equalsIgnoreCase(role)) {
1254 sep = rtExport.isEmpty() ? "" : ",";
1255 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1256 } else // covers BOTH, empty etc
1258 sep = rtGlobal.isEmpty() ? "" : ",";
1259 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1265 if (!rtImport.isEmpty()) {
1266 stackParams.put("route_targets_import", rtImport);
1268 if (!rtExport.isEmpty()) {
1269 stackParams.put("route_targets_export", rtExport);
1271 if (!rtGlobal.isEmpty()) {
1272 stackParams.put("route_targets", rtGlobal);
1275 if (commonUtils.isNullOrEmpty(shared)) {
1276 stackParams.put("shared", "False");
1278 stackParams.put("shared", shared);
1280 if (commonUtils.isNullOrEmpty(external)) {
1281 stackParams.put("external", "False");
1283 stackParams.put("external", external);
1291 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1292 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1293 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1294 * "network_policy_refs_data_sequence_minor": "0" } } ]
1296 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1297 // Resource Property
1298 List<ContrailPolicyRef> prlist = new ArrayList<>();
1301 if (pFqdns != null) {
1302 for (String pf : pFqdns) {
1303 if (!commonUtils.isNullOrEmpty(pf)) {
1304 ContrailPolicyRef pr = new ContrailPolicyRef();
1305 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1308 logger.debug("Contrail PolicyRefs Data:{}", pr);
1313 String error = "Null pFqdns at start of mergePolicyRefs";
1314 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1316 throw new MsoAdapterException(error);
1319 JsonNode node = null;
1321 ObjectMapper mapper = new ObjectMapper();
1322 node = mapper.convertValue(prlist, JsonNode.class);
1323 String jsonString = mapper.writeValueAsString(prlist);
1324 logger.debug("Json PolicyRefs Data:{}", jsonString);
1325 } catch (Exception e) {
1326 String error = "Error creating JsonNode for policyRefs Data";
1327 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1329 throw new MsoAdapterException(error);
1331 // update parameters
1332 if (pFqdns != null && node != null) {
1333 StringBuilder buf = new StringBuilder();
1335 for (String pf : pFqdns) {
1336 if (!commonUtils.isNullOrEmpty(pf)) {
1337 buf.append(sep).append(pf);
1341 String csl = buf.toString();
1342 stackParams.put("policy_refs", csl);
1343 stackParams.put("policy_refsdata", node);
1346 logger.debug("StackParams updated with policy refs");
1350 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1352 // update parameters
1353 if (rtFqdns != null) {
1354 StringBuilder buf = new StringBuilder();
1356 for (String rtf : rtFqdns) {
1357 if (!commonUtils.isNullOrEmpty(rtf)) {
1358 buf.append(sep).append(rtf);
1362 String csl = buf.toString();
1363 stackParams.put("route_table_refs", csl);
1366 logger.debug("StackParams updated with route_table refs");
1372 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1373 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1374 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1375 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1376 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1377 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1378 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1379 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1380 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1381 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1382 * "host_routes": null }
1384 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1385 throws MsoException {
1387 // Resource Property
1388 List<ContrailSubnet> cslist = new ArrayList<>();
1389 for (Subnet subnet : subnets) {
1390 logger.debug("Input Subnet:{}", subnet.toString());
1391 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1392 logger.debug("Contrail Subnet:{}", cs.toString());
1396 JsonNode node = null;
1398 ObjectMapper mapper = new ObjectMapper();
1399 node = mapper.convertValue(cslist, JsonNode.class);
1400 String jsonString = mapper.writeValueAsString(cslist);
1401 logger.debug("Json Subnet List:{}", jsonString);
1402 } catch (Exception e) {
1403 String error = "Error creating JsonNode from input subnets";
1404 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1405 throw new MsoAdapterException(error);
1407 // update parameters
1409 stackParams.put("subnet_list", node);
1411 // Outputs - All subnets are in one ipam_subnets structure
1412 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1413 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1415 // append outputs in heatTemplate
1416 int outputsIdx = heatTemplate.indexOf("outputs:");
1417 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1418 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1419 return heatTemplate;
1423 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1425 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1426 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1429 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1430 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1431 * " end: %poolend%\n";
1435 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1436 + " value: {get_resource: subnet_%subnetId%}\n";
1440 StringBuilder resourcesBuf = new StringBuilder();
1441 StringBuilder outputsBuf = new StringBuilder();
1442 for (Subnet subnet : subnets) {
1444 // build template for each subnet
1445 curR = resourceTempl;
1446 if (subnet.getSubnetId() != null) {
1447 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1449 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1450 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1451 throw new MsoAdapterException(error);
1454 if (subnet.getSubnetName() != null) {
1455 curR = curR.replace("%name%", subnet.getSubnetName());
1457 curR = curR.replace("%name%", subnet.getSubnetId());
1460 if (subnet.getCidr() != null) {
1461 curR = curR.replace("%cidr%", subnet.getCidr());
1463 String error = "Missing Required cidr for subnet in HEAT Template";
1464 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1465 throw new MsoAdapterException(error);
1468 if (subnet.getIpVersion() != null) {
1469 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1471 if (subnet.getEnableDHCP() != null) {
1472 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1474 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1475 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1478 if (subnet.getAllocationPools() != null) {
1479 curR = curR + " allocation_pools:\n";
1480 for (Pool pool : subnet.getAllocationPools()) {
1481 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1482 curR = curR + " - start: " + pool.getStart() + "\n";
1483 curR = curR + " end: " + pool.getEnd() + "\n";
1488 resourcesBuf.append(curR);
1491 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1493 outputsBuf.append(curO);
1496 // append resources and outputs in heatTemplate
1497 logger.debug("Tempate initial:{}", heatTemplate);
1498 int outputsIdx = heatTemplate.indexOf("outputs:");
1499 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1500 int resourcesIdx = heatTemplate.indexOf("resources:");
1501 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1503 logger.debug("Template updated with all subnets:{}", heatTemplate);
1504 return heatTemplate;
1507 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1509 Map<String, String> sMap = new HashMap<>();
1512 Object obj = outputs.get(key);
1513 ObjectMapper mapper = new ObjectMapper();
1514 String jStr = mapper.writeValueAsString(obj);
1515 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1517 JsonNode rootNode = mapper.readTree(jStr);
1518 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1519 logger.debug("Output Subnet Node {}", sNode.toString());
1520 String name = sNode.path("subnet_name").textValue();
1521 String uuid = sNode.path("subnet_uuid").textValue();
1522 String aaiId = name; // default
1523 // try to find aaiId for name in input subnetList
1524 if (subnets != null) {
1525 for (Subnet subnet : subnets) {
1526 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())) {
1527 if (subnet.getSubnetName().equals(name)) {
1528 aaiId = subnet.getSubnetId();
1534 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1536 } catch (Exception e) {
1537 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1538 ErrorCode.DataError.getValue(), e);
1541 logger.debug("Return sMap {}", sMap.toString());
1545 private static String insertStr(String template, String snippet, int index) {
1547 String updatedTemplate;
1549 logger.debug("Index:{} Snippet:{}", index, snippet);
1551 String templateBeg = template.substring(0, index);
1552 String templateEnd = template.substring(index);
1554 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1556 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1557 return updatedTemplate;