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 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(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
377 // Input parameters were not valid
378 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
381 if (subnets != null) {
384 template = mergeSubnetsAIC3(template, subnets, stackParams);
386 template = mergeSubnets(template, subnets);
388 } catch (MsoException me) {
389 me.addContext(CREATE_NETWORK_CONTEXT);
390 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
391 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
392 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
393 throw new NetworkException(me);
397 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
399 mergePolicyRefs(policyFqdns, stackParams);
400 } catch (MsoException me) {
401 me.addContext(CREATE_NETWORK_CONTEXT);
402 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
403 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
404 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
405 throw new NetworkException(me);
409 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
411 mergeRouteTableRefs(routeTableFqdns, stackParams);
412 } catch (MsoException me) {
413 me.addContext(CREATE_NETWORK_CONTEXT);
414 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
415 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
416 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
417 throw new NetworkException(me);
421 // Deploy the network stack
422 // Ignore MsoStackAlreadyExists exception because we already checked.
426 heatStack = heat.createStack(cloudSiteId, "CloudOwner", tenantId, networkName, null, template,
427 stackParams, true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue());
428 } catch (MsoException me) {
429 me.addContext(CREATE_NETWORK_CONTEXT);
430 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
431 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
432 throw new NetworkException(me);
435 // Reach this point if createStack is successful.
437 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
438 // and the neutronNetworkId is the network UUID returned in stack outputs.
439 networkId.value = heatStack.getCanonicalName();
440 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
442 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
444 Map<String, Object> outputs = heatStack.getOutputs();
445 Map<String, String> sMap = new HashMap<>();
446 if (outputs != null) {
447 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
448 String key = entry.getKey();
449 if (key != null && key.startsWith("subnet")) {
450 if (aic3template) // one subnet output expected
452 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
454 } else // multiples subnet_%aaid% outputs allowed
456 String subnetUUId = (String) outputs.get(key);
457 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
462 subnetIdMap.value = sMap;
464 rollback.value = networkRollback;
465 // Populate remaining rollback info and response parameters.
466 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
467 networkRollback.setNeutronNetworkId((String) heatStack.getOutputs().get(NETWORK_ID));
468 networkRollback.setNetworkCreated(true);
469 networkRollback.setNetworkType(networkType);
471 logger.debug("Network {} successfully created via HEAT", networkName);
478 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
479 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
480 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
481 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
482 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
483 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
489 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
490 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
491 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
492 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
493 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
494 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
495 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
500 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
501 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
502 * remove a VLAN), but other properties may be updated as well.
504 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
505 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
506 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
508 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
509 * VLANs on the same physical network.
511 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
512 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
513 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
515 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
516 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
517 * a subsequent operation.
519 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
520 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
521 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
522 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
523 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
525 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
526 cloudSiteId, tenantId);
528 // Will capture execution time for metrics
529 long startTime = System.currentTimeMillis();
531 // Build a default rollback object (no actions performed)
532 NetworkRollback networkRollback = new NetworkRollback();
533 networkRollback.setCloudId(cloudSiteId);
534 networkRollback.setTenantId(tenantId);
535 networkRollback.setMsoRequest(msoRequest);
537 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
538 if (!cloudSiteOpt.isPresent()) {
539 String error = String.format(
540 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
541 networkName, cloudSiteId, tenantId);
542 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
543 // Set the detailed error as the Exception 'message'
544 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
549 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
550 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
551 String mode = networkResource.getOrchestrationMode();
552 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
554 // Use an MsoNeutronUtils for all Neutron commands
556 if (NEUTRON_MODE.equals(mode)) {
558 // Verify that the Network exists
559 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
560 NetworkInfo netInfo = null;
561 long queryNetworkStarttime = System.currentTimeMillis();
563 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
564 } catch (MsoException me) {
565 me.addContext(UPDATE_NETWORK_CONTEXT);
566 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
567 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
568 throw new NetworkException(me);
571 if (netInfo == null) {
572 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
573 cloudSiteId, tenantId);
574 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
575 ErrorCode.BusinessProcesssError.getValue(), error);
576 // Does not exist. Throw an exception (can't update a non-existent network)
577 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
579 long updateNetworkStarttime = System.currentTimeMillis();
581 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
582 physicalNetworkName, vlans);
583 } catch (MsoException me) {
584 me.addContext(UPDATE_NETWORK_CONTEXT);
585 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
586 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
587 throw new NetworkException(me);
590 // Add the network ID and previously queried vlans to the rollback object
591 networkRollback.setNetworkId(netInfo.getId());
592 networkRollback.setNeutronNetworkId(netInfo.getId());
593 networkRollback.setNetworkType(networkType);
594 // Save previous parameters
595 networkRollback.setNetworkName(netInfo.getName());
596 networkRollback.setPhysicalNetwork(netInfo.getProvider());
597 networkRollback.setVlans(netInfo.getVlans());
599 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
600 } else if ("HEAT".equals(mode)) {
602 // First, look up to see that the Network already exists.
603 // For Heat-based orchestration, the networkId is the network Stack ID.
604 StackInfo heatStack = null;
605 long queryStackStarttime = System.currentTimeMillis();
607 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkName);
608 } catch (MsoException me) {
609 me.addContext(UPDATE_NETWORK_CONTEXT);
610 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
611 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
612 throw new NetworkException(me);
615 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
616 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
617 cloudSiteId, tenantId);
618 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
620 // Network stack does not exist. Return an error
621 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
624 // Get the previous parameters for rollback
625 Map<String, Object> heatParams = heatStack.getParameters();
627 String previousNetworkName = (String) heatParams.get("network_name");
628 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
630 List<Integer> previousVlans = new ArrayList<>();
631 String vlansParam = (String) heatParams.get(VLANS);
632 if (vlansParam != null) {
633 for (String vlan : vlansParam.split(",")) {
635 previousVlans.add(Integer.parseInt(vlan));
636 } catch (NumberFormatException e) {
637 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
638 ErrorCode.DataError.getValue(), vlansParam, e);
642 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
644 // Ready to deploy the updated Network via Heat
647 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
648 if (heatTemplate == null) {
649 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
650 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
652 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
655 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
657 // "Fix" the template if it has CR/LF (getting this from Oracle)
658 String template = heatTemplate.getHeatTemplate();
659 template = template.replaceAll("\r\n", "\n");
661 boolean aic3template = false;
662 String aic3nw = AIC3_NW;
664 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
666 if (template.contains(aic3nw))
669 // Build the common set of HEAT template parameters
670 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
671 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
673 // Validate (and update) the input parameters against the DB definition
674 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
676 stackParams = heat.validateStackParams(stackParams, heatTemplate);
677 } catch (IllegalArgumentException e) {
678 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
679 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
680 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
683 if (subnets != null) {
686 template = mergeSubnetsAIC3(template, subnets, stackParams);
688 template = mergeSubnets(template, subnets);
690 } catch (MsoException me) {
691 me.addContext(UPDATE_NETWORK_CONTEXT);
692 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
693 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
694 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
695 throw new NetworkException(me);
699 if (policyFqdns != null && aic3template) {
701 mergePolicyRefs(policyFqdns, stackParams);
702 } catch (MsoException me) {
703 me.addContext(UPDATE_NETWORK_CONTEXT);
704 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
705 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
706 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
707 throw new NetworkException(me);
711 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
713 mergeRouteTableRefs(routeTableFqdns, stackParams);
714 } catch (MsoException me) {
715 me.addContext(UPDATE_NETWORK_CONTEXT);
716 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
717 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
718 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
719 throw new NetworkException(me);
723 // Update the network stack
724 // Ignore MsoStackNotFound exception because we already checked.
725 long updateStackStarttime = System.currentTimeMillis();
727 heatStack = heatWithUpdate.updateStack(cloudSiteId, "CloudOwner", tenantId, networkId, template,
728 stackParams, true, heatTemplate.getTimeoutMinutes());
729 } catch (MsoException me) {
730 me.addContext(UPDATE_NETWORK_CONTEXT);
731 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
732 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
733 throw new NetworkException(me);
736 Map<String, Object> outputs = heatStack.getOutputs();
737 Map<String, String> sMap = new HashMap<>();
738 if (outputs != null) {
739 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
740 String key = entry.getKey();
741 if (key != null && key.startsWith("subnet")) {
742 if (aic3template) // one subnet output expected
744 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
746 } else // multiples subnet_%aaid% outputs allowed
748 String subnetUUId = (String) outputs.get(key);
749 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
754 subnetIdMap.value = sMap;
756 // Reach this point if createStack is successful.
757 // Populate remaining rollback info and response parameters.
758 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
759 if (null != outputs) {
760 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
762 logger.debug("outputs is NULL");
764 networkRollback.setNetworkType(networkType);
765 // Save previous parameters
766 networkRollback.setNetworkName(previousNetworkName);
767 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
768 networkRollback.setVlans(previousVlans);
770 rollback.value = networkRollback;
772 logger.debug("Network {} successfully updated via HEAT", networkId);
778 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
779 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
780 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
781 // Retrieve the Network Resource definition
782 NetworkResource networkResource = null;
783 NetworkResourceCustomization networkCust = null;
784 CollectionNetworkResourceCustomization collectionNetworkCust = null;
785 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
786 if (!commonUtils.isNullOrEmpty(networkType)) {
787 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
790 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
791 if (networkCust == null) {
792 collectionNetworkCust =
793 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
796 if (networkCust != null) {
797 logger.debug("Got Network Customization definition from Catalog: {}", networkCust.toString());
799 networkResource = networkCust.getNetworkResource();
800 } else if (collectionNetworkCust != null) {
801 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}",
802 collectionNetworkCust.toString());
803 networkResource = collectionNetworkCust.getNetworkResource();
805 if (networkResource == null) {
806 String error = String.format(
807 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
808 networkType, modelCustomizationUuid);
809 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
811 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
813 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
815 String mode = networkResource.getOrchestrationMode();
816 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
818 // All Networks are orchestrated via HEAT or Neutron
819 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
820 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
821 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
822 ErrorCode.DataError.getValue(), error);
823 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
826 MavenLikeVersioning aicV = new MavenLikeVersioning();
827 aicV.setVersion(cloudSite.getCloudVersion());
828 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
829 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
832 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
833 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
835 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
836 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
837 cloudSite.getCloudVersion());
839 String error = String.format(
840 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
841 networkType, networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(),
842 cloudSiteId, cloudSite.getCloudVersion());
843 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
844 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
847 // Validate the Network parameters.
849 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
850 if (!missing.isEmpty()) {
851 String error = "Create Network: Missing parameters: " + missing;
852 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
854 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
857 return networkResource;
861 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
862 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
863 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
864 throws NetworkException {
865 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
866 status, vlans, null, subnetIdMap);
870 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
871 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
872 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
873 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
874 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
875 status, null, routeTargets, subnetIdMap);
879 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
880 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
881 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
883 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
884 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
885 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
886 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
888 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
890 // Will capture execution time for metrics
891 long startTime = System.currentTimeMillis();
893 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
894 || commonUtils.isNullOrEmpty(networkNameOrId)) {
896 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
897 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
898 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
901 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
902 if (!cloudSiteOpt.isPresent()) {
903 String error = String.format(
904 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
905 networkNameOrId, cloudSiteId, tenantId);
906 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
907 // Set the detailed error as the Exception 'message'
908 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
911 // Use MsoNeutronUtils for all NEUTRON commands
915 // Try Heat first, since networks may be named the same as the Heat stack
916 StackInfo heatStack = null;
917 long queryStackStarttime = System.currentTimeMillis();
919 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkNameOrId);
920 } catch (MsoException me) {
921 me.addContext("QueryNetwork");
922 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
923 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
924 throw new NetworkException(me);
927 // Populate the outputs based on the returned Stack information
928 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
929 // Found it. Get the neutronNetworkId for further query
930 Map<String, Object> outputs = heatStack.getOutputs();
931 neutronId = (String) outputs.get(NETWORK_ID);
934 Map<String, String> sMap = new HashMap<>();
935 if (outputs != null) {
936 for (String key : outputs.keySet()) {
937 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
939 String subnetUUId = (String) outputs.get(key);
940 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
941 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
943 Map<String, String> map = getSubnetUUId(key, outputs, null);
949 subnetIdMap.value = sMap;
951 // Input ID was not a Heat stack ID. Try it directly in Neutron
952 neutronId = networkNameOrId;
956 // Query directly against the Neutron Network for the details
957 // no RouteTargets available for ContrailV2 in neutron net-show
958 // networkId is heatStackId
959 long queryNetworkStarttime = System.currentTimeMillis();
961 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
962 if (netInfo != null) {
963 // Found. Populate the output elements
964 networkExists.value = Boolean.TRUE;
965 if ("HEAT".equals(mode) && heatStack != null) {
966 networkId.value = heatStack.getCanonicalName();
968 networkId.value = netInfo.getId();
970 neutronNetworkId.value = netInfo.getId();
971 status.value = netInfo.getStatus();
973 vlans.value = netInfo.getVlans();
975 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
976 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
978 // Not found. Populate the status fields, leave the rest null
979 networkExists.value = Boolean.FALSE;
980 status.value = NetworkStatus.NOTFOUND;
981 neutronNetworkId.value = null;
983 vlans.value = new ArrayList<>();
985 logger.debug("Network {} not found", networkNameOrId);
987 } catch (MsoException me) {
988 me.addContext("QueryNetwork");
989 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
990 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
991 throw new NetworkException(me);
997 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
1000 * If the network is not found, it is treated as a success.
1002 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
1003 * network orchestration mode for each network type is declared in its catalog definition.
1005 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1006 * networkId should be the Neutron network UUID.
1008 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1009 * will require manual fallout in the client.
1012 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1013 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1015 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1017 // Will capture execution time for metrics
1018 long startTime = System.currentTimeMillis();
1021 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1022 || commonUtils.isNullOrEmpty(networkId)) {
1023 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1024 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1025 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1028 // Retrieve the Network Resource definition
1029 NetworkResource networkResource = null;
1031 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1032 if (!commonUtils.isNullOrEmpty(networkType)) {
1033 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1036 NetworkResourceCustomization nrc =
1037 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1039 networkResource = nrc.getNetworkResource();
1043 if (networkResource != null) {
1044 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1046 mode = networkResource.getOrchestrationMode();
1049 if (NEUTRON_MODE.equals(mode)) {
1051 // Use MsoNeutronUtils for all NEUTRON commands
1052 long deleteNetworkStarttime = System.currentTimeMillis();
1054 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1055 // was not found. So don't bother to query first.
1056 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1057 networkDeleted.value = deleted;
1058 } catch (MsoException me) {
1059 me.addContext("DeleteNetwork");
1060 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1061 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1062 throw new NetworkException(me);
1064 } else { // DEFAULT to ("HEAT".equals (mode))
1065 long deleteStackStarttime = System.currentTimeMillis();
1068 // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack
1070 // So query first to report back if stack WAS deleted or just NOTOFUND
1071 StackInfo heatStack = null;
1072 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkId);
1073 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
1074 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1075 networkDeleted.value = true;
1077 networkDeleted.value = false;
1079 } catch (MsoException me) {
1080 me.addContext("DeleteNetwork");
1081 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1082 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1083 throw new NetworkException(me);
1088 // On success, nothing is returned.
1093 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1094 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1095 * to undo the creation.
1097 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1101 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1102 // Will capture execution time for metrics
1103 long startTime = System.currentTimeMillis();
1105 if (rollback == null) {
1106 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1110 // Get the elements of the VnfRollback object for easier access
1111 String cloudSiteId = rollback.getCloudId();
1112 String tenantId = rollback.getTenantId();
1113 String networkId = rollback.getNetworkStackId();
1114 String networkType = rollback.getNetworkType();
1115 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1117 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1120 // Retrieve the Network Resource definition
1121 NetworkResource networkResource = null;
1122 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1123 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1126 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1129 if (networkResource != null) {
1131 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1133 mode = networkResource.getOrchestrationMode();
1136 if (rollback.getNetworkCreated()) {
1137 // Rolling back a newly created network, so delete it.
1138 if (NEUTRON_MODE.equals(mode)) {
1139 // Use MsoNeutronUtils for all NEUTRON commands
1140 long deleteNetworkStarttime = System.currentTimeMillis();
1142 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1143 // was not found. So don't bother to query first.
1144 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1145 } catch (MsoException me) {
1146 me.addContext("RollbackNetwork");
1147 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1148 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1149 cloudSiteId, tenantId, me);
1150 throw new NetworkException(me);
1152 } else { // DEFAULT to if ("HEAT".equals (mode))
1153 long deleteStackStarttime = System.currentTimeMillis();
1155 // The deleteStack function in MsoHeatUtils returns success if the stack
1156 // was not found. So don't bother to query first.
1157 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1158 } catch (MsoException me) {
1159 me.addContext("RollbackNetwork");
1160 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1161 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1162 cloudSiteId, tenantId, me);
1163 throw new NetworkException(me);
1171 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1172 List<Integer> vlans, List<RouteTarget> routeTargets) {
1174 StringBuilder missing = new StringBuilder();
1175 if (commonUtils.isNullOrEmpty(networkName)) {
1176 missing.append("networkName");
1180 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1181 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1182 missing.append(sep).append("physicalNetworkName");
1185 if (vlans == null || vlans.isEmpty()) {
1186 missing.append(sep).append(VLANS);
1190 return missing.toString();
1193 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1194 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1195 boolean aic3template) {
1196 // Build the common set of HEAT template parameters
1197 Map<String, Object> stackParams = new HashMap<>();
1198 stackParams.put("network_name", networkName);
1200 if (neutronNetworkType == NetworkType.PROVIDER) {
1201 // For Provider type
1202 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1203 stackParams.put("vlan", vlans.get(0).toString());
1204 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1205 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1206 // It supports all ProviderNet properties except segmentation_id, and adds a
1207 // comma-separated-list of VLANs as a "segments" property.
1208 // Note that this does not match the Neutron definition of Multi-Provider network,
1209 // which contains a list of 'segments', each having physical_network, network_type,
1210 // and segmentation_id.
1211 StringBuilder buf = new StringBuilder();
1213 for (Integer vlan : vlans) {
1214 buf.append(sep).append(vlan.toString());
1217 String csl = buf.toString();
1219 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1220 stackParams.put(VLANS, csl);
1222 if (routeTargets != null) {
1224 String rtGlobal = "";
1225 String rtImport = "";
1226 String rtExport = "";
1228 for (RouteTarget rt : routeTargets) {
1229 boolean rtIsNull = false;
1231 String routeTarget = rt.getRouteTarget();
1232 String routeTargetRole = rt.getRouteTargetRole();
1233 logger.debug("Checking for an actually null route target: {}", rt);
1234 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1236 if (routeTargetRole == null || routeTargetRole.equals("")
1237 || routeTargetRole.equalsIgnoreCase("null"))
1243 logger.debug("Input RT:{}", rt);
1244 String role = rt.getRouteTargetRole();
1245 String rtValue = rt.getRouteTarget();
1247 if ("IMPORT".equalsIgnoreCase(role)) {
1248 sep = rtImport.isEmpty() ? "" : ",";
1249 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1250 } else if ("EXPORT".equalsIgnoreCase(role)) {
1251 sep = rtExport.isEmpty() ? "" : ",";
1252 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1253 } else // covers BOTH, empty etc
1255 sep = rtGlobal.isEmpty() ? "" : ",";
1256 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1262 if (!rtImport.isEmpty()) {
1263 stackParams.put("route_targets_import", rtImport);
1265 if (!rtExport.isEmpty()) {
1266 stackParams.put("route_targets_export", rtExport);
1268 if (!rtGlobal.isEmpty()) {
1269 stackParams.put("route_targets", rtGlobal);
1272 if (commonUtils.isNullOrEmpty(shared)) {
1273 stackParams.put("shared", "False");
1275 stackParams.put("shared", shared);
1277 if (commonUtils.isNullOrEmpty(external)) {
1278 stackParams.put("external", "False");
1280 stackParams.put("external", external);
1288 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1289 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1290 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1291 * "network_policy_refs_data_sequence_minor": "0" } } ]
1293 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1294 // Resource Property
1295 List<ContrailPolicyRef> prlist = new ArrayList<>();
1298 if (pFqdns != null) {
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 String error = "Null pFqdns at start of mergePolicyRefs";
1311 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1313 throw new MsoAdapterException(error);
1316 JsonNode node = null;
1318 ObjectMapper mapper = new ObjectMapper();
1319 node = mapper.convertValue(prlist, JsonNode.class);
1320 String jsonString = mapper.writeValueAsString(prlist);
1321 logger.debug("Json PolicyRefs Data:{}", jsonString);
1322 } catch (Exception e) {
1323 String error = "Error creating JsonNode for policyRefs Data";
1324 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1326 throw new MsoAdapterException(error);
1328 // update parameters
1329 if (pFqdns != null && node != null) {
1330 StringBuilder buf = new StringBuilder();
1332 for (String pf : pFqdns) {
1333 if (!commonUtils.isNullOrEmpty(pf)) {
1334 buf.append(sep).append(pf);
1338 String csl = buf.toString();
1339 stackParams.put("policy_refs", csl);
1340 stackParams.put("policy_refsdata", node);
1343 logger.debug("StackParams updated with policy refs");
1347 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1349 // update parameters
1350 if (rtFqdns != null) {
1351 StringBuilder buf = new StringBuilder();
1353 for (String rtf : rtFqdns) {
1354 if (!commonUtils.isNullOrEmpty(rtf)) {
1355 buf.append(sep).append(rtf);
1359 String csl = buf.toString();
1360 stackParams.put("route_table_refs", csl);
1363 logger.debug("StackParams updated with route_table refs");
1369 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1370 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1371 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1372 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1373 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1374 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1375 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1376 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1377 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1378 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1379 * "host_routes": null }
1381 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1382 throws MsoException {
1384 // Resource Property
1385 List<ContrailSubnet> cslist = new ArrayList<>();
1386 for (Subnet subnet : subnets) {
1387 logger.debug("Input Subnet:{}", subnet.toString());
1388 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1389 logger.debug("Contrail Subnet:{}", cs.toString());
1393 JsonNode node = null;
1395 ObjectMapper mapper = new ObjectMapper();
1396 node = mapper.convertValue(cslist, JsonNode.class);
1397 String jsonString = mapper.writeValueAsString(cslist);
1398 logger.debug("Json Subnet List:{}", jsonString);
1399 } catch (Exception e) {
1400 String error = "Error creating JsonNode from input subnets";
1401 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1402 throw new MsoAdapterException(error);
1404 // update parameters
1406 stackParams.put("subnet_list", node);
1408 // Outputs - All subnets are in one ipam_subnets structure
1409 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1410 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1412 // append outputs in heatTemplate
1413 int outputsIdx = heatTemplate.indexOf("outputs:");
1414 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1415 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1416 return heatTemplate;
1420 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1422 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1423 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1426 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1427 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1428 * " end: %poolend%\n";
1432 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1433 + " value: {get_resource: subnet_%subnetId%}\n";
1437 StringBuilder resourcesBuf = new StringBuilder();
1438 StringBuilder outputsBuf = new StringBuilder();
1439 for (Subnet subnet : subnets) {
1441 // build template for each subnet
1442 curR = resourceTempl;
1443 if (subnet.getSubnetId() != null) {
1444 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1446 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1447 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1448 throw new MsoAdapterException(error);
1451 if (subnet.getSubnetName() != null) {
1452 curR = curR.replace("%name%", subnet.getSubnetName());
1454 curR = curR.replace("%name%", subnet.getSubnetId());
1457 if (subnet.getCidr() != null) {
1458 curR = curR.replace("%cidr%", subnet.getCidr());
1460 String error = "Missing Required cidr for subnet in HEAT Template";
1461 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1462 throw new MsoAdapterException(error);
1465 if (subnet.getIpVersion() != null) {
1466 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1468 if (subnet.getEnableDHCP() != null) {
1469 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1471 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1472 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1475 if (subnet.getAllocationPools() != null) {
1476 curR = curR + " allocation_pools:\n";
1477 for (Pool pool : subnet.getAllocationPools()) {
1478 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1479 curR = curR + " - start: " + pool.getStart() + "\n";
1480 curR = curR + " end: " + pool.getEnd() + "\n";
1485 resourcesBuf.append(curR);
1488 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1490 outputsBuf.append(curO);
1493 // append resources and outputs in heatTemplate
1494 logger.debug("Tempate initial:{}", heatTemplate);
1495 int outputsIdx = heatTemplate.indexOf("outputs:");
1496 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1497 int resourcesIdx = heatTemplate.indexOf("resources:");
1498 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1500 logger.debug("Template updated with all subnets:{}", heatTemplate);
1501 return heatTemplate;
1504 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1506 Map<String, String> sMap = new HashMap<>();
1509 Object obj = outputs.get(key);
1510 ObjectMapper mapper = new ObjectMapper();
1511 String jStr = mapper.writeValueAsString(obj);
1512 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1514 JsonNode rootNode = mapper.readTree(jStr);
1515 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1516 logger.debug("Output Subnet Node {}", sNode.toString());
1517 String name = sNode.path("subnet_name").textValue();
1518 String uuid = sNode.path("subnet_uuid").textValue();
1519 String aaiId = name; // default
1520 // try to find aaiId for name in input subnetList
1521 if (subnets != null) {
1522 for (Subnet subnet : subnets) {
1523 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())) {
1524 if (subnet.getSubnetName().equals(name)) {
1525 aaiId = subnet.getSubnetId();
1531 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1533 } catch (Exception e) {
1534 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1535 ErrorCode.DataError.getValue(), e);
1538 logger.debug("Return sMap {}", sMap.toString());
1542 private static String insertStr(String template, String snippet, int index) {
1544 String updatedTemplate;
1546 logger.debug("Index:{} Snippet:{}", index, snippet);
1548 String templateBeg = template.substring(0, index);
1549 String templateEnd = template.substring(index);
1551 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1553 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1554 return updatedTemplate;