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 static org.opendaylight.netvirt.bgpmanager.oam.BgpConstants.HISTORY_LIMIT;
12 import static org.opendaylight.netvirt.bgpmanager.oam.BgpConstants.HISTORY_THRESHOLD;
14 import com.google.common.annotations.VisibleForTesting;
15 import java.net.ConnectException;
16 import java.util.Arrays;
17 import java.util.List;
18 import java.util.function.BooleanSupplier;
19 import java.util.function.Supplier;
20 import javax.annotation.Nullable;
21 import org.apache.thrift.TException;
22 import org.apache.thrift.protocol.TBinaryProtocol;
23 import org.apache.thrift.transport.TSocket;
24 import org.apache.thrift.transport.TTransport;
25 import org.apache.thrift.transport.TTransportException;
26 import org.opendaylight.netvirt.bgpmanager.BgpConfigurationManager;
27 import org.opendaylight.netvirt.bgpmanager.RetryOnException;
28 import org.opendaylight.netvirt.bgpmanager.thrift.gen.BfdConfigData;
29 import org.opendaylight.netvirt.bgpmanager.thrift.gen.BgpConfigurator;
30 import org.opendaylight.netvirt.bgpmanager.thrift.gen.Routes;
31 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_afi;
32 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_safi;
33 import org.opendaylight.netvirt.bgpmanager.thrift.gen.encap_type;
34 import org.opendaylight.netvirt.bgpmanager.thrift.gen.layer_type;
35 import org.opendaylight.netvirt.bgpmanager.thrift.gen.protocol_type;
36 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionHistory;
37 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionType;
38 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.Bgp;
39 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.LayerType;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 public final class BgpRouter {
44 private static final Logger LOG = LoggerFactory.getLogger(BgpRouter.class);
46 private static final int THRIFT_TIMEOUT_MILLI = 10000;
47 private static final int GET_RTS_INIT = 0;
48 private static final int GET_RTS_NEXT = 1;
49 private static final int CONNECTION_TIMEOUT = 60000;
52 START, STOP, NBR, VRF, PFX, SRC, MHOP, LOG, AF, GR, MP, VRFMP, EOR, DELAY_EOR, BFD, PEER_STATUS
55 private static class BgpOp {
56 static final int IGNORE = 0;
66 layer_type thriftLayerType;
67 protocol_type thriftProtocolType;
73 encap_type thriftEncapType;
87 this.strs = bgpOp.strs;
88 this.ints = bgpOp.ints;
90 this.safi = bgpOp.safi;
91 this.type = bgpOp.type;
93 this.multiHop = bgpOp.multiHop;
94 this.asNumber = bgpOp.asNumber;
95 this.thriftProtocolType = bgpOp.thriftProtocolType;
96 this.thriftLayerType = bgpOp.thriftLayerType;
97 this.ethernetTag = bgpOp.ethernetTag;
99 this.macAddress = bgpOp.macAddress;
100 this.l2label = bgpOp.l2label;
101 this.l3label = bgpOp.l3label;
102 this.routermac = bgpOp.routermac;
103 this.thriftEncapType = bgpOp.thriftEncapType;
104 this.delayEOR = bgpOp.delayEOR;
109 public String toString() {
114 + ",multihop=" + multiHop
115 + ", strs=" + Arrays.toString(strs)
116 + ", ints=" + Arrays.toString(ints)
119 + ", asNumber=" + asNumber
120 + ", thriftLayerType=" + thriftLayerType
121 + ", thriftProtocolType=" + thriftProtocolType
122 + ", ethernetTag=" + ethernetTag
123 + ", esi='" + esi + '\''
124 + ", macAddress='" + macAddress + '\''
125 + ", l2label=" + l2label
126 + ", l3label=" + l3label
127 + ", thriftEncapType=" + thriftEncapType
128 + ", routermac='" + routermac + '\''
130 + ", delayEOR=" + delayEOR
138 private final BgpOp bop = new BgpOp();
139 private final Supplier<Bgp> bgpConfigSupplier;
140 private final BooleanSupplier isBGPEntityOwner;
142 private volatile TTransport transport;
143 private volatile BgpConfigurator.Client bgpClient;
144 private volatile boolean isConnected = false;
145 private volatile long startTS;
146 private volatile long connectTS;
147 private volatile long lastConnectedTS;
148 private final TransactionHistory transactionHistory;
149 private volatile boolean configServerUpdated = false;
151 private BgpRouter(Supplier<Bgp> bgpConfigSupplier, BooleanSupplier isBGPEntityOwner,
152 TransactionHistory transactionHistory) {
153 this.bgpConfigSupplier = bgpConfigSupplier;
154 this.isBGPEntityOwner = isBGPEntityOwner;
155 this.transactionHistory = transactionHistory;
158 // private ctor FOR UNIT TESTS ONLY
159 private BgpRouter(BgpConfigurator.Client bgpClient) {
160 this(() -> null, () -> false, new TransactionHistory(HISTORY_LIMIT, HISTORY_THRESHOLD));
161 this.bgpClient = bgpClient;
164 // FOR UNIT TESTS ONLY
166 static BgpRouter newTestingInstance(BgpConfigurator.Client bgpClient) {
167 return new BgpRouter(bgpClient);
170 public static BgpRouter newInstance(Supplier<Bgp> bgpConfigSupplier, BooleanSupplier isEntityBGPOwner,
171 TransactionHistory transactionHistory) {
172 return new BgpRouter(bgpConfigSupplier, isEntityBGPOwner,transactionHistory);
175 public TTransport getTransport() {
179 public long getLastConnectedTS() {
180 return lastConnectedTS;
183 public void setLastConnectedTS(long lastConnectedTS) {
184 this.lastConnectedTS = lastConnectedTS;
187 public long getConnectTS() {
191 public void setConnectTS(long connectTS) {
192 this.connectTS = connectTS;
195 public long getStartTS() {
199 public void setStartTS(long startTS) {
200 this.startTS = startTS;
203 public void configServerUpdated() {
204 configServerUpdated = true;
207 public synchronized void disconnect() {
210 if (transport != null) {
215 public synchronized boolean connect(String bgpHost, int bgpPort) {
216 String msgPiece = "BGP config server at " + bgpHost + ":" + bgpPort;
218 if (!BgpConfigurationManager.isValidConfigBgpHostPort(bgpHost, bgpPort)) {
219 LOG.error("Invalid config server host: {}, port: {}", bgpHost, bgpPort);
223 final int numberOfConnectRetries = 180;
224 configServerUpdated = false;
225 RetryOnException connectRetry = new RetryOnException(numberOfConnectRetries);
228 setConnectTS(System.currentTimeMillis());
230 if (!isBGPEntityOwner.getAsBoolean()) {
231 LOG.error("Non Entity BGP owner trying to connect to thrift. Returning");
235 if (configServerUpdated) {
236 LOG.error("Config server updated while connecting to server {} {}", bgpHost, bgpPort);
241 LOG.error("Trying to connect BGP config server at {} : {}", bgpHost, bgpPort);
242 TSocket ts = new TSocket(bgpHost, bgpPort, CONNECTION_TIMEOUT);
245 ts.setTimeout(THRIFT_TIMEOUT_MILLI);
247 setLastConnectedTS(System.currentTimeMillis());
248 LOG.error("Connected to BGP config server at {} : {}", bgpHost, bgpPort);
250 } catch (TTransportException tte) {
251 LOG.debug("Failed connecting to BGP config server at {} : {}. msg: {}; Exception :",
252 bgpHost, bgpPort, msgPiece, tte);
253 if (tte.getCause() instanceof ConnectException) {
254 LOG.debug("Connect exception. Failed connecting to BGP config server at {} : {}. "
255 + "msg: {}; Exception :", bgpHost, bgpPort, msgPiece, tte);
256 connectRetry.errorOccured();
258 //In Case of other exceptions we try only 3 times
259 connectRetry.errorOccured(60);
262 } while (connectRetry.shouldRetry());
264 if (!connectRetry.shouldRetry()) {
269 bgpClient = new BgpConfigurator.Client(new TBinaryProtocol(transport));
270 LOG.info("Connected to {}", msgPiece);
274 public boolean isBgpConnected() {
278 private TransactionType getTransactionType(BgpOp op) {
279 return op.add ? TransactionType.ADD : TransactionType.DELETE;
282 private void dispatch(BgpOp op) throws TException, BgpRouterException {
284 dispatchInternal(op);
285 transactionHistory.addToHistory(getTransactionType(op), new BgpOp(op));
286 LOG.trace("History size is {}", transactionHistory.getElements().size());
287 } catch (TTransportException tte) {
288 LOG.error("dispatch command to qthriftd failed, command: {}, exception:", op.toString(), tte);
290 dispatchInternal(op);
294 private void reConnect(TTransportException tte) {
295 Bgp bgpConfig = bgpConfigSupplier.get();
296 if (bgpConfig != null) {
297 LOG.error("Received TTransportException, while configuring qthriftd, goind for Disconnect/Connect "
298 + " Host: {}, Port: {}", bgpConfig.getConfigServer().getHost().getValue(),
299 bgpConfig.getConfigServer().getPort().intValue());
303 } catch (InterruptedException e) {
304 LOG.error("Exception wile reconnecting ", e);
306 connect(bgpConfig.getConfigServer().getHost().getValue(),
307 bgpConfig.getConfigServer().getPort().intValue());
309 LOG.error("Unable to send commands to thrift and fetch bgp configuration", tte);
313 private void dispatchInternal(BgpOp op) throws TException, BgpRouterException {
316 if (bgpClient == null) {
317 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
320 if (op.type == null) {
321 LOG.error("dispatchInternal called with op.type null", new Throwable("stack trace"));
325 af_afi afi = af_afi.findByValue(op.ints[0]);
326 af_safi safi = af_safi.findByValue(op.ints[1]);
330 setStartTS(System.currentTimeMillis());
331 LOG.debug("startBgp thrift call for AsId {}", op.asNumber);
332 result = bgpClient.startBgp(op.asNumber, op.strs[0],
333 BgpOp.IGNORE, BgpOp.IGNORE, BgpOp.IGNORE, op.ints[0], op.add);
334 LOG.debug("Result of startBgp thrift call for AsId {} : {}", op.asNumber, result);
337 result = bgpClient.stopBgp(op.asNumber);
341 result = bgpClient.createPeer(op.strs[0], op.asNumber);
342 if (result == 0 && op.strs[1] != null) { // createPeer worked and password is specified
343 result = bgpClient.setPeerSecret(op.strs[0], op.strs[1]);
345 throw new BgpRouterException(BgpRouterException.Function.SET_PEER_SECRET, result);
349 result = bgpClient.deletePeer(op.strs[0]);
354 ? bgpClient.addVrf(op.thriftLayerType, op.strs[0], op.irts, op.erts, op.afi, op.safi)
355 : bgpClient.delVrf(op.strs[0], op.afi, op.safi);
358 // order of args is different in addPrefix(), hence the
359 // seeming out-of-order-ness of string indices
360 afi = af_afi.findByValue(org.opendaylight.netvirt.bgpmanager.BgpUtil
361 .getAFItranslatedfromPrefix(op.strs[1]));
363 ? bgpClient.pushRoute(
364 op.thriftProtocolType,
377 : bgpClient.withdrawRoute(
378 op.thriftProtocolType,
387 result = bgpClient.setLogConfig(op.strs[0], op.strs[1]);
391 ? bgpClient.setEbgpMultihop(op.strs[0], op.ints[0])
392 : bgpClient.unsetEbgpMultihop(op.strs[0]);
396 ? bgpClient.setUpdateSource(op.strs[0], op.strs[1])
397 : bgpClient.unsetUpdateSource(op.strs[0]);
401 ? bgpClient.enableAddressFamily(op.strs[0], afi, safi)
402 : bgpClient.disableAddressFamily(op.strs[0], afi, safi);
406 ? bgpClient.enableGracefulRestart(op.ints[0])
407 : bgpClient.disableGracefulRestart();
411 ? bgpClient.enableMultipath(afi, safi)
412 : bgpClient.disableMultipath(afi, safi);
415 result = bgpClient.multipaths(bop.strs[0], bop.ints[0]);
418 result = bgpClient.sendEOR();
421 bgpClient.send_enableEORDelay(op.delayEOR);
424 BfdConfigData bfdConfigData = new BfdConfigData();
425 bfdConfigData.setBfdConfigDataVersion((byte)1);
426 bfdConfigData.setBfdDebounceDown(0);
427 bfdConfigData.setBfdDebounceUp(0);
428 bfdConfigData.setBfdFailureThreshold((byte)op.ints[0]);
429 bfdConfigData.setBfdRxInterval(op.ints[1]);
430 bfdConfigData.setBfdTxInterval(op.ints[2]);
431 bfdConfigData.setBfdMultihop(op.multiHop);
433 ? bgpClient.enableBFDFailover(bfdConfigData)
434 : bgpClient.disableBFDFailover();
437 result = bgpClient.getPeerStatus(op.strs[0], op.asNumber).getValue();
443 throw new BgpRouterException(result);
447 public synchronized void startBgp(long asNum, String rtrId, int stalepathTime, boolean announceFbit)
448 throws TException, BgpRouterException {
449 bop.type = Optype.START;
450 bop.add = announceFbit;
451 bop.asNumber = asNum;
452 bop.ints[0] = stalepathTime;
454 LOG.debug("Starting BGP with as number {} and router ID {} StalePathTime: {}", asNum, rtrId, stalepathTime);
458 public synchronized void stopBgp(long asNum)
459 throws TException, BgpRouterException {
460 bop.type = Optype.STOP;
461 bop.asNumber = asNum;
462 LOG.debug("Stopping BGP with as number {}", asNum);
466 public synchronized void addNeighbor(String nbrIp, long nbrAsNum, @Nullable String md5Secret)
467 throws TException, BgpRouterException {
468 if (md5Secret == null) {
469 LOG.debug("Adding BGP Neighbor {} with as number {} ", nbrIp, nbrAsNum);
471 LOG.debug("Adding BGP Neighbor {} with as number {} and MD5 secret {}", nbrIp, nbrAsNum, md5Secret);
473 bop.type = Optype.NBR;
476 bop.asNumber = nbrAsNum;
477 bop.strs[1] = md5Secret;
479 } // public addNeighbor( nbrIp, nbrAsNum, md5Secret )
481 public synchronized void delNeighbor(String nbrIp) throws TException, BgpRouterException {
482 bop.type = Optype.NBR;
485 LOG.debug("Deleting BGP Neighbor {} ", nbrIp);
489 public synchronized void addVrf(LayerType layerType, String rd, List<String> irts, List<String> erts)
490 throws TException, BgpRouterException {
491 bop.thriftLayerType = layerType == LayerType.LAYER2 ? layer_type.LAYER_2 : layer_type.LAYER_3;
492 bop.type = Optype.VRF;
497 LOG.debug("Adding BGP VRF rd: {} ", rd);
501 public synchronized void delVrf(String rd, long afi, long safi) throws TException, BgpRouterException {
502 bop.type = Optype.VRF;
505 bop.afi = af_afi.findByValue((int)afi);
506 bop.safi = af_safi.findByValue((int)safi);
507 LOG.debug("Deleting BGP VRF rd: {}", rd);
511 // bit of a mess-up: the order of arguments is different in
512 // the Thrift RPC: prefix-nexthop-rd-label.
514 public synchronized void addPrefix(String rd,
520 protocol_type protocolType,
524 encap_type encapType,
526 throws TException, BgpRouterException {
527 bop.type = Optype.PFX;
530 bop.strs[1] = prefix;
531 bop.strs[2] = nexthop;
532 // TODO: set label2 or label3 based on encapsulation type and protocol type once L2label is applicable
534 if (protocolType.equals(protocol_type.PROTOCOL_EVPN) && encapType.equals(encap_type.VXLAN)) {
535 bop.l3label = l3vni; //L3VPN Over VxLan
538 bop.l3label = label; // L3VPN Over MPLSGRE
540 bop.thriftProtocolType = protocolType;
541 bop.ethernetTag = ethtag;
543 bop.macAddress = macaddress;
544 bop.thriftEncapType = encapType;
545 bop.routermac = routermac;
547 LOG.debug("Adding BGP route - rd:{} prefix:{} nexthop:{} label:{} ", rd ,prefix, nexthop, label);
551 public synchronized void delPrefix(String rd, String prefix) throws TException, BgpRouterException {
552 bop.type = Optype.PFX;
555 bop.strs[1] = prefix;
556 LOG.debug("Deleting BGP route - rd:{} prefix:{} ", rd, prefix);
560 public synchronized void addBfd(int detectMult, int minRx, int minTx, boolean multiHop)
561 throws TException, BgpRouterException {
562 bop.type = Optype.BFD;
564 bop.ints[0] = detectMult;
567 bop.multiHop = multiHop;
568 LOG.debug("Adding BFD config {} {} {} {}", detectMult, minRx, minTx, multiHop);
572 public synchronized void delBfd() throws TException, BgpRouterException {
573 bop.type = Optype.BFD;
575 LOG.debug("Deleting BFD Config ");
579 public int initRibSync(BgpSyncHandle handle) throws BgpRouterException {
580 if (bgpClient == null) {
581 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
583 if (handle.getState() == BgpSyncHandle.ITERATING) {
584 return BgpRouterException.BGP_ERR_IN_ITER;
586 handle.setState(BgpSyncHandle.INITED);
591 public int endRibSync(BgpSyncHandle handle) throws BgpRouterException {
592 if (bgpClient == null) {
593 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
595 int state = handle.getState();
597 case BgpSyncHandle.INITED:
598 case BgpSyncHandle.ITERATING:
599 handle.setState(BgpSyncHandle.ABORTED);
601 case BgpSyncHandle.DONE:
603 case BgpSyncHandle.NEVER_DONE:
604 return BgpRouterException.BGP_ERR_NOT_ITER;
611 public Routes doRibSync(BgpSyncHandle handle, af_afi afi) throws TException, BgpRouterException {
612 if (bgpClient == null) {
613 throw new BgpRouterException(BgpRouterException.BGP_ERR_NOT_INITED);
615 int state = handle.getState();
616 if (state != BgpSyncHandle.INITED && state != BgpSyncHandle.ITERATING) {
617 Routes routes = new Routes();
618 routes.setErrcode(BgpRouterException.BGP_ERR_NOT_ITER);
621 int op = state == BgpSyncHandle.INITED ? GET_RTS_INIT : GET_RTS_NEXT;
622 handle.setState(BgpSyncHandle.ITERATING);
623 int winSize = handle.getMaxCount() * handle.getRouteSize();
626 // TODO: receive correct protocol_type here, currently populating with dummy protocol type
627 Routes outRoutes = bgpClient.getRoutes(protocol_type.PROTOCOL_ANY, op, winSize, afi);
628 handle.setMore(outRoutes.more);
629 if (outRoutes.more == 0) {
630 handle.setState(BgpSyncHandle.DONE);
635 public synchronized void setLogging(String fileName, String debugLevel) throws TException, BgpRouterException {
636 bop.type = Optype.LOG;
637 bop.strs[0] = fileName;
638 bop.strs[1] = debugLevel;
639 LOG.debug("Setting Log file to BGP VRF rd: {}, {}", fileName, debugLevel);
643 public synchronized void addEbgpMultihop(String nbrIp, int nhops) throws TException, BgpRouterException {
644 bop.type = Optype.MHOP;
648 LOG.debug("ebgp-multihop set for peer {}, num hops = {}",
653 public synchronized void delEbgpMultihop(String nbrIp) throws TException, BgpRouterException {
654 bop.type = Optype.MHOP;
657 LOG.debug("ebgp-multihop deleted for peer {}", nbrIp);
661 public synchronized void addUpdateSource(String nbrIp, String srcIp) throws TException, BgpRouterException {
662 bop.type = Optype.SRC;
666 LOG.debug("update-source added for peer {}, src-ip = {}",
671 public synchronized void delUpdateSource(String nbrIp) throws TException, BgpRouterException {
672 bop.type = Optype.SRC;
675 LOG.debug("update-source deleted for peer {}", nbrIp);
679 public synchronized void addAddressFamily(String nbrIp, af_afi afi, af_safi safi)
680 throws TException, BgpRouterException {
681 bop.type = Optype.AF;
684 bop.ints[0] = afi.getValue();
685 bop.ints[1] = safi.getValue();
686 LOG.debug("addr family added for peer {}, afi = {}, safi = {}",
687 nbrIp, bop.ints[0], bop.ints[1]);
691 public synchronized void delAddressFamily(String nbrIp, af_afi afi, af_safi safi)
692 throws TException, BgpRouterException {
693 bop.type = Optype.AF;
696 bop.ints[0] = afi.getValue();
697 bop.ints[1] = safi.getValue();
698 LOG.debug("addr family deleted for peer {}, afi = {}, safi = {}",
699 nbrIp, bop.ints[0], bop.ints[1]);
703 public synchronized void addGracefulRestart(int stalepathTime) throws TException, BgpRouterException {
704 bop.type = Optype.GR;
706 bop.ints[0] = stalepathTime;
707 LOG.debug("graceful restart added, stale-path-time = {}",
712 public synchronized void delGracefulRestart() throws TException, BgpRouterException {
713 bop.type = Optype.GR;
715 LOG.debug("graceful restart deleted");
719 public synchronized void enableMultipath(af_afi afi, af_safi safi) throws TException, BgpRouterException {
720 bop.type = Optype.MP;
722 LOG.debug("Enabling multipath for afi {}, safi {}", afi.getValue(), safi.getValue());
723 bop.ints[0] = afi.getValue();
724 bop.ints[1] = safi.getValue();
728 public synchronized void disableMultipath(af_afi afi, af_safi safi) throws TException, BgpRouterException {
729 bop.type = Optype.MP;
731 LOG.debug("Disabling multipath for afi {}, safi {}", afi.getValue(), safi.getValue());
732 bop.ints[0] = afi.getValue();
733 bop.ints[1] = safi.getValue();
737 public synchronized void multipaths(String rd, int maxpath) throws TException, BgpRouterException {
738 bop.type = Optype.VRFMP;
740 bop.ints[0] = maxpath;
744 public synchronized void sendEOR() throws TException, BgpRouterException {
745 bop.type = Optype.EOR;
746 LOG.debug("EOR message sent");
750 public synchronized void delayEOR(int delay) throws TException, BgpRouterException {
751 bop.type = Optype.DELAY_EOR;
752 bop.delayEOR = delay;
753 LOG.debug("EOR delay time in Seconds sent");
757 public synchronized void getPeerStatus(String nbrIp, long nbrAsNum)
758 throws TException, BgpRouterException {
759 bop.type = Optype.PEER_STATUS;
761 bop.asNumber = nbrAsNum;
763 } // public getPeerStatus( nbrIp, nbrAsNum )