5c0d2d3019606835c0bf756edd94a007018f3137
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / adapters / audit / HeatStackAudit.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 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 package org.onap.so.adapters.audit;
22
23 import java.net.URI;
24 import java.util.Arrays;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33 import org.onap.aai.domain.yang.LInterface;
34 import org.onap.aai.domain.yang.LInterfaces;
35 import org.onap.aai.domain.yang.Vlan;
36 import org.onap.aai.domain.yang.Vlans;
37 import org.onap.aai.domain.yang.Vserver;
38 import org.onap.so.openstack.utils.MsoHeatUtils;
39 import org.onap.so.openstack.utils.MsoNeutronUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42 import org.springframework.beans.factory.annotation.Autowired;
43 import org.springframework.stereotype.Component;
44 import com.woorea.openstack.heat.model.Link;
45 import com.woorea.openstack.heat.model.Resource;
46 import com.woorea.openstack.heat.model.Resources;
47 import com.woorea.openstack.heat.model.Stack;
48 import com.woorea.openstack.quantum.model.Port;
49
50 @Component
51 public class HeatStackAudit {
52
53     private static final String RESOURCES = "/resources";
54
55     protected static final Logger logger = LoggerFactory.getLogger(HeatStackAudit.class);
56
57     @Autowired
58     protected MsoHeatUtils heat;
59
60     @Autowired
61     protected MsoNeutronUtils neutron;
62
63     @Autowired
64     protected AuditVServer auditVservers;
65
66     public Optional<AAIObjectAuditList> auditHeatStack(String cloudRegion, String cloudOwner, String tenantId,
67             String heatStackName) {
68         try {
69             logger.debug("Fetching Top Level Stack Information");
70             Resources resources = heat.queryStackResources(cloudRegion, tenantId, heatStackName, 3);
71             List<Resource> novaResources = resources.getList().stream()
72                     .filter(p -> "OS::Nova::Server".equals(p.getType())).collect(Collectors.toList());
73             List<Resource> resourceGroups = resources.getList().stream()
74                     .filter(p -> "OS::Heat::ResourceGroup".equals(p.getType()) && p.getName().contains("subinterfaces"))
75                     .collect(Collectors.toList());
76             List<Optional<Port>> neutronPortDetails = retrieveNeutronPortDetails(resources, cloudRegion, tenantId);
77             if (novaResources.isEmpty())
78                 return Optional.of(new AAIObjectAuditList());
79             else {
80                 Set<Vserver> vserversToAudit = createVserverSet(resources, novaResources, neutronPortDetails);
81                 Set<Vserver> vserversWithSubInterfaces =
82                         processSubInterfaces(cloudRegion, tenantId, resourceGroups, vserversToAudit);
83                 return auditVservers.auditVservers(vserversWithSubInterfaces, tenantId, cloudOwner, cloudRegion);
84             }
85         } catch (Exception e) {
86             logger.error("Error during auditing stack resources", e);
87             return Optional.empty();
88         }
89     }
90
91     protected Set<Vserver> processSubInterfaces(String cloudRegion, String tenantId, List<Resource> resourceGroups,
92             Set<Vserver> vServersToAudit) throws Exception {
93         for (Resource resourceGroup : resourceGroups) {
94             processResourceGroups(cloudRegion, tenantId, vServersToAudit, resourceGroup);
95         }
96         return vServersToAudit;
97     }
98
99     protected void processResourceGroups(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface,
100             Resource resourceGroup) throws Exception {
101         Optional<Link> stackLink =
102                 resourceGroup.getLinks().stream().filter(link -> "nested".equals(link.getRel())).findAny();
103         if (stackLink.isPresent()) {
104             try {
105                 Optional<String> path = extractResourcePathFromHref(stackLink.get().getHref());
106                 if (path.isPresent()) {
107                     logger.debug("Fetching nested Resource Stack Information");
108                     Resources nestedResourceGroupResources =
109                             heat.executeHeatClientRequest(path.get(), cloudRegion, tenantId, Resources.class);
110                     processNestedResourceGroup(cloudRegion, tenantId, vServersWithLInterface,
111                             nestedResourceGroupResources);
112                 } else
113                     throw new Exception("Error finding Path from Self Link");
114             } catch (Exception e) {
115                 logger.error("Error Parsing Link to obtain Path", e);
116                 throw new Exception("Error finding Path from Self Link");
117             }
118         }
119     }
120
121     protected void processNestedResourceGroup(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface,
122             Resources nestedResourceGroupResources) throws Exception {
123         for (Resource resourceGroupNested : nestedResourceGroupResources) {
124             Optional<Link> subInterfaceStackLink =
125                     resourceGroupNested.getLinks().stream().filter(link -> "nested".equals(link.getRel())).findAny();
126             if (subInterfaceStackLink.isPresent()) {
127                 addSubInterface(cloudRegion, tenantId, vServersWithLInterface, subInterfaceStackLink.get());
128             }
129         }
130     }
131
132     protected void addSubInterface(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface,
133             Link subInterfaceStackLink) throws Exception {
134         Optional<String> resourcePath = extractResourcePathFromHref(subInterfaceStackLink.getHref());
135         Optional<String> stackPath = extractStackPathFromHref(subInterfaceStackLink.getHref());
136         if (resourcePath.isPresent() && stackPath.isPresent()) {
137             logger.debug("Fetching nested Sub-Interface Stack Information");
138             Stack subinterfaceStack =
139                     heat.executeHeatClientRequest(stackPath.get(), cloudRegion, tenantId, Stack.class);
140             Resources subinterfaceResources =
141                     heat.executeHeatClientRequest(resourcePath.get(), cloudRegion, tenantId, Resources.class);
142             if (subinterfaceStack != null) {
143                 addSubInterfaceToVserver(vServersWithLInterface, subinterfaceStack, subinterfaceResources);
144             }
145         } else
146             throw new Exception("Error finding Path from Self Link");
147
148     }
149
150     protected void addSubInterfaceToVserver(Set<Vserver> vServersWithLInterface, Stack subinterfaceStack,
151             Resources subinterfaceResources) throws Exception {
152         String parentNeutronPortId = (String) subinterfaceStack.getParameters().get("port_interface");
153         logger.debug("Parent neutron Port: {} on SubInterface: {}", parentNeutronPortId, subinterfaceStack.getId());
154         for (Vserver auditVserver : vServersWithLInterface)
155             for (LInterface lInterface : auditVserver.getLInterfaces().getLInterface())
156
157                 if (parentNeutronPortId.equals(lInterface.getInterfaceId())) {
158                     logger.debug("Found Parent Port on VServer: {} on Port: {}", auditVserver.getVserverId(),
159                             lInterface.getInterfaceId());
160                     Resource contrailVm = subinterfaceResources.getList().stream()
161                             .filter(resource -> "OS::ContrailV2::VirtualMachineInterface".equals(resource.getType()))
162                             .findAny().orElse(null);
163                     if (contrailVm == null) {
164                         throw new Exception("Cannnot find Contrail Virtual Machine Interface on Stack: "
165                                 + subinterfaceStack.getId());
166                     }
167                     LInterface subInterface = new LInterface();
168                     subInterface.setInterfaceId(contrailVm.getPhysicalResourceId());
169                     subInterface.setIsPortMirrored(false);
170                     subInterface.setInMaint(false);
171                     subInterface.setIsIpUnnumbered(false);
172                     String macAddr = (String) subinterfaceStack.getParameters().get("mac_address");
173                     subInterface.setMacaddr(macAddr);
174
175                     String namePrefix = (String) subinterfaceStack.getParameters().get("subinterface_name_prefix");
176                     Integer vlanIndex = Integer.parseInt((String) subinterfaceStack.getParameters().get("counter"));
177                     String vlanTagList = (String) subinterfaceStack.getParameters().get("vlan_tag");
178                     List<String> subInterfaceVlanTagList = Arrays.asList(vlanTagList.split(","));
179                     subInterface.setInterfaceName(namePrefix + "_" + subInterfaceVlanTagList.get(vlanIndex));
180                     subInterface.setVlans(new Vlans());
181                     Vlan vlan = new Vlan();
182                     vlan.setInMaint(false);
183                     vlan.setIsIpUnnumbered(false);
184                     vlan.setVlanIdInner(Long.parseLong(subInterfaceVlanTagList.get(vlanIndex)));
185                     vlan.setVlanInterface(namePrefix + "_" + subInterfaceVlanTagList.get(vlanIndex));
186                     subInterface.getVlans().getVlan().add(vlan);
187                     if (lInterface.getLInterfaces() == null)
188                         lInterface.setLInterfaces(new LInterfaces());
189
190                     lInterface.getLInterfaces().getLInterface().add(subInterface);
191                 } else
192                     logger.debug("Did Not Find Parent Port on VServer: {} Parent Port: SubInterface: {}",
193                             auditVserver.getVserverId(), lInterface.getInterfaceId(), subinterfaceStack.getId());
194     }
195
196     protected Set<Vserver> createVserverSet(Resources resources, List<Resource> novaResources,
197             List<Optional<Port>> neutronPortDetails) {
198         Set<Vserver> vserversToAudit = new HashSet<>();
199         for (Resource novaResource : novaResources) {
200             Vserver auditVserver = new Vserver();
201             auditVserver.setLInterfaces(new LInterfaces());
202             auditVserver.setVserverId(novaResource.getPhysicalResourceId());
203             Stream<Port> filteredNeutronPorts = filterNeutronPorts(novaResource, neutronPortDetails);
204             filteredNeutronPorts.forEach(port -> {
205                 LInterface lInterface = new LInterface();
206                 lInterface.setInterfaceId(port.getId());
207                 lInterface.setInterfaceName(port.getName());
208                 auditVserver.getLInterfaces().getLInterface().add(lInterface);
209             });
210             vserversToAudit.add(auditVserver);
211         }
212         return vserversToAudit;
213     }
214
215     /**
216      * @param novaResource Single openstack resource that is of type Nova
217      * @param neutronPorts List of Neutron ports created within the stack
218      * @return Filtered list of neutron ports taht relate to the nova server in openstack
219      */
220     protected Stream<Port> filterNeutronPorts(Resource novaResource, List<Optional<Port>> neutronPorts) {
221         List<Port> filteredNeutronPorts =
222                 neutronPorts.stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
223         return filteredNeutronPorts.stream()
224                 .filter(port -> port.getDeviceId().equalsIgnoreCase(novaResource.getPhysicalResourceId()));
225     }
226
227     /**
228      * @param resources Resource stream created by the stack in openstack
229      * @param cloudSiteId Unique site id to identify which openstack we talk to
230      * @param tenantId The tenant within the cloud we are talking to where resouces exist
231      * @return List of optional neutron ports found within the cloud site and tenant
232      */
233     protected List<Optional<Port>> retrieveNeutronPortDetails(Resources resources, String cloudSiteId,
234             String tenantId) {
235         return resources.getList().parallelStream().filter(resource -> "OS::Neutron::Port".equals(resource.getType()))
236                 .map(resource -> neutron.getNeutronPort(resource.getPhysicalResourceId(), tenantId, cloudSiteId))
237                 .collect(Collectors.toList());
238
239     }
240
241     protected Optional<String> extractResourcePathFromHref(String href) {
242         try {
243             Optional<String> stackPath = extractStackPathFromHref(href);
244             if (stackPath.isPresent()) {
245                 return Optional.of(stackPath.get() + RESOURCES);
246             } else
247                 return Optional.empty();
248         } catch (Exception e) {
249             logger.error("Error parsing URI", e);
250         }
251         return Optional.empty();
252     }
253
254     protected Optional<String> extractStackPathFromHref(String href) {
255         try {
256             URI uri = new URI(href);
257             Pattern p = Pattern.compile("/stacks.*");
258             Matcher m = p.matcher(uri.getPath());
259             if (m.find()) {
260                 return Optional.of(m.group());
261             } else
262                 return Optional.empty();
263         } catch (Exception e) {
264             logger.error("Error parsing URI", e);
265         }
266         return Optional.empty();
267     }
268
269
270 }
271