HC2VPP-209 - Map register TTL registration support
[hc2vpp.git] / lisp / lisp2vpp / src / main / java / io / fd / hc2vpp / lisp / gpe / translate / read / GpeForwardEntryCustomizer.java
1 /*
2  * Copyright (c) 2017 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.hc2vpp.lisp.gpe.translate.read;
18
19 import static io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor.NO_PARAMS;
20 import static java.lang.String.format;
21
22 import com.google.common.base.Optional;
23 import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer;
24 import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer;
25 import io.fd.hc2vpp.common.translate.util.NamingContext;
26 import io.fd.hc2vpp.lisp.gpe.translate.service.GpeStateCheckService;
27 import io.fd.hc2vpp.lisp.translate.read.dump.executor.params.MappingsDumpParams;
28 import io.fd.hc2vpp.lisp.translate.util.EidTranslator;
29 import io.fd.honeycomb.translate.ModificationCache;
30 import io.fd.honeycomb.translate.read.ReadContext;
31 import io.fd.honeycomb.translate.read.ReadFailedException;
32 import io.fd.honeycomb.translate.spi.read.Initialized;
33 import io.fd.honeycomb.translate.spi.read.InitializingListReaderCustomizer;
34 import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
35 import io.fd.vpp.jvpp.core.dto.GpeFwdEntriesGet;
36 import io.fd.vpp.jvpp.core.dto.GpeFwdEntriesGetReply;
37 import io.fd.vpp.jvpp.core.dto.GpeFwdEntryPathDetails;
38 import io.fd.vpp.jvpp.core.dto.GpeFwdEntryPathDetailsReplyDump;
39 import io.fd.vpp.jvpp.core.dto.GpeFwdEntryPathDump;
40 import io.fd.vpp.jvpp.core.dto.GpeFwdEntryVnisGet;
41 import io.fd.vpp.jvpp.core.dto.GpeFwdEntryVnisGetReply;
42 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
43 import io.fd.vpp.jvpp.core.types.GpeFwdEntry;
44 import io.fd.vpp.jvpp.core.types.GpeLocator;
45 import java.util.Arrays;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.stream.Collectors;
49 import java.util.stream.Stream;
50 import javax.annotation.Nonnull;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.Gpe;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.entry.table.grouping.GpeEntryTable;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.entry.table.grouping.GpeEntryTableBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.entry.table.grouping.gpe.entry.table.GpeEntry;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.entry.table.grouping.gpe.entry.table.GpeEntryBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.entry.table.grouping.gpe.entry.table.GpeEntryKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.feature.data.grouping.GpeFeatureData;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.locator.pairs.grouping.LocatorPair;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.locator.pairs.grouping.LocatorPairBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.lisp.rev170803.MapReplyAction;
61 import org.opendaylight.yangtools.concepts.Builder;
62 import org.opendaylight.yangtools.yang.binding.DataObject;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64
65 public class GpeForwardEntryCustomizer extends FutureJVppCustomizer
66         implements InitializingListReaderCustomizer<GpeEntry, GpeEntryKey, GpeEntryBuilder>, JvppReplyConsumer,
67         EidTranslator {
68
69     private final DumpCacheManager<GpeFwdEntriesGetReply, Integer> entryDumpManager;
70     private final DumpCacheManager<GpeFwdEntryPathDetailsReplyDump, Integer> entryDumpCacheManager;
71     private final DumpCacheManager<GpeFwdEntryVnisGetReply, Void> activeVnisDumpManager;
72     private final NamingContext gpeEntryMappingContext;
73     private final GpeStateCheckService gpeStateCheckService;
74
75     public GpeForwardEntryCustomizer(@Nonnull final FutureJVppCore futureJVppCore,
76                                      @Nonnull final GpeStateCheckService gpeStateCheckService,
77                                      @Nonnull final NamingContext gpeEntryMappingContext) {
78         super(futureJVppCore);
79         this.gpeStateCheckService = gpeStateCheckService;
80         this.gpeEntryMappingContext = gpeEntryMappingContext;
81         this.entryDumpManager = new DumpCacheManager.DumpCacheManagerBuilder<GpeFwdEntriesGetReply, Integer>()
82                 .acceptOnly(GpeFwdEntriesGetReply.class)
83                 .withExecutor((identifier, vni) -> {
84                     GpeFwdEntriesGet request = new GpeFwdEntriesGet();
85                     request.vni = vni;
86                     return getReplyForRead(getFutureJVpp().gpeFwdEntriesGet(request).toCompletableFuture(), identifier);
87                 }).build();
88         entryDumpCacheManager =
89                 new DumpCacheManager.DumpCacheManagerBuilder<GpeFwdEntryPathDetailsReplyDump, Integer>()
90                         .acceptOnly(GpeFwdEntryPathDetailsReplyDump.class)
91                         .withExecutor((identifier, fwdEntryIndex) -> {
92                             GpeFwdEntryPathDump request = new GpeFwdEntryPathDump();
93                             request.fwdEntryIndex = fwdEntryIndex;
94                             return getReplyForRead(getFutureJVpp().gpeFwdEntryPathDump(request).toCompletableFuture(),
95                                     identifier);
96                         }).build();
97         activeVnisDumpManager = new DumpCacheManager.DumpCacheManagerBuilder<GpeFwdEntryVnisGetReply, Void>()
98                 .acceptOnly(GpeFwdEntryVnisGetReply.class)
99                 .withExecutor((identifier, params) -> getReplyForRead(
100                         getFutureJVpp().gpeFwdEntryVnisGet(new GpeFwdEntryVnisGet()).toCompletableFuture(),
101                         identifier))
102                 .build();
103     }
104
105     @Nonnull
106     @Override
107     public Initialized<? extends DataObject> init(@Nonnull final InstanceIdentifier<GpeEntry> id,
108                                                   @Nonnull final GpeEntry readValue,
109                                                   @Nonnull final ReadContext ctx) {
110         return Initialized.create(InstanceIdentifier.create(Gpe.class)
111                 .child(GpeFeatureData.class)
112                 .child(GpeEntryTable.class)
113                 .child(GpeEntry.class, id.firstKeyOf(GpeEntry.class)), readValue);
114     }
115
116     @Nonnull
117     @Override
118     public List<GpeEntryKey> getAllIds(@Nonnull final InstanceIdentifier<GpeEntry> id,
119                                        @Nonnull final ReadContext context)
120             throws ReadFailedException {
121
122         if (!gpeStateCheckService.isGpeEnabled(context)) {
123             return Collections.emptyList();
124         }
125
126         return activeVnis(id, context.getModificationCache())
127                 .flatMap(vni -> getKeysForVni(id, vni, context).stream())
128                 .collect(Collectors.toList());
129     }
130
131     @Override
132     public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<GpeEntry> readData) {
133         ((GpeEntryTableBuilder) builder).setGpeEntry(readData);
134     }
135
136     @Nonnull
137     @Override
138     public GpeEntryBuilder getBuilder(@Nonnull final InstanceIdentifier<GpeEntry> id) {
139         return new GpeEntryBuilder();
140     }
141
142     @Override
143     public void readCurrentAttributes(@Nonnull final InstanceIdentifier<GpeEntry> id,
144                                       @Nonnull final GpeEntryBuilder builder,
145                                       @Nonnull final ReadContext ctx) throws ReadFailedException {
146         if (!gpeStateCheckService.isGpeEnabled(ctx)) {
147             return;
148         }
149
150         final String entryId = id.firstKeyOf(GpeEntry.class).getId();
151
152         // reads configured vni's, then reads entries for them and filter out current one
153         final java.util.Optional<GpeFwdEntry> entryCandicate = activeVnis(id, ctx.getModificationCache())
154                 .flatMap(vni -> getEntriesForVni(id, vni, ctx).stream())
155                 .filter(entry -> entryId
156                         .equals(gpeEntryMappingContext.getName(entry.fwdEntryIndex, ctx.getMappingContext())))
157                 .findAny();
158
159         if (entryCandicate.isPresent()) {
160             final GpeFwdEntry gpeFwdEntry = entryCandicate.get();
161
162             final int entryVni = gpeFwdEntry.vni;
163
164             if (!matchUndefinedEid(gpeFwdEntry.leid)) {
165                 builder.setLocalEid(getArrayAsGpeLocalEid(MappingsDumpParams.EidType.valueOf(gpeFwdEntry.eidType),
166                         gpeFwdEntry.leid, gpeFwdEntry.leidPrefixLen, entryVni));
167             }
168
169             builder.setId(entryId)
170                     .setDpTable((long) gpeFwdEntry.dpTable)
171                     .setRemoteEid(getArrayAsGpeRemoteEid(MappingsDumpParams.EidType.valueOf(gpeFwdEntry.eidType),
172                             gpeFwdEntry.reid, gpeFwdEntry.reidPrefixLen, entryVni))
173                     .setVni((long) entryVni);
174
175             final Optional<GpeFwdEntryPathDetailsReplyDump> locatorsDump =
176                     entryDumpCacheManager.getDump(id, ctx.getModificationCache(), gpeFwdEntry.fwdEntryIndex);
177
178             // if any locators exist,it is a positive mapping
179             if (locatorsDump.isPresent() && locatorsDump.get().gpeFwdEntryPathDetails != null &&
180                     !locatorsDump.get().gpeFwdEntryPathDetails.isEmpty()) {
181                 final List<LocatorPair> pairs =
182                         java.util.Optional.ofNullable(locatorsDump.get().gpeFwdEntryPathDetails)
183                                 .orElse(Collections.emptyList())
184                                 .stream()
185                                 .map(entry -> buildLocatorPair(entry))
186                                 .collect(Collectors.toList());
187                 builder.setLocatorPair(pairs);
188             } else {
189                 // negative otherwise
190                 builder.setAction(MapReplyAction.forValue(gpeFwdEntry.action));
191             }
192         }
193     }
194
195     // not matching by specifically sized array, easier to adapt if vpp going to change size of arrays they send
196     // addresses , because for lisp eid there are at least 3 possible sizes(v4 - 4,mac - 6,v6 - 16)
197     private static boolean matchUndefinedEid(byte[] addr) {
198         return addr == null || Arrays.equals(addr, new byte[addr.length]);
199     }
200
201     private List<GpeFwdEntry> getEntriesForVni(final InstanceIdentifier<GpeEntry> id, final int vni,
202                                                final ReadContext context) {
203         final Optional<GpeFwdEntriesGetReply> dump = getEntiesDump(id, vni, context);
204         if (dump.isPresent()) {
205             return Arrays.stream(java.util.Optional.ofNullable(dump.get().entries).orElse(new GpeFwdEntry[]{}))
206                     .collect(Collectors.toList());
207         }
208
209         return Collections.emptyList();
210     }
211
212     private List<GpeEntryKey> getKeysForVni(final InstanceIdentifier<GpeEntry> id, final int vni,
213                                             final ReadContext context) {
214
215         final Optional<GpeFwdEntriesGetReply> dump = getEntiesDump(id, vni, context);
216         if (dump.isPresent()) {
217             return Arrays.stream(java.util.Optional.ofNullable(dump.get().entries).orElse(new GpeFwdEntry[]{}))
218                     .map(entry -> gpeEntryMappingContext.getName(entry.fwdEntryIndex, context.getMappingContext()))
219                     .map(GpeEntryKey::new)
220                     .collect(Collectors.toList());
221         }
222
223         return Collections.emptyList();
224     }
225
226     private Optional<GpeFwdEntriesGetReply> getEntiesDump(final InstanceIdentifier<GpeEntry> id, final int vni,
227                                                           final ReadContext context) {
228         final Optional<GpeFwdEntriesGetReply> dump;
229         try {
230             dump = entryDumpManager.getDump(id, context.getModificationCache(), vni);
231         } catch (ReadFailedException e) {
232             throw new IllegalStateException(format("Unable to read Gpe entries for vni %s", vni), e);
233         }
234         return dump;
235     }
236
237     private LocatorPair buildLocatorPair(final GpeFwdEntryPathDetails entry) {
238         final GpeLocator lclLoc = entry.lclLoc;
239         final GpeLocator rmtLoc = entry.rmtLoc;
240         return new LocatorPairBuilder()
241                 .setLocalLocator(arrayToIpAddress(!byteToBoolean(lclLoc.isIp4), lclLoc.addr))
242                 .setRemoteLocator(arrayToIpAddress(!byteToBoolean(rmtLoc.isIp4), rmtLoc.addr))
243                 .setWeight((short) lclLoc.weight).build();
244     }
245
246     private Stream<Integer> activeVnis(final InstanceIdentifier<GpeEntry> id,
247                                        final ModificationCache cache) throws ReadFailedException {
248         final int[] vnis = activeVnisDumpManager.getDump(id, cache, NO_PARAMS).or(() -> {
249             final GpeFwdEntryVnisGetReply reply = new GpeFwdEntryVnisGetReply();
250             reply.vnis = new int[0];
251             return reply;
252         }).vnis;
253         return Arrays.stream(vnis).boxed();
254     }
255 }