BUG-6650: ep-ip/sgt, move groupbasedpolicy-ise-adapter into sxp-integration
[groupbasedpolicy.git] / sxp-integration / groupbasedpolicy-ise-adapter / src / main / java / org / opendaylight / groupbasedpolicy / gbp_ise_adapter / impl / GbpIseSgtHarvesterImpl.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.gbp_ise_adapter.impl;
10
11 import com.google.common.base.Function;
12 import com.google.common.util.concurrent.AsyncFunction;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.sun.jersey.api.client.Client;
16 import com.sun.jersey.api.client.ClientResponse;
17 import com.sun.jersey.api.client.WebResource;
18 import java.io.StringReader;
19 import java.net.URI;
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.List;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import javax.xml.XMLConstants;
26 import javax.xml.namespace.NamespaceContext;
27 import javax.xml.xpath.XPath;
28 import javax.xml.xpath.XPathConstants;
29 import javax.xml.xpath.XPathExpressionException;
30 import javax.xml.xpath.XPathFactory;
31 import org.opendaylight.groupbasedpolicy.gbp_ise_adapter.impl.util.RestClientFactory;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.groupbasedpolicy.gbp.ise.adapter.model.rev160630.gbp.ise.adapter.IseHarvestConfig;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.groupbasedpolicy.gbp.ise.adapter.model.rev160630.gbp.ise.adapter.ise.harvest.config.ConnectionConfig;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.groupbasedpolicy.gbp.ise.adapter.model.rev160630.gbp.ise.adapter.ise.harvest.config.connection.config.Header;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
40 import org.xml.sax.InputSource;
41
42 /**
43  * Purpose: harvest sgt + names available via ise-rest-api
44  */
45 public class GbpIseSgtHarvesterImpl implements GbpIseSgtHarvester {
46
47     private static final Logger LOG = LoggerFactory.getLogger(GbpIseSgtHarvesterImpl.class);
48
49     public static final String PATH_ERS_CONFIG_SGT = "/ers/config/sgt";
50     public static final String EXPRESSION_SGT_ALL_LINK_HREFS = "/ns3:searchResult/ns3:resources/ns5:resource/link/@href";
51     public static final String EXPRESSION_SGT_DETAIL  = "./ns4:sgt";
52     public static final String EXPRESSION_SGT_NAME_ATTR = "./@name";
53     public static final String EXPRESSION_SGT_VALUE = "./value/text()";
54
55     private final SgtInfoProcessor[] sgtInfoProcessors;
56
57     /**
58      * @param sgtInfoProcessors generator delegate
59      */
60     public GbpIseSgtHarvesterImpl(final SgtInfoProcessor... sgtInfoProcessors) {
61         this.sgtInfoProcessors = sgtInfoProcessors;
62     }
63
64     @Override
65     public ListenableFuture<Integer> harvest(@Nonnull final IseHarvestConfig configuration) {
66         final ConnectionConfig connectionConfig = configuration.getConnectionConfig();
67         ListenableFuture<Integer> result;
68         try {
69             final Client iseClient = RestClientFactory.createIseClient(connectionConfig);
70             final WebResource baseWebResource = iseClient.resource(connectionConfig.getIseRestUrl().getValue());
71
72             final WebResource.Builder requestBuilder = createRequestBuilder(baseWebResource,
73                     connectionConfig.getHeader(), PATH_ERS_CONFIG_SGT);
74             final String rawSgtSummary = deliverResponse(requestBuilder);
75
76             final List<SgtInfo> sgtInfos = harvestDetails(rawSgtSummary, baseWebResource, connectionConfig.getHeader());
77
78             ListenableFuture<Void> processingResult = Futures.immediateCheckedFuture(null);
79             for (SgtInfoProcessor processor : sgtInfoProcessors) {
80                 processingResult = Futures.transform(processingResult, new AsyncFunction<Void, Void>() {
81                     @Override
82                     public ListenableFuture<Void> apply(final Void input) throws Exception {
83                         LOG.debug("entering stg-info processor {}", processor.getClass().getSimpleName());
84                         return processor.processSgtInfo(configuration.getTenant(), sgtInfos);
85                     }
86                 });
87             }
88             result = Futures.transform(processingResult, new Function<Void, Integer>() {
89                 @Nullable
90                 @Override
91                 public Integer apply(@Nullable final Void input) {
92                     // always success, otherwise there will be TransactionCommitFailedException thrown
93                     return sgtInfos.size();
94                 }
95             });
96         } catch (Exception e) {
97             LOG.debug("failed to harvest ise", e);
98             result = Futures.immediateFailedFuture(e);
99         }
100
101         return result;
102     }
103
104     private static String deliverResponse(final WebResource.Builder requestBuilder) {
105         return requestBuilder.get(ClientResponse.class).getEntity(String.class);
106     }
107
108     private static WebResource.Builder createRequestBuilder(final WebResource resource, final List<Header> headers,
109                                                             final String path) {
110         final WebResource webResource = resource.path(path);
111         final WebResource.Builder requestBuilder = webResource.getRequestBuilder();
112         headers.stream().forEach(
113                 (header) -> requestBuilder.header(header.getName(), header.getValue()));
114         return requestBuilder;
115     }
116
117     private List<SgtInfo> harvestDetails(final String rawSgtSummary, final WebResource baseWebResource, final List<Header> headers) {
118         LOG.trace("rawSgtSummary: {}", rawSgtSummary);
119         final List<SgtInfo> sgtInfos = new ArrayList<>();
120
121         // parse sgtSummary
122         final XPath xpath = setupXpath();
123
124         InputSource inputSource = new InputSource(new StringReader(rawSgtSummary));
125         try {
126             final NodeList sgtLinkNodes = (NodeList) xpath.evaluate(EXPRESSION_SGT_ALL_LINK_HREFS, inputSource,
127                     XPathConstants.NODESET);
128             for (int i = 0; i < sgtLinkNodes.getLength(); i++) {
129                 final String sgtLinkHrefValue = sgtLinkNodes.item(i).getNodeValue();
130                 LOG.debug("found sgt resource [{}]: {}", i, sgtLinkHrefValue);
131
132                 // query all sgt entries (serial-vise)
133                 final URI hrefToSgtDetailUri = URI.create(sgtLinkHrefValue);
134                 final WebResource.Builder requestBuilder = createRequestBuilder(baseWebResource, headers, hrefToSgtDetailUri.getPath());
135                 final String rawSgtDetail = deliverResponse(requestBuilder);
136                 LOG.trace("rawSgtDetail: {}", rawSgtDetail);
137
138                 final Node sgtNode = (Node) xpath.evaluate(EXPRESSION_SGT_DETAIL,  new InputSource(new StringReader(rawSgtDetail)),
139                         XPathConstants.NODE);
140                 final Node sgtName = (Node) xpath.evaluate(EXPRESSION_SGT_NAME_ATTR,  sgtNode, XPathConstants.NODE);
141                 final Node sgtValue = (Node) xpath.evaluate(EXPRESSION_SGT_VALUE,  sgtNode, XPathConstants.NODE);
142                 LOG.debug("sgt value [{}]: {} -> {}", i, sgtValue, sgtName);
143
144                 // store replies into list of SgtInfo
145                 final Sgt sgt = new Sgt(Integer.parseInt(sgtValue.getNodeValue(), 10));
146                 final SgtInfo sgtInfo = new SgtInfo(sgt, sgtName.getNodeValue());
147                 sgtInfos.add(sgtInfo);
148             }
149         } catch (XPathExpressionException e) {
150             LOG.warn("failed to parse all-sgt response", e);
151         }
152
153         return sgtInfos;
154     }
155
156     /**
157      * @return initiated xpath with ise namespace context injected
158      */
159     private static XPath setupXpath() {
160         final NamespaceContext nsContext = new NamespaceContext() {
161             public String getNamespaceURI(String prefix) {
162                 final String outcome;
163                 if (prefix == null) {
164                     throw new NullPointerException("Null prefix");
165                 }
166
167                 if ("ns5".equals(prefix)) {
168                     outcome = "ers.ise.cisco.com";
169                 } else if ("ns3".equals(prefix)) {
170                     outcome = "v2.ers.ise.cisco.com";
171                 } else if ("ns4".equals(prefix)) {
172                     outcome = "trustsec.ers.ise.cisco.com";
173                 } else {
174                     outcome = XMLConstants.NULL_NS_URI;
175                 }
176                 return outcome;
177             }
178
179             // This method isn't necessary for XPath processing.
180             public String getPrefix(String uri) {
181                 throw new UnsupportedOperationException();
182             }
183
184             // This method isn't necessary for XPath processing either.
185             public Iterator getPrefixes(String uri) {
186                 throw new UnsupportedOperationException();
187             }
188         };
189
190         XPath xpath = XPathFactory.newInstance().newXPath();
191         xpath.setNamespaceContext(nsContext);
192         return xpath;
193     }
194 }