a76f0e4d61cafdc6efb7d542f4b828c53bbe75d1
[bgpcep.git] / bgp / rib-impl / src / test / java / org / opendaylight / protocol / bgp / rib / impl / GracefulRestartTest.java
1 /*
2  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkIdleState;
11 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkStateIsNotRestarting;
12 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkUpState;
13 import static org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages;
14 import static org.opendaylight.protocol.util.CheckUtil.readDataOperational;
15 import static org.opendaylight.protocol.util.CheckUtil.waitFutureSuccess;
16
17 import com.google.common.collect.ImmutableMap;
18 import io.netty.channel.Channel;
19 import io.netty.channel.ChannelFuture;
20 import java.net.InetSocketAddress;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.concurrent.ExecutionException;
30 import java.util.stream.Collectors;
31 import org.junit.After;
32 import org.junit.Assert;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.mockito.Mockito;
36 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
37 import org.opendaylight.protocol.bgp.mode.impl.add.all.paths.AllPathSelection;
38 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
39 import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Open;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.OpenBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.ProtocolVersion;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.BgpOrigin;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68
69 public class GracefulRestartTest extends AbstractAddPathTest {
70
71     private BGPSessionImpl session;
72     private BGPSessionImpl session2;
73     private BGPPeer peer;
74     private BgpPeer bgpPeer;
75     private BgpParameters parameters;
76     private final Set<TablesKey> afiSafiAdvertised = new HashSet<>();
77     private final Set<TablesKey> gracefulAfiSafiAdvertised = new HashSet<>();
78     private RIBImpl ribImpl;
79     private Channel serverChannel;
80     private SimpleSessionListener listener = new SimpleSessionListener();
81     private SimpleSessionListener listener2 = new SimpleSessionListener();
82     private static final int DEFERRAL_TIMER = 5;
83     private static final RibId RIBID = new RibId("test-rib");
84     private final Ipv4Prefix PREFIX2 = new Ipv4Prefix("2.2.2.2/32");
85     private final Ipv6Prefix PREFIX3 = new Ipv6Prefix("dead:beef::/64");
86     private final Ipv6Address IPV6_NEXT_HOP = new Ipv6Address("dead:beef::1");
87     private static final TablesKey IPV6_TABLES_KEY = new TablesKey(Ipv6AddressFamily.class,
88             UnicastSubsequentAddressFamily.class);
89
90     private static final InstanceIdentifier<LocRib> LOC_RIB_IID = InstanceIdentifier.builder(BgpRib.class)
91             .child(Rib.class, new RibKey(RIBID))
92             .child(LocRib.class)
93             .build();
94     private static final InstanceIdentifier<Tables> IPV4_IID = LOC_RIB_IID.builder()
95             .child(Tables.class,TABLES_KEY)
96             .build();
97     private static final InstanceIdentifier<Tables> IPV6_IID = LOC_RIB_IID.builder()
98             .child(Tables.class, IPV6_TABLES_KEY)
99             .build();
100
101     @Override
102     @Before
103     public void setUp() throws Exception {
104         super.setUp();
105         final Map<TablesKey, PathSelectionMode> pathTables
106                 = ImmutableMap.of(TABLES_KEY, new AllPathSelection());
107         final ArrayList<BgpTableType> tableTypes = new ArrayList<>(TABLES_TYPE);
108         tableTypes.add(new BgpTableTypeImpl(Ipv6AddressFamily.class, UnicastSubsequentAddressFamily.class));
109         this.ribImpl = new RIBImpl(this.tableRegistry, RIBID, AS_NUMBER, BGP_ID, this.ribExtension,
110                 this.serverDispatcher, this.codecsRegistry,
111                 getDomBroker(), getDataBroker(), this.policies, tableTypes, pathTables);
112
113         this.ribImpl.instantiateServiceInstance();
114         this.ribImpl.onGlobalContextUpdated(this.schemaService.getGlobalContext());
115         final ChannelFuture channelFuture = this.serverDispatcher.createServer(new InetSocketAddress(RIB_ID, PORT));
116         waitFutureSuccess(channelFuture);
117         this.serverChannel = channelFuture.channel();
118
119         gracefulAfiSafiAdvertised.add(new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class));
120         afiSafiAdvertised.addAll(gracefulAfiSafiAdvertised);
121         afiSafiAdvertised.add(IPV6_TABLES_KEY);
122         this.parameters = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
123         this.bgpPeer = Mockito.mock(BgpPeer.class);
124         Mockito.doReturn(GRACEFUL_RESTART_TIME).when(this.bgpPeer).getGracefulRestartTimer();
125         Mockito.doReturn(createParameter(false, true, Collections.singletonMap(TABLES_KEY, false))
126                 .getOptionalCapabilities()).when(this.bgpPeer).getBgpFixedCapabilities();
127         this.peer = configurePeer(this.tableRegistry, PEER1, this.ribImpl, parameters, PeerRole.Ibgp,
128                 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised, bgpPeer);
129         this.session = createPeerSession(PEER1, parameters, this.listener);
130         final BgpParameters parameters2 = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
131         configurePeer(this.tableRegistry, PEER2, this.ribImpl, parameters2, PeerRole.Ibgp,
132                 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised);
133         this.session2 = createPeerSession(PEER2, parameters2, this.listener2);
134     }
135
136     @After
137     public void tearDown() throws ExecutionException, InterruptedException {
138         waitFutureSuccess(this.serverChannel.close());
139         this.session.close();
140         super.tearDown();
141     }
142
143     /**
144      * Test correct behavior when connection restart is unnoticed.
145      * "Correct" means that the previous TCP session MUST be closed, and the new one retained.
146      * Since the previous connection is considered to be terminated, no NOTIFICATION message should be sent.
147      */
148     @Test
149     public void resetConnectionOnOpenTest() {
150
151         final Open open = createClassicOpen(true);
152         this.session.writeAndFlush(open);
153         checkIdleState(this.peer);
154         checkReceivedMessages(this.listener, 0);
155     }
156
157     /**
158      * Test that routes from peer that has advertised the Graceful Restart Capability MUST be retained
159      * for all the address families that were previously received in the Graceful Restart Capability.
160      *
161      * @throws Exception on reading Rib failure
162      */
163     @Test
164     public void retainRoutesOnPeerRestartTest() throws Exception {
165         final List<Ipv4Prefix> ipv4Prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1), new Ipv4Prefix(PREFIX2));
166         final List<Ipv6Prefix> ipv6Prefixes = Collections.singletonList(new Ipv6Prefix(PREFIX3));
167         insertRoutes(ipv4Prefixes, PEER1, ipv6Prefixes, IPV6_NEXT_HOP, this.session);
168         checkLocRibIpv4Routes(2);
169         checkLocRibIpv6Routes(1);
170
171         this.session.close();
172         checkIdleState(this.peer);
173         checkLocRibIpv4Routes(2);
174         checkLocRibIpv6Routes(0);
175     }
176
177     /**
178      * If the session does not get re-established within the "Restart Time"
179      * that the peer advertised previously, the Receiving Speaker MUST
180      * delete all the stale routes from the peer that it is retaining.
181      *
182      * @throws Exception on reading Rib failure
183      */
184     @Test
185     public void removeRoutesOnHoldTimeExpireTest() throws Exception {
186         retainRoutesOnPeerRestartTest();
187         checkStateIsNotRestarting(peer, GRACEFUL_RESTART_TIME);
188         checkLocRibIpv4Routes(0);
189         checkLocRibIpv6Routes(0);
190     }
191
192     /**
193      * Once the session is re-established, if the Graceful
194      * Restart Capability is not received in the re-established session at
195      * all, then the Receiving Speaker MUST immediately remove all the stale
196      * routes from the peer that it is retaining for that address family.
197      *
198      * @throws Exception on reading Rib failure
199      */
200     @Test
201     public void removeRoutesOnMissingGracefulRestartTest() throws Exception {
202         retainRoutesOnPeerRestartTest();
203         this.session = createPeerSession(PEER1, createParameter(false, true, null), this.listener);
204         checkUpState(listener);
205         checkLocRibIpv4Routes(0);
206         checkLocRibIpv6Routes(0);
207     }
208
209     /**
210      * Once the session is re-established, if a specific address family is not included
211      * in the newly received Graceful Restart Capability, then the Receiving Speaker
212      * MUST immediately remove all the stale routes from the peer that it is retaining
213      * for that address family.
214      *
215      *
216      * @throws Exception on reading Rib failure
217      */
218     @Test
219     public void removeRoutesOnMissingGracefulRestartAfiSafiTest() throws Exception {
220         retainRoutesOnPeerRestartTest();
221         this.session = createPeerSession(PEER1, createParameter(false, true, Collections.singletonMap(TABLES_KEY, false)),
222                 this.listener);
223         checkUpState(listener);
224         checkUpState(this.peer);
225         checkLocRibIpv4Routes(0);
226         checkLocRibIpv6Routes(0);
227     }
228
229     /**
230      * Once the End-of-RIB marker for an address family is received from the peer, it MUST
231      * immediately remove any routes from the peer that are still marked as stale for that
232      * address family.
233      *
234      * @throws Exception on reading Rib failure
235      */
236     @Test
237     public void removeStaleRoutesAfterRestartTest() throws Exception {
238         retainRoutesOnPeerRestartTest();
239         this.session = createPeerSession(PEER1, createParameter(false, true,
240                 Collections.singletonMap(TABLES_KEY, true)), this.listener);
241         checkUpState(this.listener);
242         insertRoutes(Collections.singletonList(new Ipv4Prefix(PREFIX1)), PEER1, null, null, this.session);
243         insertRoutes(null, null, null, null, this.session);
244         checkLocRibIpv4Routes(1);
245         checkLocRibIpv6Routes(0);
246     }
247
248     /**
249      * Perform local graceful restart and verify routes are preserved.
250      *
251      * @throws Exception on reading Rib failure
252      */
253     @Test
254     public void performLocalGracefulRestart() throws Exception {
255         final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
256         final List<Ipv4Prefix> ipv4prefixes2 = Arrays.asList(new Ipv4Prefix(PREFIX2));
257         final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
258         insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
259         insertRoutes(ipv4prefixes2, PEER2, null, null, this.session2);
260         checkLocRibIpv4Routes(2);
261         checkLocRibIpv6Routes(1);
262
263         this.peer.restartGracefully(DEFERRAL_TIMER).get();
264         this.session = createPeerSession(PEER1, this.parameters, this.listener);
265         checkUpState(this.listener);
266         checkUpState(this.peer);
267         checkLocRibIpv4Routes(2);
268         checkLocRibIpv6Routes(0);
269     }
270
271     /**
272      * Wait with route selection until EOT is received.
273      *
274      * @throws Exception on reading Rib failure
275      */
276     @Test
277     public void waitForEORonLocalGracefulRestart() throws Exception {
278         performLocalGracefulRestart();
279         final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
280         final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
281         insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
282         checkLocRibIpv4Routes(2);
283         checkLocRibIpv6Routes(0);
284         insertRoutes(null, null,null, null, this.session);
285         checkLocRibIpv4Routes(2);
286         checkLocRibIpv6Routes(1);
287     }
288
289     /**
290      * Wait with route selection until deferral time is expired.
291      *
292      * @throws Exception on reading Rib failure
293      */
294     @Test
295     public void waitForDeferralTimerOnLocalGracefulRestart() throws Exception {
296         performLocalGracefulRestart();
297         final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
298         final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
299         insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
300         checkLocRibIpv4Routes(2);
301         checkLocRibIpv6Routes(0);
302         checkStateIsNotRestarting(this.peer, DEFERRAL_TIMER);
303         checkLocRibIpv4Routes(2);
304         checkLocRibIpv6Routes(1);
305     }
306
307     private void checkLocRibIpv4Routes(final int expectedRoutesOnDS) throws Exception {
308         readDataOperational(getDataBroker(), IPV4_IID, table -> {
309             int size = 0;
310             final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
311             if (routesCase != null && routesCase.getIpv4Routes() != null &&
312                     routesCase.getIpv4Routes().getIpv4Route() != null) {
313                 size = routesCase.getIpv4Routes().getIpv4Route().size();
314             }
315             Assert.assertEquals(expectedRoutesOnDS, size);
316             return table;
317         });
318     }
319
320     private void checkLocRibIpv6Routes(final int expectedRoutesOnDS) throws Exception {
321         readDataOperational(getDataBroker(), IPV6_IID, table -> {
322             int size = 0;
323             final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
324             if (routesCase != null && routesCase.getIpv6Routes() != null &&
325                     routesCase.getIpv6Routes().getIpv6Route() != null) {
326                 size = routesCase.getIpv6Routes().getIpv6Route().size();
327             }
328             Assert.assertEquals(expectedRoutesOnDS, size);
329             return table;
330         });
331     }
332
333     private void insertRoutes(List<Ipv4Prefix> ipv4prefixes, Ipv4Address ipv4NextHop, List<Ipv6Prefix> ipv6prefixes,
334                               Ipv6Address ipv6NextHop, BGPSessionImpl session) {
335         if (ipv4prefixes == null && ipv6prefixes == null) {
336             waitFutureSuccess(session.writeAndFlush(PeerUtil.createEndOfRib(TABLES_KEY)));
337             waitFutureSuccess(session.writeAndFlush(PeerUtil.createEndOfRib(IPV6_TABLES_KEY)));
338             return;
339         }
340
341         if (ipv4prefixes != null && !ipv4prefixes.isEmpty()) {
342             final MpReachNlri reachIpv4 = PeerUtil.createMpReachNlri(new IpAddress(ipv4NextHop), 0,
343                     ipv4prefixes.stream()
344                             .map(IpPrefix::new)
345                             .collect(Collectors.toList()));
346             final Update update1 = PeerUtil.createUpdate(BgpOrigin.Igp, Collections.emptyList(), 100, reachIpv4, null);
347             waitFutureSuccess(session.writeAndFlush(update1));
348         }
349
350         if (ipv6prefixes != null && !ipv4prefixes.isEmpty()) {
351             final MpReachNlri reachIpv6 = PeerUtil.createMpReachNlri(new IpAddress(ipv6NextHop), 0,
352                     ipv6prefixes.stream()
353                             .map(IpPrefix::new)
354                             .collect(Collectors.toList()));
355             final Update update2 = PeerUtil.createUpdate(BgpOrigin.Igp, Collections.emptyList(), 100, reachIpv6, null);
356             waitFutureSuccess(session.writeAndFlush(update2));
357         }
358     }
359
360     private static Open createClassicOpen(boolean addGraceful) {
361         final Map<TablesKey, Boolean> graceful = new HashMap<>();
362         if (addGraceful) {
363             graceful.put(new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class), true);
364         }
365         return new OpenBuilder()
366                 .setMyAsNumber((int) AS)
367                 .setHoldTimer(HOLDTIMER)
368                 .setVersion(new ProtocolVersion((short) 4))
369                 .setBgpParameters(Collections.singletonList(createParameter(false, true, graceful)))
370                 .setBgpIdentifier(PEER1)
371                 .build();
372     }
373 }