improvements to audit inventory feature
[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.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.springframework.beans.factory.annotation.Autowired;
40 import org.springframework.stereotype.Component;
41
42 import com.woorea.openstack.heat.model.Link;
43 import com.woorea.openstack.heat.model.Resource;
44 import com.woorea.openstack.heat.model.Resources;
45 import com.woorea.openstack.heat.model.Stack;
46
47 @Component
48 public class HeatStackAudit {
49
50         private static final String RESOURCES = "/resources";
51
52         protected static final Logger logger = LoggerFactory.getLogger(HeatStackAudit.class);
53
54         @Autowired
55         protected MsoHeatUtils heat;
56
57         @Autowired
58         protected AuditVServer auditVservers;
59
60         public boolean auditHeatStackCreate(String cloudRegion, String cloudOwner, String tenantId, String heatStackName) {
61                 try {
62                         return auditStack(cloudRegion,cloudOwner,tenantId,heatStackName,true);
63                 } catch (Exception e) {
64                         logger.error("Error during auditing stack resources", e);
65                         return false;
66                 }
67         }
68         
69         public boolean auditHeatStackDeleted(String cloudRegion, String cloudOwner, String tenantId, String heatStackName) {
70                 try {
71                         return auditStack(cloudRegion,cloudOwner,tenantId,heatStackName,false);
72                 } catch (Exception e) {
73                         logger.error("Error during auditing stack resources", e);
74                         return false;
75                 }
76         }
77         
78         private boolean auditStack(String cloudRegion, String cloudOwner, String tenantId, String heatStackName,boolean isCreateAudit) throws Exception{
79                 logger.debug("Fetching Top Level Stack Information");
80                 Resources resources = heat.queryStackResources(cloudRegion, tenantId, heatStackName);
81                 List<Resource> novaResources = extractNovaResources(resources);
82                 if(novaResources.isEmpty())
83                         return true;
84                 else{
85                         List<Resource> resourceGroups = extractResourceGroups(resources);
86                         Set<Vserver> vserversToAudit = createVserverSet(resources, novaResources);
87                         Set<Vserver> vserversWithSubInterfaces = processSubInterfaces(cloudRegion, tenantId, resourceGroups,
88                                 vserversToAudit);
89                         if(isCreateAudit){
90                                 return auditVservers.auditAllVserversDoExist(vserversWithSubInterfaces, tenantId, cloudOwner, cloudRegion);
91                         }else{
92                                 return auditVservers.auditAllVserversDoNotExist(vserversWithSubInterfaces, tenantId, cloudOwner, cloudRegion);
93                         }
94                 }
95         }
96
97         private List<Resource> extractResourceGroups(Resources resources) {
98                 return resources.getList().stream()
99                                 .filter(p -> "OS::Heat::ResourceGroup".equals(p.getType()) && p.getName().contains("subinterfaces")).collect(Collectors.toList());
100         }
101
102         private List<Resource> extractNovaResources(Resources resources) {
103                 return resources.getList().stream()
104                                 .filter(p -> "OS::Nova::Server".equals(p.getType())).collect(Collectors.toList());
105         } 
106
107         protected Set<Vserver> processSubInterfaces(String cloudRegion, String tenantId, List<Resource> resourceGroups,
108                         Set<Vserver> vServersToAudit) throws Exception {
109                 for (Resource resourceGroup : resourceGroups) {
110                         processResourceGroups(cloudRegion, tenantId, vServersToAudit, resourceGroup);
111                 }
112                 return vServersToAudit;
113         }
114
115         protected void processResourceGroups(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface,
116                         Resource resourceGroup) throws Exception {
117                 Optional<Link> stackLink = resourceGroup.getLinks().stream().filter(link -> "nested".equals(link.getRel()))
118                                 .findAny();
119                 if (stackLink.isPresent()) {
120                         try {
121                                 Optional<String> path = extractResourcePathFromHref(stackLink.get().getHref());
122                                 if (path.isPresent()) {
123                                         logger.debug("Fetching nested Resource Stack Information");
124                                         Resources nestedResourceGroupResources = heat.executeHeatClientRequest(path.get(), cloudRegion,
125                                                         tenantId, Resources.class);
126                                         processNestedResourceGroup(cloudRegion, tenantId, vServersWithLInterface,
127                                                         nestedResourceGroupResources);
128                                 } else
129                                         throw new Exception("Error finding Path from Self Link");
130                         } catch (Exception e) {
131                                 logger.error("Error Parsing Link to obtain Path", e);
132                                 throw new Exception("Error finding Path from Self Link");
133                         }
134
135                 }
136         }
137
138         protected void processNestedResourceGroup(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface,
139                         Resources nestedResourceGroupResources) throws Exception {
140                 for (Resource resourceGroupNested : nestedResourceGroupResources) {
141                         Optional<Link> subInterfaceStackLink = resourceGroupNested.getLinks().stream()
142                                         .filter(link -> "nested".equals(link.getRel())).findAny();
143                         if (subInterfaceStackLink.isPresent()) {
144                                 addSubInterface(cloudRegion, tenantId, vServersWithLInterface,subInterfaceStackLink.get());
145                         }
146                 }
147         }
148
149         protected void addSubInterface(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface, Link subInterfaceStackLink) throws Exception {
150                         Optional<String> resourcePath = extractResourcePathFromHref(subInterfaceStackLink.getHref());
151                         Optional<String> stackPath = extractStackPathFromHref(subInterfaceStackLink.getHref());
152                         if (resourcePath.isPresent() && stackPath.isPresent()) {
153                                 logger.debug("Fetching nested Sub-Interface Stack Information");
154                                 Stack subinterfaceStack = heat.executeHeatClientRequest(stackPath.get(), cloudRegion, tenantId, Stack.class);
155                                 Resources subinterfaceResources = heat.executeHeatClientRequest(resourcePath.get(), cloudRegion, tenantId, Resources.class);
156                                 if (subinterfaceStack != null) {
157                                         addSubInterfaceToVserver(vServersWithLInterface, subinterfaceStack, subinterfaceResources);
158                                 }
159                         } else
160                                 throw new Exception("Error finding Path from Self Link");
161                 
162         }
163
164         protected void addSubInterfaceToVserver(Set<Vserver> vServersWithLInterface, Stack subinterfaceStack, Resources subinterfaceResources) throws Exception {
165                 String parentNeutronPortId = (String) subinterfaceStack.getParameters().get("port_interface");
166                 logger.debug("Parent neutron Port: {} on SubInterface: {}", parentNeutronPortId, subinterfaceStack.getId());
167                 for (Vserver auditVserver : vServersWithLInterface)
168                         for (LInterface lInterface : auditVserver.getLInterfaces().getLInterface())
169                                 
170                                 if (parentNeutronPortId.equals(lInterface.getInterfaceId())) {
171                                         logger.debug("Found Parent Port on VServer: {} on Port: {}", auditVserver.getVserverId(), lInterface.getInterfaceId());
172                                         Resource contrailVm = subinterfaceResources.getList().stream().filter(resource -> "OS::ContrailV2::VirtualMachineInterface".equals(resource.getType())).findAny()
173                         .orElse(null);
174                                         if(contrailVm == null){
175                                                 throw new Exception("Cannnot find Contrail Virtual Machine Interface on Stack: "+ subinterfaceStack.getId());
176                                         }
177                                         LInterface subInterface = new LInterface();
178                                         subInterface.setInterfaceId(contrailVm.getPhysicalResourceId());
179                                         
180                                         if(lInterface.getLInterfaces() == null)
181                                                 lInterface.setLInterfaces(new LInterfaces());
182                                         
183                                         lInterface.getLInterfaces().getLInterface().add(subInterface);
184                                 }else
185                                         logger.debug("Did Not Find Parent Port on VServer: {} Parent Port: SubInterface: {}",auditVserver.getVserverId(), 
186                                                         lInterface.getInterfaceId(),subinterfaceStack.getId());
187         }
188
189         protected Set<Vserver> createVserverSet(Resources resources, List<Resource> novaResources) {
190                 Set<Vserver> vserversToAudit = new HashSet<>();
191                 for (Resource novaResource : novaResources) {
192                         Vserver auditVserver = new Vserver();
193                         auditVserver.setLInterfaces(new LInterfaces());
194                         auditVserver.setVserverId(novaResource.getPhysicalResourceId());
195                         Stream<Resource> filteredNeutronNetworks = resources.getList().stream()
196                                         .filter(resource -> resource.getRequiredBy().contains(novaResource.getLogicalResourceId()))
197                                         .filter(resource -> "OS::Neutron::Port".equals(resource.getType()));
198                         filteredNeutronNetworks.forEach(network -> {
199                                 LInterface lInterface = new LInterface();
200                                 lInterface.setInterfaceId(network.getPhysicalResourceId());
201                                 auditVserver.getLInterfaces().getLInterface().add(lInterface);
202                         });
203                         vserversToAudit.add(auditVserver);
204                 }
205                 return vserversToAudit;
206         }
207
208         protected Optional<String> extractResourcePathFromHref(String href) {           
209                 try {
210                         Optional<String> stackPath = extractStackPathFromHref(href);
211                         if (stackPath.isPresent()){                                             
212                                 return Optional.of(stackPath.get()+RESOURCES);
213                         }else
214                                 return Optional.empty();
215                 } catch (Exception e) {
216                         logger.error("Error parsing URI", e);
217                 }
218                 return Optional.empty();
219         }
220         
221         protected Optional<String> extractStackPathFromHref(String href) {
222                 try {
223                         URI uri = new URI(href);        
224                         Pattern p = Pattern.compile("/stacks.*");
225                         Matcher m = p.matcher(uri.getPath());
226                         if (m.find()){                                          
227                                 return Optional.of(m.group());
228                         }else
229                                 return Optional.empty();
230                 } catch (Exception e) {
231                         logger.error("Error parsing URI", e);
232                 }
233                 return Optional.empty();
234         }
235         
236 }