2 * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.bgpmanager.thrift.client;
11 import com.google.common.annotations.VisibleForTesting;
12 import java.net.ConnectException;
13 import java.util.List;
14 import java.util.function.BooleanSupplier;
15 import java.util.function.Supplier;
16 import javax.annotation.Nullable;
17 import org.apache.thrift.TException;
18 import org.apache.thrift.protocol.TBinaryProtocol;
19 import org.apache.thrift.transport.TSocket;
20 import org.apache.thrift.transport.TTransport;
21 import org.apache.thrift.transport.TTransportException;
22 import org.opendaylight.netvirt.bgpmanager.BgpConfigurationManager;
23 import org.opendaylight.netvirt.bgpmanager.RetryOnException;
24 import org.opendaylight.netvirt.bgpmanager.thrift.gen.BfdConfigData;
25 import org.opendaylight.netvirt.bgpmanager.thrift.gen.BgpConfigurator;
26 import org.opendaylight.netvirt.bgpmanager.thrift.gen.Routes;
27 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_afi;
28 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_safi;
29 import org.opendaylight.netvirt.bgpmanager.thrift.gen.encap_type;
30 import org.opendaylight.netvirt.bgpmanager.thrift.gen.layer_type;
31 import org.opendaylight.netvirt.bgpmanager.thrift.gen.protocol_type;
32 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.Bgp;
33 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.LayerType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 public final class BgpRouter {
38 private static final Logger LOG = LoggerFactory.getLogger(BgpRouter.class);
40 private static final int THRIFT_TIMEOUT_MILLI = 10000;
41 private static final int GET_RTS_INIT = 0;
42 private static final int GET_RTS_NEXT = 1;
43 private static final int CONNECTION_TIMEOUT = 60000;
46 START, STOP, NBR, VRF, PFX, SRC, MHOP, LOG, AF, GR, MP, VRFMP, EOR, DELAY_EOR, BFD, PEER_STATUS
49 private static class BgpOp {
50 static final int IGNORE = 0;
60 layer_type thriftLayerType;
61 protocol_type thriftProtocolType;
67 encap_type thriftEncapType;
79 private final BgpOp bop = new BgpOp();
80 private final Supplier<Bgp> bgpConfigSupplier;
81 private final BooleanSupplier isBGPEntityOwner;
83 private volatile TTransport transport;
84 private volatile BgpConfigurator.Client bgpClient;
85 private volatile boolean isConnected = false;
86 private volatile long startTS;
87 private volatile long connectTS;
88 private volatile long lastConnectedTS;
89 private volatile boolean configServerUpdated = false;
91 private BgpRouter(Supplier<Bgp> bgpConfigSupplier, BooleanSupplier isBGPEntityOwner) {
92 this.bgpConfigSupplier = bgpConfigSupplier;
93 this.isBGPEntityOwner = isBGPEntityOwner;
96 // private ctor FOR UNIT TESTS ONLY
97 private BgpRouter(BgpConfigurator.Client bgpClient) {
98 this(() -> null, () -> false);
99 this.bgpClient = bgpClient;
102 // FOR UNIT TESTS ONLY
104 static BgpRouter newTestingInstance(BgpConfigurator.Client bgpClient) {
105 return new BgpRouter(bgpClient);
108 public static BgpRouter newInstance(Supplier<Bgp> bgpConfigSupplier, BooleanSupplier isEntityBGPOwner) {
109 return new BgpRouter(bgpConfigSupplier, isEntityBGPOwner);
112 public TTransport getTransport() {
116 public long getLastConnectedTS() {
117 return lastConnectedTS;
120 public void setLastConnectedTS(long lastConnectedTS) {
121 this.lastConnectedTS = lastConnectedTS;
124 public long getConnectTS() {
128 public void setConnectTS(long connectTS) {
129 this.connectTS = connectTS;
132 public long getStartTS() {
136 public void setStartTS(long startTS) {
137 this.startTS = startTS;
140 public void configServerUpdated() {
141 configServerUpdated = true;
144 public synchronized void disconnect() {
147 if (transport != null) {
152 public synchronized boolean connect(String bgpHost, int bgpPort) {
153 String msgPiece = "BGP config server at " + bgpHost + ":" + bgpPort;
155 if (!BgpConfigurationManager.isValidConfigBgpHostPort(bgpHost, bgpPort)) {
156 LOG.error("Invalid config server host: {}, port: {}", bgpHost, bgpPort);
160 final int numberOfConnectRetries = 180;
161 configServerUpdated = false;
162 RetryOnException connectRetry = new RetryOnException(numberOfConnectRetries);
165 setConnectTS(System.currentTimeMillis());
167 if (!isBGPEntityOwner.getAsBoolean()) {
168 LOG.error("Non Entity BGP owner trying to connect to thrift. Returning");
172 if (configServerUpdated) {
173 LOG.error("Config server updated while connecting to server {} {}", bgpHost, bgpPort);
178 LOG.error("Trying to connect BGP config server at {} : {}", bgpHost, bgpPort);
179 TSocket ts = new TSocket(bgpHost, bgpPort, CONNECTION_TIMEOUT);
182 ts.setTimeout(THRIFT_TIMEOUT_MILLI);
184 setLastConnectedTS(System.currentTimeMillis());
185 LOG.error("Connected to BGP config server at {} : {}", bgpHost, bgpPort);
187 } catch (TTransportException tte) {
188 LOG.debug("Failed connecting to BGP config server at {} : {}. msg: {}; Exception :",
189 bgpHost, bgpPort, msgPiece, tte);
190 if (tte.getCause() instanceof ConnectException) {
191 LOG.debug("Connect exception. Failed connecting to BGP config server at {} : {}. "
192 + "msg: {}; Exception :", bgpHost, bgpPort, msgPiece, tte);
193 connectRetry.errorOccured();
195 //In Case of other exceptions we try only 3 times
196 connectRetry.errorOccured(60);
199 } while (connectRetry.shouldRetry());
201 if (!connectRetry.shouldRetry()) {
206 bgpClient = new BgpConfigurator.Client(new TBinaryProtocol(transport));
207 LOG.info("Connected to {}", msgPiece);
211 public boolean isBgpConnected() {
215 private void dispatch(BgpOp op) throws TException, BgpRouterException {
217 dispatchInternal(op);
218 } catch (TTransportException tte) {
219 LOG.error("dispatch command to qthriftd failed, command: {}, exception:", op.toString(), tte);
221 dispatchInternal(op);
225 private void reConnect(TTransportException tte) {
226 Bgp bgpConfig = bgpConfigSupplier.get();
227 if (bgpConfig != null) {
228 LOG.error("Received TTransportException, while configuring qthriftd, goind for Disconnect/Connect "
229 + " Host: {}, Port: {}", bgpConfig.getConfigServer().getHost().getValue(),
230 bgpConfig.getConfigServer().getPort().intValue());
234 } catch (InterruptedException e) {
235 LOG.error("Exception wile reconnecting ", e);
237 connect(bgpConfig.getConfigServer().getHost().getValue(),
238 bgpConfig.getConfigServer().getPort().intValue());
240 LOG.error("Unable to send commands to thrift and fetch bgp configuration", tte);
244 private void dispatchInternal(BgpOp op) throws TException, BgpRouterException {
247 if (bgpClient == null) {
248 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
251 if (op.type == null) {
252 LOG.error("dispatchInternal called with op.type null", new Throwable("stack trace"));
256 af_afi afi = af_afi.findByValue(op.ints[0]);
257 af_safi safi = af_safi.findByValue(op.ints[1]);
261 setStartTS(System.currentTimeMillis());
262 LOG.debug("startBgp thrift call for AsId {}", op.asNumber);
263 result = bgpClient.startBgp(op.asNumber, op.strs[0],
264 BgpOp.IGNORE, BgpOp.IGNORE, BgpOp.IGNORE, op.ints[0], op.add);
265 LOG.debug("Result of startBgp thrift call for AsId {} : {}", op.asNumber, result);
268 result = bgpClient.stopBgp(op.asNumber);
272 result = bgpClient.createPeer(op.strs[0], op.asNumber);
273 if (result == 0 && op.strs[1] != null) { // createPeer worked and password is specified
274 result = bgpClient.setPeerSecret(op.strs[0], op.strs[1]);
276 throw new BgpRouterException(BgpRouterException.Function.SET_PEER_SECRET, result);
280 result = bgpClient.deletePeer(op.strs[0]);
285 ? bgpClient.addVrf(op.thriftLayerType, op.strs[0], op.irts, op.erts, op.afi, op.safi)
286 : bgpClient.delVrf(op.strs[0], op.afi, op.safi);
289 // order of args is different in addPrefix(), hence the
290 // seeming out-of-order-ness of string indices
291 afi = af_afi.findByValue(org.opendaylight.netvirt.bgpmanager.BgpUtil
292 .getAFItranslatedfromPrefix(op.strs[1]));
294 ? bgpClient.pushRoute(
295 op.thriftProtocolType,
308 : bgpClient.withdrawRoute(
309 op.thriftProtocolType,
318 result = bgpClient.setLogConfig(op.strs[0], op.strs[1]);
322 ? bgpClient.setEbgpMultihop(op.strs[0], op.ints[0])
323 : bgpClient.unsetEbgpMultihop(op.strs[0]);
327 ? bgpClient.setUpdateSource(op.strs[0], op.strs[1])
328 : bgpClient.unsetUpdateSource(op.strs[0]);
332 ? bgpClient.enableAddressFamily(op.strs[0], afi, safi)
333 : bgpClient.disableAddressFamily(op.strs[0], afi, safi);
337 ? bgpClient.enableGracefulRestart(op.ints[0])
338 : bgpClient.disableGracefulRestart();
342 ? bgpClient.enableMultipath(afi, safi)
343 : bgpClient.disableMultipath(afi, safi);
346 result = bgpClient.multipaths(bop.strs[0], bop.ints[0]);
349 result = bgpClient.sendEOR();
352 bgpClient.send_enableEORDelay(op.delayEOR);
355 BfdConfigData bfdConfigData = new BfdConfigData();
356 bfdConfigData.setBfdConfigDataVersion((byte)1);
357 bfdConfigData.setBfdDebounceDown(0);
358 bfdConfigData.setBfdDebounceUp(0);
359 bfdConfigData.setBfdFailureThreshold((byte)op.ints[0]);
360 bfdConfigData.setBfdRxInterval(op.ints[1]);
361 bfdConfigData.setBfdTxInterval(op.ints[2]);
362 bfdConfigData.setBfdMultihop(op.multiHop);
364 ? bgpClient.enableBFDFailover(bfdConfigData)
365 : bgpClient.disableBFDFailover();
368 result = bgpClient.getPeerStatus(op.strs[0], op.asNumber).getValue();
374 throw new BgpRouterException(result);
378 public synchronized void startBgp(long asNum, String rtrId, int stalepathTime, boolean announceFbit)
379 throws TException, BgpRouterException {
380 bop.type = Optype.START;
381 bop.add = announceFbit;
382 bop.asNumber = asNum;
383 bop.ints[0] = stalepathTime;
385 LOG.debug("Starting BGP with as number {} and router ID {} StalePathTime: {}", asNum, rtrId, stalepathTime);
389 public synchronized void stopBgp(long asNum)
390 throws TException, BgpRouterException {
391 bop.type = Optype.STOP;
392 bop.asNumber = asNum;
393 LOG.debug("Stopping BGP with as number {}", asNum);
397 public synchronized void addNeighbor(String nbrIp, long nbrAsNum, @Nullable String md5Secret)
398 throws TException, BgpRouterException {
399 if (md5Secret == null) {
400 LOG.debug("Adding BGP Neighbor {} with as number {} ", nbrIp, nbrAsNum);
402 LOG.debug("Adding BGP Neighbor {} with as number {} and MD5 secret {}", nbrIp, nbrAsNum, md5Secret);
404 bop.type = Optype.NBR;
407 bop.asNumber = nbrAsNum;
408 bop.strs[1] = md5Secret;
410 } // public addNeighbor( nbrIp, nbrAsNum, md5Secret )
412 public synchronized void delNeighbor(String nbrIp) throws TException, BgpRouterException {
413 bop.type = Optype.NBR;
416 LOG.debug("Deleting BGP Neighbor {} ", nbrIp);
420 public synchronized void addVrf(LayerType layerType, String rd, List<String> irts, List<String> erts)
421 throws TException, BgpRouterException {
422 bop.thriftLayerType = layerType == LayerType.LAYER2 ? layer_type.LAYER_2 : layer_type.LAYER_3;
423 bop.type = Optype.VRF;
428 LOG.debug("Adding BGP VRF rd: {} ", rd);
432 public synchronized void delVrf(String rd, long afi, long safi) throws TException, BgpRouterException {
433 bop.type = Optype.VRF;
436 bop.afi = af_afi.findByValue((int)afi);
437 bop.safi = af_safi.findByValue((int)safi);
438 LOG.debug("Deleting BGP VRF rd: {}", rd);
442 // bit of a mess-up: the order of arguments is different in
443 // the Thrift RPC: prefix-nexthop-rd-label.
445 public synchronized void addPrefix(String rd,
451 protocol_type protocolType,
455 encap_type encapType,
457 throws TException, BgpRouterException {
458 bop.type = Optype.PFX;
461 bop.strs[1] = prefix;
462 bop.strs[2] = nexthop;
463 // TODO: set label2 or label3 based on encapsulation type and protocol type once L2label is applicable
465 if (protocolType.equals(protocol_type.PROTOCOL_EVPN) && encapType.equals(encap_type.VXLAN)) {
466 bop.l3label = l3vni; //L3VPN Over VxLan
469 bop.l3label = label; // L3VPN Over MPLSGRE
471 bop.thriftProtocolType = protocolType;
472 bop.ethernetTag = ethtag;
474 bop.macAddress = macaddress;
475 bop.thriftEncapType = encapType;
476 bop.routermac = routermac;
478 LOG.debug("Adding BGP route - rd:{} prefix:{} nexthop:{} label:{} ", rd ,prefix, nexthop, label);
482 public synchronized void delPrefix(String rd, String prefix) throws TException, BgpRouterException {
483 bop.type = Optype.PFX;
486 bop.strs[1] = prefix;
487 LOG.debug("Deleting BGP route - rd:{} prefix:{} ", rd, prefix);
491 public synchronized void addBfd(int detectMult, int minRx, int minTx, boolean multiHop)
492 throws TException, BgpRouterException {
493 bop.type = Optype.BFD;
495 bop.ints[0] = detectMult;
498 bop.multiHop = multiHop;
499 LOG.debug("Adding BFD config {} {} {} {}", detectMult, minRx, minTx, multiHop);
503 public synchronized void delBfd() throws TException, BgpRouterException {
504 bop.type = Optype.BFD;
506 LOG.debug("Deleting BFD Config ");
510 public int initRibSync(BgpSyncHandle handle) throws BgpRouterException {
511 if (bgpClient == null) {
512 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
514 if (handle.getState() == BgpSyncHandle.ITERATING) {
515 return BgpRouterException.BGP_ERR_IN_ITER;
517 handle.setState(BgpSyncHandle.INITED);
522 public int endRibSync(BgpSyncHandle handle) throws BgpRouterException {
523 if (bgpClient == null) {
524 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
526 int state = handle.getState();
528 case BgpSyncHandle.INITED:
529 case BgpSyncHandle.ITERATING:
530 handle.setState(BgpSyncHandle.ABORTED);
532 case BgpSyncHandle.DONE:
534 case BgpSyncHandle.NEVER_DONE:
535 return BgpRouterException.BGP_ERR_NOT_ITER;
542 public Routes doRibSync(BgpSyncHandle handle, af_afi afi) throws TException, BgpRouterException {
543 if (bgpClient == null) {
544 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
546 int state = handle.getState();
547 if (state != BgpSyncHandle.INITED && state != BgpSyncHandle.ITERATING) {
548 Routes routes = new Routes();
549 routes.setErrcode(BgpRouterException.BGP_ERR_NOT_ITER);
552 int op = state == BgpSyncHandle.INITED ? GET_RTS_INIT : GET_RTS_NEXT;
553 handle.setState(BgpSyncHandle.ITERATING);
554 int winSize = handle.getMaxCount() * handle.getRouteSize();
557 // TODO: receive correct protocol_type here, currently populating with dummy protocol type
558 Routes outRoutes = bgpClient.getRoutes(protocol_type.PROTOCOL_ANY, op, winSize, afi);
559 handle.setMore(outRoutes.more);
560 if (outRoutes.more == 0) {
561 handle.setState(BgpSyncHandle.DONE);
566 public synchronized void setLogging(String fileName, String debugLevel) throws TException, BgpRouterException {
567 bop.type = Optype.LOG;
568 bop.strs[0] = fileName;
569 bop.strs[1] = debugLevel;
570 LOG.debug("Setting Log file to BGP VRF rd: {}, {}", fileName, debugLevel);
574 public synchronized void addEbgpMultihop(String nbrIp, int nhops) throws TException, BgpRouterException {
575 bop.type = Optype.MHOP;
579 LOG.debug("ebgp-multihop set for peer {}, num hops = {}",
584 public synchronized void delEbgpMultihop(String nbrIp) throws TException, BgpRouterException {
585 bop.type = Optype.MHOP;
588 LOG.debug("ebgp-multihop deleted for peer {}", nbrIp);
592 public synchronized void addUpdateSource(String nbrIp, String srcIp) throws TException, BgpRouterException {
593 bop.type = Optype.SRC;
597 LOG.debug("update-source added for peer {}, src-ip = {}",
602 public synchronized void delUpdateSource(String nbrIp) throws TException, BgpRouterException {
603 bop.type = Optype.SRC;
606 LOG.debug("update-source deleted for peer {}", nbrIp);
610 public synchronized void addAddressFamily(String nbrIp, af_afi afi, af_safi safi)
611 throws TException, BgpRouterException {
612 bop.type = Optype.AF;
615 bop.ints[0] = afi.getValue();
616 bop.ints[1] = safi.getValue();
617 LOG.debug("addr family added for peer {}, afi = {}, safi = {}",
618 nbrIp, bop.ints[0], bop.ints[1]);
622 public synchronized void delAddressFamily(String nbrIp, af_afi afi, af_safi safi)
623 throws TException, BgpRouterException {
624 bop.type = Optype.AF;
627 bop.ints[0] = afi.getValue();
628 bop.ints[1] = safi.getValue();
629 LOG.debug("addr family deleted for peer {}, afi = {}, safi = {}",
630 nbrIp, bop.ints[0], bop.ints[1]);
634 public synchronized void addGracefulRestart(int stalepathTime) throws TException, BgpRouterException {
635 bop.type = Optype.GR;
637 bop.ints[0] = stalepathTime;
638 LOG.debug("graceful restart added, stale-path-time = {}",
643 public synchronized void delGracefulRestart() throws TException, BgpRouterException {
644 bop.type = Optype.GR;
646 LOG.debug("graceful restart deleted");
650 public synchronized void enableMultipath(af_afi afi, af_safi safi) throws TException, BgpRouterException {
651 bop.type = Optype.MP;
653 LOG.debug("Enabling multipath for afi {}, safi {}", afi.getValue(), safi.getValue());
654 bop.ints[0] = afi.getValue();
655 bop.ints[1] = safi.getValue();
659 public synchronized void disableMultipath(af_afi afi, af_safi safi) throws TException, BgpRouterException {
660 bop.type = Optype.MP;
662 LOG.debug("Disabling multipath for afi {}, safi {}", afi.getValue(), safi.getValue());
663 bop.ints[0] = afi.getValue();
664 bop.ints[1] = safi.getValue();
668 public synchronized void multipaths(String rd, int maxpath) throws TException, BgpRouterException {
669 bop.type = Optype.VRFMP;
671 bop.ints[0] = maxpath;
675 public synchronized void sendEOR() throws TException, BgpRouterException {
676 bop.type = Optype.EOR;
677 LOG.debug("EOR message sent");
681 public synchronized void delayEOR(int delay) throws TException, BgpRouterException {
682 bop.type = Optype.DELAY_EOR;
683 bop.delayEOR = delay;
684 LOG.debug("EOR delay time in Seconds sent");
688 public synchronized void getPeerStatus(String nbrIp, long nbrAsNum)
689 throws TException, BgpRouterException {
690 bop.type = Optype.PEER_STATUS;
692 bop.asNumber = nbrAsNum;
694 } // public getPeerStatus( nbrIp, nbrAsNum )