Merge "Not found exception was in the wrong place."
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / heatbridge / HeatBridgeImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 /*
22  * Copyright (C) 2018 Bell Canada. All rights reserved.
23  *
24  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
25  * the License. You may obtain a copy of the License at
26  *
27  * http://www.apache.org/licenses/LICENSE-2.0
28  *
29  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
30  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
31  * specific language governing permissions and limitations under the License.
32  */
33 package org.onap.so.heatbridge;
34
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.Optional;
40 import java.util.concurrent.ConcurrentHashMap;
41 import java.util.function.Function;
42 import java.util.function.Predicate;
43 import java.util.stream.Collectors;
44 import javax.annotation.Nonnull;
45 import javax.ws.rs.NotFoundException;
46 import javax.ws.rs.WebApplicationException;
47 import org.apache.commons.collections.CollectionUtils;
48 import org.apache.commons.validator.routines.InetAddressValidator;
49 import org.onap.aai.domain.yang.Flavor;
50 import org.onap.aai.domain.yang.Image;
51 import org.onap.aai.domain.yang.L3InterfaceIpv4AddressList;
52 import org.onap.aai.domain.yang.LInterface;
53 import org.onap.aai.domain.yang.PInterface;
54 import org.onap.aai.domain.yang.Pserver;
55 import org.onap.aai.domain.yang.SriovPf;
56 import org.onap.aai.domain.yang.SriovPfs;
57 import org.onap.aai.domain.yang.SriovVf;
58 import org.onap.aai.domain.yang.SriovVfs;
59 import org.onap.aai.domain.yang.VfModule;
60 import org.onap.aai.domain.yang.Vlan;
61 import org.onap.aai.domain.yang.Vlans;
62 import org.onap.aai.domain.yang.Vserver;
63 import org.onap.aaiclient.client.aai.AAIObjectType;
64 import org.onap.aaiclient.client.aai.AAIResourcesClient;
65 import org.onap.aaiclient.client.aai.AAISingleTransactionClient;
66 import org.onap.aaiclient.client.aai.entities.AAIResultWrapper;
67 import org.onap.aaiclient.client.aai.entities.Relationships;
68 import org.onap.aaiclient.client.aai.entities.uri.AAIResourceUri;
69 import org.onap.aaiclient.client.aai.entities.uri.AAIUriFactory;
70 import org.onap.aaiclient.client.generated.fluentbuilders.AAIFluentTypeBuilder;
71 import org.onap.aaiclient.client.graphinventory.entities.uri.Depth;
72 import org.onap.aaiclient.client.graphinventory.exceptions.BulkProcessFailed;
73 import org.onap.logging.filter.base.ErrorCode;
74 import org.onap.so.db.catalog.beans.CloudIdentity;
75 import org.onap.so.db.catalog.beans.ServerType;
76 import org.onap.so.heatbridge.constants.HeatBridgeConstants;
77 import org.onap.so.heatbridge.factory.MsoCloudClientFactoryImpl;
78 import org.onap.so.heatbridge.helpers.AaiHelper;
79 import org.onap.so.heatbridge.openstack.api.OpenstackClient;
80 import org.onap.so.heatbridge.openstack.factory.OpenstackClientFactoryImpl;
81 import org.onap.so.heatbridge.utils.HeatBridgeUtils;
82 import org.onap.so.logger.LoggingAnchor;
83 import org.onap.so.logger.MessageEnum;
84 import org.onap.so.spring.SpringContextHelper;
85 import org.openstack4j.model.compute.Server;
86 import org.openstack4j.model.heat.Resource;
87 import org.openstack4j.model.network.IP;
88 import org.openstack4j.model.network.Network;
89 import org.openstack4j.model.network.NetworkType;
90 import org.openstack4j.model.network.Port;
91 import org.openstack4j.model.network.Subnet;
92 import org.slf4j.Logger;
93 import org.slf4j.LoggerFactory;
94 import org.springframework.core.env.Environment;
95 import com.google.common.base.Preconditions;
96 import com.google.common.base.Strings;
97 import com.google.common.collect.ImmutableMap;
98 import inet.ipaddr.IPAddressString;
99
100 /**
101  * This class provides an implementation of {@link HeatBridgeApi}
102  */
103 public class HeatBridgeImpl implements HeatBridgeApi {
104
105     private static final Logger logger = LoggerFactory.getLogger(HeatBridgeImpl.class);
106     private static final String ERR_MSG_NULL_OS_CLIENT =
107             "Initialization error: Null openstack client. Authenticate with Keystone first.";
108     private static final String OOB_MGT_NETWORK_IDENTIFIER = "Management";
109     private OpenstackClient osClient;
110     private AAIResourcesClient resourcesClient;
111     private AAISingleTransactionClient transaction;
112     private String cloudOwner;
113     private String cloudRegionId;
114     private String regionId;
115     private String tenantId;
116     private AaiHelper aaiHelper = new AaiHelper();
117     private CloudIdentity cloudIdentity;
118     private Environment env;
119
120
121     public HeatBridgeImpl(AAIResourcesClient resourcesClient, final CloudIdentity cloudIdentity,
122             @Nonnull final String cloudOwner, @Nonnull final String cloudRegionId, @Nonnull final String regionId,
123             @Nonnull final String tenantId) {
124         Objects.requireNonNull(cloudOwner, "Null cloud-owner value!");
125         Objects.requireNonNull(cloudRegionId, "Null cloud-region identifier!");
126         Objects.requireNonNull(tenantId, "Null tenant identifier!");
127         Objects.requireNonNull(regionId, "Null regionId identifier!");
128
129         this.cloudIdentity = cloudIdentity;
130         this.cloudOwner = cloudOwner;
131         this.cloudRegionId = cloudRegionId;
132         this.regionId = regionId;
133         this.tenantId = tenantId;
134         this.resourcesClient = resourcesClient;
135         if (resourcesClient != null)
136             this.transaction = resourcesClient.beginSingleTransaction();
137         if (SpringContextHelper.getAppContext() != null)
138             this.env = SpringContextHelper.getAppContext().getEnvironment();
139     }
140
141     public HeatBridgeImpl() {
142         this.resourcesClient = new AAIResourcesClient();
143         this.transaction = resourcesClient.beginSingleTransaction();
144     }
145
146     @Override
147     public OpenstackClient authenticate() throws HeatBridgeException {
148         String keystoneVersion = "";
149         if (ServerType.KEYSTONE.equals(cloudIdentity.getIdentityServerType()))
150             keystoneVersion = "v2.0";
151         else if (ServerType.KEYSTONE_V3.equals(cloudIdentity.getIdentityServerType())) {
152             keystoneVersion = "v3";
153         } else {
154             keystoneVersion = "UNKNOWN";
155         }
156         logger.trace("Keystone Version: {} ", keystoneVersion);
157         this.osClient = new MsoCloudClientFactoryImpl(new OpenstackClientFactoryImpl()).getOpenstackClient(
158                 cloudIdentity.getIdentityUrl(), cloudIdentity.getMsoId(), cloudIdentity.getMsoPass(), regionId,
159                 tenantId, keystoneVersion, cloudIdentity.getUserDomainName(), cloudIdentity.getProjectDomainName());
160         logger.trace("Successfully authenticated with keystone for tenant: {} and region: {}", tenantId, regionId);
161         return osClient;
162     }
163
164     @Override
165     public List<Resource> queryNestedHeatStackResources(final String heatStackId) {
166         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
167         Preconditions.checkState(!Strings.isNullOrEmpty(heatStackId), "Invalid heatStackId!");
168         List<Resource> stackBasedResources =
169                 osClient.getStackBasedResources(heatStackId, HeatBridgeConstants.OS_DEFAULT_HEAT_NESTING);
170         logger.debug(stackBasedResources.size() + " heat stack resources are extracted for stack: " + heatStackId);
171         return stackBasedResources;
172     }
173
174     @Override
175     public List<String> extractStackResourceIdsByResourceType(final List<Resource> stackResources,
176             final String resourceType) {
177         return stackResources.stream().filter(stackResource -> stackResource.getType().equals(resourceType))
178                 .map(Resource::getPhysicalResourceId).collect(Collectors.toList());
179     }
180
181     @Override
182     public List<String> extractNetworkIds(final List<String> networkNameList) {
183         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
184         return networkNameList.stream()
185                 .map(netName -> osClient
186                         .listNetworksByFilter(ImmutableMap.of(HeatBridgeConstants.OS_NAME_KEY, netName)))
187                 .filter(nets -> nets != null && nets.size() == 1) // extract network-id only if network-name is unique
188                 .map(nets -> nets.get(0).getId()).collect(Collectors.toList());
189     }
190
191     @Override
192     public List<Server> getAllOpenstackServers(final List<Resource> stackResources) {
193         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
194         // Filter Openstack Compute resources
195         List<String> serverIds =
196                 extractStackResourceIdsByResourceType(stackResources, HeatBridgeConstants.OS_SERVER_RESOURCE_TYPE);
197         return serverIds.stream().map(serverId -> osClient.getServerById(serverId)).collect(Collectors.toList());
198     }
199
200     @Override
201     public List<org.openstack4j.model.compute.Image> extractOpenstackImagesFromServers(final List<Server> servers) {
202         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
203         return servers.stream().map(Server::getImage)
204                 .filter(distinctByProperty(org.openstack4j.model.compute.Image::getId)).collect(Collectors.toList());
205     }
206
207     @Override
208     public List<org.openstack4j.model.compute.Flavor> extractOpenstackFlavorsFromServers(final List<Server> servers) {
209         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
210         return servers.stream().map(Server::getFlavor)
211                 .filter(distinctByProperty(org.openstack4j.model.compute.Flavor::getId)).collect(Collectors.toList());
212     }
213
214     @Override
215     public void buildAddImagesToAaiAction(final List<org.openstack4j.model.compute.Image> images)
216             throws HeatBridgeException {
217         for (org.openstack4j.model.compute.Image image : images) {
218             Image aaiImage = aaiHelper.buildImage(image);
219             try {
220                 AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.IMAGE, cloudOwner, cloudRegionId,
221                         aaiImage.getImageId());
222                 if (!resourcesClient.exists(uri)) {
223                     transaction.create(uri, aaiImage);
224                     logger.debug("Queuing AAI command to add image: " + aaiImage.getImageId());
225                 } else {
226                     logger.debug("Nothing to add since image: " + aaiImage.getImageId() + "already exists in AAI.");
227                 }
228             } catch (WebApplicationException e) {
229                 throw new HeatBridgeException(
230                         "Failed to update image to AAI: " + aaiImage.getImageId() + ". Error" + " cause: " + e, e);
231             }
232         }
233     }
234
235     @Override
236     public void buildAddFlavorsToAaiAction(final List<org.openstack4j.model.compute.Flavor> flavors)
237             throws HeatBridgeException {
238         for (org.openstack4j.model.compute.Flavor flavor : flavors) {
239             Flavor aaiFlavor = aaiHelper.buildFlavor(flavor);
240             try {
241                 AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.FLAVOR, cloudOwner, cloudRegionId,
242                         aaiFlavor.getFlavorId());
243                 transaction.createIfNotExists(uri, Optional.of(aaiFlavor));
244             } catch (WebApplicationException e) {
245                 throw new HeatBridgeException(
246                         "Failed to update flavor to AAI: " + aaiFlavor.getFlavorId() + ". Error" + " cause: " + e, e);
247             }
248         }
249     }
250
251     @Override
252     public void buildAddVserversToAaiAction(final String genericVnfId, final String vfModuleId,
253             final List<Server> servers) {
254         servers.forEach(server -> {
255             Vserver vserver = aaiHelper.buildVserver(server.getId(), server);
256
257             // Build vserver relationships to: image, flavor, pserver, vf-module
258             vserver.setRelationshipList(
259                     aaiHelper.getVserverRelationshipList(cloudOwner, cloudRegionId, genericVnfId, vfModuleId, server));
260             transaction.createIfNotExists(AAIUriFactory.createResourceUri(AAIObjectType.VSERVER, cloudOwner,
261                     cloudRegionId, tenantId, vserver.getVserverId()), Optional.of(vserver));
262         });
263     }
264
265     @Override
266     public void buildAddVserverLInterfacesToAaiAction(final List<Resource> stackResources,
267             final List<String> oobMgtNetIds, String cloudOwner) {
268         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
269         List<String> portIds =
270                 extractStackResourceIdsByResourceType(stackResources, HeatBridgeConstants.OS_PORT_RESOURCE_TYPE);
271         for (String portId : portIds) {
272             Port port = osClient.getPortById(portId);
273             Network network = osClient.getNetworkById(port.getNetworkId());
274             LInterface lIf = new LInterface();
275             lIf.setInterfaceId(port.getId());
276             lIf.setInterfaceName(port.getName());
277             lIf.setMacaddr(port.getMacAddress());
278             lIf.setNetworkName(network.getName());
279             lIf.setIsPortMirrored(false);
280             lIf.setIsIpUnnumbered(false);
281             lIf.setInMaint(false);
282             if (oobMgtNetIds != null && oobMgtNetIds.contains(port.getNetworkId())) {
283                 lIf.setInterfaceRole(OOB_MGT_NETWORK_IDENTIFIER);
284             } else {
285                 lIf.setInterfaceRole(port.getvNicType());
286             }
287             boolean isL2Multicast = false;
288             if (port.getProfile().get("trusted") != null) {
289                 String trusted = port.getProfile().get("trusted").toString();
290                 if (Boolean.parseBoolean(trusted)) {
291                     isL2Multicast = true;
292                 }
293             }
294             lIf.setL2Multicasting(isL2Multicast);
295
296             transaction.createIfNotExists(AAIUriFactory.createResourceUri(AAIObjectType.L_INTERFACE, cloudOwner,
297                     cloudRegionId, tenantId, port.getDeviceId(), lIf.getInterfaceName()), Optional.of(lIf));
298
299             updateLInterfaceIps(port, lIf);
300             if (cloudOwner.equals(env.getProperty("mso.cloudOwner.included", ""))) {
301                 updateLInterfaceVlan(port, lIf);
302             }
303
304             updateSriovPfToPserver(port, lIf);
305         }
306     }
307
308     @Override
309     public void createPserversAndPinterfacesIfNotPresentInAai(final List<Resource> stackResources)
310             throws HeatBridgeException {
311         if (stackResources == null) {
312             return;
313         }
314         Map<String, Pserver> serverHostnames = getPserverMapping(stackResources);
315         createPServerIfNotExists(serverHostnames);
316         List<String> portIds =
317                 extractStackResourceIdsByResourceType(stackResources, HeatBridgeConstants.OS_PORT_RESOURCE_TYPE);
318         for (String portId : portIds) {
319             Port port = osClient.getPortById(portId);
320             if (port.getvNicType().equalsIgnoreCase(HeatBridgeConstants.OS_SRIOV_PORT_TYPE)) {
321                 createPServerPInterfaceIfNotExists(serverHostnames.get(port.getHostId()).getHostname(),
322                         aaiHelper.buildPInterface(port));
323             }
324         }
325     }
326
327     private Map<String, Pserver> getPserverMapping(final List<Resource> stackResources) {
328         List<Server> osServers = getAllOpenstackServers(stackResources);
329         Map<String, Pserver> pserverMap = new HashMap<>();
330         if (osServers != null) {
331             for (Server server : osServers) {
332                 Pserver pserver = aaiHelper.buildPserver(server);
333                 if (pserver != null) {
334                     pserverMap.put(server.getHost(), pserver);
335                 }
336             }
337         }
338         return pserverMap;
339     }
340
341     private void createPServerIfNotExists(Map<String, Pserver> serverHostnames) {
342         for (Pserver pserver : serverHostnames.values()) {
343             AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.PSERVER, pserver.getHostname());
344             resourcesClient.createIfNotExists(uri, Optional.of(pserver));
345         }
346     }
347
348     private void createPServerPInterfaceIfNotExists(String pserverHostname, PInterface pInterface) {
349         AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.P_INTERFACE, pserverHostname,
350                 pInterface.getInterfaceName());
351         resourcesClient.createIfNotExists(uri, Optional.of(pInterface));
352     }
353
354     private void updateLInterfaceVlan(final Port port, final LInterface lIf) {
355         Vlan vlan = new Vlan();
356         Network network = osClient.getNetworkById(port.getNetworkId());
357         if (network.getNetworkType() != null && network.getNetworkType().equals(NetworkType.VLAN)) {
358             vlan.setVlanInterface(port.getName() + network.getProviderSegID());
359             vlan.setVlanIdOuter(Long.parseLong(network.getProviderSegID()));
360             vlan.setVlanIdInner(0L);
361             vlan.setInMaint(false);
362             vlan.setIsIpUnnumbered(false);
363             vlan.setIsPrivate(false);
364
365             transaction
366                     .createIfNotExists(
367                             AAIUriFactory.createResourceUri(AAIFluentTypeBuilder.cloudInfrastructure()
368                                     .cloudRegion(cloudOwner, cloudRegionId).tenant(tenantId).vserver(port.getDeviceId())
369                                     .lInterface(lIf.getInterfaceName()).vlan(vlan.getVlanInterface())),
370                             Optional.of(vlan));
371         }
372
373         if (port.getvNicType() != null && port.getvNicType().equalsIgnoreCase(HeatBridgeConstants.OS_SRIOV_PORT_TYPE)) {
374             SriovVf sriovVf = new SriovVf();
375             sriovVf.setPciId(port.getProfile().get(HeatBridgeConstants.OS_PCI_SLOT_KEY).toString());
376             sriovVf.setNeutronNetworkId(port.getNetworkId());
377             sriovVf.setVfVlanFilter("0");
378             sriovVf.setVfVlanAntiSpoofCheck(false);
379             sriovVf.setVfMacAntiSpoofCheck(false);
380
381             transaction
382                     .createIfNotExists(
383                             AAIUriFactory.createResourceUri(AAIFluentTypeBuilder.cloudInfrastructure()
384                                     .cloudRegion(cloudOwner, cloudRegionId).tenant(tenantId).vserver(port.getDeviceId())
385                                     .lInterface(lIf.getInterfaceName()).sriovVf(sriovVf.getPciId())),
386                             Optional.of(sriovVf));
387         }
388     }
389
390     /**
391      * Needs to be corrected according to the specification that is in draft If pserver/p-interface does not have a
392      * SRIOV-PF object matching the PCI-ID of the Openstack port object, then create it in AAI. Openstack SRIOV Port
393      * object has pci-id (to match sriov-pf on pserver/p-interface), physical-network ID (that matches the p-interface
394      * name).
395      *
396      * @param port Openstack port object
397      * @param lIf AAI l-interface object
398      */
399     private void updateSriovPfToPserver(final Port port, final LInterface lIf) {
400         if (port.getvNicType().equalsIgnoreCase(HeatBridgeConstants.OS_SRIOV_PORT_TYPE)) {
401             if (port.getProfile() == null || Strings
402                     .isNullOrEmpty(port.getProfile().get(HeatBridgeConstants.OS_PHYSICAL_NETWORK_KEY).toString())) {
403                 logger.debug("The SRIOV port:" + port.getName() + " is missing physical-network-id, cannot update "
404                         + "sriov-pf object for host pserver: " + port.getHostId());
405                 return;
406             }
407             Optional<String> matchingPifName = HeatBridgeUtils.getMatchingPserverPifName(
408                     port.getProfile().get(HeatBridgeConstants.OS_PHYSICAL_NETWORK_KEY).toString());
409             if (matchingPifName.isPresent()) {
410                 // Update l-interface description
411                 String pserverHostName = port.getHostId();
412                 lIf.setInterfaceDescription(
413                         "Attached to SR-IOV port: " + pserverHostName + "::" + matchingPifName.get());
414                 try {
415                     Optional<PInterface> matchingPIf = resourcesClient.get(PInterface.class, AAIUriFactory
416                             .createResourceUri(AAIObjectType.P_INTERFACE, pserverHostName, matchingPifName.get())
417                             .depth(Depth.ONE));
418                     if (matchingPIf.isPresent()) {
419                         SriovPfs pIfSriovPfs = matchingPIf.get().getSriovPfs();
420                         if (pIfSriovPfs == null) {
421                             pIfSriovPfs = new SriovPfs();
422                         }
423                         // Extract PCI-ID from OS port object
424                         String pfPciId = port.getProfile().get(HeatBridgeConstants.OS_PCI_SLOT_KEY).toString();
425
426                         List<SriovPf> existingSriovPfs = pIfSriovPfs.getSriovPf();
427                         if (CollectionUtils.isEmpty(existingSriovPfs) || existingSriovPfs.stream()
428                                 .noneMatch(existingSriovPf -> existingSriovPf.getPfPciId().equals(pfPciId))) {
429                             // Add sriov-pf object with PCI-ID to AAI
430                             SriovPf sriovPf = new SriovPf();
431                             sriovPf.setPfPciId(pfPciId);
432                             logger.debug("Queuing AAI command to update sriov-pf object to pserver: " + pserverHostName
433                                     + "/" + matchingPifName.get());
434
435                             AAIResourceUri sriovPfUri = AAIUriFactory.createResourceUri(AAIObjectType.SRIOV_PF,
436                                     pserverHostName, matchingPifName.get(), sriovPf.getPfPciId());
437
438                             if (!resourcesClient.exists(sriovPfUri)) {
439                                 transaction.create(sriovPfUri, sriovPf);
440
441                                 AAIResourceUri sriovVfUri = AAIUriFactory.createResourceUri(AAIObjectType.SRIOV_VF,
442                                         cloudOwner, cloudRegionId, tenantId, port.getDeviceId(), lIf.getInterfaceName(),
443                                         port.getProfile().get(HeatBridgeConstants.OS_PCI_SLOT_KEY).toString());
444
445                                 transaction.connect(sriovPfUri, sriovVfUri);
446                             }
447                         }
448                     }
449                 } catch (WebApplicationException e) {
450                     // Silently log that we failed to update the Pserver p-interface with PCI-ID
451                     logger.error(LoggingAnchor.NINE, MessageEnum.GENERAL_EXCEPTION, pserverHostName,
452                             matchingPifName.get(), cloudOwner, tenantId, "OpenStack", "Heatbridge",
453                             ErrorCode.DataError.getValue(), "Exception - Failed to add sriov-pf object to pserver", e);
454                 }
455             }
456         }
457     }
458
459     private void updateLInterfaceIps(final Port port, final LInterface lIf) {
460         for (IP ip : port.getFixedIps()) {
461             String ipAddress = ip.getIpAddress();
462             if (InetAddressValidator.getInstance().isValidInet4Address(ipAddress)) {
463                 Subnet subnet = osClient.getSubnetById(ip.getSubnetId());
464                 IPAddressString cidr = new IPAddressString(subnet.getCidr());
465                 L3InterfaceIpv4AddressList lInterfaceIp = new L3InterfaceIpv4AddressList();
466                 lInterfaceIp.setL3InterfaceIpv4Address(ipAddress);
467                 lInterfaceIp.setNeutronNetworkId(port.getNetworkId());
468                 lInterfaceIp.setNeutronSubnetId(ip.getSubnetId());
469                 lInterfaceIp.setL3InterfaceIpv4PrefixLength(Long.parseLong(cidr.getNetworkPrefixLength().toString()));
470
471                 transaction.createIfNotExists(
472                         AAIUriFactory.createResourceUri(AAIFluentTypeBuilder.cloudInfrastructure()
473                                 .cloudRegion(cloudOwner, cloudRegionId).tenant(tenantId).vserver(port.getDeviceId())
474                                 .lInterface(lIf.getInterfaceName()).l3InterfaceIpv4AddressList(ipAddress)),
475                         Optional.of(lInterfaceIp));
476             }
477         }
478     }
479
480     @Override
481     public void submitToAai(boolean dryrun) throws HeatBridgeException {
482         try {
483             transaction.execute(dryrun);
484         } catch (BulkProcessFailed e) {
485             String msg = "Failed to commit transaction";
486             logger.debug(msg + " with error: " + e);
487             throw new HeatBridgeException(msg, e);
488         }
489     }
490
491     @Override
492     public void deleteVfModuleData(@Nonnull final String vnfId, @Nonnull final String vfModuleId)
493             throws HeatBridgeException {
494         Objects.requireNonNull(vnfId, "Null vnf-id!");
495         Objects.requireNonNull(vfModuleId, "Null vf-module-id!");
496         try {
497             Optional<VfModule> vfModule = resourcesClient
498                     .get(AAIUriFactory.createResourceUri(AAIObjectType.VF_MODULE, vnfId, vfModuleId).depth(Depth.ONE),
499                             NotFoundException.class)
500                     .asBean(VfModule.class);
501
502             AAIResultWrapper resultWrapper = new AAIResultWrapper(vfModule.get());
503             Optional<Relationships> relationships = resultWrapper.getRelationships();
504             logger.debug("VfModule contains relationships in AAI: {}", relationships.isPresent());
505             if (relationships.isPresent()) {
506                 List<AAIResourceUri> vserverUris = relationships.get().getRelatedUris(AAIObjectType.VSERVER);
507                 logger.debug("VServer contains {} relationships in AAI", vserverUris.size());
508                 createTransactionToDeleteSriovPfFromPserver(vserverUris);
509
510                 if (!vserverUris.isEmpty()) {
511                     for (AAIResourceUri vserverUri : vserverUris) {
512                         if (env.getProperty("heatBridgeDryrun", Boolean.class, true)) {
513                             logger.debug("Would delete Vserver: {}", vserverUri.build().toString());
514                         } else {
515                             resourcesClient.delete(vserverUri);
516                         }
517                     }
518                 }
519             }
520
521         } catch (NotFoundException e) {
522             String msg = "Failed to commit delete heatbridge data transaction";
523             logger.debug(msg + " with error: " + e);
524             throw new HeatBridgeException(msg, e);
525         } catch (Exception e) {
526             String msg = "Failed to commit delete heatbridge data transaction";
527             logger.debug(msg + " with error: " + e);
528             throw new HeatBridgeException(msg, e);
529         }
530     }
531
532     private void createTransactionToDeleteSriovPfFromPserver(List<AAIResourceUri> vserverUris) {
533         Map<String, List<String>> pserverToPciIdMap = getPserverToPciIdMap(vserverUris);
534         for (Map.Entry<String, List<String>> entry : pserverToPciIdMap.entrySet()) {
535             String pserverName = entry.getKey();
536             List<String> pciIds = entry.getValue();
537             Optional<Pserver> pserver = resourcesClient.get(Pserver.class,
538                     AAIUriFactory.createResourceUri(AAIObjectType.PSERVER, pserverName).depth(Depth.TWO));
539             if (pserver.isPresent()) {
540                 // For each pserver/p-interface match sriov-vfs by pic-id and delete them.
541                 pserver.get().getPInterfaces().getPInterface().stream().filter(
542                         pIf -> pIf.getSriovPfs() != null && CollectionUtils.isNotEmpty(pIf.getSriovPfs().getSriovPf()))
543                         .forEach(pIf -> pIf.getSriovPfs().getSriovPf().forEach(sriovPf -> {
544                             if (pciIds.contains(sriovPf.getPfPciId())) {
545                                 logger.debug("creating transaction to delete SR-IOV PF: " + pIf.getInterfaceName()
546                                         + " from PServer: " + pserverName);
547                                 if (env.getProperty("heatBridgeDryrun", Boolean.class, true)) {
548                                     logger.debug("Would delete Sriov Pf: {}",
549                                             AAIUriFactory
550                                                     .createResourceUri(AAIObjectType.SRIOV_PF, pserverName,
551                                                             pIf.getInterfaceName(), sriovPf.getPfPciId())
552                                                     .build().toString());
553                                 } else {
554                                     resourcesClient.delete(AAIUriFactory.createResourceUri(AAIObjectType.SRIOV_PF,
555                                             pserverName, pIf.getInterfaceName(), sriovPf.getPfPciId()));
556                                 }
557                             }
558                         }));
559             }
560         }
561     }
562
563     private Map<String, List<String>> getPserverToPciIdMap(List<AAIResourceUri> vserverUris) {
564         Map<String, List<String>> pserverToPciIdMap = new HashMap<>();
565         for (AAIResourceUri vserverUri : vserverUris) {
566             AAIResultWrapper vserverWrapper = resourcesClient.get(vserverUri.depth(Depth.TWO));
567             Optional<Relationships> vserverRelationships = vserverWrapper.getRelationships();
568             if (vserverRelationships.isPresent()
569                     && CollectionUtils.isNotEmpty(vserverRelationships.get().getRelatedLinks(AAIObjectType.PSERVER))) {
570                 Vserver vserver = vserverWrapper.asBean(Vserver.class).get();
571                 List<String> pciIds = HeatBridgeUtils.extractPciIdsFromVServer(vserver);
572                 if (CollectionUtils.isNotEmpty(pciIds)) {
573                     List<String> matchingPservers = vserverRelationships.get().getRelatedLinks(AAIObjectType.PSERVER);
574                     if (matchingPservers != null && matchingPservers.size() == 1) {
575                         pserverToPciIdMap.put(matchingPservers.get(0), pciIds);
576                     }
577                 }
578             }
579         }
580         return pserverToPciIdMap;
581     }
582
583     private <T> Predicate<T> distinctByProperty(Function<? super T, Object> keyExtractor) {
584         Map<Object, Boolean> map = new ConcurrentHashMap<>();
585         return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
586     }
587 }