Skip to content

Instantly share code, notes, and snippets.

@jolynch
Created April 7, 2015 20:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jolynch/3fa3a863023826f61c18 to your computer and use it in GitHub Desktop.
Save jolynch/3fa3a863023826f61c18 to your computer and use it in GitHub Desktop.
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