fbnet/command_runner/device_info.py (96 lines of code) (raw):

#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. import re from collections import namedtuple from typing import NamedTuple from .base_service import ServiceObj CommandInfo = namedtuple("CommandInfo", "cmd precmd prompt_re") DeviceIP = namedtuple("DeviceIP", ["name", "addr", "mgmt_ip"]) IPInfo = NamedTuple("IPInfo", [("addr", str), ("is_pingable", bool)]) class DeviceInfo(ServiceObj): """ An abstraction to represent the network devices. """ def __init__( self, service, hostname, pref_ips, ip, vendor_data, role, ch_model, alias=None ): super().__init__(service, "DeviceInfo") self._hostname = hostname self._pref_ips = pref_ips self._ip = ip self._vendor_data = vendor_data self._role = role self._ch_model = ch_model self._alias = alias @classmethod def register_counters(cls, stats_mgr): stats_mgr.register_counter("device_info.mgmt_ip") stats_mgr.register_counter("device_info.fallback_to_mgmt_ip") stats_mgr.register_counter("device_info.default_ip") async def setup_session(self, service, device, options, loop): """ create and setup a session to the device. """ try: session = self.create_session(service, device, options, loop) await session.setup() return session except Exception as e: await session.close() # Cleanup the session raise e def create_session(self, service, device, options, loop): """ Create a session object. Note: This doesn't setup the session. The session has to be explicitly setup either by calling "setup" method or by using a context manager session = devinfo.create_session(...) await session.setup() # or you can use setup_session session = await devinfo.setup_session(...) # or you can use async context manager async with devinfo.create_session(...) as session: # do something with session """ _SessionType = self.get_session_type(options) return _SessionType(service, self, options, loop=loop) def __repr__(self): return "Device[{0!r}]".format(self._hostname) @property def hostname(self): return self._hostname @property def role(self): return self._role @property def ch_model(self): return self._ch_model @property def vendor_data(self): return self._vendor_data @property def vendor_name(self): return self._vendor_data.vendor_name @property def alias(self): return self._alias @property def prompt_re(self): return self._vendor_data.get_prompt_re() def get_prompt_re(self, trailer=None): return self._vendor_data.get_prompt_re(trailer) def _is_question(self, cmd): return cmd.endswith(b"?") def _autocomplete(self): return self.vendor_data.autocomplete def get_command_info( self, cmd, command_prompts=None, clear_command=None, ): """ get command information. * command string to send * any pre-command strings to send. This is mostly used to clear the current command line * expected prompts: this is the prompt expected after the end of command output. * clear command: this is the command to be sent to clear the command line. """ cmd = cmd.strip() prompt_rex = None trailer = None # Check if user specified a prompt override for this command if command_prompts: prompt_re = command_prompts.get(cmd) if prompt_re: prompt_rex = re.compile(b"(?P<prompt>%s)" % prompt_re) cmd += b"\n" if not prompt_rex: if self._is_question(cmd) and self._autocomplete(): # We expect the command to be echoed back after prompt trailer = cmd[:-1].strip() # remove the last char ('?') trailer = rb"(?P<command>%s)[\b\s]*" % re.escape(trailer) else: # Normal command cmd = cmd + b"\n" # Add newline prompt_rex = self.get_prompt_re(trailer) # Send a NACK to clear the current command line precmd = self._vendor_data.clear_command if clear_command == "": precmd = None elif clear_command: precmd = clear_command.encode("utf-8") return CommandInfo(cmd, precmd, prompt_rex) def get_session_type(self, options): if options["console"]: # Since console_session imports IPInfo from this file # it was resulting in a circular dependency with console_session, # so importing within the conditional from .console_session import ConsoleCommandSession return ConsoleCommandSession return self._vendor_data.select_session_type(options)