Browse Source

Added ErrorHandler.

pull/2/head 1.0.1
Richard Neumann 4 years ago
parent
commit
f07c68d897
  1. 7
      rcon/config.py
  2. 30
      rcon/errorhandler.py
  3. 46
      rcon/rconclt.py
  4. 23
      rcon/rconshell.py

7
rcon/config.py

@ -9,11 +9,12 @@ from typing import Dict, Iterator, NamedTuple, Tuple
from rcon.exceptions import InvalidConfig from rcon.exceptions import InvalidConfig
__all__ = ['servers'] __all__ = ['CONFIG_FILE', 'LOG_FORMAT', 'Config', 'servers']
CONFIG = ConfigParser() CONFIG = ConfigParser()
CONFIG_FILE = Path('/etc/rcon.conf') CONFIG_FILE = Path('/etc/rcon.conf')
LOG_FORMAT = '[%(levelname)s] %(name)s: %(message)s'
LOGGER = getLogger('RCON Config') LOGGER = getLogger('RCON Config')
@ -64,8 +65,8 @@ def entries(config_parser: ConfigParser) -> Iterator[Tuple[str, Config]]:
yield (section, Config.from_config_section(config_parser[section])) yield (section, Config.from_config_section(config_parser[section]))
def servers() -> Dict[str, Config]: def servers(config_file: Path = CONFIG_FILE) -> Dict[str, Config]:
"""Returns a dictionary of servers.""" """Returns a dictionary of servers."""
CONFIG.read(CONFIG_FILE) CONFIG.read(config_file)
return dict(entries(CONFIG)) return dict(entries(CONFIG))

30
rcon/errorhandler.py

@ -0,0 +1,30 @@
"""Common errors handler."""
from logging import Logger
from sys import exit # pylint: disable=W0622
from typing import Iterable, Tuple
__all__ = ['ErrorHandler']
ErrorMap = Iterable[Tuple[Exception, str, int]]
class ErrorHandler:
"""Handles common errors and exits."""
def __init__(self, errors: ErrorMap, logger: Logger):
"""Sets the logger."""
self.errors = errors
self.logger = logger
def __enter__(self):
return self
def __exit__(self, typ, *_):
"""Checks for connection errors and exits respectively."""
for error, (message, returncode) in self.errors.items():
if isinstance(typ, error):
self.logger.error(message)
exit(returncode)

46
rcon/rconclt.py

@ -3,12 +3,14 @@
from argparse import ArgumentParser, Namespace from argparse import ArgumentParser, Namespace
from getpass import getpass from getpass import getpass
from logging import DEBUG, INFO, basicConfig, getLogger from logging import DEBUG, INFO, basicConfig, getLogger
from pathlib import Path
from socket import timeout from socket import timeout
from sys import exit # pylint: disable=W0622 from sys import exit # pylint: disable=W0622
from typing import Tuple from typing import Tuple
from rcon.errorhandler import ErrorHandler
from rcon.exceptions import InvalidConfig from rcon.exceptions import InvalidConfig
from rcon.config import Config, servers from rcon.config import CONFIG_FILE, LOG_FORMAT, Config, servers
from rcon.exceptions import RequestIdMismatch, WrongPassword from rcon.exceptions import RequestIdMismatch, WrongPassword
from rcon.proto import Client from rcon.proto import Client
@ -16,8 +18,13 @@ from rcon.proto import Client
__all__ = ['get_credentials', 'main'] __all__ = ['get_credentials', 'main']
ERRORS = (
(ConnectionRefusedError, 'Connection refused.', 3),
(timeout, 'Connection timeout.', 4),
(RequestIdMismatch, 'Unexpected request ID mismatch.', 5),
(WrongPassword, 'Wrong password.', 6),
)
LOGGER = getLogger('rconclt') LOGGER = getLogger('rconclt')
LOG_FORMAT = '[%(levelname)s] %(name)s: %(message)s'
def get_args() -> Namespace: def get_args() -> Namespace:
@ -25,26 +32,28 @@ def get_args() -> Namespace:
parser = ArgumentParser(description='A Minecraft RCON client.') parser = ArgumentParser(description='A Minecraft RCON client.')
parser.add_argument('server', help='the server to connect to') parser.add_argument('server', help='the server to connect to')
parser.add_argument('-t', '--timeout', type=float, metavar='seconds', parser.add_argument('-c', '--config', type=Path, metavar='file',
help='connection timeout in seconds') default=CONFIG_FILE, help='the configuration file')
parser.add_argument('-d', '--debug', action='store_true', parser.add_argument('-d', '--debug', action='store_true',
help='print additional debug information') help='print additional debug information')
parser.add_argument('-t', '--timeout', type=float, metavar='seconds',
help='connection timeout in seconds')
parser.add_argument('command', help='command to execute on the server') parser.add_argument('command', help='command to execute on the server')
parser.add_argument('argument', nargs='*', default=(), parser.add_argument('argument', nargs='*', default=(),
help='arguments for the command') help='arguments for the command')
return parser.parse_args() return parser.parse_args()
def get_credentials(server: str) -> Tuple[str, int, str]: def get_credentials(args: Namespace) -> Tuple[str, int, str]:
"""Get the credentials for a server from the respective server name.""" """Get the credentials for a server from the respective server name."""
try: try:
host, port, passwd = Config.from_string(server) host, port, passwd = Config.from_string(args.server)
except InvalidConfig: except InvalidConfig:
try: try:
host, port, passwd = servers()[server] host, port, passwd = servers(args.config)[args.server]
except KeyError: except KeyError:
LOGGER.error('No such server: %s.', server) LOGGER.error('No such server: %s.', args.server)
exit(2) exit(2)
if passwd is None: if passwd is None:
@ -57,30 +66,17 @@ def get_credentials(server: str) -> Tuple[str, int, str]:
return (host, port, passwd) return (host, port, passwd)
def main(): def main():
"""Runs the RCON client.""" """Runs the RCON client."""
args = get_args() args = get_args()
log_level = DEBUG if args.debug else INFO log_level = DEBUG if args.debug else INFO
basicConfig(level=log_level, format=LOG_FORMAT) basicConfig(level=log_level, format=LOG_FORMAT)
host, port, passwd = get_credentials(args.server) host, port, passwd = get_credentials(args)
try: with ErrorHandler(ERRORS, LOGGER):
with Client(host, port, timeout=args.timeout) as client: with Client(host, port, timeout=args.timeout) as client:
client.login(passwd) client.login(passwd)
text = client.run(args.command, *args.argument) text = client.run(args.command, *args.argument)
except ConnectionRefusedError:
LOGGER.error('Connection refused.') print(text, flush=True)
exit(3)
except timeout:
LOGGER.error('Connection timeout.')
exit(4)
except RequestIdMismatch:
LOGGER.error('Unexpected request ID mismatch.')
exit(5)
except WrongPassword:
LOGGER.error('Wrong password.')
exit(6)
else:
print(text, flush=True)

23
rcon/rconshell.py

@ -2,18 +2,23 @@
from argparse import ArgumentParser, Namespace from argparse import ArgumentParser, Namespace
from logging import INFO, basicConfig, getLogger from logging import INFO, basicConfig, getLogger
from pathlib import Path
from socket import timeout from socket import timeout
from sys import exit # pylint: disable=W0622 from sys import exit # pylint: disable=W0622
from rcon.errorhandler import ErrorHandler
from rcon.rconclt import get_credentials from rcon.rconclt import get_credentials
from rcon.config import CONFIG_FILE, LOG_FORMAT
from rcon.console import rconcmd from rcon.console import rconcmd
__all__ = ['get_args', 'main'] __all__ = ['get_args', 'main']
ERRORS = (
(ConnectionRefusedError, 'Connection refused.', 3),
(timeout, 'Connection timeout.', 4)
)
LOGGER = getLogger('rconshell') LOGGER = getLogger('rconshell')
LOG_FORMAT = '[%(levelname)s] %(name)s: %(message)s'
def get_args() -> Namespace: def get_args() -> Namespace:
@ -21,6 +26,8 @@ def get_args() -> Namespace:
parser = ArgumentParser(description='An interactive RCON shell.') parser = ArgumentParser(description='An interactive RCON shell.')
parser.add_argument('server', nargs='?', help='the server to connect to') parser.add_argument('server', nargs='?', help='the server to connect to')
parser.add_argument('-c', '--config', type=Path, metavar='file',
default=CONFIG_FILE, help='the configuration file')
parser.add_argument('-p', '--prompt', default='RCON> ', metavar='PS1', parser.add_argument('-p', '--prompt', default='RCON> ', metavar='PS1',
help='the shell prompt') help='the shell prompt')
return parser.parse_args() return parser.parse_args()
@ -32,18 +39,12 @@ def main():
args = get_args() args = get_args()
basicConfig(level=INFO, format=LOG_FORMAT) basicConfig(level=INFO, format=LOG_FORMAT)
if server := args.server: if args.server:
host, port, passwd = get_credentials(server) host, port, passwd = get_credentials(args)
else: else:
host = port = passwd = None host = port = passwd = None
try: with ErrorHandler(ERRORS, LOGGER):
exit_code = rconcmd(host, port, passwd, args.prompt) exit_code = rconcmd(host, port, passwd, args.prompt)
except ConnectionRefusedError:
LOGGER.error('Connection refused.')
exit(3)
except timeout:
LOGGER.error('Connection timeout.')
exit(4)
exit(exit_code) exit(exit_code)

Loading…
Cancel
Save