/* * Copyright (c) 2017 Cisco and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.fd.hc2vpp.v3po.read.cache; import static io.fd.hc2vpp.common.translate.util.JvppReplyConsumer.INSTANCE; import static java.util.stream.Collectors.toMap; import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; import io.fd.hc2vpp.common.translate.util.NamingContext; import io.fd.honeycomb.translate.ModificationCache; import io.fd.honeycomb.translate.read.ReadContext; import io.fd.honeycomb.translate.read.ReadFailedException; import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor; import io.fd.honeycomb.translate.util.read.cache.StaticCacheKeyFactory; import io.fd.jvpp.core.dto.SwInterfaceDetails; import io.fd.jvpp.core.dto.SwInterfaceDetailsReplyDump; import io.fd.jvpp.core.dto.SwInterfaceDump; import io.fd.jvpp.core.future.FutureJVppCore; import io.fd.jvpp.core.types.InterfaceIndex; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Manager for dump data of interfaces/sub-interfaces */ final class InterfaceCacheDumpManagerImpl implements InterfaceCacheDumpManager { private static final Logger LOG = LoggerFactory.getLogger(InterfaceCacheDumpManagerImpl.class); // byNameIndex must be cached, not held as reference here, to have it destroyed with cache after transaction static final String BY_NAME_INDEX_KEY = InterfaceCacheDumpManagerImpl.class.getName() + "_byNameIndex"; private NamingContext namingContext; private final DumpCacheManager specificDumpManager; private final DumpCacheManager fullDumpManager; InterfaceCacheDumpManagerImpl(@Nonnull final FutureJVppCore jvpp, @Nonnull final NamingContext namingContext) { this.namingContext = namingContext; specificDumpManager = specificInterfaceDumpManager(jvpp); fullDumpManager = fullInterfaceDumpManager(jvpp, new StaticCacheKeyFactory(InterfaceCacheDumpManagerImpl.class.getName() + "_dump", SwInterfaceDetailsReplyDump.class)); } @Override @Nonnull public synchronized Stream getInterfaces(@Nonnull final InstanceIdentifier identifier, @Nonnull final ReadContext ctx) throws ReadFailedException { LOG.debug("Reading all interfaces[{}]", identifier); return initMapAndGet(identifier, ctx).entrySet().stream().map(Map.Entry::getValue); } @Override @Nullable public synchronized SwInterfaceDetails getInterfaceDetail(@Nonnull final InstanceIdentifier identifier, @Nonnull final ReadContext ctx, @Nonnull final String interfaceName) throws ReadFailedException { final Map interfaceIndex = getMap(ctx); // does not attempt to cover cases with concurrent updates, as tx should be atomic if (interfaceIndex != null) { // tries to find interface in map return interfaceIndex.get(interfaceName); } else { // if map is not present, use specific dump(it will be cached standard way, under key constructed from IID) return dumpSpecificDetail(identifier, ctx, interfaceName); } } private SwInterfaceDetails dumpSpecificDetail(@Nonnull final InstanceIdentifier identifier, @Nonnull final ReadContext ctx, @Nonnull final String interfaceName) throws ReadFailedException { LOG.debug("Interface {} not present in cached data, performing specific dump[{}]", interfaceName, identifier); final SwInterfaceDetailsReplyDump reply = specificDumpManager.getDump(identifier, ctx.getModificationCache(), interfaceName) .orElse(new SwInterfaceDetailsReplyDump()); if (reply.swInterfaceDetails.isEmpty()) { return null; } return reply.swInterfaceDetails.get(0); } private Map initMapAndGet(final InstanceIdentifier identifier, final ReadContext ctx) throws ReadFailedException { final ModificationCache cache = ctx.getModificationCache(); if (!cache.containsKey(BY_NAME_INDEX_KEY)) { LOG.debug("Performing dump[{}]", identifier); final SwInterfaceDetailsReplyDump dump = fullDumpManager.getDump(identifier, cache) .orElse(new SwInterfaceDetailsReplyDump()); // naming context initialization must be done here, as it is uses getName in next step, therefore it would // create artificial mapping for every interface, because this happens before interface dump is processed dump.swInterfaceDetails.forEach((elt) -> { // Store interface name from VPP in context if not yet present if (!namingContext.containsName(elt.swIfIndex, ctx.getMappingContext())) { namingContext.addName(elt.swIfIndex, ByteDataTranslator.INSTANCE.toString(elt.interfaceName), ctx.getMappingContext()); } LOG.trace("Interface with name: {}, VPP name: {} and index: {} found in VPP", getInterfaceName(ctx, elt), elt.interfaceName, elt.swIfIndex); }); final Map freshIndex = dump.swInterfaceDetails.stream() .collect(toMap(detail -> getInterfaceName(ctx, detail), detail -> detail)); putMap(freshIndex, ctx); } return getMap(ctx); } private String getInterfaceName(final ReadContext ctx, final SwInterfaceDetails elt) { return namingContext.getName(elt.swIfIndex, ctx.getMappingContext()); } private static Map getMap(final ReadContext ctx) { return (Map) ctx.getModificationCache().get(BY_NAME_INDEX_KEY); } private static void putMap(final Map map, final ReadContext ctx) { ctx.getModificationCache().put(BY_NAME_INDEX_KEY, map); } private static DumpCacheManager fullInterfaceDumpManager( final FutureJVppCore jvpp, final StaticCacheKeyFactory cacheKeyFactory) { return new DumpCacheManager.DumpCacheManagerBuilder() .withExecutor(fullInterfaceDumpExecutor(jvpp)) .withCacheKeyFactory(cacheKeyFactory) .acceptOnly(SwInterfaceDetailsReplyDump.class) .build(); } private static DumpCacheManager specificInterfaceDumpManager( final FutureJVppCore jvpp) { return new DumpCacheManager.DumpCacheManagerBuilder() .withExecutor(specificInterfaceDumpExecutor(jvpp)) .acceptOnly(SwInterfaceDetailsReplyDump.class) .build(); } private static EntityDumpExecutor fullInterfaceDumpExecutor( final FutureJVppCore api) { return (identifier, params) -> { final SwInterfaceDump request = new SwInterfaceDump(); request.swIfIndex = new InterfaceIndex(); request.swIfIndex.interfaceindex = ~0; request.nameFilter = "".getBytes(); request.nameFilterValid = 0; final CompletableFuture swInterfaceDetailsReplyDumpCompletableFuture = api.swInterfaceDump(request).toCompletableFuture(); return INSTANCE.getReplyForRead(swInterfaceDetailsReplyDumpCompletableFuture, identifier); }; } private static EntityDumpExecutor specificInterfaceDumpExecutor( final FutureJVppCore api) { return (identifier, ifaceName) -> { final SwInterfaceDump request = new SwInterfaceDump(); request.swIfIndex = new InterfaceIndex(); request.swIfIndex.interfaceindex =~0; request.nameFilter = ifaceName.getBytes(); request.nameFilterValid = 1; final CompletableFuture swInterfaceDetailsReplyDumpCompletableFuture = api.swInterfaceDump(request).toCompletableFuture(); return INSTANCE.getReplyForRead(swInterfaceDetailsReplyDumpCompletableFuture, identifier); }; } }