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.sxp_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.google.common.util.concurrent.ThreadFactoryBuilder;
16 import com.sun.jersey.api.client.Client;
17 import com.sun.jersey.api.client.WebResource;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.List;
22 import java.util.Objects;
23 import java.util.concurrent.Callable;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.Future;
27 import java.util.concurrent.TimeUnit;
28 import java.util.stream.Collectors;
29 import javax.annotation.Nonnull;
30 import javax.annotation.Nullable;
31 import javax.xml.xpath.XPath;
32 import javax.xml.xpath.XPathConstants;
33 import javax.xml.xpath.XPathExpressionException;
34 import org.opendaylight.groupbasedpolicy.sxp_ise_adapter.impl.util.IseReplyUtil;
35 import org.opendaylight.groupbasedpolicy.sxp_ise_adapter.impl.util.RestClientFactory;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.groupbasedpolicy.sxp.integration.sxp.ise.adapter.model.rev160630.gbp.sxp.ise.adapter.IseSourceConfig;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.groupbasedpolicy.sxp.integration.sxp.ise.adapter.model.rev160630.gbp.sxp.ise.adapter.ise.source.config.ConnectionConfig;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.groupbasedpolicy.sxp.integration.sxp.ise.adapter.model.rev160630.gbp.sxp.ise.adapter.ise.source.config.connection.config.Header;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
44 import org.xml.sax.InputSource;
47 * Purpose: harvest sgt + names available via ise-rest-api
49 public class GbpIseSgtHarvesterImpl implements GbpIseSgtHarvester {
51 private static final Logger LOG = LoggerFactory.getLogger(GbpIseSgtHarvesterImpl.class);
53 private final SgtInfoProcessor[] sgtInfoProcessors;
56 * @param sgtInfoProcessors generator delegate
58 public GbpIseSgtHarvesterImpl(final SgtInfoProcessor... sgtInfoProcessors) {
59 this.sgtInfoProcessors = sgtInfoProcessors;
63 public ListenableFuture<Collection<SgtInfo>> harvestAll(@Nonnull final IseContext iseContext) {
64 final IseSourceConfig iseSourceConfig = iseContext.getIseSourceConfig();
65 final ConnectionConfig connectionConfig = iseSourceConfig.getConnectionConfig();
66 ListenableFuture<Collection<SgtInfo>> result;
68 final Client iseClient = RestClientFactory.createIseClient(connectionConfig);
69 final WebResource baseWebResource = iseClient.resource(connectionConfig.getIseRestUrl().getValue());
71 final WebResource.Builder requestBuilder = RestClientFactory.createRequestBuilder(baseWebResource,
72 connectionConfig.getHeader(), RestClientFactory.PATH_ERS_CONFIG_SGT);
73 final String rawSgtSummary = IseReplyUtil.deliverResponse(requestBuilder);
75 final List<SgtInfo> sgtInfos = harvestDetails(rawSgtSummary, baseWebResource, connectionConfig.getHeader());
77 ListenableFuture<Void> processingResult = Futures.immediateCheckedFuture(null);
78 for (SgtInfoProcessor processor : sgtInfoProcessors) {
79 processingResult = Futures.transform(processingResult, new AsyncFunction<Void, Void>() {
81 public ListenableFuture<Void> apply(final Void input) throws Exception {
82 LOG.debug("entering stg-info processor {}", processor.getClass().getSimpleName());
83 return processor.processSgtInfo(iseSourceConfig.getTenant(), sgtInfos);
87 result = Futures.transform(processingResult, new Function<Void, Collection<SgtInfo>>() {
90 public Collection<SgtInfo> apply(@Nullable final Void input) {
91 // always success, otherwise there will be TransactionCommitFailedException thrown
95 } catch (Exception e) {
96 LOG.debug("failed to harvest ise", e);
97 result = Futures.immediateFailedFuture(e);
103 private List<SgtInfo> harvestDetails(final String rawSgtSummary, final WebResource baseWebResource, final List<Header> headers) {
104 LOG.trace("rawSgtSummary: {}", rawSgtSummary);
105 final List<Future<SgtInfo>> sgtInfoFutureBag = new ArrayList<>();
107 // prepare worker pool
108 final ExecutorService pool = Executors.newFixedThreadPool(
109 10, new ThreadFactoryBuilder().setNameFormat("ise-sgt-worker-%d").build());
112 final XPath xpath = IseReplyUtil.setupXpath();
114 InputSource inputSource = IseReplyUtil.createInputSource(rawSgtSummary);
116 final NodeList sgtLinkNodes = (NodeList) xpath.evaluate(IseReplyUtil.EXPRESSION_SGT_ALL_LINK_HREFS, inputSource,
117 XPathConstants.NODESET);
118 for (int i = 0; i < sgtLinkNodes.getLength(); i++) {
119 final String sgtLinkHrefValue = sgtLinkNodes.item(i).getNodeValue();
120 LOG.debug("found sgt resource [{}]: {}", i, sgtLinkHrefValue);
122 // submit all query tasks to pool
124 sgtInfoFutureBag.add(pool.submit(new Callable<SgtInfo>() {
126 public SgtInfo call() {
127 SgtInfo sgtInfo = null;
129 sgtInfo = querySgtDetail(baseWebResource, headers, xpath, idx, sgtLinkHrefValue);
130 } catch (XPathExpressionException e) {
131 LOG.info("failed to parse sgt response for {}: {}", sgtLinkHrefValue, e.getMessage());
140 final boolean terminated = pool.awaitTermination(1, TimeUnit.MINUTES);
142 LOG.debug("NOT all sgt-detail queries succeeded - timed out");
145 } catch (InterruptedException | XPathExpressionException e) {
146 LOG.warn("failed to query all-sgt details", e);
149 // harvest available details
150 return sgtInfoFutureBag.stream()
151 .map(this::gainSgtInfoSafely)
152 .filter(Objects::nonNull)
153 .collect(Collectors.toList());
156 private SgtInfo gainSgtInfoSafely(final Future<SgtInfo> response) {
157 SgtInfo result = null;
158 if (response.isDone() && ! response.isCancelled()) {
160 result = response.get();
161 } catch (Exception e) {
162 LOG.debug("sgt-detail query failed even when future was DONE", e);
168 private SgtInfo querySgtDetail(final WebResource baseWebResource, final List<Header> headers, final XPath xpath,
169 final int idx, final String sgtLinkHrefValue) throws XPathExpressionException {
170 // query all sgt entries (serial-vise)
171 final URI hrefToSgtDetailUri = URI.create(sgtLinkHrefValue);
172 final WebResource.Builder requestBuilder = RestClientFactory.createRequestBuilder(baseWebResource, headers, hrefToSgtDetailUri.getPath());
173 // time consuming operation - wait for rest response
174 final String rawSgtDetail = IseReplyUtil.deliverResponse(requestBuilder);
175 LOG.trace("rawSgtDetail: {}", rawSgtDetail);
177 // process response xml
178 final Node sgtNode = (Node) xpath.evaluate(IseReplyUtil.EXPRESSION_SGT_DETAIL, IseReplyUtil.createInputSource(rawSgtDetail),
179 XPathConstants.NODE);
180 final Node sgtName = (Node) xpath.evaluate(IseReplyUtil.EXPRESSION_SGT_NAME_ATTR, sgtNode, XPathConstants.NODE);
181 final Node sgtUuid = (Node) xpath.evaluate(IseReplyUtil.EXPRESSION_SGT_UUID_ATTR, sgtNode, XPathConstants.NODE);
182 final Node sgtValue = (Node) xpath.evaluate(IseReplyUtil.EXPRESSION_SGT_VALUE, sgtNode, XPathConstants.NODE);
183 LOG.debug("sgt value [{}]: {} -> {}", idx, sgtValue, sgtName);
185 // store replies into list of SgtInfo
186 final Sgt sgt = new Sgt(Integer.parseInt(sgtValue.getNodeValue(), 10));
187 return new SgtInfo(sgt, sgtName.getNodeValue(), sgtUuid.getNodeValue());
191 public ListenableFuture<Collection<SgtInfo>> update(@Nonnull final IseContext iseContext) {