From c3829ac60e15871b47c1da6b95ea5d675899c87e Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Fri, 9 Sep 2016 11:41:03 +0200 Subject: HONEYCOMB-204 exclude deleted interfaces from operational data Change-Id: I187ac52095e15c8c9302871a0d7e7be792e6a0e2 Signed-off-by: Maros Marsalek --- v3po/api/src/main/yang/v3po-context.yang | 29 ++++++ .../translate/v3po/DisabledInterfacesManager.java | 106 +++++++++++++++++++++ .../v3po/InterfacesStateReaderFactory.java | 50 +++++----- .../translate/v3po/InterfacesWriterFactory.java | 51 +++++----- .../io/fd/honeycomb/translate/v3po/V3poModule.java | 4 + .../translate/v3po/interfaces/VxlanCustomizer.java | 41 ++++++-- .../v3po/interfaces/VxlanGpeCustomizer.java | 68 ++++++++----- .../v3po/interfacesstate/InterfaceCustomizer.java | 81 +++++++++++----- .../v3po/DisabledInterfacesManagerTest.java | 106 +++++++++++++++++++++ .../v3po/interfaces/VxlanCustomizerTest.java | 24 ++++- .../v3po/interfaces/VxlanGpeCustomizerTest.java | 5 +- .../interfacesstate/InterfaceCustomizerTest.java | 30 +++++- .../src/main/yang/naming-context.yang | 2 + 13 files changed, 488 insertions(+), 109 deletions(-) create mode 100644 v3po/api/src/main/yang/v3po-context.yang create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManager.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManagerTest.java diff --git a/v3po/api/src/main/yang/v3po-context.yang b/v3po/api/src/main/yang/v3po-context.yang new file mode 100644 index 000000000..ae35be5dd --- /dev/null +++ b/v3po/api/src/main/yang/v3po-context.yang @@ -0,0 +1,29 @@ +module v3po-context { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:v3po:context"; + prefix "v3po-ctx"; + + revision "2016-09-09" { + description + "Initial revision of v3po specific context"; + } + + container disabled-interfaces { + config false; + // context data + + description "Index list of disabled interfaces. VPP does not always delete an interface after deletion. It just + disables it and keeps it there. Honeycomb can hide such interfaces from operational data, and this + is the place to heep track of which interfaces were deleted, but are expected to show up in VPP"; + + list disabled-interface-index { + + key "index"; + + leaf index { + type int32; + } + } + } + +} \ No newline at end of file diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManager.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManager.java new file mode 100644 index 000000000..9772923c3 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManager.java @@ -0,0 +1,106 @@ +/* + * 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.translate.v3po; + +import com.google.common.base.Optional; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.BindingBrokerReader; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.DisabledInterfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.DisabledInterfacesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndexBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndexKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +/** + * Facade on top of {@link MappingContext} making access to {@link DisabledInterfaces} easier. + */ +public class DisabledInterfacesManager { + + private static final InstanceIdentifier + DISABLED_IFCS_ROOT = InstanceIdentifier.create(DisabledInterfaces.class); + + /** + * Read the list of currently disabled interfaces. + */ + public List getDisabledInterfaces(@Nonnull final MappingContext ctx) { + final Optional read = ctx.read(DISABLED_IFCS_ROOT); + if (read.isPresent()) { + return read.get().getDisabledInterfaceIndex().stream() + .map(DisabledInterfaceIndex::getIndex) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } + + /** + * Check whether a specific interface is disabled. + */ + public boolean isInterfaceDisabled(final int index, @Nonnull final MappingContext ctx) { + return ctx.read(getKeyedId(index)) + .isPresent(); + } + + /** + * Make a specific interface disabled. + */ + public void disableInterface(final int index, @Nonnull final MappingContext ctx) { + ctx.put(getKeyedId(index), getDisabledInterfaceIndex(index)); + } + + /** + * Remove interface disability. + */ + public void removeDisabledInterface(final int index, @Nonnull final MappingContext ctx) { + ctx.delete(getKeyedId(index)); + } + + private static DisabledInterfaceIndex getDisabledInterfaceIndex(final int index) { + return new DisabledInterfaceIndexBuilder().setIndex(index).build(); + } + + private static KeyedInstanceIdentifier getKeyedId(final int id) { + return DISABLED_IFCS_ROOT.child(DisabledInterfaceIndex.class, new DisabledInterfaceIndexKey(id)); + } + + public static final class ContextsReaderFactory implements ReaderFactory { + + @Inject + @Named("honeycomb-context") + private DataBroker contextBindingBrokerDependency; + + @Override + public void init(final ModifiableReaderRegistryBuilder registry) { + registry.add(new BindingBrokerReader<>(DISABLED_IFCS_ROOT, + contextBindingBrokerDependency, + LogicalDatastoreType.OPERATIONAL, DisabledInterfacesBuilder.class)); + } + } + +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesStateReaderFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesStateReaderFactory.java index 163457b2e..f2936cd9c 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesStateReaderFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesStateReaderFactory.java @@ -66,10 +66,11 @@ import org.openvpp.jvpp.core.future.FutureJVppCore; public final class InterfacesStateReaderFactory implements ReaderFactory { - private NamingContext ifcCtx; - private NamingContext bdCtx; - private NamingContext classifyCtx; - private FutureJVppCore jvpp; + private final NamingContext ifcNamingCtx; + private final NamingContext bdNamingCtx; + private final NamingContext classifyNamingCtx; + private final DisabledInterfacesManager ifcDisableContext; + private final FutureJVppCore jvpp; static final InstanceIdentifier IFC_STATE_ID = InstanceIdentifier.create(InterfacesState.class); @@ -77,13 +78,15 @@ public final class InterfacesStateReaderFactory implements ReaderFactory { @Inject public InterfacesStateReaderFactory(final FutureJVppCore jvpp, - @Named("interface-context") final NamingContext ifcCtx, - @Named("bridge-domain-context") final NamingContext bdCtx, - @Named("classify-table-context") final NamingContext classifyCtx) { + @Named("interface-context") final NamingContext ifcNamingCtx, + @Named("bridge-domain-context") final NamingContext bdNamingCtx, + @Named("classify-table-context") final NamingContext classifyNamingCtx, + final DisabledInterfacesManager ifcDisableContext) { this.jvpp = jvpp; - this.ifcCtx = ifcCtx; - this.bdCtx = bdCtx; - this.classifyCtx = classifyCtx; + this.ifcNamingCtx = ifcNamingCtx; + this.bdNamingCtx = bdNamingCtx; + this.classifyNamingCtx = classifyNamingCtx; + this.ifcDisableContext = ifcDisableContext; } @Override @@ -91,14 +94,14 @@ public final class InterfacesStateReaderFactory implements ReaderFactory { // InterfacesState(Structural) registry.addStructuralReader(IFC_STATE_ID, InterfacesStateBuilder.class); // Interface - registry.add(new GenericListReader<>(IFC_ID, new InterfaceCustomizer(jvpp, ifcCtx))); + registry.add(new GenericListReader<>(IFC_ID, new InterfaceCustomizer(jvpp, ifcNamingCtx, ifcDisableContext))); // v3po.yang initVppIfcAugmentationReaders(registry, IFC_ID); // ietf-ip.yang initInterface2AugmentationReaders(registry, IFC_ID); // vpp-vlan.yang - new SubinterfaceStateAugmentationReaderFactory(jvpp, ifcCtx, bdCtx, classifyCtx).init(registry); + new SubinterfaceStateAugmentationReaderFactory(jvpp, ifcNamingCtx, bdNamingCtx, classifyNamingCtx).init(registry); } private void initInterface2AugmentationReaders(final ModifiableReaderRegistryBuilder registry, @@ -112,14 +115,14 @@ public final class InterfacesStateReaderFactory implements ReaderFactory { registry.add(new GenericReader<>(ipv4Id, new Ipv4Customizer(jvpp))); // Address final InstanceIdentifier
ipv4AddrId = ipv4Id.child(Address.class); - registry.add(new GenericListReader<>(ipv4AddrId, new Ipv4AddressCustomizer(jvpp, ifcCtx))); + registry.add(new GenericListReader<>(ipv4AddrId, new Ipv4AddressCustomizer(jvpp, ifcNamingCtx))); // Neighbor final InstanceIdentifier neighborId = ipv4Id.child(Neighbor.class); registry.add(new GenericListReader<>(neighborId, new Ipv4NeighbourCustomizer(jvpp))); // Ipv6 // TODO unfinished customizer final InstanceIdentifier ipv6Id = ifc2AugId.child(Ipv6.class); - registry.add(new GenericReader<>(ipv6Id, new Ipv6Customizer(jvpp, ifcCtx))); + registry.add(new GenericReader<>(ipv6Id, new Ipv6Customizer(jvpp, ifcNamingCtx))); } private void initVppIfcAugmentationReaders(final ModifiableReaderRegistryBuilder registry, @@ -128,26 +131,27 @@ public final class InterfacesStateReaderFactory implements ReaderFactory { final InstanceIdentifier vppIfcAugId = ifcId.augmentation(VppInterfaceStateAugmentation.class); registry.addStructuralReader(vppIfcAugId, VppInterfaceStateAugmentationBuilder.class); // Ethernet - registry.add(new GenericReader<>(vppIfcAugId.child(Ethernet.class), new EthernetCustomizer(jvpp, ifcCtx))); + registry.add(new GenericReader<>(vppIfcAugId.child(Ethernet.class), new EthernetCustomizer(jvpp, ifcNamingCtx))); // Tap - registry.add(new GenericReader<>(vppIfcAugId.child(Tap.class), new TapCustomizer(jvpp, ifcCtx))); + registry.add(new GenericReader<>(vppIfcAugId.child(Tap.class), new TapCustomizer(jvpp, ifcNamingCtx))); // VhostUser - registry.add(new GenericReader<>(vppIfcAugId.child(VhostUser.class), new VhostUserCustomizer(jvpp, ifcCtx))); + registry.add(new GenericReader<>(vppIfcAugId.child(VhostUser.class), new VhostUserCustomizer(jvpp, ifcNamingCtx))); // Vxlan - registry.add(new GenericReader<>(vppIfcAugId.child(Vxlan.class), new VxlanCustomizer(jvpp, ifcCtx))); + registry.add(new GenericReader<>(vppIfcAugId.child(Vxlan.class), new VxlanCustomizer(jvpp, ifcNamingCtx))); // VxlanGpe - registry.add(new GenericReader<>(vppIfcAugId.child(VxlanGpe.class), new VxlanGpeCustomizer(jvpp, ifcCtx))); + registry.add(new GenericReader<>(vppIfcAugId.child(VxlanGpe.class), new VxlanGpeCustomizer(jvpp, ifcNamingCtx))); // Gre - registry.add(new GenericReader<>(vppIfcAugId.child(Gre.class), new GreCustomizer(jvpp, ifcCtx))); + registry.add(new GenericReader<>(vppIfcAugId.child(Gre.class), new GreCustomizer(jvpp, ifcNamingCtx))); // L2 - registry.add(new GenericReader<>(vppIfcAugId.child(L2.class), new L2Customizer(jvpp, ifcCtx, bdCtx))); + registry.add(new GenericReader<>(vppIfcAugId.child(L2.class), new L2Customizer(jvpp, ifcNamingCtx, bdNamingCtx))); // Acl(Subtree) final InstanceIdentifier aclIdRelative = InstanceIdentifier.create(Acl.class); registry.subtreeAdd( Sets.newHashSet(aclIdRelative.child(L2Acl.class), aclIdRelative.child(Ip4Acl.class), aclIdRelative.child(Ip6Acl.class)), - new GenericReader<>(vppIfcAugId.child(Acl.class), new AclCustomizer(jvpp, ifcCtx, classifyCtx))); + new GenericReader<>(vppIfcAugId.child(Acl.class), new AclCustomizer(jvpp, ifcNamingCtx, + classifyNamingCtx))); // Proxy ARP registry.add(new GenericReader<>(vppIfcAugId.child(ProxyArp.class), new ProxyArpCustomizer(jvpp, - ifcCtx))); + ifcNamingCtx))); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java index 6ffe3c30c..28d7b5007 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java @@ -83,34 +83,38 @@ public final class InterfacesWriterFactory implements WriterFactory { private final FutureJVppCore jvpp; private final IetfAClWriter aclWriter; - private final NamingContext bdContext; - private final NamingContext ifcContext; - private final NamingContext classifyTableContext; + private final NamingContext bdNamingContext; + private final NamingContext ifcNamingContext; + private final NamingContext classifyTableNamingContext; + private final DisabledInterfacesManager ifcDisableContext; @Inject public InterfacesWriterFactory(final FutureJVppCore vppJvppIfcDependency, final IetfAClWriter aclWriter, @Named("bridge-domain-context") final NamingContext bridgeDomainContextDependency, @Named("interface-context") final NamingContext interfaceContextDependency, - @Named("classify-table-context") final NamingContext classifyTableContextDependency) { + @Named("classify-table-context") final NamingContext classifyTableContextDependency, + final DisabledInterfacesManager ifcDisableContext) { this.jvpp = vppJvppIfcDependency; this.aclWriter = aclWriter; - this.bdContext = bridgeDomainContextDependency; - this.ifcContext = interfaceContextDependency; - this.classifyTableContext = classifyTableContextDependency; + this.bdNamingContext = bridgeDomainContextDependency; + this.ifcNamingContext = interfaceContextDependency; + this.ifcDisableContext = ifcDisableContext; + this.classifyTableNamingContext = classifyTableContextDependency; } @Override public void init(final ModifiableWriterRegistryBuilder registry) { // Interfaces // Interface = - registry.add(new GenericListWriter<>(IFC_ID, new InterfaceCustomizer(jvpp, ifcContext))); + registry.add(new GenericListWriter<>(IFC_ID, new InterfaceCustomizer(jvpp, ifcNamingContext))); // VppInterfaceAugmentation addVppInterfaceAgmentationWriters(IFC_ID, registry); // Interface1 (ietf-ip augmentation) addInterface1AugmentationWriters(IFC_ID, registry); // SubinterfaceAugmentation TODO make dedicated module for subIfc writer factory - new SubinterfaceAugmentationWriterFactory(jvpp, aclWriter, ifcContext, bdContext, classifyTableContext).init(registry); + new SubinterfaceAugmentationWriterFactory(jvpp, aclWriter, ifcNamingContext, bdNamingContext, + classifyTableNamingContext).init(registry); } private void addInterface1AugmentationWriters(final InstanceIdentifier ifcId, @@ -121,14 +125,15 @@ public final class InterfacesWriterFactory implements WriterFactory { ifcId); // Ipv4(after interface) final InstanceIdentifier ipv4Id = ifc1AugId.child(Ipv4.class); - registry.addAfter(new GenericWriter<>(ipv4Id, new Ipv4Customizer(jvpp, ifcContext)), + registry.addAfter(new GenericWriter<>(ipv4Id, new Ipv4Customizer(jvpp, ifcNamingContext)), ifcId); // Address(after Ipv4) = final InstanceIdentifier
ipv4AddressId = ipv4Id.child(Address.class); - registry.addAfter(new GenericListWriter<>(ipv4AddressId, new Ipv4AddressCustomizer(jvpp, ifcContext)), + registry.addAfter(new GenericListWriter<>(ipv4AddressId, new Ipv4AddressCustomizer(jvpp, ifcNamingContext)), ipv4Id); // Neighbor(after ipv4Address) - registry.addAfter(new GenericListWriter<>(ipv4Id.child(Neighbor.class), new Ipv4NeighbourCustomizer(jvpp, ifcContext)), + registry.addAfter(new GenericListWriter<>(ipv4Id.child(Neighbor.class), new Ipv4NeighbourCustomizer(jvpp, + ifcNamingContext)), ipv4AddressId); } @@ -136,24 +141,24 @@ public final class InterfacesWriterFactory implements WriterFactory { final ModifiableWriterRegistryBuilder registry) { // VhostUser(Needs to be executed before Interface customizer) = final InstanceIdentifier vhostId = VPP_IFC_AUG_ID.child(VhostUser.class); - registry.addBefore(new GenericWriter<>(vhostId, new VhostUserCustomizer(jvpp, ifcContext)), + registry.addBefore(new GenericWriter<>(vhostId, new VhostUserCustomizer(jvpp, ifcNamingContext)), ifcId); // Vxlan(Needs to be executed before Interface customizer) = final InstanceIdentifier vxlanId = VPP_IFC_AUG_ID.child(Vxlan.class); - registry.addBefore(new GenericWriter<>(vxlanId, new VxlanCustomizer(jvpp, ifcContext)), + registry.addBefore(new GenericWriter<>(vxlanId, new VxlanCustomizer(jvpp, ifcNamingContext, ifcDisableContext)), ifcId); // VxlanGpe(Needs to be executed before Interface customizer) = final InstanceIdentifier vxlanGpeId = VPP_IFC_AUG_ID.child(VxlanGpe.class); - registry.addBefore(new GenericWriter<>(vxlanGpeId, new VxlanGpeCustomizer(jvpp, ifcContext)), - ifcId); + registry.addBefore(new GenericWriter<>(vxlanGpeId, + new VxlanGpeCustomizer(jvpp, ifcNamingContext, ifcDisableContext)), ifcId); // Tap(Needs to be executed before Interface customizer) = final InstanceIdentifier tapId = VPP_IFC_AUG_ID.child(Tap.class); - registry.addBefore(new GenericWriter<>(tapId, new TapCustomizer(jvpp, ifcContext)), + registry.addBefore(new GenericWriter<>(tapId, new TapCustomizer(jvpp, ifcNamingContext)), ifcId); // Gre(Needs to be executed before Interface customizer) = final InstanceIdentifier greId = VPP_IFC_AUG_ID.child(Gre.class); - registry.addBefore(new GenericWriter<>(greId, new GreCustomizer(jvpp, ifcContext)), + registry.addBefore(new GenericWriter<>(greId, new GreCustomizer(jvpp, ifcNamingContext)), ifcId); @@ -163,14 +168,14 @@ public final class InterfacesWriterFactory implements WriterFactory { registry.add(new GenericWriter<>(VPP_IFC_AUG_ID.child(Ethernet.class), new EthernetCustomizer(jvpp))); // Routing(Execute only after specific interface customizers) = registry.addAfter( - new GenericWriter<>(VPP_IFC_AUG_ID.child(Routing.class), new RoutingCustomizer(jvpp, ifcContext)), + new GenericWriter<>(VPP_IFC_AUG_ID.child(Routing.class), new RoutingCustomizer(jvpp, ifcNamingContext)), specificIfcTypes); // Routing(Execute only after specific interface customizers) = - registry.addAfter(new GenericWriter<>(L2_ID, new L2Customizer(jvpp, ifcContext, bdContext)), + registry.addAfter(new GenericWriter<>(L2_ID, new L2Customizer(jvpp, ifcNamingContext, bdNamingContext)), specificIfcTypes); // Proxy Arp (execute after specific interface customizers) registry.addAfter( - new GenericWriter<>(VPP_IFC_AUG_ID.child(ProxyArp.class), new ProxyArpCustomizer(jvpp, ifcContext)), + new GenericWriter<>(VPP_IFC_AUG_ID.child(ProxyArp.class), new ProxyArpCustomizer(jvpp, ifcNamingContext)), specificIfcTypes); // ACL (execute after classify table and session writers) // also handles L2Acl, Ip4Acl and Ip6Acl: @@ -178,7 +183,7 @@ public final class InterfacesWriterFactory implements WriterFactory { registry .subtreeAddAfter( Sets.newHashSet(aclId.child(L2Acl.class), aclId.child(Ip4Acl.class), aclId.child(Ip6Acl.class)), - new GenericWriter<>(ACL_ID, new AclCustomizer(jvpp, ifcContext, classifyTableContext)), + new GenericWriter<>(ACL_ID, new AclCustomizer(jvpp, ifcNamingContext, classifyTableNamingContext)), Sets.newHashSet(CLASSIFY_TABLE_ID, CLASSIFY_SESSION_ID)); // IETF-ACL, also handles IetfAcl, AccessLists and Acl: @@ -188,7 +193,7 @@ public final class InterfacesWriterFactory implements WriterFactory { org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.access.lists.Acl.class); registry.subtreeAdd( Sets.newHashSet(accessListsID, aclListId), - new GenericWriter<>(IETF_ACL_ID, new IetfAclCustomizer(aclWriter, ifcContext))); + new GenericWriter<>(IETF_ACL_ID, new IetfAclCustomizer(aclWriter, ifcNamingContext))); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java index ada8f9d00..0feeccab6 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java @@ -57,12 +57,16 @@ public class V3poModule extends AbstractModule { // Utils bind(IetfAClWriter.class).toProvider(IetfAClWriterProvider.class); + // Context utility for deleted interfaces + bind(DisabledInterfacesManager.class).toInstance(new DisabledInterfacesManager()); // Readers final Multibinder readerFactoryBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class); readerFactoryBinder.addBinding().to(InterfacesStateReaderFactory.class); readerFactoryBinder.addBinding().to(VppStateHoneycombReaderFactory.class); readerFactoryBinder.addBinding().to(VppClassifierReaderFactory.class); + // Expose disabled interfaces in operational data + readerFactoryBinder.addBinding().to(DisabledInterfacesManager.ContextsReaderFactory.class); // Writers final Multibinder writerFactoryBinder = Multibinder.newSetBinder(binder(), WriterFactory.class); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizer.java index efd670b38..492861037 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizer.java @@ -19,11 +19,12 @@ package io.fd.honeycomb.translate.v3po.interfaces; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.net.InetAddresses; +import io.fd.honeycomb.translate.v3po.DisabledInterfacesManager; import io.fd.honeycomb.translate.v3po.util.AbstractInterfaceTypeCustomizer; -import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.v3po.util.TranslateUtils; import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; +import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; import java.net.InetAddress; import java.util.concurrent.CompletionStage; @@ -45,11 +46,16 @@ import org.slf4j.LoggerFactory; public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer { private static final Logger LOG = LoggerFactory.getLogger(VxlanCustomizer.class); - private final NamingContext interfaceContext; - public VxlanCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + private final NamingContext interfaceNamingContext; + private final DisabledInterfacesManager interfaceDisableContext; + + public VxlanCustomizer(@Nonnull final FutureJVppCore vppApi, + @Nonnull final NamingContext interfaceNamingContext, + @Nonnull final DisabledInterfacesManager interfaceDisableContext) { super(vppApi); - this.interfaceContext = interfaceContext; + this.interfaceNamingContext = interfaceNamingContext; + this.interfaceDisableContext = interfaceDisableContext; } @Override @@ -108,7 +114,7 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer { final VxlanAddDelTunnelReply reply = TranslateUtils.getReplyForWrite(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture(), id); LOG.debug("Vxlan tunnel set successfully for: {}, vxlan: {}", swIfName, vxlan); - if(interfaceContext.containsName(reply.swIfIndex, writeContext.getMappingContext())) { + if (interfaceNamingContext.containsName(reply.swIfIndex, writeContext.getMappingContext())) { // VPP keeps vxlan tunnels present even after they are delete(reserving ID for next tunnel) // This may cause inconsistencies in mapping context when configuring tunnels like this: // 1. Add tunnel 2. Delete tunnel 3. Read interfaces (reserved mapping e.g. vxlan_tunnel0 -> 6 @@ -116,13 +122,22 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer { // reserved ID and context is invalid) // That's why a check has to be performed here removing mapping vxlan_tunnel0 -> 6 mapping and storing // new name for that ID - final String formerName = interfaceContext.getName(reply.swIfIndex, writeContext.getMappingContext()); + final String formerName = interfaceNamingContext.getName(reply.swIfIndex, writeContext.getMappingContext()); LOG.debug("Removing updated mapping of a vxlan tunnel, id: {}, former name: {}, new name: {}", reply.swIfIndex, formerName, swIfName); - interfaceContext.removeName(formerName, writeContext.getMappingContext()); + interfaceNamingContext.removeName(formerName, writeContext.getMappingContext()); + } + + // Removing disability of an interface in case a vxlan tunnel formerly deleted is being reused in VPP + // further details in above comment + if (interfaceDisableContext.isInterfaceDisabled(reply.swIfIndex, writeContext.getMappingContext())) { + LOG.debug("Removing disability of vxlan tunnel, id: {}, name: {}", reply.swIfIndex, swIfName); + interfaceDisableContext.removeDisabledInterface(reply.swIfIndex, writeContext.getMappingContext()); + } + // Add new interface to our interface context - interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); + interfaceNamingContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); } private boolean isIpv6(final Vxlan vxlan) { @@ -157,8 +172,14 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer { TranslateUtils.getReplyForWrite(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture(), id); LOG.debug("Vxlan tunnel deleted successfully for: {}, vxlan: {}", swIfName, vxlan); - // Remove interface from our interface context - interfaceContext.removeName(swIfName, writeContext.getMappingContext()); + + final int index = interfaceNamingContext.getIndex(swIfName, writeContext.getMappingContext()); + // Mark this interface as disabled to not include it in operational reads + // because VPP will keep the interface there + LOG.debug("Marking vxlan tunnel as disabled, id: {}, name: {}", index, swIfName); + interfaceDisableContext.disableInterface(index, writeContext.getMappingContext()); + // Remove interface from our interface naming context + interfaceNamingContext.removeName(swIfName, writeContext.getMappingContext()); } private static VxlanAddDelTunnel getVxlanTunnelRequest(final byte isAdd, final byte[] srcAddr, final byte[] dstAddr, diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizer.java index b97ce127e..699c33d21 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizer.java @@ -19,6 +19,7 @@ package io.fd.honeycomb.translate.v3po.interfaces; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.net.InetAddresses; +import io.fd.honeycomb.translate.v3po.DisabledInterfacesManager; import io.fd.honeycomb.translate.v3po.util.AbstractInterfaceTypeCustomizer; import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; @@ -44,11 +45,15 @@ import org.slf4j.LoggerFactory; public class VxlanGpeCustomizer extends AbstractInterfaceTypeCustomizer { private static final Logger LOG = LoggerFactory.getLogger(VxlanGpeCustomizer.class); - private final NamingContext interfaceContext; + private final NamingContext interfaceNamingContext; + private final DisabledInterfacesManager interfaceDisableContext; - public VxlanGpeCustomizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) { + public VxlanGpeCustomizer(@Nonnull final FutureJVppCore vppApi, + @Nonnull final NamingContext interfaceNamingContext, + @Nonnull final DisabledInterfacesManager interfaceDisableContext) { super(vppApi); - this.interfaceContext = interfaceContext; + this.interfaceNamingContext = interfaceNamingContext; + this.interfaceDisableContext = interfaceDisableContext; } @Override @@ -110,24 +115,32 @@ public class VxlanGpeCustomizer extends AbstractInterfaceTypeCustomizer id, final String swIfName, - final VxlanGpe VxlanGpe, final WriteContext writeContext) + final VxlanGpe vxlanGpe, final WriteContext writeContext) throws VppBaseCallException, WriteTimeoutException { - final byte isIpv6 = (byte) (isIpv6(VxlanGpe) ? 1 : 0); - final InetAddress local = InetAddresses.forString(getAddressString(VxlanGpe.getLocal())); - final InetAddress remote = InetAddresses.forString(getAddressString(VxlanGpe.getRemote())); + final byte isIpv6 = (byte) (isIpv6(vxlanGpe) ? 1 : 0); + final InetAddress local = InetAddresses.forString(getAddressString(vxlanGpe.getLocal())); + final InetAddress remote = InetAddresses.forString(getAddressString(vxlanGpe.getRemote())); - int vni = VxlanGpe.getVni().getValue().intValue(); - byte protocol = (byte) VxlanGpe.getNextProtocol().getIntValue(); - int encapVrfId = VxlanGpe.getEncapVrfId().intValue(); - int decapVrfId = VxlanGpe.getDecapVrfId().intValue(); + int vni = vxlanGpe.getVni().getValue().intValue(); + byte protocol = (byte) vxlanGpe.getNextProtocol().getIntValue(); + int encapVrfId = vxlanGpe.getEncapVrfId().intValue(); + int decapVrfId = vxlanGpe.getDecapVrfId().intValue(); - LOG.debug("Deleting VxlanGpe tunnel for interface: {}. VxlanGpe: {}", swIfName, VxlanGpe); + LOG.debug("Deleting VxlanGpe tunnel for interface: {}. VxlanGpe: {}", swIfName, vxlanGpe); final CompletionStage VxlanGpeAddDelTunnelReplyCompletionStage = getFutureJVpp().vxlanGpeAddDelTunnel(getVxlanGpeTunnelRequest((byte) 0 /* is delete */, local.getAddress(), remote.getAddress(), vni, protocol, encapVrfId, decapVrfId, isIpv6)); TranslateUtils.getReplyForWrite(VxlanGpeAddDelTunnelReplyCompletionStage.toCompletableFuture(), id); - LOG.debug("VxlanGpe tunnel deleted successfully for: {}, VxlanGpe: {}", swIfName, VxlanGpe); - // Remove interface from our interface context - interfaceContext.removeName(swIfName, writeContext.getMappingContext()); + + final int index = interfaceNamingContext.getIndex(swIfName, writeContext.getMappingContext()); + // Mark this interface as disabled to not include it in operational reads + // because VPP will keep the interface there + LOG.debug("Marking vxlan tunnel as disabled, id: {}, name: {}", index, swIfName); + interfaceDisableContext.disableInterface(index, writeContext.getMappingContext()); + // Remove interface from our interface naming context + interfaceNamingContext.removeName(swIfName, writeContext.getMappingContext()); } private static VxlanGpeAddDelTunnel getVxlanGpeTunnelRequest(final byte isAdd, final byte[] local, final byte[] remote, diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizer.java index b82a2ddc4..d0baf003a 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizer.java @@ -16,10 +16,12 @@ package io.fd.honeycomb.translate.v3po.interfacesstate; -import io.fd.honeycomb.translate.read.ReadContext; -import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.honeycomb.translate.MappingContext; import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.read.ReadContext; import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.honeycomb.translate.v3po.DisabledInterfacesManager; import io.fd.honeycomb.translate.v3po.util.FutureJVppCustomizer; import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.v3po.util.TranslateUtils; @@ -27,6 +29,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import javax.annotation.Nonnull; @@ -57,11 +60,15 @@ public class InterfaceCustomizer extends FutureJVppCustomizer public static final String DUMPED_IFCS_CONTEXT_KEY = InterfaceCustomizer.class.getName() + "dumpedInterfacesDuringGetAllIds"; - private final NamingContext interfaceContext; + private final NamingContext interfaceNamingContext; + private final DisabledInterfacesManager interfaceDisableContext; - public InterfaceCustomizer(@Nonnull final FutureJVppCore jvpp, final NamingContext interfaceContext) { + public InterfaceCustomizer(@Nonnull final FutureJVppCore jvpp, + @Nonnull final NamingContext interfaceNamingContext, + @Nonnull final DisabledInterfacesManager interfaceDisableContext) { super(jvpp); - this.interfaceContext = interfaceContext; + this.interfaceNamingContext = interfaceNamingContext; + this.interfaceDisableContext = interfaceDisableContext; } @Nonnull @@ -76,9 +83,17 @@ public class InterfaceCustomizer extends FutureJVppCustomizer LOG.debug("Reading attributes for interface: {}", id); final String ifaceName = id.firstKeyOf(id.getTargetType()).getName(); + final int index = interfaceNamingContext.getIndex(ifaceName, ctx.getMappingContext()); + + // Ignore disabled interface (such as deleted VXLAN tunnels) + if (interfaceDisableContext.isInterfaceDisabled(index, ctx.getMappingContext())) { + LOG.debug("Skipping disabled interface: {}", id); + return; + } + // Pass cached details from getAllIds to getDetails to avoid additional dumps final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), id, ifaceName, - interfaceContext.getIndex(ifaceName, ctx.getMappingContext()), ctx.getModificationCache()); + index, ctx.getModificationCache()); LOG.debug("Interface details for interface: {}, details: {}", ifaceName, iface); if (!isRegularInterface(iface)) { @@ -139,23 +154,42 @@ public class InterfaceCustomizer extends FutureJVppCustomizer context.getModificationCache().put(DUMPED_IFCS_CONTEXT_KEY, ifaces.swInterfaceDetails.stream() .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails))); - 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, context.getMappingContext())) { - interfaceContext.addName(elt.swIfIndex, TranslateUtils.toString(elt.interfaceName), - context.getMappingContext()); - } - LOG.trace("Interface with name: {}, VPP name: {} and index: {} found in VPP", - interfaceContext.getName(elt.swIfIndex, context.getMappingContext()), elt.interfaceName, - elt.swIfIndex); - - return elt; - }) - .filter(InterfaceCustomizer::isRegularInterface) // filter out sub-interfaces - .map((elt) -> new InterfaceKey(interfaceContext.getName(elt.swIfIndex, context.getMappingContext()))) - .collect(Collectors.toList()); + final MappingContext mappingCtx = context.getMappingContext(); + final Set interfacesIdxs = ifaces.swInterfaceDetails.stream() + .filter(elt -> elt != null) + // Filter out disabled interfaces, dont read them + // This also prevents child readers in being invoked such as vxlan (which relies on disabling interfaces) + .filter(elt -> !interfaceDisableContext + .isInterfaceDisabled(elt.swIfIndex, mappingCtx)) + .map((elt) -> { + // Store interface name from VPP in context if not yet present + if (!interfaceNamingContext.containsName(elt.swIfIndex, mappingCtx)) { + interfaceNamingContext.addName(elt.swIfIndex, TranslateUtils.toString(elt.interfaceName), + mappingCtx); + } + LOG.trace("Interface with name: {}, VPP name: {} and index: {} found in VPP", + interfaceNamingContext.getName(elt.swIfIndex, mappingCtx), + elt.interfaceName, + elt.swIfIndex); + + return elt; + }) + // filter out sub-interfaces + .filter(InterfaceCustomizer::isRegularInterface) + .map(elt -> elt.swIfIndex) + .collect(Collectors.toSet()); + + // Clean disabled interfaces list + interfaceDisableContext.getDisabledInterfaces(mappingCtx).stream() + // Find indices not currently in VPP + .filter(interfacesIdxs::contains) + // Remove from disabled list ... not disabled if not existing + .forEach(idx -> interfaceDisableContext.removeDisabledInterface(idx, mappingCtx)); + + // Transform indices to keys + interfacesKeys = interfacesIdxs.stream() + .map(index -> new InterfaceKey(interfaceNamingContext.getName(index, context.getMappingContext()))) + .collect(Collectors.toList()); LOG.debug("Interfaces found in VPP: {}", interfacesKeys); return interfacesKeys; @@ -174,5 +208,4 @@ public class InterfaceCustomizer extends FutureJVppCustomizer @Nonnull final List readData) { ((InterfacesStateBuilder) builder).setInterface(readData); } - } diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManagerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManagerTest.java new file mode 100644 index 000000000..7381533e5 --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/DisabledInterfacesManagerTest.java @@ -0,0 +1,106 @@ +/* + * 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.translate.v3po; + +import static org.hamcrest.CoreMatchers.hasItems; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.MappingContext; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.DisabledInterfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.DisabledInterfacesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndexBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.context.rev160909.disabled.interfaces.DisabledInterfaceIndexKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +public class DisabledInterfacesManagerTest { + + private static final InstanceIdentifier + ROOT_ID = InstanceIdentifier.create(DisabledInterfaces.class); + private static final KeyedInstanceIdentifier SPECIFIC_ID_1 = + ROOT_ID.child(DisabledInterfaceIndex.class, new DisabledInterfaceIndexKey(1)); + private static final KeyedInstanceIdentifier SPECIFIC_ID_4 = + ROOT_ID.child(DisabledInterfaceIndex.class, new DisabledInterfaceIndexKey(4)); + + @Mock + private MappingContext mappingContext; + private DisabledInterfacesManager manager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + manager = new DisabledInterfacesManager(); + doReturn(Optional.of(new DisabledInterfacesBuilder().setDisabledInterfaceIndex(toIndices(1, 2, 3)).build())) + .when(mappingContext) + .read(ROOT_ID); + doReturn(Optional.of(toIndex(1))) + .when(mappingContext) + .read(SPECIFIC_ID_1); + doReturn(Optional.absent()) + .when(mappingContext) + .read(SPECIFIC_ID_4); + } + + @Test + public void testGetAll() throws Exception { + final List disabledInterfaces = manager.getDisabledInterfaces(mappingContext); + assertThat(disabledInterfaces, hasItems(1, 2, 3)); + } + + @Test + public void testCheckOne() throws Exception { + assertTrue(manager.isInterfaceDisabled(1, mappingContext)); + assertFalse(manager.isInterfaceDisabled(4, mappingContext)); + } + + @Test + public void testDisable() throws Exception { + manager.disableInterface(1, mappingContext); + verify(mappingContext).put(SPECIFIC_ID_1, toIndex(1)); + } + + @Test + public void testRemoveDisability() throws Exception { + manager.removeDisabledInterface(1, mappingContext); + verify(mappingContext).delete(SPECIFIC_ID_1); + } + + private List toIndices(final int... indices) { + return Arrays.stream(indices) + .mapToObj(this::toIndex) + .collect(Collectors.toList()); + } + + private DisabledInterfaceIndex toIndex(final int idx) { + return new DisabledInterfaceIndexBuilder() + .setIndex(idx) + .build(); + } +} \ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizerTest.java index b61514c13..809761af6 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanCustomizerTest.java @@ -35,11 +35,12 @@ import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.base.Optional; import com.google.common.net.InetAddresses; -import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.MappingContext; import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.v3po.DisabledInterfacesManager; import io.fd.honeycomb.translate.v3po.test.TestHelperUtils; import io.fd.honeycomb.translate.v3po.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -80,6 +81,8 @@ public class VxlanCustomizerTest { private WriteContext writeContext; @Mock private MappingContext mappingContext; + @Mock + private DisabledInterfacesManager disableContext; private VxlanCustomizer customizer; private String ifaceName; @@ -96,7 +99,7 @@ public class VxlanCustomizerTest { doReturn(toBeReturned).when(writeContext).getModificationCache(); doReturn(mappingContext).when(writeContext).getMappingContext(); - customizer = new VxlanCustomizer(api, namingContext); + customizer = new VxlanCustomizer(api, namingContext, disableContext); ifaceName = "eth0"; id = InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(ifaceName)) @@ -174,6 +177,22 @@ public class VxlanCustomizerTest { verify(mappingContext).put(eq(getMappingIid(ifaceName, "test-instance")), eq(getMapping(ifaceName, 0).get())); } + @Test + public void testWriteCurrentAttributesWithExistingVxlanPlaceholder() throws Exception { + final Vxlan vxlan = generateVxlan(); + + whenVxlanAddDelTunnelThenSuccess(); + + doReturn(Optional.absent()) + .when(mappingContext).read(getMappingIid(ifaceName, "test-instance").firstIdentifierOf(Mappings.class)); + doReturn(true).when(disableContext).isInterfaceDisabled(0, mappingContext); + + customizer.writeCurrentAttributes(id, vxlan, writeContext); + verifyVxlanAddWasInvoked(vxlan); + verify(mappingContext).put(eq(getMappingIid(ifaceName, "test-instance")), eq(getMapping(ifaceName, 0).get())); + verify(disableContext).removeDisabledInterface(0, mappingContext); + } + @Test public void testWriteCurrentAttributesMappingAlreadyPresent() throws Exception { final Vxlan vxlan = generateVxlan(); @@ -231,6 +250,7 @@ public class VxlanCustomizerTest { customizer.deleteCurrentAttributes(id, vxlan, writeContext); verifyVxlanDeleteWasInvoked(vxlan); verify(mappingContext).delete(eq(getMappingIid(ifaceName, "test-instance"))); + verify(disableContext).disableInterface(1, mappingContext); } @Test diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizerTest.java index e545ef451..8eb44ef12 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/VxlanGpeCustomizerTest.java @@ -34,6 +34,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.base.Optional; import com.google.common.net.InetAddresses; import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.v3po.DisabledInterfacesManager; import io.fd.honeycomb.translate.v3po.test.ContextTestUtils; import io.fd.honeycomb.translate.v3po.test.TestHelperUtils; import io.fd.honeycomb.translate.v3po.util.NamingContext; @@ -80,6 +81,8 @@ public class VxlanGpeCustomizerTest { private WriteContext writeContext; @Mock private MappingContext mappingContext; + @Mock + private DisabledInterfacesManager interfaceDisableContext; private VxlanGpeCustomizer customizer; private String ifaceName; @@ -96,7 +99,7 @@ public class VxlanGpeCustomizerTest { doReturn(toBeReturned).when(writeContext).getModificationCache(); doReturn(mappingContext).when(writeContext).getMappingContext(); - customizer = new VxlanGpeCustomizer(api, namingContext); + customizer = new VxlanGpeCustomizer(api, namingContext, interfaceDisableContext); ifaceName = "eth0"; id = InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(ifaceName)) diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizerTest.java index 4f9b99b6d..34839f514 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceCustomizerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verifyZeroInteractions; import com.google.common.base.Optional; import com.google.common.collect.Lists; import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.honeycomb.translate.v3po.DisabledInterfacesManager; import io.fd.honeycomb.translate.v3po.test.ContextTestUtils; import io.fd.honeycomb.translate.v3po.test.InterfaceTestUtils; import io.fd.honeycomb.translate.v3po.test.ListReaderCustomizerTest; @@ -38,6 +39,7 @@ import java.util.List; import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder; import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; @@ -57,6 +59,8 @@ public class InterfaceCustomizerTest extends ListReaderCustomizerTest { private NamingContext interfacesContext; + @Mock + private DisabledInterfacesManager interfaceDisableContext; public InterfaceCustomizerTest() { super(Interface.class); @@ -89,7 +93,7 @@ public class InterfaceCustomizerTest extends doReturn(eth1).when(mappingContext).read(eth1Id); doReturn(subEth1).when(mappingContext).read(subEth1Id); - return new InterfaceCustomizer(api, interfacesContext); + return new InterfaceCustomizer(api, interfacesContext, interfaceDisableContext); } // TODO use reflexion and move to ListReaderCustomizerTest @@ -209,4 +213,28 @@ public class InterfaceCustomizerTest extends // sub-interface should not be on the list assertEquals(expectedIds, actualIds); } + + @Test + public void testGetAllIdsWithDisabled() throws Exception { + final InstanceIdentifier id = InstanceIdentifier.create(InterfacesState.class) + .child(Interface.class); + + doReturn(true).when(interfaceDisableContext).isInterfaceDisabled(1, mappingContext); + + final String swIf0Name = "eth0"; + final SwInterfaceDetails swIf0 = new SwInterfaceDetails(); + swIf0.swIfIndex = 0; + swIf0.interfaceName = swIf0Name.getBytes(); + final String swIf1Name = "eth1"; + final SwInterfaceDetails swIf1 = new SwInterfaceDetails(); + swIf1.swIfIndex = 1; + swIf1.interfaceName = swIf1Name.getBytes(); + InterfaceTestUtils.whenSwInterfaceDumpThenReturn(api, Arrays.asList(swIf0, swIf1)); + + final List expectedIds = Arrays.asList(new InterfaceKey(swIf0Name)); + final List actualIds = getCustomizer().getAllIds(id, ctx); + + // disabled interface should not be on the list + assertEquals(expectedIds, actualIds); + } } diff --git a/vpp-common/naming-context-api/src/main/yang/naming-context.yang b/vpp-common/naming-context-api/src/main/yang/naming-context.yang index fa44bdd12..c101b5c26 100644 --- a/vpp-common/naming-context-api/src/main/yang/naming-context.yang +++ b/vpp-common/naming-context-api/src/main/yang/naming-context.yang @@ -12,6 +12,8 @@ module naming-context { } container contexts { + config false; + // context data list naming-context { -- cgit 1.2.3-korg