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 com.google.common.base.Strings;
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(Strings.repeat("{} ", 3), 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(Strings.repeat("{} ", 3), 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(Strings.repeat("{} ", 3), 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(Strings.repeat("{} ", 3), 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 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
334 rollback.value = networkRollback; // Default rollback - no updates performed
336 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
338 Map<String, Object> outputs = heatStack.getOutputs();
339 Map<String, String> sMap = new HashMap<>();
340 if (outputs != null) {
341 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
342 String key = entry.getKey();
343 if (key != null && key.startsWith("subnet")) {
344 if (aic3template) // one subnet_id output
346 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
348 } else // multiples subnet_%aaid% outputs
350 String subnetUUId = (String) outputs.get(key);
351 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
356 subnetIdMap.value = sMap;
357 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
358 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
359 networkName, cloudSiteId, tenantId);
364 // Ready to deploy the new Network
365 // Build the common set of HEAT template parameters
366 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
367 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
369 // Validate (and update) the input parameters against the DB definition
370 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
371 // and inputs were already validated.
373 stackParams = heat.validateStackParams(stackParams, heatTemplate);
374 } catch (IllegalArgumentException e) {
375 String error = "Create Network: Configuration Error: " + e.getMessage();
376 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error,
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 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
443 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
445 Map<String, Object> outputs = heatStack.getOutputs();
446 Map<String, String> sMap = new HashMap<>();
447 if (outputs != null) {
448 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
449 String key = entry.getKey();
450 if (key != null && key.startsWith("subnet")) {
451 if (aic3template) // one subnet output expected
453 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
455 } else // multiples subnet_%aaid% outputs allowed
457 String subnetUUId = (String) outputs.get(key);
458 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
463 subnetIdMap.value = sMap;
465 rollback.value = networkRollback;
466 // Populate remaining rollback info and response parameters.
467 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
468 networkRollback.setNeutronNetworkId((String) heatStack.getOutputs().get(NETWORK_ID));
469 networkRollback.setNetworkCreated(true);
470 networkRollback.setNetworkType(networkType);
472 logger.debug("Network {} successfully created via HEAT", networkName);
479 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
480 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
481 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
482 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
483 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
484 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
490 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
491 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
492 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
493 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
494 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
495 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
496 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
501 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
502 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
503 * remove a VLAN), but other properties may be updated as well.
505 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
506 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
507 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
509 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
510 * VLANs on the same physical network.
512 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
513 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
514 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
516 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
517 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
518 * a subsequent operation.
520 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
521 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
522 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
523 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
524 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
526 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
527 cloudSiteId, tenantId);
529 // Will capture execution time for metrics
530 long startTime = System.currentTimeMillis();
532 // Build a default rollback object (no actions performed)
533 NetworkRollback networkRollback = new NetworkRollback();
534 networkRollback.setCloudId(cloudSiteId);
535 networkRollback.setTenantId(tenantId);
536 networkRollback.setMsoRequest(msoRequest);
538 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
539 if (!cloudSiteOpt.isPresent()) {
540 String error = String.format(
541 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
542 networkName, cloudSiteId, tenantId);
543 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
544 // Set the detailed error as the Exception 'message'
545 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
550 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
551 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
552 String mode = networkResource.getOrchestrationMode();
553 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
555 // Use an MsoNeutronUtils for all Neutron commands
557 if (NEUTRON_MODE.equals(mode)) {
559 // Verify that the Network exists
560 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
561 NetworkInfo netInfo = null;
562 long queryNetworkStarttime = System.currentTimeMillis();
564 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
565 } catch (MsoException me) {
566 me.addContext(UPDATE_NETWORK_CONTEXT);
567 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
568 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
569 throw new NetworkException(me);
572 if (netInfo == null) {
573 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
574 cloudSiteId, tenantId);
575 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_NETWORK_NOT_FOUND,
576 ErrorCode.BusinessProcesssError.getValue(), error);
577 // Does not exist. Throw an exception (can't update a non-existent network)
578 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
580 long updateNetworkStarttime = System.currentTimeMillis();
582 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
583 physicalNetworkName, vlans);
584 } catch (MsoException me) {
585 me.addContext(UPDATE_NETWORK_CONTEXT);
586 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
587 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
588 throw new NetworkException(me);
591 // Add the network ID and previously queried vlans to the rollback object
592 networkRollback.setNetworkId(netInfo.getId());
593 networkRollback.setNeutronNetworkId(netInfo.getId());
594 networkRollback.setNetworkType(networkType);
595 // Save previous parameters
596 networkRollback.setNetworkName(netInfo.getName());
597 networkRollback.setPhysicalNetwork(netInfo.getProvider());
598 networkRollback.setVlans(netInfo.getVlans());
600 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
601 } else if ("HEAT".equals(mode)) {
603 // First, look up to see that the Network already exists.
604 // For Heat-based orchestration, the networkId is the network Stack ID.
605 StackInfo heatStack = null;
606 long queryStackStarttime = System.currentTimeMillis();
608 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkName);
609 } catch (MsoException me) {
610 me.addContext(UPDATE_NETWORK_CONTEXT);
611 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
612 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
613 throw new NetworkException(me);
616 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
617 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
618 cloudSiteId, tenantId);
619 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
621 // Network stack does not exist. Return an error
622 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
625 // Get the previous parameters for rollback
626 Map<String, Object> heatParams = heatStack.getParameters();
628 String previousNetworkName = (String) heatParams.get("network_name");
629 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
631 List<Integer> previousVlans = new ArrayList<>();
632 String vlansParam = (String) heatParams.get(VLANS);
633 if (vlansParam != null) {
634 for (String vlan : vlansParam.split(",")) {
636 previousVlans.add(Integer.parseInt(vlan));
637 } catch (NumberFormatException e) {
638 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
639 ErrorCode.DataError.getValue(), vlansParam, e);
643 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
645 // Ready to deploy the updated Network via Heat
648 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
649 if (heatTemplate == null) {
650 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
651 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
653 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
656 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
658 // "Fix" the template if it has CR/LF (getting this from Oracle)
659 String template = heatTemplate.getHeatTemplate();
660 template = template.replaceAll("\r\n", "\n");
662 boolean aic3template = false;
663 String aic3nw = AIC3_NW;
665 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
667 if (template.contains(aic3nw))
670 // Build the common set of HEAT template parameters
671 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
672 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
674 // Validate (and update) the input parameters against the DB definition
675 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
677 stackParams = heat.validateStackParams(stackParams, heatTemplate);
678 } catch (IllegalArgumentException e) {
679 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
680 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(),
682 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
685 if (subnets != null) {
688 template = mergeSubnetsAIC3(template, subnets, stackParams);
690 template = mergeSubnets(template, subnets);
692 } catch (MsoException me) {
693 me.addContext(UPDATE_NETWORK_CONTEXT);
694 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
695 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
696 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
697 throw new NetworkException(me);
701 if (policyFqdns != null && aic3template) {
703 mergePolicyRefs(policyFqdns, stackParams);
704 } catch (MsoException me) {
705 me.addContext(UPDATE_NETWORK_CONTEXT);
706 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
707 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
708 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
709 throw new NetworkException(me);
713 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
715 mergeRouteTableRefs(routeTableFqdns, stackParams);
716 } catch (MsoException me) {
717 me.addContext(UPDATE_NETWORK_CONTEXT);
718 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
719 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
720 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
721 throw new NetworkException(me);
725 // Update the network stack
726 // Ignore MsoStackNotFound exception because we already checked.
727 long updateStackStarttime = System.currentTimeMillis();
729 heatStack = heatWithUpdate.updateStack(cloudSiteId, "CloudOwner", 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.toString());
801 networkResource = networkCust.getNetworkResource();
802 } else if (collectionNetworkCust != null) {
803 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}",
804 collectionNetworkCust.toString());
805 networkResource = collectionNetworkCust.getNetworkResource();
807 if (networkResource == null) {
808 String error = String.format(
809 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
810 networkType, modelCustomizationUuid);
811 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
813 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
815 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
817 String mode = networkResource.getOrchestrationMode();
818 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
820 // All Networks are orchestrated via HEAT or Neutron
821 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
822 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
823 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
824 ErrorCode.DataError.getValue(), error);
825 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
828 MavenLikeVersioning aicV = new MavenLikeVersioning();
829 aicV.setVersion(cloudSite.getCloudVersion());
830 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
831 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
834 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
835 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
837 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
838 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
839 cloudSite.getCloudVersion());
841 String error = String.format(
842 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
843 networkType, networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(),
844 cloudSiteId, cloudSite.getCloudVersion());
845 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
846 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
849 // Validate the Network parameters.
851 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
852 if (!missing.isEmpty()) {
853 String error = "Create Network: Missing parameters: " + missing;
854 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
856 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
859 return networkResource;
863 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
864 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
865 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
866 throws NetworkException {
867 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
868 status, vlans, null, subnetIdMap);
872 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
873 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
874 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
875 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
876 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
877 status, null, routeTargets, subnetIdMap);
881 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
882 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
883 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
885 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
886 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
887 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
888 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
890 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
892 // Will capture execution time for metrics
893 long startTime = System.currentTimeMillis();
895 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
896 || commonUtils.isNullOrEmpty(networkNameOrId)) {
898 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
899 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
900 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
903 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
904 if (!cloudSiteOpt.isPresent()) {
905 String error = String.format(
906 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
907 networkNameOrId, cloudSiteId, tenantId);
908 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
909 // Set the detailed error as the Exception 'message'
910 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
913 // Use MsoNeutronUtils for all NEUTRON commands
917 // Try Heat first, since networks may be named the same as the Heat stack
918 StackInfo heatStack = null;
919 long queryStackStarttime = System.currentTimeMillis();
921 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkNameOrId);
922 } catch (MsoException me) {
923 me.addContext("QueryNetwork");
924 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
925 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
926 throw new NetworkException(me);
929 // Populate the outputs based on the returned Stack information
930 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
931 // Found it. Get the neutronNetworkId for further query
932 Map<String, Object> outputs = heatStack.getOutputs();
933 neutronId = (String) outputs.get(NETWORK_ID);
936 Map<String, String> sMap = new HashMap<>();
937 if (outputs != null) {
938 for (String key : outputs.keySet()) {
939 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
941 String subnetUUId = (String) outputs.get(key);
942 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
943 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
945 Map<String, String> map = getSubnetUUId(key, outputs, null);
951 subnetIdMap.value = sMap;
953 // Input ID was not a Heat stack ID. Try it directly in Neutron
954 neutronId = networkNameOrId;
958 // Query directly against the Neutron Network for the details
959 // no RouteTargets available for ContrailV2 in neutron net-show
960 // networkId is heatStackId
961 long queryNetworkStarttime = System.currentTimeMillis();
963 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
964 if (netInfo != null) {
965 // Found. Populate the output elements
966 networkExists.value = Boolean.TRUE;
967 if ("HEAT".equals(mode)) {
968 networkId.value = heatStack.getCanonicalName();
970 networkId.value = netInfo.getId();
972 neutronNetworkId.value = netInfo.getId();
973 status.value = netInfo.getStatus();
975 vlans.value = netInfo.getVlans();
977 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
978 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
980 // Not found. Populate the status fields, leave the rest null
981 networkExists.value = Boolean.FALSE;
982 status.value = NetworkStatus.NOTFOUND;
983 neutronNetworkId.value = null;
985 vlans.value = new ArrayList<>();
987 logger.debug("Network {} not found", networkNameOrId);
989 } catch (MsoException me) {
990 me.addContext("QueryNetwork");
991 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
992 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
993 throw new NetworkException(me);
999 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
1002 * If the network is not found, it is treated as a success.
1004 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
1005 * network orchestration mode for each network type is declared in its catalog definition.
1007 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1008 * networkId should be the Neutron network UUID.
1010 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1011 * will require manual fallout in the client.
1014 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1015 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1017 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1019 // Will capture execution time for metrics
1020 long startTime = System.currentTimeMillis();
1023 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1024 || commonUtils.isNullOrEmpty(networkId)) {
1025 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1026 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1027 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1030 // Retrieve the Network Resource definition
1031 NetworkResource networkResource = null;
1033 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1034 if (!commonUtils.isNullOrEmpty(networkType)) {
1035 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1038 NetworkResourceCustomization nrc =
1039 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1041 networkResource = nrc.getNetworkResource();
1045 if (networkResource != null) {
1046 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1048 mode = networkResource.getOrchestrationMode();
1051 if (NEUTRON_MODE.equals(mode)) {
1053 // Use MsoNeutronUtils for all NEUTRON commands
1054 long deleteNetworkStarttime = System.currentTimeMillis();
1056 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1057 // was not found. So don't bother to query first.
1058 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1059 networkDeleted.value = deleted;
1060 } catch (MsoException me) {
1061 me.addContext("DeleteNetwork");
1062 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1063 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1064 throw new NetworkException(me);
1066 } else { // DEFAULT to ("HEAT".equals (mode))
1067 long deleteStackStarttime = System.currentTimeMillis();
1070 // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack
1072 // So query first to report back if stack WAS deleted or just NOTOFUND
1073 StackInfo heatStack = null;
1074 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkId);
1075 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
1076 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1077 networkDeleted.value = true;
1079 networkDeleted.value = false;
1081 } catch (MsoException me) {
1082 me.addContext("DeleteNetwork");
1083 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1084 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1085 throw new NetworkException(me);
1090 // On success, nothing is returned.
1095 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1096 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1097 * to undo the creation.
1099 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1103 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1104 // Will capture execution time for metrics
1105 long startTime = System.currentTimeMillis();
1107 if (rollback == null) {
1108 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1112 // Get the elements of the VnfRollback object for easier access
1113 String cloudSiteId = rollback.getCloudId();
1114 String tenantId = rollback.getTenantId();
1115 String networkId = rollback.getNetworkStackId();
1116 String networkType = rollback.getNetworkType();
1117 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1119 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1122 // Retrieve the Network Resource definition
1123 NetworkResource networkResource = null;
1124 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1125 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1128 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1131 if (networkResource != null) {
1133 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1135 mode = networkResource.getOrchestrationMode();
1138 if (rollback.getNetworkCreated()) {
1139 // Rolling back a newly created network, so delete it.
1140 if (NEUTRON_MODE.equals(mode)) {
1141 // Use MsoNeutronUtils for all NEUTRON commands
1142 long deleteNetworkStarttime = System.currentTimeMillis();
1144 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1145 // was not found. So don't bother to query first.
1146 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1147 } catch (MsoException me) {
1148 me.addContext("RollbackNetwork");
1149 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1150 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1151 cloudSiteId, tenantId, me);
1152 throw new NetworkException(me);
1154 } else { // DEFAULT to if ("HEAT".equals (mode))
1155 long deleteStackStarttime = System.currentTimeMillis();
1157 // The deleteStack function in MsoHeatUtils returns success if the stack
1158 // was not found. So don't bother to query first.
1159 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1160 } catch (MsoException me) {
1161 me.addContext("RollbackNetwork");
1162 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1163 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1164 cloudSiteId, tenantId, me);
1165 throw new NetworkException(me);
1173 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1174 List<Integer> vlans, List<RouteTarget> routeTargets) {
1176 StringBuilder missing = new StringBuilder();
1177 if (commonUtils.isNullOrEmpty(networkName)) {
1178 missing.append("networkName");
1182 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1183 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1184 missing.append(sep).append("physicalNetworkName");
1187 if (vlans == null || vlans.isEmpty()) {
1188 missing.append(sep).append(VLANS);
1192 return missing.toString();
1195 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1196 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1197 boolean aic3template) {
1198 // Build the common set of HEAT template parameters
1199 Map<String, Object> stackParams = new HashMap<>();
1200 stackParams.put("network_name", networkName);
1202 if (neutronNetworkType == NetworkType.PROVIDER) {
1203 // For Provider type
1204 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1205 stackParams.put("vlan", vlans.get(0).toString());
1206 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1207 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1208 // It supports all ProviderNet properties except segmentation_id, and adds a
1209 // comma-separated-list of VLANs as a "segments" property.
1210 // Note that this does not match the Neutron definition of Multi-Provider network,
1211 // which contains a list of 'segments', each having physical_network, network_type,
1212 // and segmentation_id.
1213 StringBuilder buf = new StringBuilder();
1215 for (Integer vlan : vlans) {
1216 buf.append(sep).append(vlan.toString());
1219 String csl = buf.toString();
1221 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1222 stackParams.put(VLANS, csl);
1224 if (routeTargets != null) {
1226 String rtGlobal = "";
1227 String rtImport = "";
1228 String rtExport = "";
1230 for (RouteTarget rt : routeTargets) {
1231 boolean rtIsNull = false;
1233 String routeTarget = rt.getRouteTarget();
1234 String routeTargetRole = rt.getRouteTargetRole();
1235 logger.debug("Checking for an actually null route target: {}", rt);
1236 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1238 if (routeTargetRole == null || routeTargetRole.equals("")
1239 || routeTargetRole.equalsIgnoreCase("null"))
1245 logger.debug("Input RT:{}", rt);
1246 String role = rt.getRouteTargetRole();
1247 String rtValue = rt.getRouteTarget();
1249 if ("IMPORT".equalsIgnoreCase(role)) {
1250 sep = rtImport.isEmpty() ? "" : ",";
1251 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1252 } else if ("EXPORT".equalsIgnoreCase(role)) {
1253 sep = rtExport.isEmpty() ? "" : ",";
1254 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1255 } else // covers BOTH, empty etc
1257 sep = rtGlobal.isEmpty() ? "" : ",";
1258 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1264 if (!rtImport.isEmpty()) {
1265 stackParams.put("route_targets_import", rtImport);
1267 if (!rtExport.isEmpty()) {
1268 stackParams.put("route_targets_export", rtExport);
1270 if (!rtGlobal.isEmpty()) {
1271 stackParams.put("route_targets", rtGlobal);
1274 if (commonUtils.isNullOrEmpty(shared)) {
1275 stackParams.put("shared", "False");
1277 stackParams.put("shared", shared);
1279 if (commonUtils.isNullOrEmpty(external)) {
1280 stackParams.put("external", "False");
1282 stackParams.put("external", external);
1290 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1291 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1292 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1293 * "network_policy_refs_data_sequence_minor": "0" } } ]
1295 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1296 // Resource Property
1297 List<ContrailPolicyRef> prlist = new ArrayList<>();
1299 for (String pf : pFqdns) {
1300 if (!commonUtils.isNullOrEmpty(pf)) {
1301 ContrailPolicyRef pr = new ContrailPolicyRef();
1302 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1305 logger.debug("Contrail PolicyRefs Data:{}", pr);
1310 JsonNode node = null;
1312 ObjectMapper mapper = new ObjectMapper();
1313 node = mapper.convertValue(prlist, JsonNode.class);
1314 String jsonString = mapper.writeValueAsString(prlist);
1315 logger.debug("Json PolicyRefs Data:{}", jsonString);
1316 } catch (Exception e) {
1317 String error = "Error creating JsonNode for policyRefs Data";
1318 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_MARSHING_ERROR,
1319 ErrorCode.BusinessProcesssError.getValue(), error, e);
1320 throw new MsoAdapterException(error);
1322 // update parameters
1323 if (pFqdns != null && node != null) {
1324 StringBuilder buf = new StringBuilder();
1326 for (String pf : pFqdns) {
1327 if (!commonUtils.isNullOrEmpty(pf)) {
1328 buf.append(sep).append(pf);
1332 String csl = buf.toString();
1333 stackParams.put("policy_refs", csl);
1334 stackParams.put("policy_refsdata", node);
1337 logger.debug("StackParams updated with policy refs");
1341 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1343 // update parameters
1344 if (rtFqdns != null) {
1345 StringBuilder buf = new StringBuilder();
1347 for (String rtf : rtFqdns) {
1348 if (!commonUtils.isNullOrEmpty(rtf)) {
1349 buf.append(sep).append(rtf);
1353 String csl = buf.toString();
1354 stackParams.put("route_table_refs", csl);
1357 logger.debug("StackParams updated with route_table refs");
1363 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1364 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1365 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1366 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1367 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1368 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1369 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1370 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1371 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1372 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1373 * "host_routes": null }
1375 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1376 throws MsoException {
1378 // Resource Property
1379 List<ContrailSubnet> cslist = new ArrayList<>();
1380 for (Subnet subnet : subnets) {
1381 logger.debug("Input Subnet:{}", subnet.toString());
1382 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1383 logger.debug("Contrail Subnet:{}", cs.toString());
1387 JsonNode node = null;
1389 ObjectMapper mapper = new ObjectMapper();
1390 node = mapper.convertValue(cslist, JsonNode.class);
1391 String jsonString = mapper.writeValueAsString(cslist);
1392 logger.debug("Json Subnet List:{}", jsonString);
1393 } catch (Exception e) {
1394 String error = "Error creating JsonNode from input subnets";
1395 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error,
1397 throw new MsoAdapterException(error);
1399 // update parameters
1401 stackParams.put("subnet_list", node);
1403 // Outputs - All subnets are in one ipam_subnets structure
1404 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1405 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1407 // append outputs in heatTemplate
1408 int outputsIdx = heatTemplate.indexOf("outputs:");
1409 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1410 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1411 return heatTemplate;
1415 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1417 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1418 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1421 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1422 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1423 * " end: %poolend%\n";
1427 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1428 + " value: {get_resource: subnet_%subnetId%}\n";
1432 StringBuilder resourcesBuf = new StringBuilder();
1433 StringBuilder outputsBuf = new StringBuilder();
1434 for (Subnet subnet : subnets) {
1436 // build template for each subnet
1437 curR = resourceTempl;
1438 if (subnet.getSubnetId() != null) {
1439 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1441 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1442 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(),
1444 throw new MsoAdapterException(error);
1447 if (subnet.getSubnetName() != null) {
1448 curR = curR.replace("%name%", subnet.getSubnetName());
1450 curR = curR.replace("%name%", subnet.getSubnetId());
1453 if (subnet.getCidr() != null) {
1454 curR = curR.replace("%cidr%", subnet.getCidr());
1456 String error = "Missing Required cidr for subnet in HEAT Template";
1457 logger.error(Strings.repeat("{} ", 3), MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(),
1459 throw new MsoAdapterException(error);
1462 if (subnet.getIpVersion() != null) {
1463 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1465 if (subnet.getEnableDHCP() != null) {
1466 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1468 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1469 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1472 if (subnet.getAllocationPools() != null) {
1473 curR = curR + " allocation_pools:\n";
1474 for (Pool pool : subnet.getAllocationPools()) {
1475 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1476 curR = curR + " - start: " + pool.getStart() + "\n";
1477 curR = curR + " end: " + pool.getEnd() + "\n";
1482 resourcesBuf.append(curR);
1485 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1487 outputsBuf.append(curO);
1490 // append resources and outputs in heatTemplate
1491 logger.debug("Tempate initial:{}", heatTemplate);
1492 int outputsIdx = heatTemplate.indexOf("outputs:");
1493 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1494 int resourcesIdx = heatTemplate.indexOf("resources:");
1495 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1497 logger.debug("Template updated with all subnets:{}", heatTemplate);
1498 return heatTemplate;
1501 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1503 Map<String, String> sMap = new HashMap<>();
1506 Object obj = outputs.get(key);
1507 ObjectMapper mapper = new ObjectMapper();
1508 String jStr = mapper.writeValueAsString(obj);
1509 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1511 JsonNode rootNode = mapper.readTree(jStr);
1512 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1513 logger.debug("Output Subnet Node {}", sNode.toString());
1514 String name = sNode.path("subnet_name").textValue();
1515 String uuid = sNode.path("subnet_uuid").textValue();
1516 String aaiId = name; // default
1517 // try to find aaiId for name in input subnetList
1518 if (subnets != null) {
1519 for (Subnet subnet : subnets) {
1520 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())) {
1521 if (subnet.getSubnetName().equals(name)) {
1522 aaiId = subnet.getSubnetId();
1528 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1530 } catch (Exception e) {
1531 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1532 ErrorCode.DataError.getValue(), e);
1535 logger.debug("Return sMap {}", sMap.toString());
1539 private static String insertStr(String template, String snippet, int index) {
1541 String updatedTemplate;
1543 logger.debug("Index:{} Snippet:{}", index, snippet);
1545 String templateBeg = template.substring(0, index);
1546 String templateEnd = template.substring(index);
1548 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1550 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1551 return updatedTemplate;