BUG-4827: ADD-PATH capability implementation 53/33753/5
authorIveta Halanova <ihalanov@cisco.com>
Tue, 2 Feb 2016 11:38:27 +0000 (12:38 +0100)
committerIveta Halanova <ihalanov@cisco.com>
Tue, 2 Feb 2016 11:38:27 +0000 (12:38 +0100)
Extended multiprotocol capabilities model.
Implemented and registered handler in BGPActivator.
Implemented unit test.

ref.: tools.ietf.org/html/draft-ietf-idr-add-paths-13#section-4

Change-Id: I0d7c2ea5023799b9906f1d787ab9fe824b817b7e
Signed-off-by: Iveta Halanova <ihalanov@cisco.com>
bgp/parser-api/src/main/yang/bgp-multiprotocol.yang
bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/BGPActivator.java
bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/open/AddPathCapabilityHandler.java [new file with mode: 0644]
bgp/parser-impl/src/test/java/org/opendaylight/protocol/bgp/parser/impl/AddPathCapabilityHandlerTest.java [new file with mode: 0644]

index cc1c9181369bb86706ea55a721d38f83ef6a614f..ff816f35161d4166071ea2b50fc93febc2b0c83e 100644 (file)
@@ -92,6 +92,19 @@ module bgp-multiprotocol {
                 }
             }
         }
+        container add-path-capability {
+            reference "http://tools.ietf.org/html/draft-ietf-idr-add-paths-13#section-4";
+            list address-families {
+                uses bgp-table-type;
+                leaf send-receive {
+                    type enumeration {
+                        enum receive { value 1; }
+                        enum send { value 2; }
+                        enum both { value 3; }
+                    }
+                }
+            }
+        }
     }
 
     augment "/bgp-msg:update/bgp-msg:attributes" {
index eb1808e5630dade28b611b0286e83952830a294e..0c26f09fda332d52b8a1711d2f74a2b45e628ed8 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.protocol.bgp.parser.impl.message.BGPKeepAliveMessagePars
 import org.opendaylight.protocol.bgp.parser.impl.message.BGPNotificationMessageParser;
 import org.opendaylight.protocol.bgp.parser.impl.message.BGPOpenMessageParser;
 import org.opendaylight.protocol.bgp.parser.impl.message.BGPUpdateMessageParser;
+import org.opendaylight.protocol.bgp.parser.impl.message.open.AddPathCapabilityHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.open.As4CapabilityHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.open.CapabilityParameterParser;
 import org.opendaylight.protocol.bgp.parser.impl.message.open.GracefulCapabilityHandler;
@@ -69,6 +70,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.mess
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.Origin;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.OriginatorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.update.message.WithdrawnRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.AddPathCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.GracefulRestartCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.MultiprotocolCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlri;
@@ -133,6 +135,10 @@ public final class BGPActivator extends AbstractBGPExtensionProviderActivator {
         regs.add(context.registerCapabilityParser(MultiProtocolCapabilityHandler.CODE, multi));
         regs.add(context.registerCapabilitySerializer(MultiprotocolCapability.class, multi));
 
+        final AddPathCapabilityHandler addPath = new AddPathCapabilityHandler(afiReg, safiReg);
+        regs.add(context.registerCapabilityParser(AddPathCapabilityHandler.CODE, addPath));
+        regs.add(context.registerCapabilitySerializer(AddPathCapability.class, addPath));
+
         final As4CapabilityHandler as4 = new As4CapabilityHandler();
         regs.add(context.registerCapabilityParser(As4CapabilityHandler.CODE, as4));
         regs.add(context.registerCapabilitySerializer(As4BytesCapability.class, as4));
diff --git a/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/open/AddPathCapabilityHandler.java b/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/open/AddPathCapabilityHandler.java
new file mode 100644 (file)
index 0000000..f1c44ad
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.parser.impl.message.open;
+
+import com.google.common.base.Preconditions;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import org.opendaylight.protocol.bgp.parser.spi.AddressFamilyRegistry;
+import org.opendaylight.protocol.bgp.parser.spi.CapabilityParser;
+import org.opendaylight.protocol.bgp.parser.spi.CapabilitySerializer;
+import org.opendaylight.protocol.bgp.parser.spi.CapabilityUtil;
+import org.opendaylight.protocol.bgp.parser.spi.SubsequentAddressFamilyRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.AddPathCapability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.AddPathCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.add.path.capability.AddressFamilies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.add.path.capability.AddressFamilies.SendReceive;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.add.path.capability.AddressFamiliesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.SubsequentAddressFamily;
+
+public class AddPathCapabilityHandler implements CapabilityParser, CapabilitySerializer {
+
+    public static final int CODE = 69;
+    private static final int TRIPLET_BYTE_SIZE = 4;
+
+    private final AddressFamilyRegistry afiReg;
+    private final SubsequentAddressFamilyRegistry safiReg;
+
+    public AddPathCapabilityHandler(final AddressFamilyRegistry afiReg, final SubsequentAddressFamilyRegistry safiReg) {
+        this.afiReg = Preconditions.checkNotNull(afiReg);
+        this.safiReg = Preconditions.checkNotNull(safiReg);
+    }
+
+    @Override
+    public void serializeCapability(final CParameters capability, final ByteBuf byteAggregator) {
+        if ( (capability.getAugmentation(CParameters1.class) == null)
+            || (capability.getAugmentation(CParameters1.class).getAddPathCapability() == null) ) {
+            return;
+        }
+        final AddPathCapability addPathCap = capability.getAugmentation(CParameters1.class).getAddPathCapability();
+
+        final List<AddressFamilies> families = addPathCap.getAddressFamilies();
+        final ByteBuf capBuffer = Unpooled.buffer(families.size() * TRIPLET_BYTE_SIZE);
+        for (final AddressFamilies addressFamily : families) {
+            final Class<? extends AddressFamily> afi = addressFamily.getAfi();
+            final Integer afival = this.afiReg.numberForClass(afi);
+            Preconditions.checkArgument(afival != null, "Unhandled address family " + afi);
+            capBuffer.writeShort(afival);
+
+            final Class<? extends SubsequentAddressFamily> safi = addressFamily.getSafi();
+            final Integer safival = this.safiReg.numberForClass(safi);
+            Preconditions.checkArgument(safival != null, "Unhandled subsequent address family " + safi);
+            capBuffer.writeByte(safival);
+
+            final SendReceive sendReceive = addressFamily.getSendReceive();
+            Preconditions.checkArgument(sendReceive != null, "Unhandled Send/Receive value");
+            capBuffer.writeByte(sendReceive.getIntValue());
+        }
+
+        CapabilityUtil.formatCapability(CODE, capBuffer, byteAggregator);
+    }
+
+    @Override
+    public CParameters parseCapability(final ByteBuf buffer) throws BGPDocumentedException, BGPParsingException {
+        final List<AddressFamilies> families = new ArrayList<>();
+        while (buffer.isReadable()) {
+            final int afiVal = buffer.readUnsignedShort();
+            final Class<? extends AddressFamily> afi = this.afiReg.classForFamily(afiVal);
+            if (afi == null) {
+                throw new BGPParsingException("Address Family Identifier: '" + afiVal + "' not supported.");
+            }
+            final int safiVal = buffer.readUnsignedByte();
+            final Class<? extends SubsequentAddressFamily> safi = this.safiReg.classForFamily(safiVal);
+            if (safi == null) {
+                throw new BGPParsingException("Subsequent Address Family Identifier: '" + safiVal + "' not supported.");
+            }
+            final SendReceive sendReceive = SendReceive.forValue(buffer.readUnsignedByte());
+            if (sendReceive != null) {
+                families.add(new AddressFamiliesBuilder().setAfi(afi).setSafi(safi).setSendReceive(sendReceive).build());
+            }
+        }
+        return new CParametersBuilder().addAugmentation(CParameters1.class,new CParameters1Builder().setAddPathCapability(
+            new AddPathCapabilityBuilder().setAddressFamilies(families).build()).build()).build();
+    }
+
+}
diff --git a/bgp/parser-impl/src/test/java/org/opendaylight/protocol/bgp/parser/impl/AddPathCapabilityHandlerTest.java b/bgp/parser-impl/src/test/java/org/opendaylight/protocol/bgp/parser/impl/AddPathCapabilityHandlerTest.java
new file mode 100644 (file)
index 0000000..71993b4
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.parser.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import org.opendaylight.protocol.bgp.parser.impl.message.open.AddPathCapabilityHandler;
+import org.opendaylight.protocol.bgp.parser.spi.AddressFamilyRegistry;
+import org.opendaylight.protocol.bgp.parser.spi.SubsequentAddressFamilyRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.AddPathCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.add.path.capability.AddressFamilies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.add.path.capability.AddressFamilies.SendReceive;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.add.path.capability.AddressFamiliesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv6AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
+
+public class AddPathCapabilityHandlerTest {
+    private final static Class afi = Ipv6AddressFamily.class;
+    @Mock private AddressFamilyRegistry afiRegistry;
+    @Mock private AddressFamilyRegistry afirExpection;
+    private final static Class safi = UnicastSubsequentAddressFamily.class;
+    @Mock private SubsequentAddressFamilyRegistry safiRegistry;
+    @Mock private SubsequentAddressFamilyRegistry safirException;
+    private final ByteBuf parseCorrectBytes = Unpooled.copiedBuffer(new byte[] {1, 4, 4, 1});
+    private final ByteBuf parseWrongBytes = Unpooled.copiedBuffer(new byte[] {1, 4, 4, 4});
+    private final byte[] serializedBytes = new byte[] {AddPathCapabilityHandler.CODE, 4, 1, 4, 4, 1};
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        Mockito.doReturn(260).when(this.afiRegistry).numberForClass(afi);
+        Mockito.doReturn(this.afi).when(this.afiRegistry).classForFamily(260);
+
+        Mockito.doReturn(null).when(this.afirExpection).numberForClass(afi);
+        Mockito.doReturn(null).when(this.afirExpection).classForFamily(260);
+
+        Mockito.doReturn(4).when(this.safiRegistry).numberForClass(safi);
+        Mockito.doReturn(this.safi).when(this.safiRegistry).classForFamily(4);
+
+        Mockito.doReturn(null).when(this.safirException).numberForClass(safi);
+        Mockito.doReturn(null).when(this.safirException).classForFamily(4);
+    }
+
+    @Test
+    public void testCapabilityHandler() throws BGPDocumentedException, BGPParsingException {
+        final List<AddressFamilies> family = new ArrayList<AddressFamilies>();
+        family.add(new AddressFamiliesBuilder().setAfi(this.afi).setSafi(this.safi).setSendReceive(SendReceive.forValue(1)).build());
+
+        final CParameters capabilityToSerialize = new CParametersBuilder().addAugmentation(CParameters1.class, new CParameters1Builder().setAddPathCapability(
+            new AddPathCapabilityBuilder().setAddressFamilies(family).build()).build()).build();
+
+        final ByteBuf bytes = Unpooled.buffer(6);
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afiRegistry, this.safiRegistry);
+        handler.serializeCapability(capabilityToSerialize, bytes);
+        assertArrayEquals(this.serializedBytes, bytes.array());
+
+        final CParameters newCaps = handler.parseCapability(this.parseCorrectBytes);
+        assertEquals(capabilityToSerialize.hashCode(), newCaps.hashCode());
+    }
+
+    @Test(expected=BGPParsingException.class)
+    public void testAfiException() throws BGPDocumentedException, BGPParsingException {
+        final ByteBuf bytes = this.parseWrongBytes.copy();
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afirExpection, this.safiRegistry);
+        handler.parseCapability(bytes);
+    }
+
+    @Test(expected=BGPParsingException.class)
+    public void testSafiException() throws BGPDocumentedException, BGPParsingException {
+        final ByteBuf bytes = this.parseWrongBytes.copy();
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afiRegistry, this.safirException);
+        handler.parseCapability(bytes);
+    }
+
+    @Test
+    public void testSendReceiveIgnored() throws BGPDocumentedException, BGPParsingException {
+        final CParameters capabilityToSerialize = new CParametersBuilder().addAugmentation(CParameters1.class, new CParameters1Builder().setAddPathCapability(
+            new AddPathCapabilityBuilder().setAddressFamilies(new ArrayList<AddressFamilies>()).build()).build()).build();
+
+        final ByteBuf bytes = this.parseWrongBytes.copy();
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afiRegistry, this.safiRegistry);
+        final CParameters newCaps = handler.parseCapability(bytes);
+        assertEquals(capabilityToSerialize.hashCode(), newCaps.hashCode());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testUnhandledAfi() {
+        final List<AddressFamilies> family = new ArrayList<AddressFamilies>();
+        family.add(new AddressFamiliesBuilder().setAfi(this.afi).setSafi(this.safi).setSendReceive(SendReceive.forValue(2)).build());
+
+        final CParameters capabilityToSerialize = new CParametersBuilder().addAugmentation(CParameters1.class, new CParameters1Builder().setAddPathCapability(
+            new AddPathCapabilityBuilder().setAddressFamilies(family).build()).build()).build();
+
+        final ByteBuf bytes = Unpooled.buffer();
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afirExpection, this.safiRegistry);
+        handler.serializeCapability(capabilityToSerialize, bytes);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testUnhandledSafi() {
+        final List<AddressFamilies> family = new ArrayList<AddressFamilies>();
+        family.add(new AddressFamiliesBuilder().setAfi(this.afi).setSafi(this.safi).setSendReceive(SendReceive.forValue(3)).build());
+
+        final CParameters capabilityToSerialize = new CParametersBuilder().addAugmentation(CParameters1.class, new CParameters1Builder().setAddPathCapability(
+            new AddPathCapabilityBuilder().setAddressFamilies(family).build()).build()).build();
+
+        final ByteBuf bytes = Unpooled.buffer();
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afiRegistry, this.safirException);
+        handler.serializeCapability(capabilityToSerialize, bytes);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testUnhandledSendReceive() {
+        final List<AddressFamilies> family = new ArrayList<AddressFamilies>();
+        family.add(new AddressFamiliesBuilder().setAfi(this.afi).setSafi(this.safi).setSendReceive(SendReceive.forValue(4)).build());
+
+        final CParameters capabilityToSerialize = new CParametersBuilder().addAugmentation(CParameters1.class, new CParameters1Builder().setAddPathCapability(
+            new AddPathCapabilityBuilder().setAddressFamilies(family).build()).build()).build();
+
+        final ByteBuf bytes = Unpooled.buffer();
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afiRegistry, this.safiRegistry);
+        handler.serializeCapability(capabilityToSerialize, bytes);
+    }
+
+    @Test
+    public void noSerializationTest() {
+        final CParameters capabilityNoAugmentation = new CParametersBuilder().addAugmentation(CParameters1.class, null).build();
+        final CParameters capabilityNoMP = new CParametersBuilder().addAugmentation(CParameters1.class, new CParameters1Builder().build()).build();
+
+        final ByteBuf bytes = Unpooled.buffer();
+        final AddPathCapabilityHandler handler = new AddPathCapabilityHandler(this.afiRegistry, this.safirException);
+        handler.serializeCapability(capabilityNoAugmentation, bytes);
+        assertEquals(0, bytes.readableBytes());
+        handler.serializeCapability(capabilityNoMP, bytes);
+        assertEquals(0, bytes.readableBytes());
+    }
+}