Bug-4827: BGP Add-Path OpenConfig Support
[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.base.Optional;
21 import com.google.common.base.Preconditions;
22 import com.google.common.net.InetAddresses;
23 import io.netty.util.concurrent.Future;
24 import java.net.InetSocketAddress;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
30 import org.opendaylight.protocol.bgp.openconfig.spi.BGPConfigModuleTracker;
31 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigProvider;
32 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenconfigMapper;
33 import org.opendaylight.protocol.bgp.openconfig.spi.InstanceConfigurationIdentifier;
34 import org.opendaylight.protocol.bgp.openconfig.spi.pojo.BGPPeerInstanceConfiguration;
35 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
36 import org.opendaylight.protocol.bgp.parser.spi.MultiprotocolCapabilitiesUtil;
37 import org.opendaylight.protocol.bgp.rib.impl.BGPPeer;
38 import org.opendaylight.protocol.bgp.rib.impl.StrictBGPPeerRegistry;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
40 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
41 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
42 import org.opendaylight.protocol.util.Ipv6Util;
43 import org.opendaylight.tcpmd5.api.KeyMapping;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.BgpParameters;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.BgpParametersBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.OptionalCapabilities;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.OptionalCapabilitiesBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.CParametersBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.message.bgp.parameters.optional.capabilities.c.parameters.As4BytesCapabilityBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.CParameters1Builder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.AddPathCapabilityBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.GracefulRestartCapabilityBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.MultiprotocolCapabilityBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.mp.capabilities.add.path.capability.AddressFamilies;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.tcpmd5.cfg.rev140427.Rfc2385Key;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 /**
65  *
66  */
67 public final class BGPPeerModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerModule {
68     private static final Logger LOG = LoggerFactory.getLogger(BGPPeerModule.class);
69
70     public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
71         final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
72         super(identifier, dependencyResolver);
73     }
74
75     public BGPPeerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
76         final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final BGPPeerModule oldModule,
77         final java.lang.AutoCloseable oldInstance) {
78
79         super(identifier, dependencyResolver, oldModule, oldInstance);
80     }
81
82     @Override
83     protected void customValidation() {
84         final IpAddress host = getHost();
85         JmxAttributeValidationException.checkNotNull(host, "value is not set.", hostJmxAttribute);
86         JmxAttributeValidationException.checkCondition(host.getIpv4Address() != null || host.getIpv6Address() != null,
87             "Unexpected host", hostJmxAttribute);
88
89         JmxAttributeValidationException.checkNotNull(getPort(), "value is not set.", portJmxAttribute);
90
91         if (getOptionaPassword(getPassword()).isPresent()) {
92             /*
93              *  This is a nasty hack, but we don't have another clean solution. We cannot allow
94              *  password being set if the injected dispatcher does not have the optional
95              *  md5-server-channel-factory set.
96              *
97              *  FIXME: this is a use case for Module interfaces, e.g. RibImplModule
98              *         should something like isMd5ServerSupported()
99              */
100
101             final RIBImplModuleMXBean ribProxy = this.dependencyResolver.newMXBeanProxy(getRib(), RIBImplModuleMXBean.class);
102             final BGPDispatcherImplModuleMXBean bgpDispatcherProxy = this.dependencyResolver.newMXBeanProxy(
103                 ribProxy.getBgpDispatcher(), BGPDispatcherImplModuleMXBean.class);
104             final boolean isMd5Supported = bgpDispatcherProxy.getMd5ChannelFactory() != null;
105
106             JmxAttributeValidationException.checkCondition(isMd5Supported,
107                 "Underlying dispatcher does not support MD5 clients", passwordJmxAttribute);
108
109         }
110
111         if (getPeerRole() != null) {
112             final boolean isNotPeerRoleInternal= getPeerRole() != PeerRole.Internal;
113             JmxAttributeValidationException.checkCondition(isNotPeerRoleInternal,
114                 "Internal Peer Role is reserved for Application Peer use.", peerRoleJmxAttribute);
115         }
116     }
117
118     private InetSocketAddress createAddress() {
119         final IpAddress ip = getHost();
120         Preconditions.checkArgument(ip.getIpv4Address() != null || ip.getIpv6Address() != null, "Failed to handle host %s", ip);
121         if (ip.getIpv4Address() != null) {
122             return new InetSocketAddress(InetAddresses.forString(ip.getIpv4Address().getValue()), getPort().getValue());
123         }
124         return new InetSocketAddress(InetAddresses.forString(ip.getIpv6Address().getValue()), getPort().getValue());
125     }
126
127     @Override
128     public java.lang.AutoCloseable createInstance() {
129         final RIB r = getRibDependency();
130
131         final List<BgpParameters> tlvs = getTlvs(r);
132         final AsNumber remoteAs = getAsOrDefault(r);
133         final BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), remoteAs, tlvs);
134         final BGPPeer bgpClientPeer;
135         final IpAddress host = getNormalizedHost();
136         if (getPeerRole() != null) {
137             bgpClientPeer = new BGPPeer(peerName(host), r, getPeerRole(), getRpcRegistryDependency());
138         } else {
139             bgpClientPeer = new BGPPeer(peerName(host), r, PeerRole.Ibgp, getRpcRegistryDependency());
140         }
141
142         bgpClientPeer.registerRootRuntimeBean(getRootRuntimeBeanRegistratorWrapper());
143
144         getPeerRegistryBackwards().addPeer(host, bgpClientPeer, prefs);
145
146         final BGPPeerModuleTracker moduleTracker = new BGPPeerModuleTracker(r.getOpenConfigProvider());
147         moduleTracker.onInstanceCreate();
148
149         final CloseableNoEx peerCloseable = new CloseableNoEx() {
150             @Override
151             public void close() {
152                 bgpClientPeer.close();
153                 getPeerRegistryBackwards().removePeer(host);
154                 moduleTracker.onInstanceClose();
155             }
156         };
157
158         // Initiate connection
159         if(getInitiateConnection()) {
160             final Future<Void> cf = initiateConnection(createAddress(), getOptionaPassword(getPassword()), getPeerRegistryBackwards());
161             return new CloseableNoEx() {
162                 @Override
163                 public void close() {
164                     cf.cancel(true);
165                     peerCloseable.close();
166                 }
167             };
168         } else {
169             return peerCloseable;
170         }
171     }
172
173     private interface CloseableNoEx extends AutoCloseable {
174         @Override
175         void close();
176     }
177
178     private AsNumber getAsOrDefault(final RIB r) {
179         // Remote AS number defaults to our local AS
180         final AsNumber remoteAs;
181         if (getRemoteAs() != null) {
182             remoteAs = new AsNumber(getRemoteAs());
183         } else {
184             remoteAs = r.getLocalAs();
185         }
186         return remoteAs;
187     }
188
189     private List<BgpParameters> getTlvs(final RIB r) {
190         final List<BgpParameters> tlvs = new ArrayList<>();
191         final List<OptionalCapabilities> caps = new ArrayList<>();
192         caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().setAs4BytesCapability(
193             new As4BytesCapabilityBuilder().setAsNumber(r.getLocalAs()).build()).build()).build());
194         caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().addAugmentation(CParameters1.class,
195             new CParameters1Builder().setGracefulRestartCapability(new GracefulRestartCapabilityBuilder().build()).build()).build()).build());
196
197         if (getRouteRefresh()) {
198             caps.add(new OptionalCapabilitiesBuilder().setCParameters(MultiprotocolCapabilitiesUtil.RR_CAPABILITY).build());
199         }
200
201         if (!getAddPathDependency().isEmpty()) {
202             final List<AddressFamilies> addPathFamilies = filterAddPathDependency(getAddPathDependency());
203             caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().addAugmentation(CParameters1.class,
204                 new CParameters1Builder().setAddPathCapability(new AddPathCapabilityBuilder().setAddressFamilies(addPathFamilies).build()).build()).build()).build());
205         }
206
207         for (final BgpTableType t : getAdvertizedTableDependency()) {
208             if (!r.getLocalTables().contains(t)) {
209                 LOG.info("RIB instance does not list {} in its local tables. Incoming data will be dropped.", t);
210             }
211
212             caps.add(new OptionalCapabilitiesBuilder().setCParameters(new CParametersBuilder().addAugmentation(CParameters1.class,
213                 new CParameters1Builder().setMultiprotocolCapability(new MultiprotocolCapabilityBuilder(t).build()).build()).build()).build());
214         }
215         tlvs.add(new BgpParametersBuilder().setOptionalCapabilities(caps).build());
216         return tlvs;
217     }
218
219     private List<AddressFamilies> filterAddPathDependency(final List<AddressFamilies> addPathDependency) {
220         final Map<BgpTableType, AddressFamilies> filteredFamilies = new HashMap<BgpTableType, AddressFamilies>();
221         for (final AddressFamilies family : addPathDependency) {
222             final BgpTableType key = new BgpTableTypeImpl(family.getAfi(), family.getSafi());
223             if (!filteredFamilies.containsKey(key)) {
224                 filteredFamilies.put(key, family);
225             } else {
226                 LOG.info("Ignoring Add-path dependency {}", family);
227             }
228         }
229         return new ArrayList<AddressFamilies>(filteredFamilies.values());
230     }
231
232     public IpAddress getNormalizedHost() {
233         final IpAddress host = getHost();
234         if(host.getIpv6Address() != null){
235             return new IpAddress(Ipv6Util.getFullForm(host.getIpv6Address()));
236         }
237         return host;
238     }
239
240     private io.netty.util.concurrent.Future<Void> initiateConnection(final InetSocketAddress address, final Optional<Rfc2385Key> password, final BGPPeerRegistry registry) {
241         KeyMapping keys = null;
242         if (password.isPresent()) {
243             keys = new KeyMapping();
244             keys.put(address.getAddress(), password.get().getValue().getBytes(Charsets.US_ASCII));
245         }
246
247         final RIB rib = getRibDependency();
248         final Optional<KeyMapping> optionalKey = Optional.fromNullable(keys);
249         return rib.getDispatcher().createReconnectingClient(address, registry, getRetrytimer(), optionalKey);
250     }
251
252     private BGPPeerRegistry getPeerRegistryBackwards() {
253         return getPeerRegistry() == null ? StrictBGPPeerRegistry.GLOBAL : getPeerRegistryDependency();
254     }
255
256     private static String peerName(final IpAddress host) {
257         if (host.getIpv4Address() != null) {
258             return host.getIpv4Address().getValue();
259         }
260         if (host.getIpv6Address() != null) {
261             return host.getIpv6Address().getValue();
262         }
263
264         return null;
265     }
266
267     private final class BGPPeerModuleTracker implements BGPConfigModuleTracker {
268
269         private final BGPOpenconfigMapper<BGPPeerInstanceConfiguration> neighborProvider;
270         private final BGPPeerInstanceConfiguration bgpPeerInstanceConfiguration;
271
272         public BGPPeerModuleTracker(final Optional<BGPOpenConfigProvider> openconfigProvider) {
273             if (openconfigProvider.isPresent()) {
274                 this.neighborProvider = openconfigProvider.get().getOpenConfigMapper(BGPPeerInstanceConfiguration.class);
275             } else {
276                 this.neighborProvider = null;
277             }
278             final InstanceConfigurationIdentifier identifier = new InstanceConfigurationIdentifier(getIdentifier().getInstanceName());
279             this.bgpPeerInstanceConfiguration = new BGPPeerInstanceConfiguration(identifier, Rev130715Util.getIpvAddress(getNormalizedHost()),
280                     Rev130715Util.getPort(getPort().getValue()), getHoldtimer(), getPeerRole(), getInitiateConnection(),
281                         getAdvertizedTableDependency(), Rev130715Util.getASNumber(getAsOrDefault(getRibDependency()).getValue()),
282                         getOptionaPassword(getPassword()), getAddPathDependency());
283         }
284
285         @Override
286         public void onInstanceCreate() {
287             if (this.neighborProvider != null) {
288                 this.neighborProvider.writeConfiguration(this.bgpPeerInstanceConfiguration);
289             }
290         }
291
292         @Override
293         public void onInstanceClose() {
294             if (this.neighborProvider != null) {
295                 this.neighborProvider.removeConfiguration(this.bgpPeerInstanceConfiguration);
296             }
297         }
298
299     }
300
301     private Optional<Rfc2385Key> getOptionaPassword(final Rfc2385Key password) {
302         return password != null && ! password.getValue().isEmpty() ? Optional.of(password) : Optional.<Rfc2385Key>absent();
303     }
304
305 }