Replaced all tabs with spaces in java and pom.xml
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / heatbridge / HeatBridgeImpl.java
1 /*
2  * Copyright (C) 2018 Bell Canada. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5  * the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11  * specific language governing permissions and limitations under the License.
12  */
13 package org.onap.so.heatbridge;
14
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Objects;
18 import java.util.Optional;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.function.Function;
21 import java.util.function.Predicate;
22 import java.util.stream.Collectors;
23 import javax.annotation.Nonnull;
24 import javax.ws.rs.WebApplicationException;
25 import org.apache.commons.collections.CollectionUtils;
26 import org.apache.commons.validator.routines.InetAddressValidator;
27 import org.onap.aai.domain.yang.Flavor;
28 import org.onap.aai.domain.yang.Image;
29 import org.onap.aai.domain.yang.L3InterfaceIpv4AddressList;
30 import org.onap.aai.domain.yang.LInterface;
31 import org.onap.aai.domain.yang.PInterface;
32 import org.onap.aai.domain.yang.SriovPf;
33 import org.onap.aai.domain.yang.SriovPfs;
34 import org.onap.aai.domain.yang.SriovVf;
35 import org.onap.aai.domain.yang.SriovVfs;
36 import org.onap.aai.domain.yang.Vlan;
37 import org.onap.aai.domain.yang.Vlans;
38 import org.onap.aai.domain.yang.Vserver;
39 import org.onap.so.client.aai.AAIObjectType;
40 import org.onap.so.client.aai.AAIResourcesClient;
41 import org.onap.so.client.aai.AAISingleTransactionClient;
42 import org.onap.so.client.aai.entities.uri.AAIResourceUri;
43 import org.onap.so.client.aai.entities.uri.AAIUriFactory;
44 import org.onap.so.client.graphinventory.entities.uri.Depth;
45 import org.onap.so.client.graphinventory.exceptions.BulkProcessFailed;
46 import org.onap.so.db.catalog.beans.CloudIdentity;
47 import org.onap.so.heatbridge.constants.HeatBridgeConstants;
48 import org.onap.so.heatbridge.factory.MsoCloudClientFactoryImpl;
49 import org.onap.so.heatbridge.helpers.AaiHelper;
50 import org.onap.so.heatbridge.openstack.api.OpenstackClient;
51 import org.onap.so.heatbridge.openstack.factory.OpenstackClientFactoryImpl;
52 import org.onap.so.heatbridge.utils.HeatBridgeUtils;
53 import org.onap.so.logger.MessageEnum;
54 import org.onap.so.logger.ErrorCode;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57 import org.openstack4j.model.compute.Server;
58 import org.openstack4j.model.heat.Resource;
59 import org.openstack4j.model.network.IP;
60 import org.openstack4j.model.network.Network;
61 import org.openstack4j.model.network.NetworkType;
62 import org.openstack4j.model.network.Port;
63 import com.google.common.base.Preconditions;
64 import com.google.common.base.Strings;
65 import com.google.common.collect.ImmutableMap;
66
67 /**
68  * This class provides an implementation of {@link HeatBridgeApi}
69  */
70 public class HeatBridgeImpl implements HeatBridgeApi {
71
72     private static final Logger logger = LoggerFactory.getLogger(HeatBridgeImpl.class);
73     private static final String ERR_MSG_NULL_OS_CLIENT =
74             "Initialization error: Null openstack client. Authenticate with Keystone first.";
75     private static final String OOB_MGT_NETWORK_IDENTIFIER = "Management";
76     private OpenstackClient osClient;
77     private AAIResourcesClient resourcesClient;
78     private AAISingleTransactionClient transaction;
79     private String cloudOwner;
80     private String cloudRegionId;
81     private String tenantId;
82     private AaiHelper aaiHelper = new AaiHelper();
83     private CloudIdentity cloudIdentity;
84
85
86     public HeatBridgeImpl(AAIResourcesClient resourcesClient, final CloudIdentity cloudIdentity,
87             @Nonnull final String cloudOwner, @Nonnull final String cloudRegionId, @Nonnull final String tenantId) {
88         Objects.requireNonNull(cloudOwner, "Null cloud-owner value!");
89         Objects.requireNonNull(cloudRegionId, "Null cloud-region identifier!");
90         Objects.requireNonNull(tenantId, "Null tenant identifier!");
91         Objects.requireNonNull(tenantId, "Null AAI actions list!");
92
93         this.cloudIdentity = cloudIdentity;
94         this.cloudOwner = cloudOwner;
95         this.cloudRegionId = cloudRegionId;
96         this.tenantId = tenantId;
97         this.resourcesClient = resourcesClient;
98         this.transaction = resourcesClient.beginSingleTransaction();
99     }
100
101     @Override
102     public OpenstackClient authenticate() throws HeatBridgeException {
103         this.osClient = new MsoCloudClientFactoryImpl(new OpenstackClientFactoryImpl()).getOpenstackClient(
104                 cloudIdentity.getIdentityUrl(), cloudIdentity.getMsoId(), cloudIdentity.getMsoPass(), cloudRegionId,
105                 tenantId);
106         logger.debug("Successfully authenticated with keystone for tenant: " + tenantId + " and cloud " + "region: "
107                 + cloudRegionId);
108         return osClient;
109     }
110
111     @Override
112     public List<Resource> queryNestedHeatStackResources(final String heatStackId) {
113         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
114         Preconditions.checkState(!Strings.isNullOrEmpty(heatStackId), "Invalid heatStackId!");
115         List<Resource> stackBasedResources =
116                 osClient.getStackBasedResources(heatStackId, HeatBridgeConstants.OS_DEFAULT_HEAT_NESTING);
117         logger.debug(stackBasedResources.size() + " heat stack resources are extracted for stack: " + heatStackId);
118         return stackBasedResources;
119     }
120
121     @Override
122     public List<String> extractStackResourceIdsByResourceType(final List<Resource> stackResources,
123             final String resourceType) {
124         return stackResources.stream().filter(stackResource -> stackResource.getType().equals(resourceType))
125                 .map(Resource::getPhysicalResourceId).collect(Collectors.toList());
126     }
127
128     @Override
129     public List<String> extractNetworkIds(final List<String> networkNameList) {
130         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
131         return networkNameList.stream()
132                 .map(netName -> osClient
133                         .listNetworksByFilter(ImmutableMap.of(HeatBridgeConstants.OS_NAME_KEY, netName)))
134                 .filter(nets -> nets != null && nets.size() == 1) // extract network-id only if network-name is unique
135                 .map(nets -> nets.get(0).getId()).collect(Collectors.toList());
136     }
137
138     @Override
139     public List<Server> getAllOpenstackServers(final List<Resource> stackResources) {
140         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
141
142         // Filter Openstack Compute resources
143         List<String> serverIds =
144                 extractStackResourceIdsByResourceType(stackResources, HeatBridgeConstants.OS_SERVER_RESOURCE_TYPE);
145         return serverIds.stream().map(serverId -> osClient.getServerById(serverId)).collect(Collectors.toList());
146     }
147
148     @Override
149     public List<org.openstack4j.model.compute.Image> extractOpenstackImagesFromServers(final List<Server> servers) {
150         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
151         return servers.stream().map(Server::getImage)
152                 .filter(distinctByProperty(org.openstack4j.model.compute.Image::getId)).collect(Collectors.toList());
153     }
154
155     @Override
156     public List<org.openstack4j.model.compute.Flavor> extractOpenstackFlavorsFromServers(final List<Server> servers) {
157         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
158         return servers.stream().map(Server::getFlavor)
159                 .filter(distinctByProperty(org.openstack4j.model.compute.Flavor::getId)).collect(Collectors.toList());
160     }
161
162     @Override
163     public void buildAddImagesToAaiAction(final List<org.openstack4j.model.compute.Image> images)
164             throws HeatBridgeException {
165         for (org.openstack4j.model.compute.Image image : images) {
166             Image aaiImage = aaiHelper.buildImage(image);
167             try {
168                 AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.IMAGE, cloudOwner, cloudRegionId,
169                         aaiImage.getImageId());
170                 if (!resourcesClient.exists(uri)) {
171                     transaction.create(uri, aaiImage);
172                     logger.debug("Queuing AAI command to add image: " + aaiImage.getImageId());
173                 } else {
174                     logger.debug("Nothing to add since image: " + aaiImage.getImageId() + "already exists in AAI.");
175                 }
176             } catch (WebApplicationException e) {
177                 throw new HeatBridgeException(
178                         "Failed to update image to AAI: " + aaiImage.getImageId() + ". Error" + " cause: " + e, e);
179             }
180         }
181     }
182
183     @Override
184     public void buildAddFlavorsToAaiAction(final List<org.openstack4j.model.compute.Flavor> flavors)
185             throws HeatBridgeException {
186         for (org.openstack4j.model.compute.Flavor flavor : flavors) {
187             Flavor aaiFlavor = aaiHelper.buildFlavor(flavor);
188             try {
189                 AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.FLAVOR, cloudOwner, cloudRegionId,
190                         aaiFlavor.getFlavorId());
191                 if (!resourcesClient.exists(uri)) {
192                     transaction.create(uri, aaiFlavor);
193                     logger.debug("Queuing AAI command to add flavor: " + aaiFlavor.getFlavorId());
194                 } else {
195                     logger.debug("Nothing to add since flavor: " + aaiFlavor.getFlavorId() + "already exists in AAI.");
196                 }
197             } catch (WebApplicationException e) {
198                 throw new HeatBridgeException(
199                         "Failed to update flavor to AAI: " + aaiFlavor.getFlavorId() + ". Error" + " cause: " + e, e);
200             }
201         }
202     }
203
204     @Override
205     public void buildAddVserversToAaiAction(final String genericVnfId, final String vfModuleId,
206             final List<Server> servers) {
207         servers.forEach(server -> {
208             Vserver vserver = aaiHelper.buildVserver(server.getId(), server);
209
210             // Build vserver relationships to: image, flavor, pserver, vf-module
211             vserver.setRelationshipList(
212                     aaiHelper.getVserverRelationshipList(cloudOwner, cloudRegionId, genericVnfId, vfModuleId, server));
213             transaction.create(AAIUriFactory.createResourceUri(AAIObjectType.VSERVER, cloudOwner, cloudRegionId,
214                     tenantId, vserver.getVserverId()), vserver);
215         });
216     }
217
218     @Override
219     public void buildAddVserverLInterfacesToAaiAction(final List<Resource> stackResources,
220             final List<String> oobMgtNetIds) {
221         Objects.requireNonNull(osClient, ERR_MSG_NULL_OS_CLIENT);
222         List<String> portIds =
223                 extractStackResourceIdsByResourceType(stackResources, HeatBridgeConstants.OS_PORT_RESOURCE_TYPE);
224         for (String portId : portIds) {
225             Port port = osClient.getPortById(portId);
226             LInterface lIf = new LInterface();
227             lIf.setInterfaceId(port.getId());
228             lIf.setInterfaceName(port.getName());
229             lIf.setMacaddr(port.getMacAddress());
230             if (oobMgtNetIds != null && oobMgtNetIds.contains(port.getNetworkId())) {
231                 lIf.setInterfaceRole(OOB_MGT_NETWORK_IDENTIFIER);
232             } else {
233                 lIf.setInterfaceRole(port.getvNicType());
234             }
235
236             updateLInterfaceIps(port, lIf);
237             updateLInterfaceVlan(port, lIf);
238
239             // Update l-interface to the vserver
240             transaction.create(AAIUriFactory.createResourceUri(AAIObjectType.L_INTERFACE, cloudOwner, cloudRegionId,
241                     tenantId, port.getDeviceId(), lIf.getInterfaceName()), lIf);
242         }
243     }
244
245     private void updateLInterfaceVlan(final Port port, final LInterface lIf) {
246         Vlan vlan = new Vlan();
247         Network network = osClient.getNetworkById(port.getNetworkId());
248         lIf.setNetworkName(network.getName());
249         if (network.getNetworkType().equals(NetworkType.VLAN)) {
250             vlan.setVlanInterface(network.getProviderSegID());
251             Vlans vlans = new Vlans();
252             List<Vlan> vlanList = vlans.getVlan();
253             vlanList.add(vlan);
254             lIf.setVlans(vlans);
255         }
256         // Build sriov-vf to the l-interface
257         if (port.getvNicType().equalsIgnoreCase(HeatBridgeConstants.OS_SRIOV_PORT_TYPE)) {
258             SriovVfs sriovVfs = new SriovVfs();
259             // JAXB does not generate setters for list, however getter ensures its creation.
260             // Thus, all list manipulations must be made on live list.
261             List<SriovVf> sriovVfList = sriovVfs.getSriovVf();
262             SriovVf sriovVf = new SriovVf();
263             sriovVf.setPciId(port.getProfile().get(HeatBridgeConstants.OS_PCI_SLOT_KEY).toString());
264             sriovVf.setNeutronNetworkId(port.getNetworkId());
265             if (port.getVifDetails() != null) {
266                 sriovVf.setVfVlanFilter((String) port.getVifDetails().get(HeatBridgeConstants.OS_VLAN_NETWORK_KEY));
267             }
268             sriovVfList.add(sriovVf);
269
270             lIf.setSriovVfs(sriovVfs);
271
272             // For the given port create sriov-pf for host pserver/p-interface if absent
273             updateSriovPfToPserver(port, lIf);
274         }
275     }
276
277     /**
278      * Needs to be corrected according to the specification that is in draft If pserver/p-interface does not have a
279      * SRIOV-PF object matching the PCI-ID of the Openstack port object, then create it in AAI. Openstack SRIOV Port
280      * object has pci-id (to match sriov-pf on pserver/p-interface), physical-network ID (that matches the p-interface
281      * name).
282      *
283      * @param port Openstack port object
284      * @param lIf AAI l-interface object
285      */
286     private void updateSriovPfToPserver(final Port port, final LInterface lIf) {
287         if (port.getProfile() == null || Strings
288                 .isNullOrEmpty(port.getProfile().get(HeatBridgeConstants.OS_PHYSICAL_NETWORK_KEY).toString())) {
289             logger.debug("The SRIOV port:" + port.getName() + " is missing physical-network-id, cannot update "
290                     + "sriov-pf object for host pserver: " + port.getHostId());
291             return;
292         }
293         Optional<String> matchingPifName = HeatBridgeUtils.getMatchingPserverPifName(
294                 port.getProfile().get(HeatBridgeConstants.OS_PHYSICAL_NETWORK_KEY).toString());
295         if (matchingPifName.isPresent()) {
296             // Update l-interface description
297             String pserverHostName = port.getHostId();
298             lIf.setInterfaceDescription("Attached to SR-IOV port: " + pserverHostName + "::" + matchingPifName.get());
299             try {
300                 Optional<PInterface> matchingPIf = resourcesClient.get(PInterface.class,
301                         AAIUriFactory
302                                 .createResourceUri(AAIObjectType.P_INTERFACE, pserverHostName, matchingPifName.get())
303                                 .depth(Depth.ONE));
304                 if (matchingPIf.isPresent()) {
305                     SriovPfs pIfSriovPfs = matchingPIf.get().getSriovPfs();
306                     if (pIfSriovPfs == null) {
307                         pIfSriovPfs = new SriovPfs();
308                     }
309                     // Extract PCI-ID from OS port object
310                     String pfPciId = port.getProfile().get(HeatBridgeConstants.OS_PCI_SLOT_KEY).toString();
311
312                     List<SriovPf> existingSriovPfs = pIfSriovPfs.getSriovPf();
313                     if (CollectionUtils.isEmpty(existingSriovPfs) || existingSriovPfs.stream()
314                             .noneMatch(existingSriovPf -> existingSriovPf.getPfPciId().equals(pfPciId))) {
315                         // Add sriov-pf object with PCI-ID to AAI
316                         SriovPf sriovPf = new SriovPf();
317                         sriovPf.setPfPciId(pfPciId);
318                         logger.debug("Queuing AAI command to update sriov-pf object to pserver: " + pserverHostName
319                                 + "/" + matchingPifName.get());
320                         transaction.create(AAIUriFactory.createResourceUri(AAIObjectType.SRIOV_PF, pserverHostName,
321                                 matchingPifName.get(), sriovPf.getPfPciId()), sriovPf);
322                     }
323                 }
324             } catch (WebApplicationException e) {
325                 // Silently log that we failed to update the Pserver p-interface with PCI-ID
326                 logger.error("{} {} {} {} {} {} {} {} {}", MessageEnum.GENERAL_EXCEPTION, pserverHostName,
327                         matchingPifName.get(), cloudOwner, tenantId, "OpenStack", "Heatbridge",
328                         ErrorCode.DataError.getValue(), "Exception - Failed to add sriov-pf object to pserver", e);
329             }
330         }
331     }
332
333     private void updateLInterfaceIps(final Port port, final LInterface lIf) {
334         List<L3InterfaceIpv4AddressList> lInterfaceIps = lIf.getL3InterfaceIpv4AddressList();
335         for (IP ip : port.getFixedIps()) {
336             String ipAddress = ip.getIpAddress();
337             if (InetAddressValidator.getInstance().isValidInet4Address(ipAddress)) {
338                 L3InterfaceIpv4AddressList lInterfaceIp = new L3InterfaceIpv4AddressList();
339                 lInterfaceIp.setL3InterfaceIpv4Address(ipAddress);
340                 lInterfaceIp.setNeutronNetworkId(port.getNetworkId());
341                 lInterfaceIp.setNeutronSubnetId(ip.getSubnetId());
342                 lInterfaceIp.setL3InterfaceIpv4PrefixLength(32L);
343                 lInterfaceIps.add(lInterfaceIp);
344             }
345         }
346     }
347
348     @Override
349     public void submitToAai() throws HeatBridgeException {
350         try {
351             transaction.execute();
352         } catch (BulkProcessFailed e) {
353             String msg = "Failed to commit transaction";
354             logger.debug(msg + " with error: " + e);
355             throw new HeatBridgeException(msg, e);
356         }
357     }
358
359     private <T> Predicate<T> distinctByProperty(Function<? super T, Object> keyExtractor) {
360         Map<Object, Boolean> map = new ConcurrentHashMap<>();
361         return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
362     }
363 }