/core/script/cli/beef.py

https://github.com/nocproject/noc · Python · 99 lines · 66 code · 20 blank · 13 comment · 9 complexity · c5263bcc0095504caab872fb547a2086 MD5 · raw file

  1. # ----------------------------------------------------------------------
  2. # BeefCLI
  3. # ----------------------------------------------------------------------
  4. # Copyright (C) 2007-2020 The NOC Project
  5. # See LICENSE for details
  6. # ----------------------------------------------------------------------
  7. # Python modules
  8. import asyncio
  9. from typing import Optional
  10. # NOC modules
  11. from .cli import CLI
  12. from .stream import BaseStream
  13. from .telnet import TelnetStream
  14. class BeefStream(TelnetStream):
  15. def __init__(self, cli: CLI):
  16. super().__init__(cli)
  17. self.cli = cli
  18. self.beef = None
  19. self.read_buffer = []
  20. self.read_event = asyncio.Event()
  21. def close(self):
  22. self.cli = None
  23. async def connect(self, address: str, port: Optional[int] = None):
  24. self.beef = self.cli.script.request_beef()
  25. if not self.beef:
  26. # Connection refused
  27. raise ConnectionRefusedError
  28. self.cli.set_state("start")
  29. async def read(self, n: int):
  30. while True:
  31. await self.read_event.wait()
  32. if self.read_buffer:
  33. data = self.read_buffer.pop(0)
  34. if not self.read_buffer:
  35. self.read_event.clear()
  36. return data
  37. async def write(self, data: bytes, raw: bool = False):
  38. try:
  39. self.read_buffer += list(self.beef.iter_cli_reply(data))
  40. except KeyError:
  41. self.read_buffer += [self.cli.SYNTAX_ERROR_CODE]
  42. self.read_event.set()
  43. async def wait_for_read(self):
  44. pass
  45. async def wait_for_write(self):
  46. pass
  47. async def enter_state(self, state):
  48. self.read_buffer += list(self.beef.iter_fsm_state_reply(state))
  49. self.read_event.set()
  50. class BeefCLI(CLI):
  51. name = "beef_cli"
  52. def __init__(self, *args, **kwargs):
  53. super().__init__(*args, **kwargs)
  54. self.state = "notconnected"
  55. def get_stream(self) -> BaseStream:
  56. return BeefStream(self)
  57. def set_state(self, state):
  58. changed = self.state != state
  59. super().set_state(state)
  60. # Force state enter reply
  61. if changed:
  62. asyncio.get_running_loop().create_task(self.stream.enter_state(state))
  63. async def reply_state(self, state):
  64. self.logger.debug("Replying '%s' state", state)
  65. beef = self.script.request_beef()
  66. for reply in beef.iter_fsm_state_reply(state):
  67. await self.stream.write(reply)
  68. async def send(self, cmd: bytes) -> None:
  69. self.logger.debug("Send: %r", cmd)
  70. if self.state != "prompt":
  71. return # Will be replied via reply_state
  72. await self.stream.write(cmd[: -len(self.profile.command_submit)])
  73. def is_beef(self) -> bool:
  74. return True
  75. async def send_pager_reply(self, data, match):
  76. """
  77. Beef need no pagers
  78. """
  79. self.collected_data += [data]