spigotmc.chart.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. # -*- coding: utf-8 -*-
  2. # Description: spigotmc netdata python.d module
  3. # Author: Austin S. Hemmelgarn (Ferroin)
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5. import socket
  6. import platform
  7. from bases.FrameworkServices.SimpleService import SimpleService
  8. from third_party import mcrcon
  9. # Update only every 5 seconds because collection takes in excess of
  10. # 100ms sometimes, and mos tpeople won't care about second-by-second data.
  11. update_every = 5
  12. PRECISION = 100
  13. ORDER = [
  14. 'tps',
  15. 'users',
  16. ]
  17. CHARTS = {
  18. 'tps': {
  19. 'options': [None, 'Spigot Ticks Per Second', 'ticks', 'spigotmc', 'spigotmc.tps', 'line'],
  20. 'lines': [
  21. ['tps1', '1 Minute Average', 'absolute', 1, PRECISION],
  22. ['tps5', '5 Minute Average', 'absolute', 1, PRECISION],
  23. ['tps15', '15 Minute Average', 'absolute', 1, PRECISION]
  24. ]
  25. },
  26. 'users': {
  27. 'options': [None, 'Minecraft Users', 'users', 'spigotmc', 'spigotmc.users', 'area'],
  28. 'lines': [
  29. ['users', 'Users', 'absolute', 1, 1]
  30. ]
  31. }
  32. }
  33. class Service(SimpleService):
  34. def __init__(self, configuration=None, name=None):
  35. SimpleService.__init__(self, configuration=configuration, name=name)
  36. self.order = ORDER
  37. self.definitions = CHARTS
  38. self.host = self.configuration.get('host', 'localhost')
  39. self.port = self.configuration.get('port', 25575)
  40. self.password = self.configuration.get('password', '')
  41. self.console = mcrcon.MCRcon()
  42. self.alive = True
  43. def check(self):
  44. if platform.system() != 'Linux':
  45. self.error('Only supported on Linux.')
  46. return False
  47. try:
  48. self.connect()
  49. except (mcrcon.MCRconException, socket.error) as err:
  50. self.error('Error connecting.')
  51. self.error(repr(err))
  52. return False
  53. return True
  54. def connect(self):
  55. self.console.connect(self.host, self.port, self.password)
  56. def reconnect(self):
  57. try:
  58. try:
  59. self.console.disconnect()
  60. except mcrcon.MCRconException:
  61. pass
  62. self.console.connect(self.host, self.port, self.password)
  63. self.alive = True
  64. except (mcrcon.MCRconException, socket.error) as err:
  65. self.error('Error connecting.')
  66. self.error(repr(err))
  67. return False
  68. return True
  69. def is_alive(self):
  70. if (not self.alive) or \
  71. self.console.socket.getsockopt(socket.IPPROTO_TCP, socket.TCP_INFO, 0) != 1:
  72. return self.reconnect()
  73. return True
  74. def _get_data(self):
  75. if not self.is_alive():
  76. return None
  77. data = {}
  78. try:
  79. raw = self.console.command('tps')
  80. # The above command returns a string that looks like this:
  81. # '§6TPS from last 1m, 5m, 15m: §a19.99, §a19.99, §a19.99\n'
  82. # The values we care about are the three numbers after the :
  83. tmp = raw.split(':')[1].split(',')
  84. data['tps1'] = float(tmp[0].lstrip(u' §a*')) * PRECISION
  85. data['tps5'] = float(tmp[1].lstrip(u' §a*')) * PRECISION
  86. data['tps15'] = float(tmp[2].lstrip(u' §a*').rstrip()) * PRECISION
  87. except mcrcon.MCRconException:
  88. self.error('Unable to fetch TPS values.')
  89. except socket.error:
  90. self.error('Connection is dead.')
  91. self.alive = False
  92. return None
  93. except (TypeError, LookupError):
  94. self.error('Unable to process TPS values.')
  95. try:
  96. raw = self.console.command('list')
  97. # The above command returns a string that looks like this:
  98. # 'There are 0/20 players online:'
  99. # We care about the first number here.
  100. data['users'] = int(raw.split()[2].split('/')[0])
  101. except mcrcon.MCRconException:
  102. self.error('Unable to fetch user counts.')
  103. except socket.error:
  104. self.error('Connection is dead.')
  105. self.alive = False
  106. return None
  107. except (TypeError, LookupError):
  108. self.error('Unable to process user counts.')
  109. return data