2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.so.adapters.audit;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Optional;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 import java.util.stream.Collectors;
31 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.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;
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;
50 public class HeatStackAudit {
52 private static final String RESOURCES = "/resources";
54 protected static final Logger logger = LoggerFactory.getLogger(HeatStackAudit.class);
57 protected MsoHeatUtils heat;
60 protected MsoNeutronUtils neutron;
63 protected AuditVServer auditVservers;
65 public boolean auditHeatStackCreate(String cloudRegion, String cloudOwner, String tenantId, String heatStackName) {
67 return auditStack(cloudRegion,cloudOwner,tenantId,heatStackName,true);
68 } catch (Exception e) {
69 logger.error("Error during auditing stack resources", e);
74 public boolean auditHeatStackDeleted(String cloudRegion, String cloudOwner, String tenantId, String heatStackName) {
76 return auditStack(cloudRegion,cloudOwner,tenantId,heatStackName,false);
77 } catch (Exception e) {
78 logger.error("Error during auditing stack resources", e);
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())
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,
96 return auditVservers.auditAllVserversDoExist(vserversWithSubInterfaces, tenantId, cloudOwner, cloudRegion);
98 return auditVservers.auditAllVserversDoNotExist(vserversWithSubInterfaces, tenantId, cloudOwner, cloudRegion);
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());
108 private List<Resource> extractNovaResources(Resources resources) {
109 return resources.getList().stream()
110 .filter(p -> "OS::Nova::Server".equals(p.getType())).collect(Collectors.toList());
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);
118 return vServersToAudit;
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()))
125 if (stackLink.isPresent()) {
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);
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");
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());
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);
166 throw new Exception("Error finding Path from Self Link");
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())
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()
180 if(contrailVm == null){
181 throw new Exception("Cannnot find Contrail Virtual Machine Interface on Stack: "+ subinterfaceStack.getId());
183 LInterface subInterface = new LInterface();
184 subInterface.setInterfaceId(contrailVm.getPhysicalResourceId());
186 if(lInterface.getLInterfaces() == null)
187 lInterface.setLInterfaces(new LInterfaces());
189 lInterface.getLInterfaces().getLInterface().add(subInterface);
191 logger.debug("Did Not Find Parent Port on VServer: {} Parent Port: SubInterface: {}",auditVserver.getVserverId(),
192 lInterface.getInterfaceId(),subinterfaceStack.getId());
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);
207 vserversToAudit.add(auditVserver);
209 return vserversToAudit;
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
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()));
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
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());
237 protected Optional<String> extractResourcePathFromHref(String href) {
239 Optional<String> stackPath = extractStackPathFromHref(href);
240 if (stackPath.isPresent()){
241 return Optional.of(stackPath.get()+RESOURCES);
243 return Optional.empty();
244 } catch (Exception e) {
245 logger.error("Error parsing URI", e);
247 return Optional.empty();
250 protected Optional<String> extractStackPathFromHref(String href) {
252 URI uri = new URI(href);
253 Pattern p = Pattern.compile("/stacks.*");
254 Matcher m = p.matcher(uri.getPath());
256 return Optional.of(m.group());
258 return Optional.empty();
259 } catch (Exception e) {
260 logger.error("Error parsing URI", e);
262 return Optional.empty();