Shows how to manipulate a plug qdisc manually using pyroute2
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