2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.groupbasedpolicy.gbp_ise_adapter.impl;
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;
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;
43 * Purpose: harvest sgt + names available via ise-rest-api
45 public class GbpIseSgtHarvesterImpl implements GbpIseSgtHarvester {
47 private static final Logger LOG = LoggerFactory.getLogger(GbpIseSgtHarvesterImpl.class);
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()";
55 private final SgtInfoProcessor[] sgtInfoProcessors;
58 * @param sgtInfoProcessors generator delegate
60 public GbpIseSgtHarvesterImpl(final SgtInfoProcessor... sgtInfoProcessors) {
61 this.sgtInfoProcessors = sgtInfoProcessors;
65 public ListenableFuture<Integer> harvest(@Nonnull final IseHarvestConfig configuration) {
66 final ConnectionConfig connectionConfig = configuration.getConnectionConfig();
67 ListenableFuture<Integer> result;
69 final Client iseClient = RestClientFactory.createIseClient(connectionConfig);
70 final WebResource baseWebResource = iseClient.resource(connectionConfig.getIseRestUrl().getValue());
72 final WebResource.Builder requestBuilder = createRequestBuilder(baseWebResource,
73 connectionConfig.getHeader(), PATH_ERS_CONFIG_SGT);
74 final String rawSgtSummary = deliverResponse(requestBuilder);
76 final List<SgtInfo> sgtInfos = harvestDetails(rawSgtSummary, baseWebResource, connectionConfig.getHeader());
78 ListenableFuture<Void> processingResult = Futures.immediateCheckedFuture(null);
79 for (SgtInfoProcessor processor : sgtInfoProcessors) {
80 processingResult = Futures.transform(processingResult, new AsyncFunction<Void, Void>() {
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);
88 result = Futures.transform(processingResult, new Function<Void, Integer>() {
91 public Integer apply(@Nullable final Void input) {
92 // always success, otherwise there will be TransactionCommitFailedException thrown
93 return sgtInfos.size();
96 } catch (Exception e) {
97 LOG.debug("failed to harvest ise", e);
98 result = Futures.immediateFailedFuture(e);
104 private static String deliverResponse(final WebResource.Builder requestBuilder) {
105 return requestBuilder.get(ClientResponse.class).getEntity(String.class);
108 private static WebResource.Builder createRequestBuilder(final WebResource resource, final List<Header> headers,
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;
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<>();
122 final XPath xpath = setupXpath();
124 InputSource inputSource = new InputSource(new StringReader(rawSgtSummary));
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);
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);
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);
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);
149 } catch (XPathExpressionException e) {
150 LOG.warn("failed to parse all-sgt response", e);
157 * @return initiated xpath with ise namespace context injected
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");
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";
174 outcome = XMLConstants.NULL_NS_URI;
179 // This method isn't necessary for XPath processing.
180 public String getPrefix(String uri) {
181 throw new UnsupportedOperationException();
184 // This method isn't necessary for XPath processing either.
185 public Iterator getPrefixes(String uri) {
186 throw new UnsupportedOperationException();
190 XPath xpath = XPathFactory.newInstance().newXPath();
191 xpath.setNamespaceContext(nsContext);