9f4cd7e77618dc1d0480b7424ad70b9402c6ba3d
[honeycomb.git] / v3po / v3po2vpp / src / main / java / io / fd / honeycomb / translate / v3po / vppclassifier / ClassifySessionReader.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.translate.v3po.vppclassifier;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.base.Preconditions.checkState;
22
23 import com.google.common.base.Optional;
24 import com.google.common.primitives.UnsignedInts;
25 import io.fd.honeycomb.translate.MappingContext;
26 import io.fd.honeycomb.translate.read.ReadContext;
27 import io.fd.honeycomb.translate.read.ReadFailedException;
28 import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer;
29 import io.fd.honeycomb.translate.v3po.interfacesstate.InterfaceDataTranslator;
30 import io.fd.honeycomb.translate.vpp.util.FutureJVppCustomizer;
31 import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
32 import io.fd.vpp.jvpp.core.dto.ClassifySessionDetails;
33 import io.fd.vpp.jvpp.core.dto.ClassifySessionDetailsReplyDump;
34 import io.fd.vpp.jvpp.core.dto.ClassifySessionDump;
35 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.stream.Collectors;
40 import javax.annotation.Nonnull;
41 import javax.annotation.Nullable;
42 import javax.xml.bind.DatatypeConverter;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.OpaqueIndex;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.VppNode;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySession;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySessionBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySessionKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTable;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTableBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.state.ClassifyTableKey;
52 import org.opendaylight.yangtools.concepts.Builder;
53 import org.opendaylight.yangtools.yang.binding.DataObject;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * Reader customizer responsible for classify session read.<br> to VPP.<br> Equivalent to invoking {@code vppctl show
60  * class table verbose} command.
61  */
62 public class ClassifySessionReader extends FutureJVppCustomizer
63         implements ListReaderCustomizer<ClassifySession, ClassifySessionKey, ClassifySessionBuilder>,
64         InterfaceDataTranslator, VppNodeReader, JvppReplyConsumer {
65
66     private static final Logger LOG = LoggerFactory.getLogger(ClassifySessionReader.class);
67     static final String CACHE_KEY = ClassifySessionReader.class.getName();
68
69     private final VppClassifierContextManager classifyTableContext;
70
71     public ClassifySessionReader(@Nonnull final FutureJVppCore futureJVppCore,
72                                  @Nonnull final VppClassifierContextManager classifyTableContext) {
73         super(futureJVppCore);
74         this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null");
75     }
76
77     @Override
78     public void merge(@Nonnull final Builder<? extends DataObject> builder,
79                       @Nonnull final List<ClassifySession> readData) {
80         ((ClassifyTableBuilder) builder).setClassifySession(readData);
81     }
82
83     @Nonnull
84     @Override
85     public ClassifySessionBuilder getBuilder(@Nonnull final InstanceIdentifier<ClassifySession> id) {
86         return new ClassifySessionBuilder();
87     }
88
89     @Override
90     public void readCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id,
91                                       @Nonnull final ClassifySessionBuilder builder, @Nonnull final ReadContext ctx)
92             throws ReadFailedException {
93         LOG.debug("Reading attributes for classify session: {}", id);
94
95         final ClassifySessionKey key = id.firstKeyOf(ClassifySession.class);
96         checkArgument(key != null, "could not find ClassifySession key in {}", id);
97
98         final ClassifySessionDetailsReplyDump classifySessionDump = dumpClassifySessions(id, ctx);
99         final byte[] match = DatatypeConverter.parseHexBinary(key.getMatch().getValue().replace(":", ""));
100         final Optional<ClassifySessionDetails> classifySession =
101                 findClassifySessionDetailsByMatch(classifySessionDump, match);
102
103         if (classifySession.isPresent()) {
104             final ClassifySessionDetails detail = classifySession.get();
105             builder.setHitNext(
106                     readVppNode(detail.tableId, detail.hitNextIndex, classifyTableContext, ctx.getMappingContext(), LOG)
107                             .get());
108             if (detail.opaqueIndex != ~0) {
109                 // value is specified:
110                 builder.setOpaqueIndex(readOpaqueIndex(detail.tableId, detail.opaqueIndex, ctx.getMappingContext()));
111             }
112             builder.setAdvance(detail.advance);
113             builder.setMatch(key.getMatch());
114
115             if (LOG.isTraceEnabled()) {
116                 LOG.trace("Attributes for classify session {} successfully read: {}", id, builder.build());
117             }
118         }
119     }
120
121     private OpaqueIndex readOpaqueIndex(final int tableIndex, final int opaqueIndex, final MappingContext ctx) {
122         // We first try to map the value to a vpp node, if that fails, simply wrap the u32 value
123         // TODO: HONEYCOMB-118 the approach might fail if the opaqueIndex contains small value that collides
124         // with some of the adjacent nodes
125
126         final Optional<VppNode> node = readVppNode(tableIndex, opaqueIndex, classifyTableContext, ctx, LOG);
127         if (node.isPresent()) {
128             return new OpaqueIndex(node.get());
129         } else {
130             return new OpaqueIndex(UnsignedInts.toLong(opaqueIndex));
131         }
132     }
133
134     @Nullable
135     private ClassifySessionDetailsReplyDump dumpClassifySessions(@Nonnull final InstanceIdentifier<?> id,
136                                                                  @Nonnull final ReadContext ctx)
137             throws ReadFailedException {
138         final ClassifyTableKey tableKey = id.firstKeyOf(ClassifyTable.class);
139         checkArgument(tableKey != null, "could not find ClassifyTable key in {}", id);
140
141         final String cacheKey = CACHE_KEY + tableKey;
142
143         ClassifySessionDetailsReplyDump classifySessionDump =
144                 (ClassifySessionDetailsReplyDump) ctx.getModificationCache().get(cacheKey);
145         if (classifySessionDump != null) {
146             LOG.debug("Classify sessions is present in cache: {}", cacheKey);
147             return classifySessionDump;
148         }
149
150         final String tableName = tableKey.getName();
151         checkState(classifyTableContext.containsTable(tableName, ctx.getMappingContext()),
152                 "Reading classify sessions for table {}, but table index could not be found in the classify table context",
153                 tableName);
154         final int tableId = classifyTableContext.getTableIndex(tableName, ctx.getMappingContext());
155         LOG.debug("Dumping classify sessions for classify table id={}", tableId);
156
157
158         final ClassifySessionDump dumpRequest = new ClassifySessionDump();
159         dumpRequest.tableId = tableId;
160         final int timeOut = 30; // there can be many session with current ietf-acl implementation (could be probably
161         // removed after fixing HONEYCOMB-247)
162         classifySessionDump =
163                 getReplyForRead(getFutureJVpp().classifySessionDump(dumpRequest).toCompletableFuture(), id, timeOut);
164
165         if (classifySessionDump != null) {
166             // update the cache:
167             ctx.getModificationCache().put(cacheKey, classifySessionDump);
168         }
169
170         return classifySessionDump;
171     }
172
173     private static Optional<ClassifySessionDetails> findClassifySessionDetailsByMatch(
174             @Nullable final ClassifySessionDetailsReplyDump classifySessionDump, @Nonnull final byte[] match) {
175         if (classifySessionDump != null && classifySessionDump.classifySessionDetails != null) {
176             final List<ClassifySessionDetails> details = classifySessionDump.classifySessionDetails;
177             final List<ClassifySessionDetails> filteredSessions = details.stream()
178                     .filter(singleDetail -> Arrays.equals(singleDetail.match, match)).collect(Collectors.toList());
179             if (filteredSessions.isEmpty()) {
180                 return Optional.absent();
181             } else if (filteredSessions.size() == 1) {
182                 return Optional.of(filteredSessions.get(0));
183             } else {
184                 throw new IllegalStateException(String.format(
185                         "Found %d classify sessions witch given match. Single session expected.",
186                         filteredSessions.size()));
187             }
188         }
189         return Optional.absent();
190     }
191
192     @Nonnull
193     @Override
194     public List<ClassifySessionKey> getAllIds(@Nonnull final InstanceIdentifier<ClassifySession> id,
195                                               @Nonnull final ReadContext ctx) throws ReadFailedException {
196         LOG.debug("Reading list of keys for classify sessions: {}", id);
197
198         final ClassifySessionDetailsReplyDump classifySessionDump = dumpClassifySessions(id, ctx);
199         if (classifySessionDump != null && classifySessionDump.classifySessionDetails != null) {
200             return classifySessionDump.classifySessionDetails.stream()
201                     .map(detail -> new ClassifySessionKey(new HexString(printHexBinary(detail.match))))
202                     .collect(Collectors.toList());
203         } else {
204             return Collections.emptyList();
205         }
206     }
207 }