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