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.Arrays;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Optional;
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;
51 public class HeatStackAudit {
53 private static final String RESOURCES = "/resources";
55 protected static final Logger logger = LoggerFactory.getLogger(HeatStackAudit.class);
57 private static final String exceptionMsg = "Error finding Path from Self Link";
60 protected MsoHeatUtils heat;
63 protected MsoNeutronUtils neutron;
66 protected AuditVServer auditVservers;
68 public Optional<AAIObjectAuditList> auditHeatStack(String cloudRegion, String cloudOwner, String tenantId,
69 String heatStackName) {
71 logger.debug("Fetching Top Level Stack Information");
72 Resources resources = heat.queryStackResources(cloudRegion, tenantId, heatStackName, 3);
73 List<Resource> novaResources = resources.getList().stream()
74 .filter(p -> "OS::Nova::Server".equals(p.getType())).collect(Collectors.toList());
75 List<Resource> resourceGroups = resources.getList().stream()
76 .filter(p -> "OS::Heat::ResourceGroup".equals(p.getType()) && p.getName().contains("subinterfaces"))
77 .collect(Collectors.toList());
78 List<Optional<Port>> neutronPortDetails = retrieveNeutronPortDetails(resources, cloudRegion, tenantId);
79 if (novaResources.isEmpty())
80 return Optional.of(new AAIObjectAuditList());
82 Set<Vserver> vserversToAudit = createVserverSet(resources, novaResources, neutronPortDetails);
83 Set<Vserver> vserversWithSubInterfaces =
84 processSubInterfaces(cloudRegion, tenantId, resourceGroups, vserversToAudit);
85 return auditVservers.auditVservers(vserversWithSubInterfaces, tenantId, cloudOwner, cloudRegion);
87 } catch (Exception e) {
88 logger.error("Error during auditing stack resources", e);
89 return Optional.empty();
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);
98 return vServersToAudit;
101 protected void processResourceGroups(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface,
102 Resource resourceGroup) throws Exception {
103 Optional<Link> stackLink =
104 resourceGroup.getLinks().stream().filter(link -> "nested".equals(link.getRel())).findAny();
105 if (stackLink.isPresent()) {
107 Optional<String> path = extractResourcePathFromHref(stackLink.get().getHref());
108 if (path.isPresent()) {
109 logger.debug("Fetching nested Resource Stack Information");
110 Resources nestedResourceGroupResources =
111 heat.executeHeatClientRequest(path.get(), cloudRegion, tenantId, Resources.class);
112 processNestedResourceGroup(cloudRegion, tenantId, vServersWithLInterface,
113 nestedResourceGroupResources);
115 throw new Exception(exceptionMsg);
116 } catch (Exception e) {
117 logger.error("Error Parsing Link to obtain Path", e);
118 throw new Exception(exceptionMsg);
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 =
127 resourceGroupNested.getLinks().stream().filter(link -> "nested".equals(link.getRel())).findAny();
128 if (subInterfaceStackLink.isPresent()) {
129 addSubInterface(cloudRegion, tenantId, vServersWithLInterface, subInterfaceStackLink.get());
134 protected void addSubInterface(String cloudRegion, String tenantId, Set<Vserver> vServersWithLInterface,
135 Link subInterfaceStackLink) throws Exception {
136 Optional<String> resourcePath = extractResourcePathFromHref(subInterfaceStackLink.getHref());
137 Optional<String> stackPath = extractStackPathFromHref(subInterfaceStackLink.getHref());
138 if (resourcePath.isPresent() && stackPath.isPresent()) {
139 logger.debug("Fetching nested Sub-Interface Stack Information");
140 Stack subinterfaceStack =
141 heat.executeHeatClientRequest(stackPath.get(), cloudRegion, tenantId, Stack.class);
142 Resources subinterfaceResources =
143 heat.executeHeatClientRequest(resourcePath.get(), cloudRegion, tenantId, Resources.class);
144 if (subinterfaceStack != null) {
145 addSubInterfaceToVserver(vServersWithLInterface, subinterfaceStack, subinterfaceResources);
148 throw new Exception(exceptionMsg);
152 protected void addSubInterfaceToVserver(Set<Vserver> vServersWithLInterface, Stack subinterfaceStack,
153 Resources subinterfaceResources) throws Exception {
154 String parentNeutronPortId = (String) subinterfaceStack.getParameters().get("port_interface");
155 logger.debug("Parent neutron Port: {} on SubInterface: {}", parentNeutronPortId, subinterfaceStack.getId());
156 for (Vserver auditVserver : vServersWithLInterface)
157 for (LInterface lInterface : auditVserver.getLInterfaces().getLInterface())
159 if (parentNeutronPortId.equals(lInterface.getInterfaceId())) {
160 logger.debug("Found Parent Port on VServer: {} on Port: {}", auditVserver.getVserverId(),
161 lInterface.getInterfaceId());
162 Resource contrailVm = subinterfaceResources.getList().stream()
163 .filter(resource -> "OS::ContrailV2::VirtualMachineInterface".equals(resource.getType()))
164 .findAny().orElse(null);
165 if (contrailVm == null) {
166 throw new Exception("Cannnot find Contrail Virtual Machine Interface on Stack: "
167 + subinterfaceStack.getId());
169 LInterface subInterface = new LInterface();
170 subInterface.setInterfaceId(contrailVm.getPhysicalResourceId());
171 subInterface.setIsPortMirrored(false);
172 subInterface.setInMaint(false);
173 subInterface.setIsIpUnnumbered(false);
174 String macAddr = (String) subinterfaceStack.getParameters().get("mac_address");
175 subInterface.setMacaddr(macAddr);
177 String namePrefix = (String) subinterfaceStack.getParameters().get("subinterface_name_prefix");
178 Integer vlanIndex = Integer.parseInt((String) subinterfaceStack.getParameters().get("counter"));
179 String vlanTagList = (String) subinterfaceStack.getParameters().get("vlan_tag");
180 List<String> subInterfaceVlanTagList = Arrays.asList(vlanTagList.split(","));
181 subInterface.setInterfaceName(namePrefix + "_" + subInterfaceVlanTagList.get(vlanIndex));
182 subInterface.setVlans(new Vlans());
183 Vlan vlan = new Vlan();
184 vlan.setInMaint(false);
185 vlan.setIsIpUnnumbered(false);
186 vlan.setVlanIdInner(Long.parseLong(subInterfaceVlanTagList.get(vlanIndex)));
187 vlan.setVlanInterface(namePrefix + "_" + subInterfaceVlanTagList.get(vlanIndex));
188 subInterface.getVlans().getVlan().add(vlan);
189 if (lInterface.getLInterfaces() == null)
190 lInterface.setLInterfaces(new LInterfaces());
192 lInterface.getLInterfaces().getLInterface().add(subInterface);
194 logger.debug("Did Not Find Parent Port on VServer: {} Parent Port: SubInterface: {}",
195 auditVserver.getVserverId(), lInterface.getInterfaceId(), subinterfaceStack.getId());
198 protected Set<Vserver> createVserverSet(Resources resources, List<Resource> novaResources,
199 List<Optional<Port>> neutronPortDetails) {
200 Set<Vserver> vserversToAudit = new HashSet<>();
201 for (Resource novaResource : novaResources) {
202 Vserver auditVserver = new Vserver();
203 auditVserver.setLInterfaces(new LInterfaces());
204 auditVserver.setVserverId(novaResource.getPhysicalResourceId());
205 Stream<Port> filteredNeutronPorts = filterNeutronPorts(novaResource, neutronPortDetails);
206 filteredNeutronPorts.forEach(port -> {
207 LInterface lInterface = new LInterface();
208 lInterface.setInterfaceId(port.getId());
209 lInterface.setInterfaceName(port.getName());
210 auditVserver.getLInterfaces().getLInterface().add(lInterface);
212 vserversToAudit.add(auditVserver);
214 return vserversToAudit;
218 * @param novaResource Single openstack resource that is of type Nova
219 * @param neutronPorts List of Neutron ports created within the stack
220 * @return Filtered list of neutron ports taht relate to the nova server in openstack
222 protected Stream<Port> filterNeutronPorts(Resource novaResource, List<Optional<Port>> neutronPorts) {
223 List<Port> filteredNeutronPorts =
224 neutronPorts.stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
225 return filteredNeutronPorts.stream()
226 .filter(port -> port.getDeviceId().equalsIgnoreCase(novaResource.getPhysicalResourceId()));
230 * @param resources Resource stream created by the stack in openstack
231 * @param cloudSiteId Unique site id to identify which openstack we talk to
232 * @param tenantId The tenant within the cloud we are talking to where resouces exist
233 * @return List of optional neutron ports found within the cloud site and tenant
235 protected List<Optional<Port>> retrieveNeutronPortDetails(Resources resources, String cloudSiteId,
237 return resources.getList().parallelStream().filter(resource -> "OS::Neutron::Port".equals(resource.getType()))
238 .map(resource -> neutron.getNeutronPort(resource.getPhysicalResourceId(), tenantId, cloudSiteId))
239 .collect(Collectors.toList());
243 protected Optional<String> extractResourcePathFromHref(String href) {
245 Optional<String> stackPath = extractStackPathFromHref(href);
246 if (stackPath.isPresent()) {
247 return Optional.of(stackPath.get() + RESOURCES);
249 return Optional.empty();
250 } catch (Exception e) {
251 logger.error("Error parsing URI", e);
253 return Optional.empty();
256 protected Optional<String> extractStackPathFromHref(String href) {
258 URI uri = new URI(href);
259 Pattern p = Pattern.compile("/stacks.*");
260 Matcher m = p.matcher(uri.getPath());
262 return Optional.of(m.group());
264 return Optional.empty();
265 } catch (Exception e) {
266 logger.error("Error parsing URI", e);
268 return Optional.empty();