update deprecated transform and addCallback methods
[groupbasedpolicy.git] / sxp-integration / sxp-ise-adapter / src / main / java / org / opendaylight / groupbasedpolicy / sxp_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.sxp_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.google.common.util.concurrent.MoreExecutors;
16 import com.google.common.util.concurrent.ThreadFactoryBuilder;
17 import com.sun.jersey.api.client.Client;
18 import com.sun.jersey.api.client.WebResource;
19 import java.net.URI;
20 import java.security.GeneralSecurityException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Objects;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 import java.util.concurrent.Future;
30 import java.util.concurrent.TimeUnit;
31 import java.util.stream.Collectors;
32 import javax.annotation.Nonnull;
33 import javax.annotation.Nullable;
34 import javax.xml.xpath.XPath;
35 import javax.xml.xpath.XPathExpressionException;
36 import org.opendaylight.groupbasedpolicy.sxp_ise_adapter.impl.util.IseReplyUtil;
37 import org.opendaylight.groupbasedpolicy.sxp_ise_adapter.impl.util.RestClientFactory;
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.IseSourceConfig;
39 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;
40 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;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.w3c.dom.Node;
45 import org.w3c.dom.NodeList;
46 import org.xml.sax.InputSource;
47
48 /**
49  * Purpose: harvest sgt + names available via ise-rest-api
50  */
51 public class GbpIseSgtHarvesterImpl implements GbpIseSgtHarvester {
52
53     private static final Logger LOG = LoggerFactory.getLogger(GbpIseSgtHarvesterImpl.class);
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<Collection<SgtInfo>> harvestAll(@Nonnull final IseContext iseContext) {
66         LOG.debug("ise-source: harvestAll {} -> {}", iseContext.getIseSourceConfig().getTenant(),
67                 iseContext.getIseSourceConfig().getConnectionConfig().getIseRestUrl());
68         ListenableFuture<Collection<SgtInfo>> result;
69         try {
70             final IseSourceConfig iseSourceConfig = iseContext.getIseSourceConfig();
71             final ConnectionConfig connectionConfig = iseSourceConfig.getConnectionConfig();
72             final WebResource baseWebResource = createWebResource(connectionConfig);
73
74             final WebResource.Builder requestBuilder = RestClientFactory.createRequestBuilder(baseWebResource,
75                     connectionConfig.getHeader(), RestClientFactory.PATH_ERS_CONFIG_SGT);
76             final String rawSgtSummary = IseReplyUtil.deliverResponse(requestBuilder);
77
78             final List<SgtInfo> sgtInfos = harvestDetails(rawSgtSummary, baseWebResource, connectionConfig, iseContext.getUuidToSgtMap());
79
80             ListenableFuture<Void> processingResult = Futures.immediateCheckedFuture(null);
81             for (SgtInfoProcessor processor : sgtInfoProcessors) {
82                 processingResult = Futures.transformAsync(processingResult, new AsyncFunction<Void, Void>() {
83                     @Override
84                     public ListenableFuture<Void> apply(final Void input) throws Exception {
85                         LOG.debug("entering stg-info processor {}", processor.getClass().getSimpleName());
86                         return processor.processSgtInfo(iseSourceConfig.getTenant(), sgtInfos);
87                     }
88                 }, MoreExecutors.directExecutor());
89             }
90             result = Futures.transform(processingResult, new Function<Void, Collection<SgtInfo>>() {
91                 @Nullable
92                 @Override
93                 public Collection<SgtInfo> apply(@Nullable final Void input) {
94                     // update uuid map
95                     for (SgtInfo sgtInfo : sgtInfos) {
96                         iseContext.getUuidToSgtMap().put(sgtInfo.getUuid(), sgtInfo.getSgt().getValue());
97                     }
98                     //TODO: store harvest stats to DS/operational
99                     // always success, otherwise there will be TransactionCommitFailedException thrown
100                     return sgtInfos;
101                 }
102             }, MoreExecutors.directExecutor());
103         } catch (Exception e) {
104             LOG.debug("failed to harvest ise", e);
105             result = Futures.immediateFailedFuture(e);
106         }
107
108         return result;
109     }
110
111     private WebResource createWebResource(final ConnectionConfig connectionConfig) throws GeneralSecurityException {
112         final Client iseClient = RestClientFactory.createIseClient(connectionConfig);
113         return iseClient.resource(connectionConfig.getIseRestUrl().getValue());
114     }
115
116     private List<SgtInfo> harvestDetails(final String rawSgtSummary, final WebResource baseWebResource,
117                                          final ConnectionConfig connectionConfig, final Map<String, Integer> uuidToSgtMap) {
118         LOG.trace("rawSgtSummary: {}", rawSgtSummary);
119         final List<Future<SgtInfo>> sgtInfoFutureBag = new ArrayList<>();
120
121         // prepare worker pool
122         final ExecutorService pool = Executors.newFixedThreadPool(
123                 10, new ThreadFactoryBuilder().setNameFormat("ise-sgt-worker-%d").build());
124
125         // parse sgtSummary
126         final XPath xpath = IseReplyUtil.setupXpath();
127
128         final InputSource inputSource = IseReplyUtil.createInputSource(rawSgtSummary);
129         try {
130             final NodeList sgtResources = IseReplyUtil.findAllSgtResourceNodes(xpath, inputSource);
131             final Collection<Node> sgtLinkNodes = IseReplyUtil.filterNewResourcesByID(uuidToSgtMap, xpath, sgtResources);
132
133             int counter = 0;
134             for (Node sgtLinkNode : sgtLinkNodes) {
135                 final String sgtLinkHrefValue = sgtLinkNode.getNodeValue();
136                 LOG.debug("found sgt resource: {}", sgtLinkHrefValue);
137
138                 // submit all query tasks to pool
139                 final int idx = counter++;
140                 sgtInfoFutureBag.add(pool.submit(new Callable<SgtInfo>() {
141                     @Override
142                     public SgtInfo call() {
143                         SgtInfo sgtInfo = null;
144                         try {
145                             sgtInfo = querySgtDetail(baseWebResource, connectionConfig.getHeader(), xpath, idx, sgtLinkHrefValue);
146                         } catch (XPathExpressionException e) {
147                             LOG.info("failed to parse sgt response for {}: {}", sgtLinkHrefValue, e.getMessage());
148                         }
149                         return sgtInfo;
150                     }
151                 }));
152             }
153
154             // stop pool
155             pool.shutdown();
156             final boolean terminated = pool.awaitTermination(1, TimeUnit.MINUTES);
157             if (! terminated) {
158                 LOG.debug("NOT all sgt-detail queries succeeded - timed out");
159                 pool.shutdownNow();
160             }
161         } catch (InterruptedException | XPathExpressionException e) {
162             LOG.warn("failed to query all-sgt details", e);
163         }
164
165         // harvest available details
166         return sgtInfoFutureBag.stream()
167                 .map(this::gainSgtInfoSafely)
168                 .filter(Objects::nonNull)
169                 .collect(Collectors.toList());
170     }
171
172     private SgtInfo gainSgtInfoSafely(final Future<SgtInfo> response) {
173         SgtInfo result = null;
174         if (response.isDone() && ! response.isCancelled()) {
175             try {
176                 result = response.get();
177             } catch (Exception e) {
178                 LOG.debug("sgt-detail query failed even when future was DONE", e);
179             }
180         }
181         return result;
182     }
183
184     private SgtInfo querySgtDetail(final WebResource baseWebResource, final List<Header> headers, final XPath xpath,
185                                    final int idx, final String sgtLinkHrefValue) throws XPathExpressionException {
186         // query all sgt entries (serial-vise)
187         final URI hrefToSgtDetailUri = URI.create(sgtLinkHrefValue);
188         final WebResource.Builder requestBuilder = RestClientFactory.createRequestBuilder(baseWebResource, headers,
189                 hrefToSgtDetailUri.getPath());
190         // time consuming operation - wait for rest response
191         final String rawSgtDetail = IseReplyUtil.deliverResponse(requestBuilder);
192         LOG.trace("rawSgtDetail: {}", rawSgtDetail);
193
194         // process response xml
195         final Node sgtNode = IseReplyUtil.findSgtDetailNode(xpath, rawSgtDetail);
196         final Node sgtName = IseReplyUtil.gainSgtName(xpath, sgtNode);
197         final Node sgtUuid = IseReplyUtil.gainSgtUuid(xpath, sgtNode);
198         final Node sgtValue = IseReplyUtil.gainSgtValue(xpath, sgtNode);
199         LOG.debug("sgt value [{}]: {} -> {}", idx, sgtValue, sgtName);
200
201         // store replies into list of SgtInfo
202         final Sgt sgt = new Sgt(Integer.parseInt(sgtValue.getNodeValue(), 10));
203         return new SgtInfo(sgt, sgtName.getNodeValue(), sgtUuid.getNodeValue());
204     }
205
206 }