-
-
Save johnmccabe/b389619169513532229f to your computer and use it in GitHub Desktop.
Shows how to manipulate a plug qdisc manually using pyroute2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pyroute2 | |
from pyroute2 import IPRoute | |
from pyroute2.iproute import transform_handle | |
from pyroute2.netlink import NLM_F_ACK | |
from pyroute2.netlink import NLM_F_REQUEST | |
from pyroute2.netlink.rtnl.tcmsg import tcmsg | |
PLUG_CLASS = '1:4' | |
PLUG_QDISC = '40:' | |
def manage_plug_via_netlink(interface_name, action='unplug'): | |
""" Manipulates a plug qdisc via netlink""" | |
ip = IPRoute() | |
index = ip.link_lookup(ifname=interface_name)[0] | |
# See the linux source at include/uapi/linux/pkt_sched.h | |
# #define TCQ_PLUG_BUFFER 0 | |
# #define TCQ_PLUG_RELEASE_ONE 1 | |
# #define TCQ_PLUG_RELEASE_INDEFINITE 2 | |
# #define TCQ_PLUG_LIMIT 3 | |
action = {'unplug': 2, 'plug': 0}[action] | |
limit = 10000 | |
handle = transform_handle(PLUG_QDISC) | |
parent = transform_handle(PLUG_CLASS) | |
# This is a bit of magic sauce, inspired by xen's remus project | |
flags = NLM_F_REQUEST | NLM_F_ACK | |
command = pyroute2.netlink.rtnl.RTM_NEWQDISC | |
opts = struct.pack('iI', action, limit) | |
msg = tcmsg() | |
msg['index'] = index | |
msg['handle'] = handle | |
msg['parent'] = parent | |
msg['attrs'] = [['TCA_KIND', 'plug']] | |
msg['attrs'].append(['TCA_OPTIONS', opts]) | |
try: | |
nlm_response = ip.nlm_request(msg, msg_type=command, msg_flags=flags) | |
except pyroute2.netlink.NetlinkError as nle: | |
if nle.code == 22: | |
# This is an old kernel and we're talking to a qfifo, chill | |
print('Detected a non plug qdisc, likely due to an old kernel.\n' | |
'If you wish to have zero downtime haproxy restarts, ' | |
'upgrade your kernel.\n' | |
'Doing nothing to the SYN traffic lane...') | |
return | |
else: | |
raise | |
# As per the netlink manpage (man 7 netlink), we expect an | |
# acknowledgment as a NLMSG_ERROR packet with the error field being 0, | |
# which it looks like pyroute2 treats as None. Really we want it to be | |
# non negative. | |
if not(len(nlm_response) > 0 and | |
nlm_response[0]['event'] == 'NLMSG_ERROR' and | |
nlm_response[0]['header']['error'] is None): | |
raise RuntimeError( | |
'Had an error while communicating with netlink: {0}'.format( | |
nlm_response)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment