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