diff options
author | 2016-05-06 09:41:51 +0200 | |
---|---|---|
committer | 2016-05-11 09:21:01 +0200 | |
commit | 44348596ff0c675bf3b799f88288111d92d771e6 (patch) | |
tree | 17edea7566a34efe7dedb5ee3d7833616e24f121 | |
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>
16 files changed, 877 insertions, 117 deletions
diff --git a/v3po/api/src/main/yang/v3po.yang b/v3po/api/src/main/yang/v3po.yang index 9587cc642..065ff6535 100644 --- a/v3po/api/src/main/yang/v3po.yang +++ b/v3po/api/src/main/yang/v3po.yang @@ -49,6 +49,10 @@ module v3po { identity vhost-user { base if:interface-type; } + + identity tap { + base if:interface-type; + } typedef vxlan-vni { // FIXME: should be in a vxlan-specific model @@ -94,6 +98,26 @@ module v3po { } } + grouping tap-interface-base-attributes { + leaf tap-name { + type string; + } + } + + grouping tap-interface-config-attributes { + leaf mac { + type yang:phys-address; + mandatory false; + description "Mac address to be set for the tap interface. Random will be used if not configured"; + } + + leaf device-instance { + type uint32; + mandatory false; + description "Custom device instance. Autogenerated will be used if not configured"; + } + } + augment /if:interfaces/if:interface { ext:augment-identifier "vpp-interface-augmentation"; @@ -101,6 +125,14 @@ module v3po { // 1. The link between interface type and this augmentation is unclear // 2. Only this augmentation with combination of ifc type is trigger to do something for vpp, what if user only configures base interface stuff ? + We need to get leaves defined by ietf-interfaces when we are processing this augment + // TODO grouping + container tap { + when "../if:type = 'v3po:tap'"; + + uses tap-interface-base-attributes; + uses tap-interface-config-attributes; + } + container ethernet { when "../if:type = 'ianaift:ethernetCsmacd'"; leaf mtu { @@ -245,6 +277,12 @@ module v3po { leaf description { type string; } + + container tap { + when "../if:type = 'v3po:tap'"; + uses tap-interface-base-attributes; + } + container ethernet { when "../if:type = 'ianaift:ethernetCsmacd'"; leaf mtu { diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizer.java new file mode 100644 index 000000000..38107edff --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizer.java @@ -0,0 +1,59 @@ +/* + * 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.util.read; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; +import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.DataObject; + +/** + * Might be slow ! + */ +public class ReflexiveAugmentReaderCustomizer<C extends DataObject, B extends Builder<C>> + extends ReflexiveRootReaderCustomizer<C, B> + implements ChildReaderCustomizer<C,B> { + + private final Class<C> augType; + + public ReflexiveAugmentReaderCustomizer(final Class<B> builderClass, final Class<C> augType) { + super(builderClass); + this.augType = augType; + } + + @Override + public void merge(final Builder<? extends DataObject> parentBuilder, final C readValue) { + final Optional<Method> method = + ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "addAugmentation", + Lists.newArrayList(Class.class, Augmentation.class), parentBuilder.getClass()); + + checkArgument(method.isPresent(), "Not possible to add augmentations to builder: %s", parentBuilder); + try { + method.get().invoke(parentBuilder, augType, readValue); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e); + } + } + +} diff --git a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizerTest.java b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizerTest.java new file mode 100644 index 000000000..3edc001a8 --- /dev/null +++ b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizerTest.java @@ -0,0 +1,46 @@ +/* + * 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.util.read; + +import static org.junit.Assert.assertSame; + +import org.junit.Before; +import org.junit.Test; +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.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentationBuilder; + +public class ReflexiveAugmentReaderCustomizerTest { + + private ReflexiveAugmentReaderCustomizer<VppInterfaceStateAugmentation, VppInterfaceStateAugmentationBuilder> + vppIfcStateAugmentCustomizer; + + @Before + public void setUp() throws Exception { + vppIfcStateAugmentCustomizer = + new ReflexiveAugmentReaderCustomizer<>(VppInterfaceStateAugmentationBuilder.class, + VppInterfaceStateAugmentation.class); + } + + @Test + public void testAddAugment() throws Exception { + final InterfaceBuilder parentBuilder = new InterfaceBuilder(); + final VppInterfaceStateAugmentation augmentation = vppIfcStateAugmentCustomizer.getBuilder(null).build(); + vppIfcStateAugmentCustomizer.merge(parentBuilder, augmentation); + assertSame(augmentation, parentBuilder.getAugmentation(VppInterfaceStateAugmentation.class)); + } +}
\ No newline at end of file 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); diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java index 769589b3d..2095f3000 100644 --- a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java +++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java @@ -13,6 +13,7 @@ import io.fd.honeycomb.v3po.translate.v3po.interfaces.EthernetCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.InterfaceCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.L2Customizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.RoutingCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.interfaces.TapCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.VxlanCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv4Customizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv6Customizer; @@ -28,6 +29,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Routing; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Tap; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Vxlan; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.ChildOf; @@ -96,12 +98,15 @@ public class InterfacesHoneycombWriterModule extends org.opendaylight.yang.gen.v final ChildWriter<Vxlan> vxlanWriter = new CompositeChildWriter<>(Vxlan.class, new VxlanCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); + final ChildWriter<Tap> tapWriter = new CompositeChildWriter<>(Tap.class, + new TapCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); + final ChildWriter<L2> l2Writer = new CompositeChildWriter<>(L2.class, new L2Customizer(getVppJvppIfcDependency(), getInterfaceContextDependency(), getBridgeDomainContextDependency())); final List<ChildWriter<? extends ChildOf<VppInterfaceAugmentation>>> vppIfcChildWriters = Lists.newArrayList(); - // TODO what's the order here ? vppIfcChildWriters.add(vxlanWriter); + vppIfcChildWriters.add(tapWriter); vppIfcChildWriters.add(ethernetWriter); vppIfcChildWriters.add(l2Writer); vppIfcChildWriters.add(routingWriter); diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java index e753cf56e..13fcf008e 100644 --- a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java +++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java @@ -6,20 +6,28 @@ import static io.fd.honeycomb.v3po.translate.util.RWUtils.emptyChildReaderList; import static io.fd.honeycomb.v3po.translate.util.RWUtils.singletonAugReaderList; import static io.fd.honeycomb.v3po.translate.util.RWUtils.singletonChildReaderList; +import com.google.common.collect.Lists; import io.fd.honeycomb.v3po.translate.impl.read.CompositeChildReader; import io.fd.honeycomb.v3po.translate.impl.read.CompositeListReader; import io.fd.honeycomb.v3po.translate.impl.read.CompositeRootReader; import io.fd.honeycomb.v3po.translate.read.ChildReader; import io.fd.honeycomb.v3po.translate.util.read.CloseableReader; +import io.fd.honeycomb.v3po.translate.util.read.ReflexiveAugmentReaderCustomizer; import io.fd.honeycomb.v3po.translate.util.read.ReflexiveRootReaderCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.EthernetCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCustomizer; -import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.VppInterfaceStateCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.TapCustomizer; +import java.util.List; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; 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.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.Tap; +import org.opendaylight.yangtools.yang.binding.ChildOf; public class InterfacesStateHoneycombReaderModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractInterfacesStateHoneycombReaderModule { @@ -43,10 +51,23 @@ public class InterfacesStateHoneycombReaderModule extends @Override public java.lang.AutoCloseable createInstance() { + final ChildReader<? extends ChildOf<VppInterfaceStateAugmentation>> ethernetReader = + new CompositeChildReader<>(Ethernet.class, + new EthernetCustomizer(getVppJvppDependency(), getInterfaceContextIfcStateDependency())); + + final ChildReader<? extends ChildOf<VppInterfaceStateAugmentation>> tapReader = + new CompositeChildReader<>(Tap.class, + new TapCustomizer(getVppJvppDependency(), getInterfaceContextIfcStateDependency())); + + final List<ChildReader<? extends ChildOf<VppInterfaceStateAugmentation>>> childReaders = Lists.newArrayList(); + childReaders.add(ethernetReader); + childReaders.add(tapReader); + final ChildReader<VppInterfaceStateAugmentation> vppInterfaceStateAugmentationChildReader = new CompositeChildReader<>(VppInterfaceStateAugmentation.class, - new VppInterfaceStateCustomizer(getVppJvppDependency())); - + childReaders, + new ReflexiveAugmentReaderCustomizer<>(VppInterfaceStateAugmentationBuilder.class, + VppInterfaceStateAugmentation.class)); final CompositeListReader<Interface, InterfaceKey, InterfaceBuilder> interfaceReader = new CompositeListReader<>(Interface.class, diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizerTest.java new file mode 100644 index 000000000..668eed4cf --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizerTest.java @@ -0,0 +1,145 @@ +/* + * 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import java.util.concurrent.CompletableFuture; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +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.interfaces.rev140508.interfaces.InterfaceKey; +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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.TapBuilder; +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; + +public class TapCustomizerTest { + + @Mock + private FutureJVpp vppApi; + private NamingContext ctx; + private TapCustomizer tapCustomizer; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + ctx = new NamingContext("ifcintest"); + tapCustomizer = new TapCustomizer(vppApi, ctx); + } + + @Test + public void testCreate() throws Exception { + doAnswer(new Answer() { + + int idx = 0; + + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable { + final CompletableFuture<Object> reply = new CompletableFuture<>(); + final TapConnectReply t = new TapConnectReply(); + t.swIfIndex = idx++; + t.retval = 0; + reply.complete(t); + return reply; + } + }).when(vppApi).tapConnect(any(TapConnect.class)); + + tapCustomizer.writeCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), new Context()); + tapCustomizer.writeCurrentAttributes(getTapId("tap2"), getTapData("tap2", "ff:ff:ff:ff:ff:ff"), new Context()); + + verify(vppApi, times(2)).tapConnect(any(TapConnect.class)); + assertTrue(ctx.containsIndex("tap")); + assertTrue(ctx.containsIndex("tap2")); + } + + @Test + public void testModify() throws Exception { + final CompletableFuture<TapConnectReply> reply = new CompletableFuture<>(); + final TapConnectReply t = new TapConnectReply(); + t.swIfIndex = 0; + reply.complete(t); + doReturn(reply).when(vppApi).tapConnect(any(TapConnect.class)); + + final CompletableFuture<TapModifyReply> replyModif = new CompletableFuture<>(); + final TapModifyReply tmodif = new TapModifyReply(); + tmodif.swIfIndex = 0; + tmodif.retval = 0; + replyModif.complete(tmodif); + doReturn(replyModif).when(vppApi).tapModify(any(TapModify.class)); + + tapCustomizer.writeCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), new Context()); + tapCustomizer.updateCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), getTapData("tap", "ff:ff:ff:ff:ff:f1"), new Context()); + + verify(vppApi).tapConnect(any(TapConnect.class)); + verify(vppApi).tapModify(any(TapModify.class)); + assertTrue(ctx.containsIndex("tap")); + assertFalse(ctx.containsIndex("tap2")); + } + + @Test + public void testDelete() throws Exception { + final CompletableFuture<TapConnectReply> reply = new CompletableFuture<>(); + final TapConnectReply t = new TapConnectReply(); + t.swIfIndex = 0; + reply.complete(t); + doReturn(reply).when(vppApi).tapConnect(any(TapConnect.class)); + + final CompletableFuture<TapDeleteReply> replyDelete = new CompletableFuture<>(); + final TapDeleteReply tmodif = new TapDeleteReply(); + tmodif.retval = 0; + replyDelete.complete(tmodif); + doReturn(replyDelete).when(vppApi).tapDelete(any(TapDelete.class)); + + tapCustomizer.writeCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), new Context()); + tapCustomizer.deleteCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), new Context()); + + verify(vppApi).tapConnect(any(TapConnect.class)); + verify(vppApi).tapDelete(any(TapDelete.class)); + assertFalse(ctx.containsIndex("tap")); + } + + private InstanceIdentifier<Tap> getTapId(final String tap) { + return InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(tap)).augmentation( + VppInterfaceAugmentation.class).child(Tap.class); + } + + private Tap getTapData(final String tap, final String mac) { + return new TapBuilder().setTapName(tap).setMac(new PhysAddress(mac)).build(); + } +}
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java index 7c88c3198..3f33d141f 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java @@ -16,16 +16,16 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; -import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.YangIfIndexToVpp; +import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.yangIfIndexToVpp; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.test.ListReaderCustomizerTest; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; @@ -63,6 +63,8 @@ public class InterfaceCustomizerTest extends @Override protected RootReaderCustomizer<Interface, InterfaceBuilder> initCustomizer() { + interfacesContext.addName(0, "eth0"); + interfacesContext.addName(1, "eth1"); return new InterfaceCustomizer(api, interfacesContext); } @@ -75,10 +77,11 @@ public class InterfaceCustomizerTest extends verify(builder).setInterface(value); } - private void verifyBridgeDomainDumpUpdateWasInvoked(final int nameFilterValid, final String ifaceName) { + private void verifyBridgeDomainDumpUpdateWasInvoked(final int nameFilterValid, final String ifaceName, + final int dumpIfcsInvocationCount) { // TODO adding equals methods for jvpp DTOs would make ArgumentCaptor usage obsolete ArgumentCaptor<SwInterfaceDump> argumentCaptor = ArgumentCaptor.forClass(SwInterfaceDump.class); - verify(api).swInterfaceDump(argumentCaptor.capture()); + verify(api, times(dumpIfcsInvocationCount)).swInterfaceDump(argumentCaptor.capture()); final SwInterfaceDump actual = argumentCaptor.getValue(); assertEquals(nameFilterValid, actual.nameFilterValid); assertArrayEquals(ifaceName.getBytes(), actual.nameFilter); @@ -86,7 +89,7 @@ public class InterfaceCustomizerTest extends private static void assertIfacesAreEqual(final Interface iface, final SwInterfaceDetails details) { assertEquals(iface.getName(), new String(details.interfaceName)); - assertEquals(YangIfIndexToVpp(iface.getIfIndex().intValue()), details.swIfIndex); + assertEquals(yangIfIndexToVpp(iface.getIfIndex().intValue()), details.swIfIndex); assertEquals(iface.getPhysAddress().getValue(), InterfaceUtils.vppPhysAddrToYang(details.l2Address)); } @@ -119,7 +122,7 @@ public class InterfaceCustomizerTest extends getCustomizer().readCurrentAttributes(id, builder, ctx); - verifyBridgeDomainDumpUpdateWasInvoked(1, ifaceName); + verifyBridgeDomainDumpUpdateWasInvoked(1, ifaceName, 1); assertIfacesAreEqual(builder.build(), iface); } @@ -134,8 +137,8 @@ public class InterfaceCustomizerTest extends try { getCustomizer().readCurrentAttributes(id, builder, ctx); - } catch (ReadFailedException e) { - verifyBridgeDomainDumpUpdateWasInvoked(1, ifaceName); + } catch (IllegalArgumentException e) { + verifyBridgeDomainDumpUpdateWasInvoked(0, ifaceName, 2); return; } @@ -149,16 +152,18 @@ public class InterfaceCustomizerTest extends final String swIf0Name = "eth0"; final SwInterfaceDetails swIf0 = new SwInterfaceDetails(); + swIf0.swIfIndex = 0; swIf0.interfaceName = swIf0Name.getBytes(); - final String swIf1Name = "eth0"; + final String swIf1Name = "eth1"; final SwInterfaceDetails swIf1 = new SwInterfaceDetails(); + swIf1.swIfIndex = 1; swIf1.interfaceName = swIf1Name.getBytes(); whenSwInterfaceDumpThenReturn(Arrays.asList(swIf0, swIf1)); final List<InterfaceKey> expectedIds = Arrays.asList(new InterfaceKey(swIf0Name), new InterfaceKey(swIf1Name)); final List<InterfaceKey> actualIds = getCustomizer().getAllIds(id, ctx); - verifyBridgeDomainDumpUpdateWasInvoked(0, ""); + verifyBridgeDomainDumpUpdateWasInvoked(0, "", 1); assertEquals(expectedIds, actualIds); } diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java index 115eb2b9c..b28234db4 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java @@ -1,6 +1,7 @@ package io.fd.honeycomb.v3po.translate.v3po.utils; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -14,4 +15,41 @@ public class V3poUtilsTest { final String jString = V3poUtils.toString(cString); assertArrayEquals(expected, jString.getBytes()); } -}
\ No newline at end of file + + @Test + public void testParseMac() throws Exception { + byte[] bytes = V3poUtils.parseMac("00:fF:7f:15:5e:A9"); + assertMac(bytes); + } + + private void assertMac(final byte[] bytes) { + assertEquals(6, bytes.length); + assertEquals((byte)0, bytes[0]); + assertEquals((byte)255, bytes[1]); + assertEquals((byte)127, bytes[2]); + assertEquals((byte)21, bytes[3]); + assertEquals((byte)94, bytes[4]); + assertEquals((byte)169, bytes[5]); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseMacLonger() throws Exception { + byte[] bytes = V3poUtils.parseMac("00:fF:7f:15:5e:A9:88:77"); + assertMac(bytes); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseMacShorter() throws Exception { + V3poUtils.parseMac("00:fF:7f"); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseRandomString() throws Exception { + V3poUtils.parseMac("random{}}@$*&*!"); + } + + @Test(expected = NumberFormatException.class) + public void testParseMacNumberFormatEx() throws Exception { + V3poUtils.parseMac("00:XX:7f:15:5e:77\""); + } +} |