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";
93 private static final String CLOUD_OWNER = "CloudOwner";
95 private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
98 private CloudConfig cloudConfig;
100 private Environment environment;
102 private MsoNeutronUtils neutron;
104 private MsoHeatUtils heat;
106 private MsoHeatUtilsWithUpdate heatWithUpdate;
108 private MsoCommonUtils commonUtils;
111 private NetworkResourceCustomizationRepository networkCustomRepo;
114 private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
117 private NetworkResourceRepository networkResourceRepo;
119 public MsoNetworkAdapterImpl() {}
122 * Health Check web method. Does nothing but return to show the adapter is deployed.
125 public void healthCheck() {
126 logger.debug("Health check call in Network Adapter");
130 * Do not use this constructor or the msoPropertiesFactory will be NULL.
132 * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
136 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
137 String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
138 Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
139 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
140 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
141 Holder<String> networkFqdn = new Holder<>();
142 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
143 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
144 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
148 public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
149 String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
150 String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
151 Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
152 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
153 Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
154 throws NetworkException {
155 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
156 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
157 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
161 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
162 * the specified cloud and tenant. The tenant must exist at the time this service is called.
164 * If a network with the same name already exists, this can be considered a success or failure, depending on the
165 * value of the 'failIfExists' parameter.
167 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
168 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
169 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
171 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
172 * multiple VLANs on the same physical network.
174 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
175 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
176 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
178 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
179 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
180 * created but the orchestration fails on a subsequent operation.
183 private void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
184 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
185 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
186 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
187 Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
188 Holder<NetworkRollback> rollback) throws NetworkException {
189 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
191 // Will capture execution time for metrics
192 long startTime = System.currentTimeMillis();
194 // Build a default rollback object (no actions performed)
195 NetworkRollback networkRollback = new NetworkRollback();
196 networkRollback.setCloudId(cloudSiteId);
197 networkRollback.setTenantId(tenantId);
198 networkRollback.setMsoRequest(msoRequest);
199 networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
201 // tenant query is not required here.
202 // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
203 // So this is just catching that error in a bit more obvious way up front.
205 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
206 if (!cloudSiteOpt.isPresent()) {
207 String error = String.format(
208 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
209 networkName, cloudSiteId, tenantId);
210 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
211 // Set the detailed error as the Exception 'message'
212 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
216 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
217 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
218 String mode = networkResource.getOrchestrationMode();
219 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
221 if (NEUTRON_MODE.equals(mode)) {
223 // Use an MsoNeutronUtils for all neutron commands
225 // See if the Network already exists (by name)
226 NetworkInfo netInfo = null;
227 long queryNetworkStarttime = System.currentTimeMillis();
229 netInfo = neutron.queryNetwork(networkName, tenantId, cloudSiteId);
230 } catch (MsoException me) {
232 "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
233 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkName,
234 cloudSiteId, tenantId, me);
235 me.addContext(CREATE_NETWORK_CONTEXT);
236 throw new NetworkException(me);
239 if (netInfo != null) {
240 // Exists. If that's OK, return success with the network ID.
241 // Otherwise, return an exception.
242 if (failIfExists != null && failIfExists) {
243 String error = String.format("Create Nework: Network %s already exists in %s/%s with ID %s",
244 networkName, cloudSiteId, tenantId, netInfo.getId());
245 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
246 ErrorCode.DataError.getValue(), error);
247 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
249 // Populate the outputs from the existing network.
250 networkId.value = netInfo.getId();
251 neutronNetworkId.value = netInfo.getId();
252 rollback.value = networkRollback; // Default rollback - no updates performed
253 logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
254 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), netInfo.getStatus());
259 long createNetworkStarttime = System.currentTimeMillis();
261 netInfo = neutron.createNetwork(cloudSiteId, tenantId, neutronNetworkType, networkName,
262 physicalNetworkName, vlans);
263 } catch (MsoException me) {
264 me.addContext(CREATE_NETWORK_CONTEXT);
265 logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
266 ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
268 throw new NetworkException(me);
271 // Note: ignoring MsoNetworkAlreadyExists because we already checked.
273 // If reach this point, network creation is successful.
274 // Since directly created via Neutron, networkId tracked by MSO is the same
275 // as the neutron network ID.
276 networkId.value = netInfo.getId();
277 neutronNetworkId.value = netInfo.getId();
279 networkRollback.setNetworkCreated(true);
280 networkRollback.setNetworkId(netInfo.getId());
281 networkRollback.setNeutronNetworkId(netInfo.getId());
282 networkRollback.setNetworkType(networkType);
284 logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
285 } else if ("HEAT".equals(mode)) {
287 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
288 if (heatTemplate == null) {
289 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
290 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
292 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
295 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
297 // "Fix" the template if it has CR/LF (getting this from Oracle)
298 String template = heatTemplate.getHeatTemplate();
299 template = template.replaceAll("\r\n", "\n");
301 boolean aic3template = false;
302 String aic3nw = AIC3_NW;
304 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
306 if (template.contains(aic3nw))
309 // First, look up to see if the Network already exists (by name).
310 // For HEAT orchestration of networks, the stack name will always match the network name
311 StackInfo heatStack = null;
312 long queryNetworkStarttime = System.currentTimeMillis();
314 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
315 } catch (MsoException me) {
316 me.addContext(CREATE_NETWORK_CONTEXT);
317 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
318 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName, cloudSiteId,
320 throw new NetworkException(me);
323 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
324 // Stack exists. Return success or error depending on input directive
325 if (failIfExists != null && failIfExists) {
326 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
327 cloudSiteId, tenantId, heatStack.getCanonicalName());
328 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ALREADY_EXIST,
329 ErrorCode.DataError.getValue(), error);
330 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
332 // Populate the outputs from the existing stack.
333 networkId.value = heatStack.getCanonicalName();
334 Map<String, String> sMap = new HashMap<>();
335 if (heatStack.getOutputs() != null) {
336 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
337 rollback.value = networkRollback; // Default rollback - no updates performed
339 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
341 Map<String, Object> outputs = heatStack.getOutputs();
343 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
344 String key = entry.getKey();
345 if (key != null && key.startsWith("subnet")) {
346 if (aic3template) // one subnet_id output
348 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
350 } else // multiples subnet_%aaid% outputs
352 String subnetUUId = (String) outputs.get(key);
353 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
358 subnetIdMap.value = sMap;
359 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
360 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
361 networkName, cloudSiteId, tenantId);
366 // Ready to deploy the new Network
367 // Build the common set of HEAT template parameters
368 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
369 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
371 // Validate (and update) the input parameters against the DB definition
372 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
373 // and inputs were already validated.
375 stackParams = heat.validateStackParams(stackParams, heatTemplate);
376 } catch (IllegalArgumentException e) {
377 String error = "Create Network: Configuration Error: " + e.getMessage();
378 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
379 // Input parameters were not valid
380 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
383 if (subnets != null) {
386 template = mergeSubnetsAIC3(template, subnets, stackParams);
388 template = mergeSubnets(template, subnets);
390 } catch (MsoException me) {
391 me.addContext(CREATE_NETWORK_CONTEXT);
392 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
393 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
394 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
395 throw new NetworkException(me);
399 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
401 mergePolicyRefs(policyFqdns, stackParams);
402 } catch (MsoException me) {
403 me.addContext(CREATE_NETWORK_CONTEXT);
404 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
405 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
406 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
407 throw new NetworkException(me);
411 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
413 mergeRouteTableRefs(routeTableFqdns, stackParams);
414 } catch (MsoException me) {
415 me.addContext(CREATE_NETWORK_CONTEXT);
416 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
417 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
418 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
419 throw new NetworkException(me);
423 // Deploy the network stack
424 // Ignore MsoStackAlreadyExists exception because we already checked.
428 heatStack = heat.createStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName, null, template,
429 stackParams, true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue());
430 } catch (MsoException me) {
431 me.addContext(CREATE_NETWORK_CONTEXT);
432 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
433 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
434 throw new NetworkException(me);
437 // Reach this point if createStack is successful.
439 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
440 // and the neutronNetworkId is the network UUID returned in stack outputs.
441 networkId.value = heatStack.getCanonicalName();
442 if (heatStack.getOutputs() != null) {
443 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
445 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
448 Map<String, Object> outputs = heatStack.getOutputs();
449 Map<String, String> sMap = new HashMap<>();
450 if (outputs != null) {
451 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
452 String key = entry.getKey();
453 if (key != null && key.startsWith("subnet")) {
454 if (aic3template) // one subnet output expected
456 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
458 } else // multiples subnet_%aaid% outputs allowed
460 String subnetUUId = (String) outputs.get(key);
461 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
466 subnetIdMap.value = sMap;
468 rollback.value = networkRollback;
469 // Populate remaining rollback info and response parameters.
470 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
471 networkRollback.setNeutronNetworkId((String) heatStack.getOutputs().get(NETWORK_ID));
472 networkRollback.setNetworkCreated(true);
473 networkRollback.setNetworkType(networkType);
475 logger.debug("Network {} successfully created via HEAT", networkName);
482 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
483 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
484 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
485 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
486 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
487 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
493 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
494 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
495 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
496 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
497 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
498 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
499 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
504 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
505 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
506 * remove a VLAN), but other properties may be updated as well.
508 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
509 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
510 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
512 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
513 * VLANs on the same physical network.
515 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
516 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
517 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
519 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
520 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
521 * a subsequent operation.
523 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
524 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
525 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
526 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
527 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
529 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
530 cloudSiteId, tenantId);
532 // Will capture execution time for metrics
533 long startTime = System.currentTimeMillis();
535 // Build a default rollback object (no actions performed)
536 NetworkRollback networkRollback = new NetworkRollback();
537 networkRollback.setCloudId(cloudSiteId);
538 networkRollback.setTenantId(tenantId);
539 networkRollback.setMsoRequest(msoRequest);
541 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
542 if (!cloudSiteOpt.isPresent()) {
543 String error = String.format(
544 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
545 networkName, cloudSiteId, tenantId);
546 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
547 // Set the detailed error as the Exception 'message'
548 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
553 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
554 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
555 String mode = networkResource.getOrchestrationMode();
556 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
558 // Use an MsoNeutronUtils for all Neutron commands
560 if (NEUTRON_MODE.equals(mode)) {
562 // Verify that the Network exists
563 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
564 NetworkInfo netInfo = null;
565 long queryNetworkStarttime = System.currentTimeMillis();
567 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
568 } catch (MsoException me) {
569 me.addContext(UPDATE_NETWORK_CONTEXT);
570 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
571 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
572 throw new NetworkException(me);
575 if (netInfo == null) {
576 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
577 cloudSiteId, tenantId);
578 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND,
579 ErrorCode.BusinessProcesssError.getValue(), error);
580 // Does not exist. Throw an exception (can't update a non-existent network)
581 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
583 long updateNetworkStarttime = System.currentTimeMillis();
585 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
586 physicalNetworkName, vlans);
587 } catch (MsoException me) {
588 me.addContext(UPDATE_NETWORK_CONTEXT);
589 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
590 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
591 throw new NetworkException(me);
594 // Add the network ID and previously queried vlans to the rollback object
595 networkRollback.setNetworkId(netInfo.getId());
596 networkRollback.setNeutronNetworkId(netInfo.getId());
597 networkRollback.setNetworkType(networkType);
598 // Save previous parameters
599 networkRollback.setNetworkName(netInfo.getName());
600 networkRollback.setPhysicalNetwork(netInfo.getProvider());
601 networkRollback.setVlans(netInfo.getVlans());
603 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
604 } else if ("HEAT".equals(mode)) {
606 // First, look up to see that the Network already exists.
607 // For Heat-based orchestration, the networkId is the network Stack ID.
608 StackInfo heatStack = null;
609 long queryStackStarttime = System.currentTimeMillis();
611 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkName);
612 } catch (MsoException me) {
613 me.addContext(UPDATE_NETWORK_CONTEXT);
614 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
615 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
616 throw new NetworkException(me);
619 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
620 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
621 cloudSiteId, tenantId);
622 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
624 // Network stack does not exist. Return an error
625 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
628 // Get the previous parameters for rollback
629 Map<String, Object> heatParams = heatStack.getParameters();
631 String previousNetworkName = (String) heatParams.get("network_name");
632 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
634 List<Integer> previousVlans = new ArrayList<>();
635 String vlansParam = (String) heatParams.get(VLANS);
636 if (vlansParam != null) {
637 for (String vlan : vlansParam.split(",")) {
639 previousVlans.add(Integer.parseInt(vlan));
640 } catch (NumberFormatException e) {
641 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
642 ErrorCode.DataError.getValue(), vlansParam, e);
646 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
648 // Ready to deploy the updated Network via Heat
651 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
652 if (heatTemplate == null) {
653 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
654 logger.error(LoggingAnchor.THREE, MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
656 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
659 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
661 // "Fix" the template if it has CR/LF (getting this from Oracle)
662 String template = heatTemplate.getHeatTemplate();
663 template = template.replaceAll("\r\n", "\n");
665 boolean aic3template = false;
666 String aic3nw = AIC3_NW;
668 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
670 if (template.contains(aic3nw))
673 // Build the common set of HEAT template parameters
674 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
675 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
677 // Validate (and update) the input parameters against the DB definition
678 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
680 stackParams = heat.validateStackParams(stackParams, heatTemplate);
681 } catch (IllegalArgumentException e) {
682 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
683 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
684 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
687 if (subnets != null) {
690 template = mergeSubnetsAIC3(template, subnets, stackParams);
692 template = mergeSubnets(template, subnets);
694 } catch (MsoException me) {
695 me.addContext(UPDATE_NETWORK_CONTEXT);
696 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
697 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
698 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
699 throw new NetworkException(me);
703 if (policyFqdns != null && aic3template) {
705 mergePolicyRefs(policyFqdns, stackParams);
706 } catch (MsoException me) {
707 me.addContext(UPDATE_NETWORK_CONTEXT);
708 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
709 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
710 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
711 throw new NetworkException(me);
715 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
717 mergeRouteTableRefs(routeTableFqdns, stackParams);
718 } catch (MsoException me) {
719 me.addContext(UPDATE_NETWORK_CONTEXT);
720 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
721 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
722 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
723 throw new NetworkException(me);
727 // Update the network stack
728 // Ignore MsoStackNotFound exception because we already checked.
729 long updateStackStarttime = System.currentTimeMillis();
731 heatStack = heatWithUpdate.updateStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId, template,
732 stackParams, true, heatTemplate.getTimeoutMinutes());
733 } catch (MsoException me) {
734 me.addContext(UPDATE_NETWORK_CONTEXT);
735 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
736 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
737 throw new NetworkException(me);
740 Map<String, Object> outputs = heatStack.getOutputs();
741 Map<String, String> sMap = new HashMap<>();
742 if (outputs != null) {
743 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
744 String key = entry.getKey();
745 if (key != null && key.startsWith("subnet")) {
746 if (aic3template) // one subnet output expected
748 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
750 } else // multiples subnet_%aaid% outputs allowed
752 String subnetUUId = (String) outputs.get(key);
753 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
758 subnetIdMap.value = sMap;
760 // Reach this point if createStack is successful.
761 // Populate remaining rollback info and response parameters.
762 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
763 if (null != outputs) {
764 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
766 logger.debug("outputs is NULL");
768 networkRollback.setNetworkType(networkType);
769 // Save previous parameters
770 networkRollback.setNetworkName(previousNetworkName);
771 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
772 networkRollback.setVlans(previousVlans);
774 rollback.value = networkRollback;
776 logger.debug("Network {} successfully updated via HEAT", networkId);
782 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
783 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
784 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
785 // Retrieve the Network Resource definition
786 NetworkResource networkResource = null;
787 NetworkResourceCustomization networkCust = null;
788 CollectionNetworkResourceCustomization collectionNetworkCust = null;
789 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
790 if (!commonUtils.isNullOrEmpty(networkType)) {
791 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
794 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
795 if (networkCust == null) {
796 collectionNetworkCust =
797 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
800 if (networkCust != null) {
801 logger.debug("Got Network Customization definition from Catalog: {}", networkCust.toString());
803 networkResource = networkCust.getNetworkResource();
804 } else if (collectionNetworkCust != null) {
805 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}",
806 collectionNetworkCust.toString());
807 networkResource = collectionNetworkCust.getNetworkResource();
809 if (networkResource == null) {
810 String error = String.format(
811 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
812 networkType, modelCustomizationUuid);
813 logger.error(LoggingAnchor.THREE, MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
815 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
817 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
819 String mode = networkResource.getOrchestrationMode();
820 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
822 // All Networks are orchestrated via HEAT or Neutron
823 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
824 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
825 logger.error(LoggingAnchor.THREE, MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
826 ErrorCode.DataError.getValue(), error);
827 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
830 MavenLikeVersioning aicV = new MavenLikeVersioning();
831 aicV.setVersion(cloudSite.getCloudVersion());
832 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
833 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
836 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
837 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
839 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
840 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
841 cloudSite.getCloudVersion());
843 String error = String.format(
844 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
845 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
846 cloudSite.getCloudVersion());
847 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
848 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
851 // Validate the Network parameters.
853 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
854 if (!missing.isEmpty()) {
855 String error = "Create Network: Missing parameters: " + missing;
856 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
858 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
861 return networkResource;
865 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
866 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
867 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
868 throws NetworkException {
869 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
870 status, vlans, null, subnetIdMap);
874 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
875 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
876 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
877 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
878 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
879 status, null, routeTargets, subnetIdMap);
883 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
884 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
885 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
887 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
888 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
889 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
890 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
892 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
894 // Will capture execution time for metrics
895 long startTime = System.currentTimeMillis();
897 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
898 || commonUtils.isNullOrEmpty(networkNameOrId)) {
900 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
901 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
902 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
905 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
906 if (!cloudSiteOpt.isPresent()) {
907 String error = String.format(
908 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
909 networkNameOrId, cloudSiteId, tenantId);
910 logger.error(LoggingAnchor.THREE, MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
911 // Set the detailed error as the Exception 'message'
912 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
915 // Use MsoNeutronUtils for all NEUTRON commands
919 // Try Heat first, since networks may be named the same as the Heat stack
920 StackInfo heatStack = null;
921 long queryStackStarttime = System.currentTimeMillis();
923 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkNameOrId);
924 } catch (MsoException me) {
925 me.addContext("QueryNetwork");
926 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
927 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
928 throw new NetworkException(me);
931 // Populate the outputs based on the returned Stack information
932 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
933 // Found it. Get the neutronNetworkId for further query
934 Map<String, Object> outputs = heatStack.getOutputs();
935 neutronId = (String) outputs.get(NETWORK_ID);
938 Map<String, String> sMap = new HashMap<>();
939 if (outputs != null) {
940 for (String key : outputs.keySet()) {
941 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
943 String subnetUUId = (String) outputs.get(key);
944 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
945 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
947 Map<String, String> map = getSubnetUUId(key, outputs, null);
953 subnetIdMap.value = sMap;
955 // Input ID was not a Heat stack ID. Try it directly in Neutron
956 neutronId = networkNameOrId;
960 // Query directly against the Neutron Network for the details
961 // no RouteTargets available for ContrailV2 in neutron net-show
962 // networkId is heatStackId
963 long queryNetworkStarttime = System.currentTimeMillis();
965 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
966 if (netInfo != null) {
967 // Found. Populate the output elements
968 networkExists.value = Boolean.TRUE;
969 if ("HEAT".equals(mode) && heatStack != null) {
970 networkId.value = heatStack.getCanonicalName();
972 networkId.value = netInfo.getId();
974 neutronNetworkId.value = netInfo.getId();
975 status.value = netInfo.getStatus();
977 vlans.value = netInfo.getVlans();
979 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
980 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
982 // Not found. Populate the status fields, leave the rest null
983 networkExists.value = Boolean.FALSE;
984 status.value = NetworkStatus.NOTFOUND;
985 neutronNetworkId.value = null;
987 vlans.value = new ArrayList<>();
989 logger.debug("Network {} not found", networkNameOrId);
991 } catch (MsoException me) {
992 me.addContext("QueryNetwork");
993 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
994 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
995 throw new NetworkException(me);
1001 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
1004 * If the network is not found, it is treated as a success.
1006 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
1007 * network orchestration mode for each network type is declared in its catalog definition.
1009 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1010 * networkId should be the Neutron network UUID.
1012 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1013 * will require manual fallout in the client.
1016 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1017 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1019 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1021 // Will capture execution time for metrics
1022 long startTime = System.currentTimeMillis();
1025 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1026 || commonUtils.isNullOrEmpty(networkId)) {
1027 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1028 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1029 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1032 // Retrieve the Network Resource definition
1033 NetworkResource networkResource = null;
1035 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1036 if (!commonUtils.isNullOrEmpty(networkType)) {
1037 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1040 NetworkResourceCustomization nrc =
1041 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1043 networkResource = nrc.getNetworkResource();
1047 if (networkResource != null) {
1048 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1050 mode = networkResource.getOrchestrationMode();
1053 if (NEUTRON_MODE.equals(mode)) {
1055 // Use MsoNeutronUtils for all NEUTRON commands
1056 long deleteNetworkStarttime = System.currentTimeMillis();
1058 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1059 // was not found. So don't bother to query first.
1060 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1061 networkDeleted.value = deleted;
1062 } catch (MsoException me) {
1063 me.addContext("DeleteNetwork");
1064 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1065 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1066 throw new NetworkException(me);
1068 } else { // DEFAULT to ("HEAT".equals (mode))
1069 long deleteStackStarttime = System.currentTimeMillis();
1072 // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack
1074 // So query first to report back if stack WAS deleted or just NOTOFUND
1075 StackInfo heatStack = null;
1076 heatStack = heat.queryStack(cloudSiteId, CLOUD_OWNER, tenantId, networkId);
1077 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
1078 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true);
1079 networkDeleted.value = true;
1081 networkDeleted.value = false;
1083 } catch (MsoException me) {
1084 me.addContext("DeleteNetwork");
1085 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1086 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1087 throw new NetworkException(me);
1092 // On success, nothing is returned.
1097 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1098 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1099 * to undo the creation.
1101 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1105 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1106 // Will capture execution time for metrics
1107 long startTime = System.currentTimeMillis();
1109 if (rollback == null) {
1110 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1114 // Get the elements of the VnfRollback object for easier access
1115 String cloudSiteId = rollback.getCloudId();
1116 String tenantId = rollback.getTenantId();
1117 String networkId = rollback.getNetworkStackId();
1118 String networkType = rollback.getNetworkType();
1119 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1121 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1124 // Retrieve the Network Resource definition
1125 NetworkResource networkResource = null;
1126 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1127 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1130 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1133 if (networkResource != null) {
1135 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1137 mode = networkResource.getOrchestrationMode();
1140 if (rollback.getNetworkCreated()) {
1141 // Rolling back a newly created network, so delete it.
1142 if (NEUTRON_MODE.equals(mode)) {
1143 // Use MsoNeutronUtils for all NEUTRON commands
1144 long deleteNetworkStarttime = System.currentTimeMillis();
1146 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1147 // was not found. So don't bother to query first.
1148 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1149 } catch (MsoException me) {
1150 me.addContext("RollbackNetwork");
1151 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1152 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1153 cloudSiteId, tenantId, me);
1154 throw new NetworkException(me);
1156 } else { // DEFAULT to if ("HEAT".equals (mode))
1157 long deleteStackStarttime = System.currentTimeMillis();
1159 // The deleteStack function in MsoHeatUtils returns success if the stack
1160 // was not found. So don't bother to query first.
1161 heat.deleteStack(tenantId, CLOUD_OWNER, cloudSiteId, networkId, true);
1162 } catch (MsoException me) {
1163 me.addContext("RollbackNetwork");
1164 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1165 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1166 cloudSiteId, tenantId, me);
1167 throw new NetworkException(me);
1175 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1176 List<Integer> vlans, List<RouteTarget> routeTargets) {
1178 StringBuilder missing = new StringBuilder();
1179 if (commonUtils.isNullOrEmpty(networkName)) {
1180 missing.append("networkName");
1184 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1185 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1186 missing.append(sep).append("physicalNetworkName");
1189 if (vlans == null || vlans.isEmpty()) {
1190 missing.append(sep).append(VLANS);
1194 return missing.toString();
1197 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1198 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1199 boolean aic3template) {
1200 // Build the common set of HEAT template parameters
1201 Map<String, Object> stackParams = new HashMap<>();
1202 stackParams.put("network_name", networkName);
1204 if (neutronNetworkType == NetworkType.PROVIDER) {
1205 // For Provider type
1206 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1207 stackParams.put("vlan", vlans.get(0).toString());
1208 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1209 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1210 // It supports all ProviderNet properties except segmentation_id, and adds a
1211 // comma-separated-list of VLANs as a "segments" property.
1212 // Note that this does not match the Neutron definition of Multi-Provider network,
1213 // which contains a list of 'segments', each having physical_network, network_type,
1214 // and segmentation_id.
1215 StringBuilder buf = new StringBuilder();
1217 for (Integer vlan : vlans) {
1218 buf.append(sep).append(vlan.toString());
1221 String csl = buf.toString();
1223 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1224 stackParams.put(VLANS, csl);
1226 if (routeTargets != null) {
1228 String rtGlobal = "";
1229 String rtImport = "";
1230 String rtExport = "";
1232 for (RouteTarget rt : routeTargets) {
1233 boolean rtIsNull = false;
1235 String routeTarget = rt.getRouteTarget();
1236 String routeTargetRole = rt.getRouteTargetRole();
1237 logger.debug("Checking for an actually null route target: {}", rt);
1238 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1240 if (routeTargetRole == null || routeTargetRole.equals("")
1241 || routeTargetRole.equalsIgnoreCase("null"))
1247 logger.debug("Input RT:{}", rt);
1248 String role = rt.getRouteTargetRole();
1249 String rtValue = rt.getRouteTarget();
1251 if ("IMPORT".equalsIgnoreCase(role)) {
1252 sep = rtImport.isEmpty() ? "" : ",";
1253 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1254 } else if ("EXPORT".equalsIgnoreCase(role)) {
1255 sep = rtExport.isEmpty() ? "" : ",";
1256 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1257 } else // covers BOTH, empty etc
1259 sep = rtGlobal.isEmpty() ? "" : ",";
1260 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1266 if (!rtImport.isEmpty()) {
1267 stackParams.put("route_targets_import", rtImport);
1269 if (!rtExport.isEmpty()) {
1270 stackParams.put("route_targets_export", rtExport);
1272 if (!rtGlobal.isEmpty()) {
1273 stackParams.put("route_targets", rtGlobal);
1276 if (commonUtils.isNullOrEmpty(shared)) {
1277 stackParams.put("shared", "False");
1279 stackParams.put("shared", shared);
1281 if (commonUtils.isNullOrEmpty(external)) {
1282 stackParams.put("external", "False");
1284 stackParams.put("external", external);
1292 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1293 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1294 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1295 * "network_policy_refs_data_sequence_minor": "0" } } ]
1297 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1298 // Resource Property
1299 List<ContrailPolicyRef> prlist = new ArrayList<>();
1302 if (pFqdns != null) {
1303 for (String pf : pFqdns) {
1304 if (!commonUtils.isNullOrEmpty(pf)) {
1305 ContrailPolicyRef pr = new ContrailPolicyRef();
1306 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1309 logger.debug("Contrail PolicyRefs Data:{}", pr);
1314 String error = "Null pFqdns at start of mergePolicyRefs";
1315 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1317 throw new MsoAdapterException(error);
1320 JsonNode node = null;
1322 ObjectMapper mapper = new ObjectMapper();
1323 node = mapper.convertValue(prlist, JsonNode.class);
1324 String jsonString = mapper.writeValueAsString(prlist);
1325 logger.debug("Json PolicyRefs Data:{}", jsonString);
1326 } catch (Exception e) {
1327 String error = "Error creating JsonNode for policyRefs Data";
1328 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1330 throw new MsoAdapterException(error);
1332 // update parameters
1333 if (pFqdns != null && node != null) {
1334 StringBuilder buf = new StringBuilder();
1336 for (String pf : pFqdns) {
1337 if (!commonUtils.isNullOrEmpty(pf)) {
1338 buf.append(sep).append(pf);
1342 String csl = buf.toString();
1343 stackParams.put("policy_refs", csl);
1344 stackParams.put("policy_refsdata", node);
1347 logger.debug("StackParams updated with policy refs");
1351 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1353 // update parameters
1354 if (rtFqdns != null) {
1355 StringBuilder buf = new StringBuilder();
1357 for (String rtf : rtFqdns) {
1358 if (!commonUtils.isNullOrEmpty(rtf)) {
1359 buf.append(sep).append(rtf);
1363 String csl = buf.toString();
1364 stackParams.put("route_table_refs", csl);
1367 logger.debug("StackParams updated with route_table refs");
1373 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1374 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1375 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1376 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1377 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1378 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1379 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1380 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1381 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1382 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1383 * "host_routes": null }
1385 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1386 throws MsoException {
1388 // Resource Property
1389 List<ContrailSubnet> cslist = new ArrayList<>();
1390 for (Subnet subnet : subnets) {
1391 logger.debug("Input Subnet:{}", subnet.toString());
1392 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1393 logger.debug("Contrail Subnet:{}", cs.toString());
1397 JsonNode node = null;
1399 ObjectMapper mapper = new ObjectMapper();
1400 node = mapper.convertValue(cslist, JsonNode.class);
1401 String jsonString = mapper.writeValueAsString(cslist);
1402 logger.debug("Json Subnet List:{}", jsonString);
1403 } catch (Exception e) {
1404 String error = "Error creating JsonNode from input subnets";
1405 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1406 throw new MsoAdapterException(error);
1408 // update parameters
1410 stackParams.put("subnet_list", node);
1412 // Outputs - All subnets are in one ipam_subnets structure
1413 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1414 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1416 // append outputs in heatTemplate
1417 int outputsIdx = heatTemplate.indexOf("outputs:");
1418 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1419 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1420 return heatTemplate;
1424 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1426 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1427 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1430 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1431 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1432 * " end: %poolend%\n";
1436 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1437 + " value: {get_resource: subnet_%subnetId%}\n";
1441 StringBuilder resourcesBuf = new StringBuilder();
1442 StringBuilder outputsBuf = new StringBuilder();
1443 for (Subnet subnet : subnets) {
1445 // build template for each subnet
1446 curR = resourceTempl;
1447 if (subnet.getSubnetId() != null) {
1448 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1450 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1451 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1452 throw new MsoAdapterException(error);
1455 if (subnet.getSubnetName() != null) {
1456 curR = curR.replace("%name%", subnet.getSubnetName());
1458 curR = curR.replace("%name%", subnet.getSubnetId());
1461 if (subnet.getCidr() != null) {
1462 curR = curR.replace("%cidr%", subnet.getCidr());
1464 String error = "Missing Required cidr for subnet in HEAT Template";
1465 logger.error(LoggingAnchor.THREE, MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1466 throw new MsoAdapterException(error);
1469 if (subnet.getIpVersion() != null) {
1470 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1472 if (subnet.getEnableDHCP() != null) {
1473 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1475 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1476 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1479 if (subnet.getAllocationPools() != null) {
1480 StringBuilder tempBuf = new StringBuilder();
1481 tempBuf.append(curR);
1482 tempBuf.append(" allocation_pools:\n");
1483 for (Pool pool : subnet.getAllocationPools()) {
1484 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1485 tempBuf.append(" - start: ");
1486 tempBuf.append(pool.getStart());
1487 tempBuf.append("\n end: ");
1488 tempBuf.append(pool.getEnd());
1489 tempBuf.append("\n");
1492 curR = tempBuf.toString();
1495 resourcesBuf.append(curR);
1498 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1500 outputsBuf.append(curO);
1502 // append resources and outputs in heatTemplate
1503 logger.debug("Tempate initial:{}", heatTemplate);
1504 int outputsIdx = heatTemplate.indexOf("outputs:");
1505 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1506 int resourcesIdx = heatTemplate.indexOf("resources:");
1507 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1509 logger.debug("Template updated with all subnets:{}", heatTemplate);
1510 return heatTemplate;
1513 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1515 Map<String, String> sMap = new HashMap<>();
1518 Object obj = outputs.get(key);
1519 ObjectMapper mapper = new ObjectMapper();
1520 String jStr = mapper.writeValueAsString(obj);
1521 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1523 JsonNode rootNode = mapper.readTree(jStr);
1524 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1525 logger.debug("Output Subnet Node {}", sNode.toString());
1526 String name = sNode.path("subnet_name").textValue();
1527 String uuid = sNode.path("subnet_uuid").textValue();
1528 String aaiId = name; // default
1529 // try to find aaiId for name in input subnetList
1530 if (subnets != null) {
1531 for (Subnet subnet : subnets) {
1532 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())) {
1533 if (subnet.getSubnetName().equals(name)) {
1534 aaiId = subnet.getSubnetId();
1540 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1542 } catch (Exception e) {
1543 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1544 ErrorCode.DataError.getValue(), e);
1547 logger.debug("Return sMap {}", sMap.toString());
1551 private static String insertStr(String template, String snippet, int index) {
1553 String updatedTemplate;
1555 logger.debug("Index:{} Snippet:{}", index, snippet);
1557 String templateBeg = template.substring(0, index);
1558 String templateEnd = template.substring(index);
1560 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1562 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1563 return updatedTemplate;