diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-05-06 09:41:51 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-05-11 09:21:01 +0200 |
commit | 44348596ff0c675bf3b799f88288111d92d771e6 (patch) | |
tree | 17edea7566a34efe7dedb5ee3d7833616e24f121 /v3po/v3po2vpp/src/main/java/io/fd/honeycomb | |
parent | 557acf16589e03bf17d255af7438614e63c2d2d3 (diff) |
HONEYCOMG-47: Tap interface CRUD support
Tap interface specific configuration and state was added to V3po
yang model.
TapCustomizer added.
Fixed customizers for Interfaces state.
Fixed bug in Bridge domain customizers.
Change-Id: I9dd47b8ada5153df8732c02cb59d331ab1adc71e
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/v3po2vpp/src/main/java/io/fd/honeycomb')
8 files changed, 505 insertions, 102 deletions
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java index 5d5a6c08b..b6d8748b9 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java @@ -97,15 +97,12 @@ public class InterfaceCustomizer extends FutureJVppCustomizer implements ListWri private void setInterface(final InstanceIdentifier<Interface> id, final Interface swIf) throws VppApiInvocationException, WriteFailedException { - LOG.info("Setting interface {}, type: {}", swIf.getName(), swIf.getType().getSimpleName()); - LOG.debug("Setting interface {}", swIf); - + LOG.debug("Setting interface: {} to: {}", id, swIf); setInterfaceAttributes(swIf, swIf.getName()); } private void setInterfaceAttributes(final Interface swIf, final String swIfName) throws VppApiInvocationException { - LOG.debug("Creating {} interface {}", swIf.getType().getSimpleName(), swIf.getName()); setInterfaceFlags(swIfName, interfaceContext.getIndex(swIfName), swIf.isEnabled() ? (byte) 1 : (byte) 0); @@ -114,8 +111,7 @@ public class InterfaceCustomizer extends FutureJVppCustomizer implements ListWri private void updateInterface(final InstanceIdentifier<Interface> id, final Interface dataBefore, final Interface dataAfter) throws VppApiInvocationException { - LOG.info("Updating interface {}, type: {}", dataAfter.getName(), dataAfter.getType().getSimpleName()); - LOG.debug("Updating interface {}", dataAfter); + LOG.debug("Updating interface:{} to: {}", id, dataAfter); setInterfaceAttributes(dataAfter, dataAfter.getName()); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizer.java new file mode 100644 index 000000000..f05238dda --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizer.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2016 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.honeycomb.v3po.translate.v3po.interfaces; + +import com.google.common.base.Optional; +import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import io.fd.honeycomb.v3po.translate.v3po.util.VppApiInvocationException; +import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; +import io.fd.honeycomb.v3po.translate.write.WriteFailedException; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Tap; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.dto.TapConnect; +import org.openvpp.jvpp.dto.TapConnectReply; +import org.openvpp.jvpp.dto.TapDelete; +import org.openvpp.jvpp.dto.TapDeleteReply; +import org.openvpp.jvpp.dto.TapModify; +import org.openvpp.jvpp.dto.TapModifyReply; +import org.openvpp.jvpp.future.FutureJVpp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TapCustomizer extends FutureJVppCustomizer implements ChildWriterCustomizer<Tap> { + + private static final Logger LOG = LoggerFactory.getLogger(TapCustomizer.class); + private final NamingContext interfaceContext; + + public TapCustomizer(final FutureJVpp vppApi, final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = interfaceContext; + } + + @Nonnull + @Override + public Optional<Tap> extract(@Nonnull final InstanceIdentifier<Tap> currentId, + @Nonnull final DataObject parentData) { + return Optional.fromNullable(((VppInterfaceAugmentation) parentData).getTap()); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, @Nonnull final Tap dataAfter, + @Nonnull final Context writeContext) + throws WriteFailedException.CreateFailedException { + try { + createTap(id.firstKeyOf(Interface.class).getName(), dataAfter); + } catch (VppApiInvocationException e) { + LOG.warn("Write of Tap failed", e); + throw new WriteFailedException.CreateFailedException(id, dataAfter, e); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, @Nonnull final Tap dataBefore, + @Nonnull final Tap dataAfter, @Nonnull final Context writeContext) + throws WriteFailedException.UpdateFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + + final int index; + try { + index = interfaceContext.getIndex(ifcName); + } catch (IllegalArgumentException e) { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); + } + + try { + modifyTap(ifcName, index, dataAfter); + } catch (VppApiInvocationException e) { + LOG.warn("Write of Tap failed", e); + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); + } + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, @Nonnull final Tap dataBefore, + @Nonnull final Context writeContext) + throws WriteFailedException.DeleteFailedException { + final String ifcName = id.firstKeyOf(Interface.class).getName(); + + final int index; + try { + index = interfaceContext.getIndex(ifcName); + } catch (IllegalArgumentException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + + try { + deleteTap(ifcName, index, dataBefore); + } catch (VppApiInvocationException e) { + LOG.warn("Delete of Tap failed", e); + throw new WriteFailedException.DeleteFailedException(id, e); + } + } + + private void createTap(final String swIfName, final Tap tap) throws VppApiInvocationException { + LOG.debug("Setting tap interface: {}. Tap: {}", swIfName, tap); + final CompletionStage<TapConnectReply> tapConnectFuture = + getFutureJVpp().tapConnect(getTapConnectRequest(tap.getTapName(), tap.getMac(), tap.getDeviceInstance())); + final TapConnectReply reply = + V3poUtils.getReply(tapConnectFuture.toCompletableFuture()); + if (reply.retval < 0) { + LOG.warn("Failed to set tap interface: {}, tap: {}", swIfName, tap); + throw new VppApiInvocationException("tap_connect", reply.context, reply.retval); + } else { + LOG.debug("Tap set successfully for: {}, tap: {}", swIfName, tap); + // Add new interface to our interface context + interfaceContext.addName(reply.swIfIndex, swIfName); + } + } + + private void modifyTap(final String swIfName, final int index, final Tap tap) throws VppApiInvocationException { + LOG.debug("Modifying tap interface: {}. Tap: {}", swIfName, tap); + final CompletionStage<TapModifyReply> vxlanAddDelTunnelReplyCompletionStage = + getFutureJVpp().tapModify(getTapModifyRequest(tap.getTapName(), index, tap.getMac(), tap.getDeviceInstance())); + final TapModifyReply reply = + V3poUtils.getReply(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture()); + if (reply.retval < 0) { + LOG.warn("Failed to modify tap interface: {}, tap: {}", swIfName, tap); + throw new VppApiInvocationException("tap_modify", reply.context, reply.retval); + } else { + LOG.debug("Tap modified successfully for: {}, tap: {}", swIfName, tap); + } + } + + private void deleteTap(final String swIfName, final int index, final Tap dataBefore) + throws VppApiInvocationException { + LOG.debug("Deleting tap interface: {}. Tap: {}", swIfName, dataBefore); + final CompletionStage<TapDeleteReply> vxlanAddDelTunnelReplyCompletionStage = + getFutureJVpp().tapDelete(getTapDeleteRequest(index)); + final TapDeleteReply reply = + V3poUtils.getReply(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture()); + if (reply.retval < 0) { + LOG.warn("Failed to delete tap interface: {}, tap: {}", swIfName, dataBefore); + throw new VppApiInvocationException("tap_modify", reply.context, reply.retval); + } else { + LOG.debug("Tap deleted successfully for: {}, tap: {}", swIfName, dataBefore); + // Remove deleted interface from interface context + interfaceContext.removeName(swIfName); + } + } + + private TapConnect getTapConnectRequest(final String tapName, final PhysAddress mac, final Long deviceInstance) { + final TapConnect tapConnect = new TapConnect(); + tapConnect.tapName = tapName.getBytes(); + + if(mac == null) { + tapConnect.useRandomMac = 1; + tapConnect.macAddress = new byte[6]; + } else { + tapConnect.useRandomMac = 0; + tapConnect.macAddress = V3poUtils.parseMac(mac.getValue()); + } + + if(deviceInstance == null) { + tapConnect.renumber = 0; + } else { + tapConnect.renumber = 1; + tapConnect.customDevInstance = Math.toIntExact(deviceInstance); + } + + return tapConnect; + } + + private TapModify getTapModifyRequest(final String tapName, final int swIndex, final PhysAddress mac, final Long deviceInstance) { + final TapModify tapConnect = new TapModify(); + tapConnect.tapName = tapName.getBytes(); + tapConnect.swIfIndex = swIndex; + + if(mac == null) { + tapConnect.useRandomMac = 1; + tapConnect.macAddress = new byte[6]; + } else { + tapConnect.useRandomMac = 0; + tapConnect.macAddress = V3poUtils.parseMac(mac.getValue()); + } + + if(deviceInstance == null) { + tapConnect.renumber = 0; + } else { + tapConnect.renumber = 1; + tapConnect.customDevInstance = Math.toIntExact(deviceInstance); + } + + return tapConnect; + } + + private TapDelete getTapDeleteRequest(final int swIndex) { + final TapDelete tapConnect = new TapDelete(); + tapConnect.swIfIndex = swIndex; + return tapConnect; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VppInterfaceStateCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/EthernetCustomizer.java index 2bc8beaaa..eca13c1c9 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VppInterfaceStateCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/EthernetCustomizer.java @@ -16,15 +16,16 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; +import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCustomizer.getCachedInterfaceDump; + import io.fd.honeycomb.v3po.translate.Context; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentation; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentationBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Ethernet; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.EthernetBuilder; @@ -37,54 +38,50 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class VppInterfaceStateCustomizer extends FutureJVppCustomizer - implements ChildReaderCustomizer<VppInterfaceStateAugmentation, VppInterfaceStateAugmentationBuilder> { +public class EthernetCustomizer extends FutureJVppCustomizer + implements ChildReaderCustomizer<Ethernet, EthernetBuilder> { - private static final Logger LOG = LoggerFactory.getLogger(VppInterfaceStateCustomizer.class); + private static final Logger LOG = LoggerFactory.getLogger(EthernetCustomizer.class); + private NamingContext interfaceContext; - public VppInterfaceStateCustomizer(@Nonnull final FutureJVpp jvpp) { + public EthernetCustomizer(@Nonnull final FutureJVpp jvpp, + final NamingContext interfaceContext) { super(jvpp); + this.interfaceContext = interfaceContext; } @Override public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, - @Nonnull VppInterfaceStateAugmentation readValue) { - ((InterfaceBuilder) parentBuilder).addAugmentation(VppInterfaceStateAugmentation.class, readValue); + @Nonnull Ethernet readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setEthernet(readValue); } @Nonnull @Override - public VppInterfaceStateAugmentationBuilder getBuilder( - @Nonnull InstanceIdentifier<VppInterfaceStateAugmentation> id) { - return new VppInterfaceStateAugmentationBuilder(); + public EthernetBuilder getBuilder( + @Nonnull InstanceIdentifier<Ethernet> id) { + return new EthernetBuilder(); } @Override - public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VppInterfaceStateAugmentation> id, - @Nonnull final VppInterfaceStateAugmentationBuilder builder, + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Ethernet> id, + @Nonnull final EthernetBuilder builder, @Nonnull final Context ctx) throws ReadFailedException { final InterfaceKey key = id.firstKeyOf(Interface.class); - final SwInterfaceDetails iface; - try { - iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key); - } catch (Exception e) { - throw new ReadFailedException(id, e); - } + final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, + interfaceContext.getIndex(key.getName()), getCachedInterfaceDump(ctx)); - final EthernetBuilder ethernet = new EthernetBuilder(); - ethernet.setMtu((int) iface.linkMtu); + builder.setMtu((int) iface.linkMtu); switch (iface.linkDuplex) { case 1: - ethernet.setDuplex(Ethernet.Duplex.Half); + builder.setDuplex(Ethernet.Duplex.Half); break; case 2: - ethernet.setDuplex(Ethernet.Duplex.Full); + builder.setDuplex(Ethernet.Duplex.Full); break; default: break; } - - builder.setEthernet(ethernet.build()); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java index f7d473f73..82e114603 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java @@ -24,10 +24,10 @@ import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesStateBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus; @@ -44,11 +44,15 @@ import org.openvpp.jvpp.future.FutureJVpp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +/** + * Customizer for reading ietf-interfaces:interfaces-state/interface + */ public class InterfaceCustomizer extends FutureJVppCustomizer implements ListReaderCustomizer<Interface, InterfaceKey, InterfaceBuilder> { private static final Logger LOG = LoggerFactory.getLogger(InterfaceCustomizer.class); + public static final String DUMPED_IFCS_CONTEXT_KEY = InterfaceCustomizer.class.getName() + "dumpedInterfacesDuringGetAllIds"; + private final NamingContext interfaceContext; public InterfaceCustomizer(@Nonnull final FutureJVpp jvpp, final NamingContext interfaceContext) { @@ -56,47 +60,53 @@ public class InterfaceCustomizer extends FutureJVppCustomizer this.interfaceContext = interfaceContext; } + @Nonnull @Override - public InterfaceBuilder getBuilder(InstanceIdentifier<Interface> id) { + public InterfaceBuilder getBuilder(@Nonnull InstanceIdentifier<Interface> id) { return new InterfaceBuilder(); } @Override - public void readCurrentAttributes(InstanceIdentifier<Interface> id, InterfaceBuilder builder, Context ctx) - throws ReadFailedException { + public void readCurrentAttributes(@Nonnull InstanceIdentifier<Interface> id, @Nonnull InterfaceBuilder builder, + @Nonnull Context ctx) throws ReadFailedException { + LOG.debug("Reading attributes for interface: {}", id); final InterfaceKey key = id.firstKeyOf(id.getTargetType()); - final SwInterfaceDetails iface; - try { - iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key); - } catch (Exception e) { - throw new ReadFailedException(id, e); - } + final Map<Integer, SwInterfaceDetails> cachedDump = getCachedInterfaceDump(ctx); + // Pass cached details from getAllIds to getDetails to avoid additional dumps + final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, + interfaceContext.getIndex(key.getName()), cachedDump); + LOG.debug("Interface details for interface: {}, details: {}", key.getName(), iface); builder.setName(key.getName()); - // FIXME: report interface type based on name - //Tunnel.class l2vlan(802.1q) bridge (transparent bridge?) - builder.setType(EthernetCsmacd.class); + builder.setType(InterfaceUtils.getInterfaceType(new String(iface.interfaceName).intern())); builder.setIfIndex(InterfaceUtils.vppIfIndexToYang(iface.swIfIndex)); - builder.setAdminStatus(iface.adminUpDown == 1 - ? AdminStatus.Up - : AdminStatus.Down); - builder.setOperStatus(1 == iface.linkUpDown - ? OperStatus.Up - : OperStatus.Down); + builder.setAdminStatus(1 == iface.adminUpDown ? AdminStatus.Up : AdminStatus.Down); + builder.setOperStatus(1 == iface.linkUpDown ? OperStatus.Up : OperStatus.Down); if (0 != iface.linkSpeed) { builder.setSpeed(InterfaceUtils.vppInterfaceSpeedToYang(iface.linkSpeed)); } if (iface.l2AddressLength == 6) { builder.setPhysAddress(new PhysAddress(InterfaceUtils.vppPhysAddrToYang(iface.l2Address))); } + LOG.trace("Base attributes read for interface: {} as: {}", key.getName(), builder); + } + + @Nonnull + @SuppressWarnings("unchecked") + public static Map<Integer, SwInterfaceDetails> getCachedInterfaceDump(final @Nonnull Context ctx) { + return ctx.get(DUMPED_IFCS_CONTEXT_KEY) == null + ? Collections.emptyMap() + : (Map<Integer, SwInterfaceDetails>) ctx.get(DUMPED_IFCS_CONTEXT_KEY); } @Nonnull @Override public List<InterfaceKey> getAllIds(@Nonnull final InstanceIdentifier<Interface> id, @Nonnull final Context context) throws ReadFailedException { + LOG.trace("Dumping all interfaces to get all IDs"); + final SwInterfaceDump request = new SwInterfaceDump(); request.nameFilter = "".getBytes(); request.nameFilterValid = 0; @@ -105,21 +115,31 @@ public class InterfaceCustomizer extends FutureJVppCustomizer getFutureJVpp().swInterfaceDump(request).toCompletableFuture(); final SwInterfaceDetailsReplyDump ifaces = V3poUtils.getReply(swInterfaceDetailsReplyDumpCompletableFuture); - // TODO can we get null here? if (null == ifaces || null == ifaces.swInterfaceDetails) { + LOG.debug("No interfaces found in VPP"); return Collections.emptyList(); } - return ifaces.swInterfaceDetails.stream() - .filter(elt -> elt != null) - .map((elt) -> { - // Store interface name from VPP in context if not yet present - if(!interfaceContext.containsName(elt.swIfIndex)){ - interfaceContext.addName(elt.swIfIndex, V3poUtils.toString(elt.interfaceName)); - } - return new InterfaceKey(interfaceContext.getName(elt.swIfIndex)); - }) - .collect(Collectors.toList()); + // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes + context.put(DUMPED_IFCS_CONTEXT_KEY, ifaces.swInterfaceDetails.stream() + .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails))); + + final List<InterfaceKey> interfacesKeys = ifaces.swInterfaceDetails.stream() + .filter(elt -> elt != null) + .map((elt) -> { + // Store interface name from VPP in context if not yet present + if (!interfaceContext.containsName(elt.swIfIndex)) { + interfaceContext.addName(elt.swIfIndex, V3poUtils.toString(elt.interfaceName)); + } + LOG.trace("Interface with name: {}, VPP name: {} and index: {} found in VPP", + interfaceContext.getName(elt.swIfIndex), elt.interfaceName, elt.swIfIndex); + + return new InterfaceKey(interfaceContext.getName(elt.swIfIndex)); + }) + .collect(Collectors.toList()); + + LOG.debug("Interfaces found in VPP: {}", interfacesKeys); + return interfacesKeys; } @Override diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java index 691b95f98..62cac3550 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java @@ -18,13 +18,18 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; +import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; import java.math.BigInteger; +import java.util.Map; import java.util.Objects; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.CompletionStage; import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Gauge64; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel; import org.openvpp.jvpp.dto.SwInterfaceDetails; import org.openvpp.jvpp.dto.SwInterfaceDetailsReplyDump; import org.openvpp.jvpp.dto.SwInterfaceDump; @@ -82,6 +87,7 @@ public final class InterfaceUtils { sb.append(HEX_CHARS[v & 15]); } + // TODO rename and move to V3poUtils /** * Reads first 6 bytes of supplied byte array and converts to string as Yang dictates * <p> Replace later with @@ -125,7 +131,7 @@ public final class InterfaceUtils { * @param yangIfIndex if-index from ietf-interfaces. * @return VPP's representation of the if-index */ - public static int YangIfIndexToVpp(int yangIfIndex) { + public static int yangIfIndexToVpp(int yangIfIndex) { Preconditions.checkArgument(yangIfIndex >= 1, "YANG if-index has invalid value %s", yangIfIndex); return yangIfIndex - 1; } @@ -136,27 +142,60 @@ public final class InterfaceUtils { * * @param futureJvpp VPP Java Future API * @param key interface key + * @param index VPP index of the interface + * @param allInterfaces cached interfaces dump with all the interfaces. If interface not present, another dump all + * will be performed + * * @return SwInterfaceDetails DTO or null if interface was not found - * @throws ExecutionException if exception has been thrown while executing VPP query - * @throws InterruptedException if the current thread was interrupted + * + * @throws IllegalArgumentException If interface cannot be found */ - @Nullable + @Nonnull public static SwInterfaceDetails getVppInterfaceDetails(@Nonnull final FutureJVpp futureJvpp, - @Nonnull InterfaceKey key) - throws ExecutionException, InterruptedException { + @Nonnull InterfaceKey key, final int index, + @Nonnull final Map<Integer, SwInterfaceDetails> allInterfaces) { final SwInterfaceDump request = new SwInterfaceDump(); request.nameFilter = key.getName().getBytes(); request.nameFilterValid = 1; - // TODO should we use timeout? - SwInterfaceDetailsReplyDump ifaces = futureJvpp.swInterfaceDump(request).toCompletableFuture().get(); - if (null == ifaces) { // TODO can we get null here? - LOG.warn("VPP returned null instead of interface by key {}", key.getName().getBytes()); - return null; - } + CompletionStage<SwInterfaceDetailsReplyDump> requestFuture = futureJvpp.swInterfaceDump(request); + SwInterfaceDetailsReplyDump ifaces = V3poUtils.getReply(requestFuture.toCompletableFuture()); + if (null == ifaces || null == ifaces.swInterfaceDetails || ifaces.swInterfaceDetails.isEmpty()) { + LOG.warn("VPP returned null instead of interface by key {}", key.getName()); + LOG.warn("Iterating through all the interfaces to find interface: {}", key.getName()); + request.nameFilterValid = 0; + + // Returned cached if available + if(allInterfaces.containsKey(index)) { + return allInterfaces.get(index); + } + // Or else just perform full dump and do inefficient filtering + requestFuture = futureJvpp.swInterfaceDump(request); + ifaces = V3poUtils.getReply(requestFuture.toCompletableFuture()); + + return ifaces.swInterfaceDetails.stream().filter((swIfc) -> swIfc.swIfIndex == index) + .findFirst().orElseThrow(() -> new IllegalArgumentException("Unable to find interface " + key.getName())); + } return Iterables.getOnlyElement(ifaces.swInterfaceDetails); } + /** + * Determine interface type based on its VPP name (relying on VPP's interface naming conventions) + * + * @param interfaceName VPP generated interface name + * @return Interface type + */ + @Nonnull + public static Class<? extends InterfaceType> getInterfaceType(@Nonnull final String interfaceName) { + if(interfaceName.startsWith("tap")) { + return Tap.class; + } + if(interfaceName.startsWith("vxlan")) { + return VxlanTunnel.class; + } + + return EthernetCsmacd.class; + } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java new file mode 100644 index 000000000..a505436c3 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016 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.honeycomb.v3po.translate.v3po.interfacesstate; + +import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadFailedException; +import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.TapBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.dto.SwInterfaceTapDetails; +import org.openvpp.jvpp.dto.SwInterfaceTapDetailsReplyDump; +import org.openvpp.jvpp.dto.SwInterfaceTapDump; +import org.openvpp.jvpp.future.FutureJVpp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class TapCustomizer extends FutureJVppCustomizer + implements ChildReaderCustomizer<Tap, TapBuilder> { + + private static final Logger LOG = LoggerFactory.getLogger(TapCustomizer.class); + public static final String DUMPED_TAPS_CONTEXT_KEY = TapCustomizer.class.getName() + "dumpedTapsDuringGetAllIds"; + private NamingContext interfaceContext; + + public TapCustomizer(@Nonnull final FutureJVpp jvpp, + final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, + @Nonnull Tap readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setTap(readValue); + } + + @Nonnull + @Override + public TapBuilder getBuilder( + @Nonnull InstanceIdentifier<Tap> id) { + return new TapBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, + @Nonnull final TapBuilder builder, + @Nonnull final Context ctx) throws ReadFailedException { + final InterfaceKey key = id.firstKeyOf(Interface.class); + + @SuppressWarnings("unchecked") + Map<Integer, SwInterfaceTapDetails> mappedTaps = + (Map<Integer, SwInterfaceTapDetails>) ctx.get(DUMPED_TAPS_CONTEXT_KEY); + + if(mappedTaps == null) { + // Full Tap dump has to be performed here, no filter or anything is here to help so at least we cache it + final SwInterfaceTapDump request = new SwInterfaceTapDump(); + final CompletionStage<SwInterfaceTapDetailsReplyDump> swInterfaceTapDetailsReplyDumpCompletionStage = + getFutureJVpp().swInterfaceTapDump(request); + final SwInterfaceTapDetailsReplyDump reply = + V3poUtils.getReply(swInterfaceTapDetailsReplyDumpCompletionStage.toCompletableFuture()); + + if(null == reply || null == reply.swInterfaceTapDetails) { + mappedTaps = Collections.emptyMap(); + } else { + final List<SwInterfaceTapDetails> swInterfaceTapDetails = reply.swInterfaceTapDetails; + // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes + mappedTaps = swInterfaceTapDetails.stream() + .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails)); + } + + ctx.put(DUMPED_TAPS_CONTEXT_KEY, mappedTaps); + } + + // Relying here that parent InterfaceCustomizer was invoked first to fill in the context with initial ifc mapping + final int index = interfaceContext.getIndex(key.getName()); + final SwInterfaceTapDetails swInterfaceTapDetails = mappedTaps.get(index); + if(swInterfaceTapDetails == null) { + // Not a Tap interface type + return; + } + + builder.setTapName(V3poUtils.toString(swInterfaceTapDetails.devName)); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java index b4217df46..392dc4666 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java @@ -16,34 +16,22 @@ package io.fd.honeycomb.v3po.translate.v3po.utils; +import static com.google.common.base.Preconditions.checkArgument; + import com.google.common.base.Splitter; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.SoftwareLoopback; +import java.util.function.BiConsumer; +import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel; import org.openvpp.jvpp.dto.JVppReply; public final class V3poUtils { // TODO move to vpp-translate-utils - public static final int RESPONSE_NOT_READY = -77; - public static final int RELEASE = 1; - public static final Splitter DOT_SPLITTER = Splitter.on('.'); - public static final BiMap<String, Class<? extends InterfaceType>> IFC_TYPES = HashBiMap.create(); - static { - V3poUtils.IFC_TYPES.put("vxlan", VxlanTunnel.class); - V3poUtils.IFC_TYPES.put("lo", SoftwareLoopback.class); - V3poUtils.IFC_TYPES.put("Ether", EthernetCsmacd.class); - // TODO missing types below -// V3poUtils.IFC_TYPES.put("l2tpv3_tunnel", EthernetCsmacd.class); -// V3poUtils.IFC_TYPES.put("tap", EthernetCsmacd.class); - } + public static final Splitter COLON_SPLITTER = Splitter.on(':'); private V3poUtils() {} @@ -77,4 +65,35 @@ public final class V3poUtils { public static String toString(final byte[] cString) { return new String(cString).replaceAll("\\u0000", "").intern(); } + + /** + * Parse string represented mac address (using ":" as separator) into a byte array + */ + @Nonnull + public static byte[] parseMac(@Nonnull final String macAddress) { + final List<String> parts = COLON_SPLITTER.splitToList(macAddress); + checkArgument(parts.size() == 6, "Mac address is expected to have 6 parts but was: %s", macAddress); + return parseMacLikeString(parts); + } + + private static byte[] parseMacLikeString(final List<String> strings) { + return strings.stream().limit(6).map(V3poUtils::parseHexByte).collect( + () -> new byte[strings.size()], + new BiConsumer<byte[], Byte>() { + + private int i = -1; + + @Override + public void accept(final byte[] bytes, final Byte aByte) { + bytes[++i] = aByte; + } + }, + (bytes, bytes2) -> { + throw new UnsupportedOperationException("Parallel collect not supported"); + }); + } + + private static byte parseHexByte(final String aByte) { + return (byte)Integer.parseInt(aByte, 16); + } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java index addb425f6..1a3855ced 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java @@ -109,21 +109,27 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer try { final L2FibTableEntryReplyDump dump = getFutureJVpp().l2FibTableDump(l2FibRequest).toCompletableFuture().get(); - final List<L2Fib> l2Fibs = Lists.newArrayListWithCapacity(dump.l2FibTableEntry.size()); - for (L2FibTableEntry entry : dump.l2FibTableEntry) { - // entry.mac is a long value in the format 66:55:44:33:22:11:XX:XX - // where mac address is 11:22:33:44:55:66 - final PhysAddress address = new PhysAddress(getMacAddress(Longs.toByteArray(entry.mac))); - l2Fibs.add(new L2FibBuilder() + final List<L2Fib> l2Fibs; + + if(null == dump || null == dump.l2FibTableEntry) { + l2Fibs = Collections.emptyList(); + } else { + l2Fibs = Lists.newArrayListWithCapacity(dump.l2FibTableEntry.size()); + for (L2FibTableEntry entry : dump.l2FibTableEntry) { + // entry.mac is a long value in the format 66:55:44:33:22:11:XX:XX + // where mac address is 11:22:33:44:55:66 + final PhysAddress address = new PhysAddress(getMacAddress(Longs.toByteArray(entry.mac))); + l2Fibs.add(new L2FibBuilder() .setAction((byteToBoolean(entry.filterMac) - ? L2Fib.Action.Filter - : L2Fib.Action.Forward)) + ? L2Fib.Action.Filter + : L2Fib.Action.Forward)) .setBridgedVirtualInterface(byteToBoolean(entry.bviMac)) .setOutgoingInterface(interfaceContext.getName(entry.swIfIndex)) .setStaticConfig(byteToBoolean(entry.staticMac)) .setPhysAddress(address) .setKey(new L2FibKey(address)) .build()); + } } builder.setL2Fib(l2Fibs); |