multipathd prio callout for open-iscsi
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mpath_prio_open-iscsi 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. __author__ = "Bernd Zeimetz <bzed@debian.org>"
  4. __license__ = """
  5. Copyright (C) 2009 Bernd Zeimetz <bzed@debian.org>
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License along
  15. with this program; if not, write to the Free Software Foundation, Inc.,
  16. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. The FastPinger class is based on code from pyip, which is released
  18. under PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2.
  19. Author: Kenneth Jiang, kenneth.jiang@gmail.com
  20. Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation
  21. """
  22. import os
  23. import re
  24. import select
  25. import sys
  26. import time
  27. import icmp
  28. import ip
  29. from subprocess import Popen, PIPE
  30. from ping import Pinger, PingSocket
  31. # simple regexp to retrieve the IP of the first connection of the node
  32. __conn_ip_re=re.compile(r'^node\.conn\[0\]\.address = (.*)$', re.M)
  33. class FastPinger(Pinger):
  34. """
  35. The original Pinger class is a bit buggy when you want the result fast,
  36. so lets fix it here until I've uploaded a fixed package to Debian.
  37. Also we can skip the DNS lookup as we know that we have an IP address.
  38. This ensures that the prio callout still works at times when the name
  39. lookup is broken for whatever reason.
  40. """
  41. def __init__(self, addr, num):
  42. self.num = num
  43. self.last = 0
  44. self.sent = 0
  45. self.times = {}
  46. self.deltas = []
  47. self.sock = PingSocket(addr)
  48. self.pid = os.getpid()
  49. self.addr = addr
  50. self.destinfo = (addr, addr)
  51. def wait(self):
  52. start = time.time()
  53. timeout = 1.0
  54. while 1:
  55. rd, wt, er = select.select([self.sock.socket], [], [], timeout)
  56. if rd:
  57. arrival = time.time()
  58. try:
  59. pkt, who = self.sock.recvfrom(4096)
  60. except socket.error:
  61. continue
  62. # could also use the ip module to get the payload
  63. repip = ip.disassemble(pkt)
  64. try:
  65. reply = icmp.disassemble(repip.data)
  66. except ValueError:
  67. continue
  68. if reply.get_id() == self.pid:
  69. self.recv_packet(reply, arrival)
  70. self.last_arrival = arrival
  71. break
  72. timeout = (start + 1.0) - time.time()
  73. if timeout < 0:
  74. break
  75. def __get_conn_ip(device):
  76. """ Retrieves the node's IP by looking at the session information
  77. based on the session number retrieved from the sysfs path of the device """
  78. session_path = os.path.realpath('/sys/block/%s/device/scsi_device:0:0:0:0/device' %
  79. (device, ))
  80. # iscsiadm is not always happy about the long version of the path,
  81. # so lets drop all unnecessary parts:
  82. # /sys/devices/platform/host2/session3/target2:0:0/2:0:0:0/scsi_device:0:0:0:0/device
  83. # becomes /sys/devices/platform/host2/session3
  84. session_path = '/'.join(session_path.split('/')[:6])
  85. session_info = Popen(["/usr/bin/iscsiadm", "-m", "session", "-r", session_path],
  86. stdout=PIPE).communicate()[0]
  87. return __conn_ip_re.findall(session_info)[0]
  88. def getprio(device):
  89. """ For now all we take care of is the ping time and packet loss of a node. """
  90. ip = __get_conn_ip(device)
  91. pinger = FastPinger(ip, 5)
  92. pinger.ping()
  93. dmin, davg, dmax, sent, recv, loss = pinger.get_summary()
  94. # packet loss is bad and should not happen at all, so lets give
  95. # the path a really low prio:
  96. if loss > 0:
  97. return 0
  98. # return the priority based on the average response time
  99. # (davg is measured ms, rounded to an int)
  100. # lets assume that 100ms is the worst case of a useful connection
  101. # and base the priority on that
  102. # also we do not care about changes < 5ms, changing the priority too
  103. # often doesn't make much sense.
  104. prio = 100 - int(davg/5)*5 # davg should be an int,
  105. # but lets make sure the result is)
  106. # return 0 if prio < 0:
  107. return max((0, prio))
  108. if __name__ == "__main__":
  109. try:
  110. device = os.path.basename(sys.argv[1])
  111. except IndexError:
  112. sys.stderr.write("Usage: %s [device]\n" % (sys.argv[0], ))
  113. sys.exit(1)
  114. prio=getprio(device)
  115. print prio
  116. sys.exit(0)