BUG-196 : support graceful restart
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / controller / config / yang / bgp / rib / impl / BGPPeerModule.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  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 /**
9  * Generated file
10
11  * Generated from: yang module name: bgp-rib-impl  yang module local name: bgp-peer
12  * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
13  * Generated at: Sat Jan 25 11:00:14 CET 2014
14  *
15  * Do not modify this file unless it is present under src/main directory
16  */
17 package org.opendaylight.controller.config.yang.bgp.rib.impl;
18
19 import com.google.common.base.Charsets;
20 import com.google.common.net.InetAddresses;
21 import io.netty.util.concurrent.Future;
22 import java.net.InetSocketAddress;
23 import java.util.ArrayList;
24 import java.util.List;
25 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
26 import org.opendaylight.protocol.bgp.rib.impl.BGPPeer;
27 import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
28 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
29 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
30 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
31 import org.opendaylight.tcpmd5.api.KeyMapping;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParameters;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParametersBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.OptionalCapabilities;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.OptionalCapabilitiesBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.As4BytesCaseBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.as4.bytes._case.As4BytesCapabilityBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.GracefulRestartCaseBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.MultiprotocolCaseBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.graceful.restart._case.GracefulRestartCapabilityBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.optional.capabilities.c.parameters.multiprotocol._case.MultiprotocolCapabilityBuilder;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  *
50  */
51 public final class BGPPeerModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerModule {
52     private static final Logger LOG = LoggerFactory.getLogger(BGPPeerModule.class);
53
54     public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
55         final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
56         super(identifier, dependencyResolver);
57     }
58
59     public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
60         final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final BGPPeerModule oldModule,
61         final java.lang.AutoCloseable oldInstance) {
62
63         super(identifier, dependencyResolver, oldModule, oldInstance);
64     }
65
66     @Override
67     protected void customValidation() {
68         JmxAttributeValidationException.checkNotNull(getHost(), "value is not set.", hostJmxAttribute);
69         JmxAttributeValidationException.checkNotNull(getPort(), "value is not set.", portJmxAttribute);
70
71         if (getPassword() != null) {
72             /*
73              *  This is a nasty hack, but we don't have another clean solution. We cannot allow
74              *  password being set if the injected dispatcher does not have the optional
75              *  md5-server-channel-factory set.
76              *
77              *  FIXME: this is a use case for Module interfaces, e.g. RibImplModule
78              *         should something like isMd5ServerSupported()
79              */
80
81             final RIBImplModuleMXBean ribProxy = this.dependencyResolver.newMXBeanProxy(getRib(), RIBImplModuleMXBean.class);
82             final BGPDispatcherImplModuleMXBean bgpDispatcherProxy = this.dependencyResolver.newMXBeanProxy(
83                 ribProxy.getBgpDispatcher(), BGPDispatcherImplModuleMXBean.class);
84             final boolean isMd5Supported = bgpDispatcherProxy.getMd5ChannelFactory() != null;
85
86             JmxAttributeValidationException.checkCondition(isMd5Supported,
87                 "Underlying dispatcher does not support MD5 clients", passwordJmxAttribute);
88
89         }
90     }
91
92     private InetSocketAddress createAddress() {
93         final IpAddress ip = getHost();
94         if (ip.getIpv4Address() != null) {
95             return new InetSocketAddress(InetAddresses.forString(ip.getIpv4Address().getValue()), getPort().getValue());
96         } else if (ip.getIpv6Address() != null) {
97             return new InetSocketAddress(InetAddresses.forString(ip.getIpv6Address().getValue()), getPort().getValue());
98         } else {
99             throw new IllegalStateException("Failed to handle host " + getHost());
100         }
101     }
102
103     @Override
104     public java.lang.AutoCloseable createInstance() {
105         final RIB r = getRibDependency();
106
107         final List<BgpParameters> tlvs = getTlvs(r);
108         final AsNumber remoteAs = getAsOrDefault(r);
109         final String password = getPasswordOrNull();
110
111         final BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), tlvs);
112         final BGPPeer bgpClientPeer = new BGPPeer(peerName(getHostWithoutValue()), r);
113         bgpClientPeer.registerRootRuntimeBean(getRootRuntimeBeanRegistratorWrapper());
114
115         getPeerRegistryBackwards().addPeer(getHostWithoutValue(), bgpClientPeer, prefs);
116
117         final CloseableNoEx peerCloseable = new CloseableNoEx() {
118             @Override
119             public void close() {
120                 bgpClientPeer.close();
121                 getPeerRegistryBackwards().removePeer(getHostWithoutValue());
122             }
123         };
124
125         // Initiate connection
126         if(getInitiateConnection()) {
127             final Future<Void> cf = initiateConnection(createAddress(), password, remoteAs, getPeerRegistryBackwards());
128             return new CloseableNoEx() {
129                 @Override
130                 public void close() {
131                     cf.cancel(true);
132                     peerCloseable.close();
133                 }
134             };
135         } else {
136             return peerCloseable;
137         }
138     }
139
140     private interface CloseableNoEx extends AutoCloseable {
141         @Override
142         void close();
143     }
144
145     private String getPasswordOrNull() {
146         final String password;
147         if (getPassword() != null) {
148             password = getPassword().getValue();
149         } else {
150             password = null;
151         }
152         return password;
153     }
154
155     private AsNumber getAsOrDefault(final RIB r) {
156         // Remote AS number defaults to our local AS
157         final AsNumber remoteAs;
158         if (getRemoteAs() != null) {
159             remoteAs = new AsNumber(getRemoteAs());
160         } else {
161             remoteAs = r.getLocalAs();
162         }
163         return remoteAs;
164     }
165
166     private List<BgpParameters> getTlvs(final RIB r) {
167         final List<BgpParameters> tlvs = new ArrayList<>();
168         final List<OptionalCapabilities> caps = new ArrayList<>();
169         caps.add(new OptionalCapabilitiesBuilder().setCParameters(
170             new As4BytesCaseBuilder().setAs4BytesCapability(new As4BytesCapabilityBuilder().setAsNumber(r.getLocalAs()).build()).build()).build());
171         caps.add(new OptionalCapabilitiesBuilder().setCParameters(
172             new GracefulRestartCaseBuilder().setGracefulRestartCapability(
173                 new GracefulRestartCapabilityBuilder().build()).build()).build());
174         for (final BgpTableType t : getAdvertizedTableDependency()) {
175             if (!r.getLocalTables().contains(t)) {
176                 LOG.info("RIB instance does not list {} in its local tables. Incoming data will be dropped.", t);
177             }
178
179             caps.add(new OptionalCapabilitiesBuilder().setCParameters(
180                 new MultiprotocolCaseBuilder().setMultiprotocolCapability(new MultiprotocolCapabilityBuilder(t).build()).build()).build());
181         }
182         tlvs.add(new BgpParametersBuilder().setOptionalCapabilities(caps).build());
183         return tlvs;
184     }
185
186     public IpAddress getHostWithoutValue() {
187         // FIXME we need to remove field "value" from IpAddress since equals does not work as expected when value being present
188         // Remove after this bug is fixed https://bugs.opendaylight.org/show_bug.cgi?id=1276
189         final IpAddress host = super.getHost();
190         if(host.getIpv4Address() != null) {
191             return new IpAddress(host.getIpv4Address());
192         } else if(host.getIpv6Address() != null){
193             return new IpAddress(host.getIpv6Address());
194         }
195
196         throw new IllegalArgumentException("Unexpected host " + host);
197     }
198
199     private io.netty.util.concurrent.Future<Void> initiateConnection(final InetSocketAddress address, final String password, final AsNumber remoteAs, final BGPPeerRegistry registry) {
200         final KeyMapping keys;
201         if (password != null) {
202             keys = new KeyMapping();
203             keys.put(address.getAddress(), password.getBytes(Charsets.US_ASCII));
204         } else {
205             keys = null;
206         }
207
208         final RIB rib = getRibDependency();
209         return rib.getDispatcher().createReconnectingClient(address, remoteAs, registry, rib.getTcpStrategyFactory(),
210             rib.getSessionStrategyFactory(), keys);
211     }
212
213     private BGPPeerRegistry getPeerRegistryBackwards() {
214         return getPeerRegistry() == null ? StrictBGPPeerRegistry.GLOBAL : getPeerRegistryDependency();
215     }
216
217     private static String peerName(final IpAddress host) {
218         if (host.getIpv4Address() != null) {
219             return host.getIpv4Address().getValue();
220         }
221         if (host.getIpv6Address() != null) {
222             return host.getIpv6Address().getValue();
223         }
224
225         return null;
226     }
227
228 }