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.adapters.network.beans.ContrailPolicyRef;
37 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
38 import org.onap.so.adapters.network.beans.ContrailSubnet;
39 import org.onap.so.adapters.network.exceptions.NetworkException;
40 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
41 import org.onap.so.cloud.CloudConfig;
42 import org.onap.so.db.catalog.beans.CloudSite;
43 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
44 import org.onap.so.db.catalog.beans.HeatTemplate;
45 import org.onap.so.db.catalog.beans.NetworkResource;
46 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
47 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
48 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
49 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
50 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
51 import org.onap.so.entity.MsoRequest;
52 import org.onap.so.logger.ErrorCode;
53 import org.onap.so.logger.MessageEnum;
54 import org.onap.so.openstack.beans.HeatStatus;
55 import org.onap.so.openstack.beans.NetworkInfo;
56 import org.onap.so.openstack.beans.NetworkRollback;
57 import org.onap.so.openstack.beans.NetworkStatus;
58 import org.onap.so.openstack.beans.Pool;
59 import org.onap.so.openstack.beans.RouteTarget;
60 import org.onap.so.openstack.beans.StackInfo;
61 import org.onap.so.openstack.beans.Subnet;
62 import org.onap.so.openstack.exceptions.MsoAdapterException;
63 import org.onap.so.openstack.exceptions.MsoException;
64 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
65 import org.onap.so.openstack.utils.MsoCommonUtils;
66 import org.onap.so.openstack.utils.MsoHeatUtils;
67 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
68 import org.onap.so.openstack.utils.MsoNeutronUtils;
69 import org.onap.so.openstack.utils.MsoNeutronUtils.NetworkType;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.beans.factory.annotation.Autowired;
73 import org.springframework.core.env.Environment;
74 import org.springframework.stereotype.Component;
75 import org.springframework.transaction.annotation.Transactional;
79 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.onap.so.adapters.network.MsoNetworkAdapter",
80 targetNamespace = "http://org.onap.so/network")
81 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
83 private static final String AIC3_NW_PROPERTY = "org.onap.so.adapters.network.aic3nw";
84 private static final String AIC3_NW = "OS::ContrailV2::VirtualNetwork";
85 private static final String VLANS = "vlans";
86 private static final String PHYSICAL_NETWORK = "physical_network";
87 private static final String UPDATE_NETWORK_CONTEXT = "UpdateNetwork";
88 private static final String NETWORK_ID = "network_id";
89 private static final String NETWORK_FQDN = "network_fqdn";
90 private static final String CREATE_NETWORK_CONTEXT = "CreateNetwork";
91 private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
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;
119 * Health Check web method. Does nothing but return to show the adapter is deployed.
122 public void healthCheck() {
123 logger.debug("Health check call in Network Adapter");
127 * Do not use this constructor or the msoPropertiesFactory will be NULL.
129 * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
131 public MsoNetworkAdapterImpl() {}
134 public void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
135 String networkName, String physicalNetworkName, List<Integer> vlans, String shared, String external,
136 Boolean failIfExists, Boolean backout, List<Subnet> subnets, Map<String, String> networkParams,
137 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
138 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
139 Holder<String> networkFqdn = new Holder<>();
140 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, physicalNetworkName,
141 vlans, null, shared, external, failIfExists, backout, subnets, null, null, msoRequest, networkId,
142 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
146 public void createNetworkContrail(String cloudSiteId, String tenantId, String networkType,
147 String modelCustomizationUuid, String networkName, List<RouteTarget> routeTargets, String shared,
148 String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
149 Map<String, String> networkParams, List<String> policyFqdns, List<String> routeTableFqdns,
150 MsoRequest msoRequest, Holder<String> networkId, Holder<String> neutronNetworkId,
151 Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback)
152 throws NetworkException {
153 createNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkName, null, null, routeTargets,
154 shared, external, failIfExists, backout, subnets, policyFqdns, routeTableFqdns, msoRequest, networkId,
155 neutronNetworkId, networkFqdn, subnetIdMap, rollback);
159 * This is the "Create Network" web service implementation. It will create a new Network of the requested type in
160 * the specified cloud and tenant. The tenant must exist at the time this service is called.
162 * If a network with the same name already exists, this can be considered a success or failure, depending on the
163 * value of the 'failIfExists' parameter.
165 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
166 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
167 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs)
169 * Initially, all provider networks must be "vlan" type, and multiple segments in a multi-provider network must be
170 * multiple VLANs on the same physical network.
172 * This service supports two modes of Network creation/update: - via Heat Templates - via Neutron API The network
173 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
174 * support some subset of the same input parameters: network_name, physical_network, vlan(s).
176 * The method returns the network ID and a NetworkRollback object. This latter object can be passed as-is to the
177 * rollbackNetwork operation to undo everything that was created. This is useful if a network is successfully
178 * created but the orchestration fails on a subsequent operation.
181 private void createNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
182 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
183 String shared, String external, Boolean failIfExists, Boolean backout, List<Subnet> subnets,
184 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest, Holder<String> networkId,
185 Holder<String> neutronNetworkId, Holder<String> networkFqdn, Holder<Map<String, String>> subnetIdMap,
186 Holder<NetworkRollback> rollback) throws NetworkException {
187 logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
189 // Will capture execution time for metrics
190 long startTime = System.currentTimeMillis();
192 // Build a default rollback object (no actions performed)
193 NetworkRollback networkRollback = new NetworkRollback();
194 networkRollback.setCloudId(cloudSiteId);
195 networkRollback.setTenantId(tenantId);
196 networkRollback.setMsoRequest(msoRequest);
197 networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
199 // tenant query is not required here.
200 // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
201 // So this is just catching that error in a bit more obvious way up front.
203 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
204 if (!cloudSiteOpt.isPresent()) {
205 String error = String.format(
206 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
207 networkName, cloudSiteId, tenantId);
208 logger.error("{} {} {}", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
209 // Set the detailed error as the Exception 'message'
210 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
214 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
215 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
216 String mode = networkResource.getOrchestrationMode();
217 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
219 if (NEUTRON_MODE.equals(mode)) {
221 // Use an MsoNeutronUtils for all neutron commands
223 // See if the Network already exists (by name)
224 NetworkInfo netInfo = null;
225 long queryNetworkStarttime = System.currentTimeMillis();
227 netInfo = neutron.queryNetwork(networkName, tenantId, cloudSiteId);
228 } catch (MsoException me) {
230 "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
231 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkName,
232 cloudSiteId, tenantId, me);
233 me.addContext(CREATE_NETWORK_CONTEXT);
234 throw new NetworkException(me);
237 if (netInfo != null) {
238 // Exists. If that's OK, return success with the network ID.
239 // Otherwise, return an exception.
240 if (failIfExists != null && failIfExists) {
241 String error = String.format("Create Nework: Network %s already exists in %s/%s with ID %s",
242 networkName, cloudSiteId, tenantId, netInfo.getId());
243 logger.error("{} {} {}", MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
245 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
247 // Populate the outputs from the existing network.
248 networkId.value = netInfo.getId();
249 neutronNetworkId.value = netInfo.getId();
250 rollback.value = networkRollback; // Default rollback - no updates performed
251 logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
252 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), netInfo.getStatus());
257 long createNetworkStarttime = System.currentTimeMillis();
259 netInfo = neutron.createNetwork(cloudSiteId, tenantId, neutronNetworkType, networkName,
260 physicalNetworkName, vlans);
261 } catch (MsoException me) {
262 me.addContext(CREATE_NETWORK_CONTEXT);
263 logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
264 ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
266 throw new NetworkException(me);
269 // Note: ignoring MsoNetworkAlreadyExists because we already checked.
271 // If reach this point, network creation is successful.
272 // Since directly created via Neutron, networkId tracked by MSO is the same
273 // as the neutron network ID.
274 networkId.value = netInfo.getId();
275 neutronNetworkId.value = netInfo.getId();
277 networkRollback.setNetworkCreated(true);
278 networkRollback.setNetworkId(netInfo.getId());
279 networkRollback.setNeutronNetworkId(netInfo.getId());
280 networkRollback.setNetworkType(networkType);
282 logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
283 } else if ("HEAT".equals(mode)) {
285 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
286 if (heatTemplate == null) {
287 String error = String.format("Network error - undefined Heat Template. Network Type = %s", networkType);
288 logger.error("{} {} {}", MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
289 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
292 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
294 // "Fix" the template if it has CR/LF (getting this from Oracle)
295 String template = heatTemplate.getHeatTemplate();
296 template = template.replaceAll("\r\n", "\n");
298 boolean aic3template = false;
299 String aic3nw = AIC3_NW;
301 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
303 if (template.contains(aic3nw))
306 // First, look up to see if the Network already exists (by name).
307 // For HEAT orchestration of networks, the stack name will always match the network name
308 StackInfo heatStack = null;
309 long queryNetworkStarttime = System.currentTimeMillis();
311 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkName);
312 } catch (MsoException me) {
313 me.addContext(CREATE_NETWORK_CONTEXT);
314 logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
315 MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName, cloudSiteId,
317 throw new NetworkException(me);
320 if (heatStack != null && (heatStack.getStatus() != HeatStatus.NOTFOUND)) {
321 // Stack exists. Return success or error depending on input directive
322 if (failIfExists != null && failIfExists) {
323 String error = String.format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName,
324 cloudSiteId, tenantId, heatStack.getCanonicalName());
325 logger.error("{} {} {}", MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
327 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
329 // Populate the outputs from the existing stack.
330 networkId.value = heatStack.getCanonicalName();
331 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
332 rollback.value = networkRollback; // Default rollback - no updates performed
334 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
336 Map<String, Object> outputs = heatStack.getOutputs();
337 Map<String, String> sMap = new HashMap<>();
338 if (outputs != null) {
339 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
340 String key = entry.getKey();
341 if (key != null && key.startsWith("subnet")) {
342 if (aic3template) // one subnet_id output
344 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
346 } else // multiples subnet_%aaid% outputs
348 String subnetUUId = (String) outputs.get(key);
349 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
354 subnetIdMap.value = sMap;
355 logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
356 MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(), heatStack.getStatus(),
357 networkName, cloudSiteId, tenantId);
362 // Ready to deploy the new Network
363 // Build the common set of HEAT template parameters
364 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
365 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
367 // Validate (and update) the input parameters against the DB definition
368 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
369 // and inputs were already validated.
371 stackParams = heat.validateStackParams(stackParams, heatTemplate);
372 } catch (IllegalArgumentException e) {
373 String error = "Create Network: Configuration Error: " + e.getMessage();
374 logger.error("{} {} {} ", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error, e);
375 // Input parameters were not valid
376 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
379 if (subnets != null) {
382 template = mergeSubnetsAIC3(template, subnets, stackParams);
384 template = mergeSubnets(template, subnets);
386 } catch (MsoException me) {
387 me.addContext(CREATE_NETWORK_CONTEXT);
388 logger.error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
389 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
390 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
391 throw new NetworkException(me);
395 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
397 mergePolicyRefs(policyFqdns, stackParams);
398 } catch (MsoException me) {
399 me.addContext(CREATE_NETWORK_CONTEXT);
400 logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
401 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
402 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
403 throw new NetworkException(me);
407 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
409 mergeRouteTableRefs(routeTableFqdns, stackParams);
410 } catch (MsoException me) {
411 me.addContext(CREATE_NETWORK_CONTEXT);
412 logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
413 MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
414 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
415 throw new NetworkException(me);
419 // Deploy the network stack
420 // Ignore MsoStackAlreadyExists exception because we already checked.
424 heatStack = heat.createStack(cloudSiteId, "CloudOwner", tenantId, networkName, null, template,
425 stackParams, true, heatTemplate.getTimeoutMinutes(), null, null, null, backout.booleanValue());
426 } catch (MsoException me) {
427 me.addContext(CREATE_NETWORK_CONTEXT);
428 logger.error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
429 ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
430 throw new NetworkException(me);
433 // Reach this point if createStack is successful.
435 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
436 // and the neutronNetworkId is the network UUID returned in stack outputs.
437 networkId.value = heatStack.getCanonicalName();
438 neutronNetworkId.value = (String) heatStack.getOutputs().get(NETWORK_ID);
440 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
442 Map<String, Object> outputs = heatStack.getOutputs();
443 Map<String, String> sMap = new HashMap<>();
444 if (outputs != null) {
445 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
446 String key = entry.getKey();
447 if (key != null && key.startsWith("subnet")) {
448 if (aic3template) // one subnet output expected
450 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
452 } else // multiples subnet_%aaid% outputs allowed
454 String subnetUUId = (String) outputs.get(key);
455 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
460 subnetIdMap.value = sMap;
462 rollback.value = networkRollback;
463 // Populate remaining rollback info and response parameters.
464 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
465 networkRollback.setNeutronNetworkId((String) heatStack.getOutputs().get(NETWORK_ID));
466 networkRollback.setNetworkCreated(true);
467 networkRollback.setNetworkType(networkType);
469 logger.debug("Network {} successfully created via HEAT", networkName);
476 public void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
477 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans, String shared,
478 String external, List<Subnet> subnets, Map<String, String> networkParams, MsoRequest msoRequest,
479 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
480 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName,
481 physicalNetworkName, vlans, null, shared, external, subnets, null, null, msoRequest, subnetIdMap,
487 public void updateNetworkContrail(String cloudSiteId, String tenantId, String networkType,
488 String modelCustomizationUuid, String networkId, String networkName, List<RouteTarget> routeTargets,
489 String shared, String external, List<Subnet> subnets, Map<String, String> networkParams,
490 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
491 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
492 updateNetwork(cloudSiteId, tenantId, networkType, modelCustomizationUuid, networkId, networkName, null, null,
493 routeTargets, shared, external, subnets, policyFqdns, routeTableFqdns, msoRequest, subnetIdMap,
498 * This is the "Update Network" web service implementation. It will update an existing Network of the requested type
499 * in the specified cloud and tenant. The typical use will be to replace the VLANs with the supplied list (to add or
500 * remove a VLAN), but other properties may be updated as well.
502 * There will be a pre-defined set of network types defined in the MSO Catalog. All such networks will have a
503 * similar configuration, based on the allowable Openstack networking definitions. This includes basic networks,
504 * provider networks (with a single VLAN), and multi-provider networks (one or more VLANs).
506 * Initially, all provider networks must currently be "vlan" type, and multi-provider networks must be multiple
507 * VLANs on the same physical network.
509 * This service supports two modes of Network update: - via Heat Templates - via Neutron API The network
510 * orchestration mode for each network type is declared in its catalog definition. All Heat-based templates must
511 * support some subset of the same input parameters: network_name, physical_network, vlan, segments.
513 * The method returns a NetworkRollback object. This object can be passed as-is to the rollbackNetwork operation to
514 * undo everything that was updated. This is useful if a network is successfully updated but orchestration fails on
515 * a subsequent operation.
517 private void updateNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
518 String networkId, String networkName, String physicalNetworkName, List<Integer> vlans,
519 List<RouteTarget> routeTargets, String shared, String external, List<Subnet> subnets,
520 List<String> policyFqdns, List<String> routeTableFqdns, MsoRequest msoRequest,
521 Holder<Map<String, String>> subnetIdMap, Holder<NetworkRollback> rollback) throws NetworkException {
523 logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
524 cloudSiteId, tenantId);
526 // Will capture execution time for metrics
527 long startTime = System.currentTimeMillis();
529 // Build a default rollback object (no actions performed)
530 NetworkRollback networkRollback = new NetworkRollback();
531 networkRollback.setCloudId(cloudSiteId);
532 networkRollback.setTenantId(tenantId);
533 networkRollback.setMsoRequest(msoRequest);
535 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
536 if (!cloudSiteOpt.isPresent()) {
537 String error = String.format(
538 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
539 networkName, cloudSiteId, tenantId);
540 logger.error("{} {} {}", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
541 // Set the detailed error as the Exception 'message'
542 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
547 NetworkResource networkResource = networkCheck(startTime, networkType, modelCustomizationUuid, networkName,
548 physicalNetworkName, vlans, routeTargets, cloudSiteId, cloudSiteOpt.get());
549 String mode = networkResource.getOrchestrationMode();
550 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
552 // Use an MsoNeutronUtils for all Neutron commands
554 if (NEUTRON_MODE.equals(mode)) {
556 // Verify that the Network exists
557 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
558 NetworkInfo netInfo = null;
559 long queryNetworkStarttime = System.currentTimeMillis();
561 netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
562 } catch (MsoException me) {
563 me.addContext(UPDATE_NETWORK_CONTEXT);
564 logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
565 ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
566 throw new NetworkException(me);
569 if (netInfo == null) {
570 String error = String.format("Update Nework: Network %s does not exist in %s/%s", networkId,
571 cloudSiteId, tenantId);
572 logger.error("{} {} {}", MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.BusinessProcesssError.getValue(),
574 // Does not exist. Throw an exception (can't update a non-existent network)
575 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
577 long updateNetworkStarttime = System.currentTimeMillis();
579 netInfo = neutron.updateNetwork(cloudSiteId, tenantId, networkId, neutronNetworkType,
580 physicalNetworkName, vlans);
581 } catch (MsoException me) {
582 me.addContext(UPDATE_NETWORK_CONTEXT);
583 logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
584 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
585 throw new NetworkException(me);
588 // Add the network ID and previously queried vlans to the rollback object
589 networkRollback.setNetworkId(netInfo.getId());
590 networkRollback.setNeutronNetworkId(netInfo.getId());
591 networkRollback.setNetworkType(networkType);
592 // Save previous parameters
593 networkRollback.setNetworkName(netInfo.getName());
594 networkRollback.setPhysicalNetwork(netInfo.getProvider());
595 networkRollback.setVlans(netInfo.getVlans());
597 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
598 } else if ("HEAT".equals(mode)) {
600 // First, look up to see that the Network already exists.
601 // For Heat-based orchestration, the networkId is the network Stack ID.
602 StackInfo heatStack = null;
603 long queryStackStarttime = System.currentTimeMillis();
605 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkName);
606 } catch (MsoException me) {
607 me.addContext(UPDATE_NETWORK_CONTEXT);
608 logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
609 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
610 throw new NetworkException(me);
613 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
614 String error = String.format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName,
615 cloudSiteId, tenantId);
616 logger.error("{} {} {}", MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(), error);
617 // Network stack does not exist. Return an error
618 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
621 // Get the previous parameters for rollback
622 Map<String, Object> heatParams = heatStack.getParameters();
624 String previousNetworkName = (String) heatParams.get("network_name");
625 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
627 List<Integer> previousVlans = new ArrayList<>();
628 String vlansParam = (String) heatParams.get(VLANS);
629 if (vlansParam != null) {
630 for (String vlan : vlansParam.split(",")) {
632 previousVlans.add(Integer.parseInt(vlan));
633 } catch (NumberFormatException e) {
634 logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
635 ErrorCode.DataError.getValue(), vlansParam, e);
639 logger.debug("Update Stack: Previous VLANS: {}", previousVlans);
641 // Ready to deploy the updated Network via Heat
644 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
645 if (heatTemplate == null) {
646 String error = "Network error - undefined Heat Template. Network Type=" + networkType;
647 logger.error("{} {} {}", MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(), error);
648 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
651 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
653 // "Fix" the template if it has CR/LF (getting this from Oracle)
654 String template = heatTemplate.getHeatTemplate();
655 template = template.replaceAll("\r\n", "\n");
657 boolean aic3template = false;
658 String aic3nw = AIC3_NW;
660 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
662 if (template.contains(aic3nw))
665 // Build the common set of HEAT template parameters
666 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType, networkName,
667 physicalNetworkName, vlans, routeTargets, shared, external, aic3template);
669 // Validate (and update) the input parameters against the DB definition
670 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
672 stackParams = heat.validateStackParams(stackParams, heatTemplate);
673 } catch (IllegalArgumentException e) {
674 String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
675 logger.error("{} {} {} ", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
676 throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
679 if (subnets != null) {
682 template = mergeSubnetsAIC3(template, subnets, stackParams);
684 template = mergeSubnets(template, subnets);
686 } catch (MsoException me) {
687 me.addContext(UPDATE_NETWORK_CONTEXT);
688 logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
689 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
690 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
691 throw new NetworkException(me);
695 if (policyFqdns != null && aic3template) {
697 mergePolicyRefs(policyFqdns, stackParams);
698 } catch (MsoException me) {
699 me.addContext(UPDATE_NETWORK_CONTEXT);
700 logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
701 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
702 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
703 throw new NetworkException(me);
707 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
709 mergeRouteTableRefs(routeTableFqdns, stackParams);
710 } catch (MsoException me) {
711 me.addContext(UPDATE_NETWORK_CONTEXT);
712 logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
713 MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
714 neutronNetworkType.toString(), cloudSiteId, tenantId, me);
715 throw new NetworkException(me);
719 // Update the network stack
720 // Ignore MsoStackNotFound exception because we already checked.
721 long updateStackStarttime = System.currentTimeMillis();
723 heatStack = heatWithUpdate.updateStack(cloudSiteId, "CloudOwner", tenantId, networkId, template,
724 stackParams, true, heatTemplate.getTimeoutMinutes());
725 } catch (MsoException me) {
726 me.addContext(UPDATE_NETWORK_CONTEXT);
727 logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
728 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
729 throw new NetworkException(me);
732 Map<String, Object> outputs = heatStack.getOutputs();
733 Map<String, String> sMap = new HashMap<>();
734 if (outputs != null) {
735 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
736 String key = entry.getKey();
737 if (key != null && key.startsWith("subnet")) {
738 if (aic3template) // one subnet output expected
740 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
742 } else // multiples subnet_%aaid% outputs allowed
744 String subnetUUId = (String) outputs.get(key);
745 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
750 subnetIdMap.value = sMap;
752 // Reach this point if createStack is successful.
753 // Populate remaining rollback info and response parameters.
754 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
755 if (null != outputs) {
756 networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
758 logger.debug("outputs is NULL");
760 networkRollback.setNetworkType(networkType);
761 // Save previous parameters
762 networkRollback.setNetworkName(previousNetworkName);
763 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
764 networkRollback.setVlans(previousVlans);
766 rollback.value = networkRollback;
768 logger.debug("Network {} successfully updated via HEAT", networkId);
774 private NetworkResource networkCheck(long startTime, String networkType, String modelCustomizationUuid,
775 String networkName, String physicalNetworkName, List<Integer> vlans, List<RouteTarget> routeTargets,
776 String cloudSiteId, CloudSite cloudSite) throws NetworkException {
777 // Retrieve the Network Resource definition
778 NetworkResource networkResource = null;
779 NetworkResourceCustomization networkCust = null;
780 CollectionNetworkResourceCustomization collectionNetworkCust = null;
781 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
782 if (!commonUtils.isNullOrEmpty(networkType)) {
783 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
786 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
787 if (networkCust == null) {
788 collectionNetworkCust =
789 collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
792 if (networkCust != null) {
793 logger.debug("Got Network Customization definition from Catalog: {}", networkCust.toString());
795 networkResource = networkCust.getNetworkResource();
796 } else if (collectionNetworkCust != null) {
797 logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}",
798 collectionNetworkCust.toString());
799 networkResource = collectionNetworkCust.getNetworkResource();
801 if (networkResource == null) {
802 String error = String.format(
803 "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
804 networkType, modelCustomizationUuid);
805 logger.error("{} {} {} ", MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
807 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
809 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
811 String mode = networkResource.getOrchestrationMode();
812 NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
814 // All Networks are orchestrated via HEAT or Neutron
815 if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
816 String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
817 logger.error("{} {} {}", MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT, ErrorCode.DataError.getValue(),
819 throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
822 MavenLikeVersioning aicV = new MavenLikeVersioning();
823 aicV.setVersion(cloudSite.getCloudVersion());
824 if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin())
825 || aicV.isTheSameVersion(networkResource.getAicVersionMin())) // aic
828 && (aicV.isTheSameVersion(networkResource.getAicVersionMax())
829 || !(aicV.isMoreRecentThan(networkResource.getAicVersionMax())))) // aic <= max
831 logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
832 networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
833 cloudSite.getCloudVersion());
835 String error = String.format(
836 "Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
837 networkType, networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(),
838 cloudSiteId, cloudSite.getCloudVersion());
839 logger.error("{} {} {} ", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
840 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
843 // Validate the Network parameters.
845 validateNetworkParams(neutronNetworkType, networkName, physicalNetworkName, vlans, routeTargets);
846 if (!missing.isEmpty()) {
847 String error = "Create Network: Missing parameters: " + missing;
848 logger.error("{} {} {}", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
850 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
853 return networkResource;
857 public void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
858 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
859 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<Map<String, String>> subnetIdMap)
860 throws NetworkException {
861 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
862 status, vlans, null, subnetIdMap);
866 public void queryNetworkContrail(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
867 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
868 Holder<NetworkStatus> status, Holder<List<RouteTarget>> routeTargets,
869 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
870 queryNetwork(cloudSiteId, tenantId, networkNameOrId, msoRequest, networkExists, networkId, neutronNetworkId,
871 status, null, routeTargets, subnetIdMap);
875 * This is the queryNetwork method. It returns the existence and status of the specified network, along with its
876 * Neutron UUID and list of VLANs. This method attempts to find the network using both Heat and Neutron. Heat stacks
877 * are first searched based on the provided network name/id. If none is found, the Neutron is directly queried.
879 private void queryNetwork(String cloudSiteId, String tenantId, String networkNameOrId, MsoRequest msoRequest,
880 Holder<Boolean> networkExists, Holder<String> networkId, Holder<String> neutronNetworkId,
881 Holder<NetworkStatus> status, Holder<List<Integer>> vlans, Holder<List<RouteTarget>> routeTargets,
882 Holder<Map<String, String>> subnetIdMap) throws NetworkException {
884 logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
886 // Will capture execution time for metrics
887 long startTime = System.currentTimeMillis();
889 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
890 || commonUtils.isNullOrEmpty(networkNameOrId)) {
892 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
893 logger.error("{} {} {}", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
894 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
897 Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
898 if (!cloudSiteOpt.isPresent()) {
899 String error = String.format(
900 "Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
901 networkNameOrId, cloudSiteId, tenantId);
902 logger.error("{} {} {}", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
903 // Set the detailed error as the Exception 'message'
904 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
907 // Use MsoNeutronUtils for all NEUTRON commands
911 // Try Heat first, since networks may be named the same as the Heat stack
912 StackInfo heatStack = null;
913 long queryStackStarttime = System.currentTimeMillis();
915 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkNameOrId);
916 } catch (MsoException me) {
917 me.addContext("QueryNetwork");
918 logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
919 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
920 throw new NetworkException(me);
923 // Populate the outputs based on the returned Stack information
924 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
925 // Found it. Get the neutronNetworkId for further query
926 Map<String, Object> outputs = heatStack.getOutputs();
927 neutronId = (String) outputs.get(NETWORK_ID);
930 Map<String, String> sMap = new HashMap<>();
931 if (outputs != null) {
932 for (String key : outputs.keySet()) {
933 if (key != null && key.startsWith("subnet_id_")) // multiples subnet_%aaid% outputs
935 String subnetUUId = (String) outputs.get(key);
936 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
937 } else if (key != null && key.startsWith("subnet")) // one subnet output expected
939 Map<String, String> map = getSubnetUUId(key, outputs, null);
945 subnetIdMap.value = sMap;
947 // Input ID was not a Heat stack ID. Try it directly in Neutron
948 neutronId = networkNameOrId;
952 // Query directly against the Neutron Network for the details
953 // no RouteTargets available for ContrailV2 in neutron net-show
954 // networkId is heatStackId
955 long queryNetworkStarttime = System.currentTimeMillis();
957 NetworkInfo netInfo = neutron.queryNetwork(neutronId, tenantId, cloudSiteId);
958 if (netInfo != null) {
959 // Found. Populate the output elements
960 networkExists.value = Boolean.TRUE;
961 if ("HEAT".equals(mode)) {
962 networkId.value = heatStack.getCanonicalName();
964 networkId.value = netInfo.getId();
966 neutronNetworkId.value = netInfo.getId();
967 status.value = netInfo.getStatus();
969 vlans.value = netInfo.getVlans();
971 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
972 ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
974 // Not found. Populate the status fields, leave the rest null
975 networkExists.value = Boolean.FALSE;
976 status.value = NetworkStatus.NOTFOUND;
977 neutronNetworkId.value = null;
979 vlans.value = new ArrayList<>();
981 logger.debug("Network {} not found", networkNameOrId);
983 } catch (MsoException me) {
984 me.addContext("QueryNetwork");
985 logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
986 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
987 throw new NetworkException(me);
993 * This is the "Delete Network" web service implementation. It will delete a Network in the specified cloud and
996 * If the network is not found, it is treated as a success.
998 * This service supports two modes of Network creation/update/delete: - via Heat Templates - via Neutron API The
999 * network orchestration mode for each network type is declared in its catalog definition.
1001 * For Heat-based orchestration, the networkId should be the stack ID. For Neutron-based orchestration, the
1002 * networkId should be the Neutron network UUID.
1004 * The method returns nothing on success. Rollback is not possible for delete commands, so any failure on delete
1005 * will require manual fallout in the client.
1008 public void deleteNetwork(String cloudSiteId, String tenantId, String networkType, String modelCustomizationUuid,
1009 String networkId, MsoRequest msoRequest, Holder<Boolean> networkDeleted) throws NetworkException {
1011 logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1013 // Will capture execution time for metrics
1014 long startTime = System.currentTimeMillis();
1017 if (commonUtils.isNullOrEmpty(cloudSiteId) || commonUtils.isNullOrEmpty(tenantId)
1018 || commonUtils.isNullOrEmpty(networkId)) {
1019 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1020 logger.error("{} {} {} ", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1021 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1024 // Retrieve the Network Resource definition
1025 NetworkResource networkResource = null;
1027 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1028 if (!commonUtils.isNullOrEmpty(networkType)) {
1029 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1032 NetworkResourceCustomization nrc =
1033 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1035 networkResource = nrc.getNetworkResource();
1039 if (networkResource != null) {
1040 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1042 mode = networkResource.getOrchestrationMode();
1045 if (NEUTRON_MODE.equals(mode)) {
1047 // Use MsoNeutronUtils for all NEUTRON commands
1048 long deleteNetworkStarttime = System.currentTimeMillis();
1050 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1051 // was not found. So don't bother to query first.
1052 boolean deleted = neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1053 networkDeleted.value = deleted;
1054 } catch (MsoException me) {
1055 me.addContext("DeleteNetwork");
1056 logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1057 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1058 throw new NetworkException(me);
1060 } else { // DEFAULT to ("HEAT".equals (mode))
1061 long deleteStackStarttime = System.currentTimeMillis();
1064 // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack
1066 // So query first to report back if stack WAS deleted or just NOTOFUND
1067 StackInfo heatStack = null;
1068 heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkId);
1069 if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND) {
1070 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1071 networkDeleted.value = true;
1073 networkDeleted.value = false;
1075 } catch (MsoException me) {
1076 me.addContext("DeleteNetwork");
1077 logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1078 ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1079 throw new NetworkException(me);
1084 // On success, nothing is returned.
1089 * This web service endpoint will rollback a previous Create VNF operation. A rollback object is returned to the
1090 * client in a successful creation response. The client can pass that object as-is back to the rollbackVnf operation
1091 * to undo the creation.
1093 * The rollback includes removing the VNF and deleting the tenant if the tenant did not exist prior to the VNF
1097 public void rollbackNetwork(NetworkRollback rollback) throws NetworkException {
1098 // Will capture execution time for metrics
1099 long startTime = System.currentTimeMillis();
1101 if (rollback == null) {
1102 logger.error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1106 // Get the elements of the VnfRollback object for easier access
1107 String cloudSiteId = rollback.getCloudId();
1108 String tenantId = rollback.getTenantId();
1109 String networkId = rollback.getNetworkStackId();
1110 String networkType = rollback.getNetworkType();
1111 String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1113 logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1116 // Retrieve the Network Resource definition
1117 NetworkResource networkResource = null;
1118 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1119 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1122 networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1125 if (networkResource != null) {
1127 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1129 mode = networkResource.getOrchestrationMode();
1132 if (rollback.getNetworkCreated()) {
1133 // Rolling back a newly created network, so delete it.
1134 if (NEUTRON_MODE.equals(mode)) {
1135 // Use MsoNeutronUtils for all NEUTRON commands
1136 long deleteNetworkStarttime = System.currentTimeMillis();
1138 // The deleteNetwork function in MsoNeutronUtils returns success if the network
1139 // was not found. So don't bother to query first.
1140 neutron.deleteNetwork(networkId, tenantId, cloudSiteId);
1141 } catch (MsoException me) {
1142 me.addContext("RollbackNetwork");
1143 logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1144 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1145 cloudSiteId, tenantId, me);
1146 throw new NetworkException(me);
1148 } else { // DEFAULT to if ("HEAT".equals (mode))
1149 long deleteStackStarttime = System.currentTimeMillis();
1151 // The deleteStack function in MsoHeatUtils returns success if the stack
1152 // was not found. So don't bother to query first.
1153 heat.deleteStack(tenantId, "CloudOwner", cloudSiteId, networkId, true);
1154 } catch (MsoException me) {
1155 me.addContext("RollbackNetwork");
1156 logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1157 MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(), networkId,
1158 cloudSiteId, tenantId, me);
1159 throw new NetworkException(me);
1167 private String validateNetworkParams(NetworkType neutronNetworkType, String networkName, String physicalNetwork,
1168 List<Integer> vlans, List<RouteTarget> routeTargets) {
1170 StringBuilder missing = new StringBuilder();
1171 if (commonUtils.isNullOrEmpty(networkName)) {
1172 missing.append("networkName");
1176 if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1177 if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1178 missing.append(sep).append("physicalNetworkName");
1181 if (vlans == null || vlans.isEmpty()) {
1182 missing.append(sep).append(VLANS);
1186 return missing.toString();
1189 private Map<String, Object> populateNetworkParams(NetworkType neutronNetworkType, String networkName,
1190 String physicalNetwork, List<Integer> vlans, List<RouteTarget> routeTargets, String shared, String external,
1191 boolean aic3template) {
1192 // Build the common set of HEAT template parameters
1193 Map<String, Object> stackParams = new HashMap<>();
1194 stackParams.put("network_name", networkName);
1196 if (neutronNetworkType == NetworkType.PROVIDER) {
1197 // For Provider type
1198 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1199 stackParams.put("vlan", vlans.get(0).toString());
1200 } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1201 // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1202 // It supports all ProviderNet properties except segmentation_id, and adds a
1203 // comma-separated-list of VLANs as a "segments" property.
1204 // Note that this does not match the Neutron definition of Multi-Provider network,
1205 // which contains a list of 'segments', each having physical_network, network_type,
1206 // and segmentation_id.
1207 StringBuilder buf = new StringBuilder();
1209 for (Integer vlan : vlans) {
1210 buf.append(sep).append(vlan.toString());
1213 String csl = buf.toString();
1215 stackParams.put(PHYSICAL_NETWORK, physicalNetwork);
1216 stackParams.put(VLANS, csl);
1218 if (routeTargets != null) {
1220 String rtGlobal = "";
1221 String rtImport = "";
1222 String rtExport = "";
1224 for (RouteTarget rt : routeTargets) {
1225 boolean rtIsNull = false;
1227 String routeTarget = rt.getRouteTarget();
1228 String routeTargetRole = rt.getRouteTargetRole();
1229 logger.debug("Checking for an actually null route target: {}", rt);
1230 if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1232 if (routeTargetRole == null || routeTargetRole.equals("")
1233 || routeTargetRole.equalsIgnoreCase("null"))
1239 logger.debug("Input RT:{}", rt);
1240 String role = rt.getRouteTargetRole();
1241 String rtValue = rt.getRouteTarget();
1243 if ("IMPORT".equalsIgnoreCase(role)) {
1244 sep = rtImport.isEmpty() ? "" : ",";
1245 rtImport = aic3template ? rtImport + sep + "target:" + rtValue : rtImport + sep + rtValue;
1246 } else if ("EXPORT".equalsIgnoreCase(role)) {
1247 sep = rtExport.isEmpty() ? "" : ",";
1248 rtExport = aic3template ? rtExport + sep + "target:" + rtValue : rtExport + sep + rtValue;
1249 } else // covers BOTH, empty etc
1251 sep = rtGlobal.isEmpty() ? "" : ",";
1252 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue : rtGlobal + sep + rtValue;
1258 if (!rtImport.isEmpty()) {
1259 stackParams.put("route_targets_import", rtImport);
1261 if (!rtExport.isEmpty()) {
1262 stackParams.put("route_targets_export", rtExport);
1264 if (!rtGlobal.isEmpty()) {
1265 stackParams.put("route_targets", rtGlobal);
1268 if (commonUtils.isNullOrEmpty(shared)) {
1269 stackParams.put("shared", "False");
1271 stackParams.put("shared", shared);
1273 if (commonUtils.isNullOrEmpty(external)) {
1274 stackParams.put("external", "False");
1276 stackParams.put("external", external);
1284 * policyRef_list structure in stackParams [ { "network_policy_refs_data_sequence": {
1285 * "network_policy_refs_data_sequence_major": "1", "network_policy_refs_data_sequence_minor": "0" } }, {
1286 * "network_policy_refs_data_sequence": { "network_policy_refs_data_sequence_major": "2",
1287 * "network_policy_refs_data_sequence_minor": "0" } } ]
1289 private void mergePolicyRefs(List<String> pFqdns, Map<String, Object> stackParams) throws MsoException {
1290 // Resource Property
1291 List<ContrailPolicyRef> prlist = new ArrayList<>();
1293 for (String pf : pFqdns) {
1294 if (!commonUtils.isNullOrEmpty(pf)) {
1295 ContrailPolicyRef pr = new ContrailPolicyRef();
1296 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1299 logger.debug("Contrail PolicyRefs Data:{}", pr);
1304 JsonNode node = null;
1306 ObjectMapper mapper = new ObjectMapper();
1307 node = mapper.convertValue(prlist, JsonNode.class);
1308 String jsonString = mapper.writeValueAsString(prlist);
1309 logger.debug("Json PolicyRefs Data:{}", jsonString);
1310 } catch (Exception e) {
1311 String error = "Error creating JsonNode for policyRefs Data";
1312 logger.error("{} {} {} ", MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(), error,
1314 throw new MsoAdapterException(error);
1316 // update parameters
1317 if (pFqdns != null && node != null) {
1318 StringBuilder buf = new StringBuilder();
1320 for (String pf : pFqdns) {
1321 if (!commonUtils.isNullOrEmpty(pf)) {
1322 buf.append(sep).append(pf);
1326 String csl = buf.toString();
1327 stackParams.put("policy_refs", csl);
1328 stackParams.put("policy_refsdata", node);
1331 logger.debug("StackParams updated with policy refs");
1335 private void mergeRouteTableRefs(List<String> rtFqdns, Map<String, Object> stackParams) throws MsoException {
1337 // update parameters
1338 if (rtFqdns != null) {
1339 StringBuilder buf = new StringBuilder();
1341 for (String rtf : rtFqdns) {
1342 if (!commonUtils.isNullOrEmpty(rtf)) {
1343 buf.append(sep).append(rtf);
1347 String csl = buf.toString();
1348 stackParams.put("route_table_refs", csl);
1351 logger.debug("StackParams updated with route_table refs");
1357 * Subnet Output structure from Juniper { "ipam_subnets": [ { "subnet": { "ip_prefix": "10.100.1.0",
1358 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": false, "default_gateway": "10.100.1.1",
1359 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1360 * "allocation_pools": [ { "start": "10.100.1.3", "end": "10.100.1.5" }, { "start": "10.100.1.6", "end":
1361 * "10.100.1.9" } ], "host_routes": null, "dns_server_address": "10.100.1.13", "subnet_name":
1362 * "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0" }, { "subnet": { "ip_prefix": "10.100.2.16",
1363 * "ip_prefix_len": 28 }, "addr_from_start": null, "enable_dhcp": true, "default_gateway": "10.100.2.17",
1364 * "dns_nameservers": [], "dhcp_option_list": null, "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1365 * "allocation_pools": [ { "start": "10.100.2.18", "end": "10.100.2.20" } ], "host_routes": null,
1366 * "dns_server_address": "10.100.2.29", "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1" } ],
1367 * "host_routes": null }
1369 private String mergeSubnetsAIC3(String heatTemplate, List<Subnet> subnets, Map<String, Object> stackParams)
1370 throws MsoException {
1372 // Resource Property
1373 List<ContrailSubnet> cslist = new ArrayList<>();
1374 for (Subnet subnet : subnets) {
1375 logger.debug("Input Subnet:{}", subnet.toString());
1376 ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1377 logger.debug("Contrail Subnet:{}", cs.toString());
1381 JsonNode node = null;
1383 ObjectMapper mapper = new ObjectMapper();
1384 node = mapper.convertValue(cslist, JsonNode.class);
1385 String jsonString = mapper.writeValueAsString(cslist);
1386 logger.debug("Json Subnet List:{}", jsonString);
1387 } catch (Exception e) {
1388 String error = "Error creating JsonNode from input subnets";
1389 logger.error("{} {} {} ", MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1390 throw new MsoAdapterException(error);
1392 // update parameters
1394 stackParams.put("subnet_list", node);
1396 // Outputs - All subnets are in one ipam_subnets structure
1397 String outputTempl = " subnet:\n" + " description: Openstack subnet identifier\n"
1398 + " value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1400 // append outputs in heatTemplate
1401 int outputsIdx = heatTemplate.indexOf("outputs:");
1402 heatTemplate = insertStr(heatTemplate, outputTempl, outputsIdx + 8);
1403 logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1404 return heatTemplate;
1408 private String mergeSubnets(String heatTemplate, List<Subnet> subnets) throws MsoException {
1410 String resourceTempl = " subnet_%subnetId%:\n" + " type: OS::Neutron::Subnet\n" + " properties:\n"
1411 + " name: %name%\n" + " network_id: { get_resource: network }\n" + " cidr: %cidr%\n";
1414 * make these optional + " ip_version: %ipversion%\n" + " enable_dhcp: %enabledhcp%\n" +
1415 * " gateway_ip: %gatewayip%\n" + " allocation_pools:\n" + " - start: %poolstart%\n" +
1416 * " end: %poolend%\n";
1420 String outputTempl = " subnet_id_%subnetId%:\n" + " description: Openstack subnet identifier\n"
1421 + " value: {get_resource: subnet_%subnetId%}\n";
1425 StringBuilder resourcesBuf = new StringBuilder();
1426 StringBuilder outputsBuf = new StringBuilder();
1427 for (Subnet subnet : subnets) {
1429 // build template for each subnet
1430 curR = resourceTempl;
1431 if (subnet.getSubnetId() != null) {
1432 curR = curR.replace("%subnetId%", subnet.getSubnetId());
1434 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1435 logger.error("{} {} {} ", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1436 throw new MsoAdapterException(error);
1439 if (subnet.getSubnetName() != null) {
1440 curR = curR.replace("%name%", subnet.getSubnetName());
1442 curR = curR.replace("%name%", subnet.getSubnetId());
1445 if (subnet.getCidr() != null) {
1446 curR = curR.replace("%cidr%", subnet.getCidr());
1448 String error = "Missing Required cidr for subnet in HEAT Template";
1449 logger.error("{} {} {} ", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1450 throw new MsoAdapterException(error);
1453 if (subnet.getIpVersion() != null) {
1454 curR = curR + " ip_version: " + subnet.getIpVersion() + "\n";
1456 if (subnet.getEnableDHCP() != null) {
1457 curR = curR + " enable_dhcp: " + Boolean.toString(subnet.getEnableDHCP()) + "\n";
1459 if (subnet.getGatewayIp() != null && !subnet.getGatewayIp().isEmpty()) {
1460 curR = curR + " gateway_ip: " + subnet.getGatewayIp() + "\n";
1463 if (subnet.getAllocationPools() != null) {
1464 curR = curR + " allocation_pools:\n";
1465 for (Pool pool : subnet.getAllocationPools()) {
1466 if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd())) {
1467 curR = curR + " - start: " + pool.getStart() + "\n";
1468 curR = curR + " end: " + pool.getEnd() + "\n";
1473 resourcesBuf.append(curR);
1476 curO = curO.replace("%subnetId%", subnet.getSubnetId());
1478 outputsBuf.append(curO);
1481 // append resources and outputs in heatTemplate
1482 logger.debug("Tempate initial:{}", heatTemplate);
1483 int outputsIdx = heatTemplate.indexOf("outputs:");
1484 heatTemplate = insertStr(heatTemplate, outputsBuf.toString(), outputsIdx + 8);
1485 int resourcesIdx = heatTemplate.indexOf("resources:");
1486 heatTemplate = insertStr(heatTemplate, resourcesBuf.toString(), resourcesIdx + 10);
1488 logger.debug("Template updated with all subnets:{}", heatTemplate);
1489 return heatTemplate;
1492 private Map<String, String> getSubnetUUId(String key, Map<String, Object> outputs, List<Subnet> subnets) {
1494 Map<String, String> sMap = new HashMap<>();
1497 Object obj = outputs.get(key);
1498 ObjectMapper mapper = new ObjectMapper();
1499 String jStr = mapper.writeValueAsString(obj);
1500 logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1502 JsonNode rootNode = mapper.readTree(jStr);
1503 for (JsonNode sNode : rootNode.path("ipam_subnets")) {
1504 logger.debug("Output Subnet Node {}", sNode.toString());
1505 String name = sNode.path("subnet_name").textValue();
1506 String uuid = sNode.path("subnet_uuid").textValue();
1507 String aaiId = name; // default
1508 // try to find aaiId for name in input subnetList
1509 if (subnets != null) {
1510 for (Subnet subnet : subnets) {
1511 if (subnet != null && !commonUtils.isNullOrEmpty(subnet.getSubnetName())) {
1512 if (subnet.getSubnetName().equals(name)) {
1513 aaiId = subnet.getSubnetId();
1519 sMap.put(aaiId, uuid); // bpmn needs aaid to uuid map
1521 } catch (Exception e) {
1522 logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1523 ErrorCode.DataError.getValue(), e);
1526 logger.debug("Return sMap {}", sMap.toString());
1530 private static String insertStr(String template, String snippet, int index) {
1532 String updatedTemplate;
1534 logger.debug("Index:{} Snippet:{}", index, snippet);
1536 String templateBeg = template.substring(0, index);
1537 String templateEnd = template.substring(index);
1539 updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1541 logger.debug("Template updated with a subnet:{}", updatedTemplate);
1542 return updatedTemplate;