Пример #1
0
class EOSDevice(BaseDevice):
    """Arista EOS Device Implementation."""

    vendor = "arista"

    def __init__(self,
                 host,
                 username,
                 password,
                 transport="http",
                 port=None,
                 timeout=None,
                 **kwargs):
        super().__init__(host,
                         username,
                         password,
                         device_type="arista_eos_eapi")
        self.transport = transport
        self.port = port
        self.timeout = timeout
        eapi_args = {
            "transport": transport,
            "host": host,
            "username": username,
            "password": password,
        }
        optional_args = ("port", "timeout")
        for arg in optional_args:
            value = getattr(self, arg)
            if value is not None:
                eapi_args[arg] = value
        self.connection = eos_connect(**eapi_args)
        self.native = EOSNative(self.connection)
        # _connected indicates Netmiko ssh connection
        self._connected = False

    def _file_copy_instance(self, src, dest=None, file_system="flash:"):
        if dest is None:
            dest = os.path.basename(src)

        fc = FileTransfer(self.native_ssh, src, dest, file_system=file_system)
        return fc

    def _get_file_system(self):
        """Determines the default file system or directory for device.

        Returns:
            str: The name of the default file system or directory for the device.

        Raises:
            FileSystemNotFound: When the module is unable to determine the default file system.
        """
        raw_data = self.show("dir", raw_text=True)
        try:
            file_system = re.match(r"\s*.*?(\S+:)", raw_data).group(1)
        except AttributeError:
            raise FileSystemNotFoundError(hostname=self.hostname,
                                          command="dir")

        return file_system

    def _image_booted(self, image_name, **vendor_specifics):
        version_data = self.show("show boot", raw_text=True)
        if re.search(image_name, version_data):
            return True

        return False

    def _interfaces_status_list(self):
        interfaces_list = []
        interfaces_status_dictionary = self.show(
            "show interfaces status")["interfaceStatuses"]
        for key in interfaces_status_dictionary:
            interface_dictionary = interfaces_status_dictionary[key]
            interface_dictionary["interface"] = key
            interfaces_list.append(interface_dictionary)

        return convert_list_by_key(interfaces_list,
                                   INTERFACES_KM,
                                   fill_in=True,
                                   whitelist=["interface"])

    def _parse_response(self, response, raw_text):
        if raw_text:
            return list(x["result"]["output"] for x in response)
        else:
            return list(x["result"] for x in response)

    def _uptime_to_string(self, uptime):
        days = uptime / (24 * 60 * 60)
        uptime = uptime % (24 * 60 * 60)

        hours = uptime / (60 * 60)
        uptime = uptime % (60 * 60)

        mins = uptime / 60
        uptime = uptime % 60

        seconds = uptime

        return "%02d:%02d:%02d:%02d" % (days, hours, mins, seconds)

    def _wait_for_device_reboot(self, timeout=3600):
        start = time.time()
        while time.time() - start < timeout:
            try:
                self.show("show hostname")
                return
            except:  # noqa E722 # nosec
                pass

        raise RebootTimeoutError(hostname=self.hostname, wait_time=timeout)

    def backup_running_config(self, filename):
        with open(filename, "w") as f:
            f.write(self.running_config)

    @property
    def boot_options(self):
        image = self.show("show boot-config")["softwareImage"]
        image = image.replace("flash:", "")
        return dict(sys=image)

    def checkpoint(self, checkpoint_file):
        self.show("copy running-config %s" % checkpoint_file)

    def close(self):
        pass

    def config(self, commands):
        """Send configuration commands to a device.

        Args:
            commands (str, list): String with single command, or list with multiple commands.

        Raises:
            CommandError: Issue with the command provided.
            CommandListError: Issue with a command in the list provided.
        """
        try:
            self.native.config(commands)
        except EOSCommandError as e:
            if isinstance(commands, str):
                raise CommandError(commands, e.message)
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    def config_list(self, commands):
        """Send configuration commands in list format to a device.

        DEPRECATED - Use the `config` method.

        Args:
            commands (list): List with multiple commands.
        """
        warnings.warn("config_list() is deprecated; use config().",
                      DeprecationWarning)
        self.config(commands)

    def enable(self):
        """Ensure device is in enable mode.
        Returns:
            None: Device prompt is set to enable mode.
        """
        # Netmiko reports enable and config mode as being enabled
        if not self.native_ssh.check_enable_mode():
            self.native_ssh.enable()
        # Ensure device is not in config mode
        if self.native_ssh.check_config_mode():
            self.native_ssh.exit_config_mode()

    @property
    def uptime(self):
        if self._uptime is None:
            sh_version_output = self.show("show version")
            self._uptime = int(time.time() -
                               sh_version_output["bootupTimestamp"])

        return self._uptime

    @property
    def uptime_string(self):
        if self._uptime_string is None:
            self._uptime_string = self._uptime_to_string(self.uptime)

        return self._uptime_string

    @property
    def hostname(self):
        if self._hostname is None:
            sh_hostname_output = self.show("show hostname")
            self._hostname = sh_hostname_output["hostname"]

        return self._hostname

    @property
    def interfaces(self):
        if self._interfaces is None:
            iface_detailed_list = self._interfaces_status_list()
            self._interfaces = sorted(
                list(x["interface"] for x in iface_detailed_list))

        return self._interfaces

    @property
    def vlans(self):
        if self._vlans is None:
            vlans = EOSVlans(self)
            self._vlans = vlans.get_list()

        return self._vlans

    @property
    def fqdn(self):
        if self._fqdn is None:
            sh_hostname_output = self.show("show hostname")
            self._fqdn = sh_hostname_output["fqdn"]

        return self._fqdn

    @property
    def model(self):
        if self._model is None:
            sh_version_output = self.show("show version")
            self._model = sh_version_output["modelName"]

        return self._model

    @property
    def os_version(self):
        if self._os_version is None:
            sh_version_output = self.show("show version")
            self._os_version = sh_version_output["internalVersion"]

        return self._os_version

    @property
    def serial_number(self):
        if self._serial_number is None:
            sh_version_output = self.show("show version")
            self._serial_number = sh_version_output["serialNumber"]

        return self._serial_number

    def file_copy(self, src, dest=None, file_system=None):
        """[summary]

        Args:
            src (string): source file
            dest (string, optional): Destintion file. Defaults to None.
            file_system (string, optional): Describes device file system. Defaults to None.

        Raises:
            FileTransferError: raise exception if there is an error
        """
        self.open()
        self.enable()

        if file_system is None:
            file_system = self._get_file_system()

        if not self.file_copy_remote_exists(src, dest, file_system):
            fc = self._file_copy_instance(src, dest, file_system=file_system)

            try:
                fc.enable_scp()
                fc.establish_scp_conn()
                fc.transfer_file()
            except:  # noqa E722
                raise FileTransferError
            finally:
                fc.close_scp_chan()

            if not self.file_copy_remote_exists(src, dest, file_system):
                raise FileTransferError(
                    message=
                    "Attempted file copy, but could not validate file existed after transfer"
                )

    # TODO: Make this an internal method since exposing file_copy should be sufficient
    def file_copy_remote_exists(self, src, dest=None, file_system=None):
        self.enable()
        if file_system is None:
            file_system = self._get_file_system()

        fc = self._file_copy_instance(src, dest, file_system=file_system)
        if fc.check_file_exists() and fc.compare_md5():
            return True

        return False

    def install_os(self, image_name, **vendor_specifics):
        timeout = vendor_specifics.get("timeout", 3600)
        if not self._image_booted(image_name):
            self.set_boot_options(image_name, **vendor_specifics)
            self.reboot()
            self._wait_for_device_reboot(timeout=timeout)
            if not self._image_booted(image_name):
                raise OSInstallError(hostname=self.hostname,
                                     desired_boot=image_name)

            return True

        return False

    def open(self):
        """Opens ssh connection with Netmiko ConnectHandler to be used with FileTransfer"""
        if self._connected:
            try:
                self.native_ssh.find_prompt()
            except Exception:
                self._connected = False

        if not self._connected:
            self.native_ssh = ConnectHandler(
                device_type="arista_eos",
                ip=self.host,
                username=self.username,
                password=self.password,
                # port=self.port,
                # global_delay_factor=self.global_delay_factor,
                # secret=self.secret,
                verbose=False,
            )
            self._connected = True

    def reboot(self, timer=0, **kwargs):
        """
        Reload the controller or controller pair.

        Args:
            timer (int, optional): The time to wait before reloading. Defaults to 0.

        Raises:
            RebootTimeoutError: When the device is still unreachable after the timeout period.

        Example:
            >>> device = EOSDevice(**connection_args)
            >>> device.reboot()
            >>>

        """
        if kwargs.get("confirm"):
            warnings.warn("Passing 'confirm' to reboot method is deprecated.",
                          DeprecationWarning)

        if timer != 0:
            raise RebootTimerError(self.device_type)

        self.show("reload now")

    def rollback(self, rollback_to):
        try:
            self.show("configure replace %s force" % rollback_to)
        except (CommandError, CommandListError):
            raise RollbackError("Rollback unsuccessful. %s may not exist." %
                                rollback_to)

    @property
    def running_config(self):
        return self.show("show running-config", raw_text=True)

    def save(self, filename="startup-config"):
        self.show("copy running-config %s" % filename)
        return True

    def set_boot_options(self, image_name, **vendor_specifics):
        file_system = vendor_specifics.get("file_system")
        if file_system is None:
            file_system = self._get_file_system()

        file_system_files = self.show("dir {0}".format(file_system),
                                      raw_text=True)
        if re.search(image_name, file_system_files) is None:
            raise NTCFileNotFoundError(hostname=self.hostname,
                                       file=image_name,
                                       dir=file_system)

        self.show("install source {0}{1}".format(file_system, image_name))
        if self.boot_options["sys"] != image_name:
            raise CommandError(
                command="install source {0}".format(image_name),
                message="Setting install source did not yield expected results",
            )

    def show(self, commands, raw_text=False):
        """Send configuration commands to a device.

        Args:
            commands (str, list): String with single command, or list with multiple commands.
            raw_text (bool, optional): False if encode should be json, True if encoding is text. Defaults to False.

        Raises:
            CommandError: Issue with the command provided.
            CommandListError: Issue with a command in the list provided.
        """
        if not raw_text:
            encoding = "json"
        else:
            encoding = "text"

        original_commands_is_str = isinstance(commands, str)
        if original_commands_is_str:
            commands = [commands]
        try:
            response = self.native.enable(commands, encoding=encoding)
            response_list = self._parse_response(response, raw_text=raw_text)
            if original_commands_is_str:
                return response_list[0]
            return response_list
        except EOSCommandError as e:
            if original_commands_is_str:
                raise CommandError(e.commands, e.message)
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    def show_list(self, commands):
        """Send show commands in list format to a device.
        DEPRECATED - Use the `show` method.

        Args:
            commands (list): List with multiple commands.
        """
        warnings.warn("show_list() is deprecated; use show().",
                      DeprecationWarning)
        self.show(commands)

    @property
    def startup_config(self):
        return self.show("show startup-config", raw_text=True)
Пример #2
0
class EOSDevice(BaseDevice):
    def __init__(self,
                 host,
                 username,
                 password,
                 transport='http',
                 timeout=60,
                 **kwargs):
        super(EOSDevice, self).__init__(host,
                                        username,
                                        password,
                                        vendor='arista',
                                        device_type='arista_eos_eapi')
        self.transport = transport
        self.timeout = timeout
        self.connection = eos_connect(transport,
                                      host=host,
                                      username=username,
                                      password=password,
                                      timeout=timeout)
        self.native = EOSNative(self.connection)

    def _get_interface_list(self):
        iface_detailed_list = self._interfaces_status_list()
        iface_list = sorted(list(x['interface'] for x in iface_detailed_list))

        return iface_list

    def _get_vlan_list(self):
        vlans = EOSVlans(self)
        vlan_list = vlans.get_list()

        return vlan_list

    def _interfaces_status_list(self):
        interfaces_list = []
        interfaces_status_dictionary = self.show(
            'show interfaces status')['interfaceStatuses']
        for key in interfaces_status_dictionary:
            interface_dictionary = interfaces_status_dictionary[key]
            interface_dictionary['interface'] = key
            interfaces_list.append(interface_dictionary)

        return convert_list_by_key(interfaces_list,
                                   eos_key_maps.INTERFACES_KM,
                                   fill_in=True,
                                   whitelist=['interface'])

    def _parse_response(self, response, raw_text):
        if raw_text:
            return list(x['result']['output'] for x in response)
        else:
            return list(x['result'] for x in response)

    def _uptime_to_string(self, uptime):
        days = uptime / (24 * 60 * 60)
        uptime = uptime % (24 * 60 * 60)

        hours = uptime / (60 * 60)
        uptime = uptime % (60 * 60)

        mins = uptime / 60
        uptime = uptime % 60

        seconds = uptime

        return '%02d:%02d:%02d:%02d' % (days, hours, mins, seconds)

    def backup_running_config(self, filename):
        with open(filename, 'w') as f:
            f.write(self.running_config)

    def checkpoint(self, checkpoint_file):
        self.show('copy running-config %s' % checkpoint_file)

    def close(self):
        pass

    def config(self, command):
        try:
            self.config_list([command])
        except CommandListError as e:
            raise CommandError(e.command, e.message)

    def config_list(self, commands):
        try:
            self.native.config(commands)
        except EOSCommandError as e:
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    @property
    def facts(self):
        if hasattr(self, '_facts'):
            return self._facts

        facts = {}
        facts['vendor'] = self.vendor

        sh_version_output = self.show('show version')
        facts.update(
            convert_dict_by_key(sh_version_output,
                                eos_key_maps.BASIC_FACTS_KM))

        uptime = int(time.time() - sh_version_output['bootupTimestamp'])
        facts['uptime'] = uptime
        facts['uptime_string'] = self._uptime_to_string(uptime)

        sh_hostname_output = self.show('show hostname')
        facts.update(
            convert_dict_by_key(sh_hostname_output, {},
                                fill_in=True,
                                whitelist=['hostname', 'fqdn']))

        facts['interfaces'] = self._get_interface_list()
        facts['vlans'] = self._get_vlan_list()

        self._facts = facts
        return facts

    def file_copy(self, src, dest=None, **kwargs):
        fc = EOSFileCopy(self, src, dest)
        fc.send()

    def file_copy_remote_exists(self, src, dest=None, **kwargs):
        fc = EOSFileCopy(self, src, dest)
        if fc.remote_file_exists():
            if fc.already_transfered():
                return True
        return False

    def get_boot_options(self):
        image = self.show('show boot-config')['softwareImage']
        image = image.replace('flash:', '')
        return dict(sys=image)

    def open(self):
        pass

    def reboot(self, confirm=False, timer=0):
        if timer != 0:
            raise RebootTimerError(self.device_type)

        if confirm:
            self.show('reload now')
        else:
            print('Need to confirm reboot with confirm=True')

    def rollback(self, rollback_to):
        try:
            self.show('configure replace %s force' % rollback_to)
        except (CommandError, CommandListError):
            raise RollbackError('Rollback unsuccessful. %s may not exist.' %
                                rollback_to)

    @property
    def running_config(self):
        return self.show('show running-config', raw_text=True)

    def save(self, filename='startup-config'):
        self.show('copy running-config %s' % filename)
        return True

    def set_boot_options(self, image_name, **vendor_specifics):
        self.show('install source %s' % image_name)

    def show(self, command, raw_text=False):
        try:
            response_list = self.show_list([command], raw_text=raw_text)
            return response_list[0]
        except CommandListError as e:
            raise CommandError(e.command, e.message)

    def show_list(self, commands, raw_text=False):
        if raw_text:
            encoding = 'text'
        else:
            encoding = 'json'

        try:
            return strip_unicode(
                self._parse_response(self.native.enable(commands,
                                                        encoding=encoding),
                                     raw_text=raw_text))
        except EOSCommandError as e:
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    @property
    def startup_config(self):
        return self.show('show startup-config', raw_text=True)
Пример #3
0
class EOSDevice(BaseDevice):

    def __init__(self, host, username, password, transport='http', timeout=60, **kwargs):
        super(EOSDevice, self).__init__(host, username, password, vendor='arista', device_type=EOS_API_DEVICE_TYPE)
        self.transport = transport
        self.timeout = timeout

        self.connection = eos_connect(
            transport, host=host, username=username, password=password, timeout=timeout)

        self.native = EOSNative(self.connection)

    def open(self):
        pass

    def close(self):
        pass

    def _parse_response(self, response, raw_text):
        if raw_text:
            return list(x['result']['output'] for x in response)
        else:
            return list(x['result'] for x in response)

    def config(self, command):
        try:
            self.config_list([command])
        except CommandListError as e:
            raise CommandError(e.command, e.message)

    def config_list(self, commands):
        try:
            self.native.config(commands)
        except EOSCommandError as e:
            raise CommandListError(
                commands, e.commands[len(e.commands) - 1], e.message)

    def show(self, command, raw_text=False):
        try:
            response_list = self.show_list([command], raw_text=raw_text)
            return response_list[0]
        except CommandListError as e:
            raise CommandError(e.command, e.message)

    def show_list(self, commands, raw_text=False):
        if raw_text:
            encoding = 'text'
        else:
            encoding = 'json'

        try:
            return strip_unicode(
                self._parse_response(
                    self.native.enable(
                        commands, encoding=encoding), raw_text=raw_text))
        except EOSCommandError as e:
            raise CommandListError(commands,
                                   e.commands[len(e.commands) - 1],
                                   e.message)

    def save(self, filename='startup-config'):
        self.show('copy running-config %s' % filename)
        return True

    def file_copy_remote_exists(self, src, dest=None, **kwargs):
        fc = EOSFileCopy(self, src, dest)
        if fc.remote_file_exists():
            return True
        return False

    def file_copy(self, src, dest=None, **kwargs):
        fc = EOSFileCopy(self, src, dest)
        fc.send()

    def reboot(self, confirm=False, timer=0):
        if timer != 0:
            raise RebootTimerError(self.device_type)

        if confirm:
            self.show('reload now')
        else:
            print('Need to confirm reboot with confirm=True')

    def get_boot_options(self):
        image = self.show('show boot-config')['softwareImage']
        image = image.replace('flash:', '')
        return dict(sys=image)

    def set_boot_options(self, image_name, **vendor_specifics):
        self.show('install source %s' % image_name)

    def backup_running_config(self, filename):
        with open(filename, 'w') as f:
            f.write(self.running_config)

    def _interfaces_status_list(self):
        interfaces_list = []
        interfaces_status_dictionary = self.show(
            'show interfaces status')['interfaceStatuses']
        for key in interfaces_status_dictionary:
            interface_dictionary = interfaces_status_dictionary[key]
            interface_dictionary['interface'] = key
            interfaces_list.append(interface_dictionary)

        return convert_list_by_key(interfaces_list,
                                   eos_key_maps.INTERFACES_KM,
                                   fill_in=True,
                                   whitelist=['interface'])

    def _get_interface_list(self):
        iface_detailed_list = self._interfaces_status_list()
        iface_list = sorted(list(x['interface'] for x in iface_detailed_list))

        return iface_list

    def _get_vlan_list(self):
        vlans = EOSVlans(self)
        vlan_list = vlans.get_list()

        return vlan_list

    def _uptime_to_string(self, uptime):
        days = uptime / (24 * 60 * 60)
        uptime = uptime % (24 * 60 * 60)

        hours = uptime / (60 * 60)
        uptime = uptime % (60 * 60)

        mins = uptime / 60
        uptime = uptime % 60

        seconds = uptime

        return '%02d:%02d:%02d:%02d' % (days, hours, mins, seconds)

    def rollback(self, rollback_to):
        try:
            self.show('configure replace %s force' % rollback_to)
        except (CommandError, CommandListError):
            raise RollbackError(
                'Rollback unsuccessful. %s may not exist.' % rollback_to)

    def checkpoint(self, checkpoint_file):
        self.show('copy running-config %s' % checkpoint_file)

    @property
    def facts(self):
        if hasattr(self, '_facts'):
            return self._facts

        facts = {}
        facts['vendor'] = self.vendor

        sh_version_output = self.show('show version')
        facts.update(
            convert_dict_by_key(
                sh_version_output, eos_key_maps.BASIC_FACTS_KM))

        uptime = int(time.time() - sh_version_output['bootupTimestamp'])
        facts['uptime'] = uptime
        facts['uptime_string'] = self._uptime_to_string(uptime)

        sh_hostname_output = self.show('show hostname')
        facts.update(
            convert_dict_by_key(
                sh_hostname_output, {}, fill_in=True, whitelist=['hostname', 'fqdn']))

        facts['interfaces'] = self._get_interface_list()
        facts['vlans'] = self._get_vlan_list()

        self._facts = facts
        return facts

    @property
    def running_config(self):
        return self.show('show running-config', raw_text=True)

    @property
    def startup_config(self):
        return self.show('show startup-config', raw_text=True)
Пример #4
0
class EOSDevice(BaseDevice):
    def __init__(self,
                 host,
                 username,
                 password,
                 transport="http",
                 timeout=60,
                 **kwargs):
        super(EOSDevice, self).__init__(host,
                                        username,
                                        password,
                                        vendor="arista",
                                        device_type="arista_eos_eapi")
        self.transport = transport
        self.timeout = timeout
        self.connection = eos_connect(transport,
                                      host=host,
                                      username=username,
                                      password=password,
                                      timeout=timeout)
        self.native = EOSNative(self.connection)

    def _get_file_system(self):
        """Determines the default file system or directory for device.

        Returns:
            str: The name of the default file system or directory for the device.

        Raises:
            FileSystemNotFound: When the module is unable to determine the default file system.
        """
        raw_data = self.show("dir", raw_text=True)
        try:
            file_system = re.match(r"\s*.*?(\S+:)", raw_data).group(1)
            return file_system
        except AttributeError:
            raise FileSystemNotFoundError(hostname=self.facts.get("hostname"),
                                          command="dir")

    def _get_interface_list(self):
        iface_detailed_list = self._interfaces_status_list()
        iface_list = sorted(list(x["interface"] for x in iface_detailed_list))

        return iface_list

    def _get_vlan_list(self):
        vlans = EOSVlans(self)
        vlan_list = vlans.get_list()

        return vlan_list

    def _image_booted(self, image_name, **vendor_specifics):
        version_data = self.show("show boot", raw_text=True)
        if re.search(image_name, version_data):
            return True

        return False

    def _interfaces_status_list(self):
        interfaces_list = []
        interfaces_status_dictionary = self.show(
            "show interfaces status")["interfaceStatuses"]
        for key in interfaces_status_dictionary:
            interface_dictionary = interfaces_status_dictionary[key]
            interface_dictionary["interface"] = key
            interfaces_list.append(interface_dictionary)

        return convert_list_by_key(interfaces_list,
                                   eos_key_maps.INTERFACES_KM,
                                   fill_in=True,
                                   whitelist=["interface"])

    def _parse_response(self, response, raw_text):
        if raw_text:
            return list(x["result"]["output"] for x in response)
        else:
            return list(x["result"] for x in response)

    def _uptime_to_string(self, uptime):
        days = uptime / (24 * 60 * 60)
        uptime = uptime % (24 * 60 * 60)

        hours = uptime / (60 * 60)
        uptime = uptime % (60 * 60)

        mins = uptime / 60
        uptime = uptime % 60

        seconds = uptime

        return "%02d:%02d:%02d:%02d" % (days, hours, mins, seconds)

    def _wait_for_device_reboot(self, timeout=3600):
        start = time.time()
        while time.time() - start < timeout:
            try:
                self.show("show hostname")
                return
            except:
                pass

        raise RebootTimeoutError(hostname=self.facts["hostname"],
                                 wait_time=timeout)

    def backup_running_config(self, filename):
        with open(filename, "w") as f:
            f.write(self.running_config)

    def checkpoint(self, checkpoint_file):
        self.show("copy running-config %s" % checkpoint_file)

    def close(self):
        pass

    def config(self, command):
        try:
            self.config_list([command])
        except CommandListError as e:
            raise CommandError(e.command, e.message)

    def config_list(self, commands):
        try:
            self.native.config(commands)
        except EOSCommandError as e:
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    @property
    def facts(self):
        if self._facts is None:
            sh_version_output = self.show("show version")
            self._facts = convert_dict_by_key(sh_version_output,
                                              eos_key_maps.BASIC_FACTS_KM)
            self._facts["vendor"] = self.vendor

            uptime = int(time.time() - sh_version_output["bootupTimestamp"])
            self._facts["uptime"] = uptime
            self._facts["uptime_string"] = self._uptime_to_string(uptime)

            sh_hostname_output = self.show("show hostname")
            self._facts.update(
                convert_dict_by_key(sh_hostname_output, {},
                                    fill_in=True,
                                    whitelist=["hostname", "fqdn"]))

            self._facts["interfaces"] = self._get_interface_list()
            self._facts["vlans"] = self._get_vlan_list()

        return self._facts

    def file_copy(self, src, dest=None, **kwargs):
        if not self.file_copy_remote_exists(src, dest, **kwargs):
            fc = EOSFileCopy(self, src, dest)
            fc.send()

            if not self.file_copy_remote_exists(src, dest, **kwargs):
                raise FileTransferError(
                    message=
                    "Attempted file copy, but could not validate file existed after transfer"
                )

    # TODO: Make this an internal method since exposing file_copy should be sufficient
    def file_copy_remote_exists(self, src, dest=None, **kwargs):
        fc = EOSFileCopy(self, src, dest)
        if fc.remote_file_exists() and fc.already_transferred():
            return True
        return False

    def get_boot_options(self):
        image = self.show("show boot-config")["softwareImage"]
        image = image.replace("flash:", "")
        return dict(sys=image)

    def install_os(self, image_name, **vendor_specifics):
        timeout = vendor_specifics.get("timeout", 3600)
        if not self._image_booted(image_name):
            self.set_boot_options(image_name, **vendor_specifics)
            self.reboot(confirm=True)
            self._wait_for_device_reboot(timeout=timeout)
            if not self._image_booted(image_name):
                raise OSInstallError(hostname=self.facts.get("hostname"),
                                     desired_boot=image_name)

            return True

        return False

    def open(self):
        pass

    def reboot(self, confirm=False, timer=0):
        if timer != 0:
            raise RebootTimerError(self.device_type)

        if confirm:
            self.show("reload now")
        else:
            print("Need to confirm reboot with confirm=True")

    def rollback(self, rollback_to):
        try:
            self.show("configure replace %s force" % rollback_to)
        except (CommandError, CommandListError):
            raise RollbackError("Rollback unsuccessful. %s may not exist." %
                                rollback_to)

    @property
    def running_config(self):
        return self.show("show running-config", raw_text=True)

    def save(self, filename="startup-config"):
        self.show("copy running-config %s" % filename)
        return True

    def set_boot_options(self, image_name, **vendor_specifics):
        file_system = vendor_specifics.get("file_system")
        if file_system is None:
            file_system = self._get_file_system()

        file_system_files = self.show("dir {0}".format(file_system),
                                      raw_text=True)
        if re.search(image_name, file_system_files) is None:
            raise NTCFileNotFoundError(hostname=self.facts.get("hostname"),
                                       file=image_name,
                                       dir=file_system)

        self.show("install source {0}{1}".format(file_system, image_name))
        if self.get_boot_options()["sys"] != image_name:
            raise CommandError(
                command="install source {0}".format(image_name),
                message="Setting install source did not yield expected results",
            )

    def show(self, command, raw_text=False):
        try:
            response_list = self.show_list([command], raw_text=raw_text)
            return response_list[0]
        except CommandListError as e:
            raise CommandError(e.command, e.message)

    def show_list(self, commands, raw_text=False):
        if raw_text:
            encoding = "text"
        else:
            encoding = "json"

        try:
            return strip_unicode(
                self._parse_response(self.native.enable(commands,
                                                        encoding=encoding),
                                     raw_text=raw_text))
        except EOSCommandError as e:
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    @property
    def startup_config(self):
        return self.show("show startup-config", raw_text=True)
Пример #5
0
class EOSDevice(BaseDevice):
    """Arista EOS Device Implementation."""

    vendor = "arista"

    def __init__(self,
                 host,
                 username,
                 password,
                 transport="http",
                 timeout=60,
                 **kwargs):
        super().__init__(host,
                         username,
                         password,
                         device_type="arista_eos_eapi")
        self.transport = transport
        self.timeout = timeout
        self.connection = eos_connect(transport,
                                      host=host,
                                      username=username,
                                      password=password,
                                      timeout=timeout)
        self.native = EOSNative(self.connection)
        # _connected indicates Netmiko ssh connection
        self._connected = False

    def _file_copy_instance(self, src, dest=None, file_system="flash:"):
        if dest is None:
            dest = os.path.basename(src)

        fc = FileTransfer(self.native_ssh, src, dest, file_system=file_system)
        return fc

    def _get_file_system(self):
        """Determines the default file system or directory for device.

        Returns:
            str: The name of the default file system or directory for the device.

        Raises:
            FileSystemNotFound: When the module is unable to determine the default file system.
        """
        raw_data = self.show("dir", raw_text=True)
        try:
            file_system = re.match(r"\s*.*?(\S+:)", raw_data).group(1)
        except AttributeError:
            raise FileSystemNotFoundError(hostname=self.facts.get("hostname"),
                                          command="dir")

        return file_system

    def _get_interface_list(self):
        iface_detailed_list = self._interfaces_status_list()
        iface_list = sorted(list(x["interface"] for x in iface_detailed_list))

        return iface_list

    def _get_vlan_list(self):
        vlans = EOSVlans(self)
        vlan_list = vlans.get_list()

        return vlan_list

    def _image_booted(self, image_name, **vendor_specifics):
        version_data = self.show("show boot", raw_text=True)
        if re.search(image_name, version_data):
            return True

        return False

    def _interfaces_status_list(self):
        interfaces_list = []
        interfaces_status_dictionary = self.show(
            "show interfaces status")["interfaceStatuses"]
        for key in interfaces_status_dictionary:
            interface_dictionary = interfaces_status_dictionary[key]
            interface_dictionary["interface"] = key
            interfaces_list.append(interface_dictionary)

        return convert_list_by_key(interfaces_list,
                                   INTERFACES_KM,
                                   fill_in=True,
                                   whitelist=["interface"])

    def _parse_response(self, response, raw_text):
        if raw_text:
            return list(x["result"]["output"] for x in response)
        else:
            return list(x["result"] for x in response)

    def _uptime_to_string(self, uptime):
        days = uptime / (24 * 60 * 60)
        uptime = uptime % (24 * 60 * 60)

        hours = uptime / (60 * 60)
        uptime = uptime % (60 * 60)

        mins = uptime / 60
        uptime = uptime % 60

        seconds = uptime

        return "%02d:%02d:%02d:%02d" % (days, hours, mins, seconds)

    def _wait_for_device_reboot(self, timeout=3600):
        start = time.time()
        while time.time() - start < timeout:
            try:
                self.show("show hostname")
                return
            except:  # noqa E722
                pass

        raise RebootTimeoutError(hostname=self.facts["hostname"],
                                 wait_time=timeout)

    def backup_running_config(self, filename):
        with open(filename, "w") as f:
            f.write(self.running_config)

    @property
    def boot_options(self):
        image = self.show("show boot-config")["softwareImage"]
        image = image.replace("flash:", "")
        return dict(sys=image)

    def checkpoint(self, checkpoint_file):
        self.show("copy running-config %s" % checkpoint_file)

    def close(self):
        pass

    def config(self, command):
        try:
            self.config_list([command])
        except CommandListError as e:
            raise CommandError(e.command, e.message)

    def config_list(self, commands):
        try:
            self.native.config(commands)
        except EOSCommandError as e:
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    def enable(self):
        """Ensure device is in enable mode.
        Returns:
            None: Device prompt is set to enable mode.
        """
        # Netmiko reports enable and config mode as being enabled
        if not self.native_ssh.check_enable_mode():
            self.native_ssh.enable()
        # Ensure device is not in config mode
        if self.native_ssh.check_config_mode():
            self.native_ssh.exit_config_mode()

    @property
    def facts(self):
        if self._facts is None:
            sh_version_output = self.show("show version")
            self._facts = convert_dict_by_key(sh_version_output,
                                              BASIC_FACTS_KM)
            self._facts["vendor"] = self.vendor

            uptime = int(time.time() - sh_version_output["bootupTimestamp"])
            self._facts["uptime"] = uptime
            self._facts["uptime_string"] = self._uptime_to_string(uptime)

            sh_hostname_output = self.show("show hostname")
            self._facts.update(
                convert_dict_by_key(sh_hostname_output, {},
                                    fill_in=True,
                                    whitelist=["hostname", "fqdn"]))

            self._facts["interfaces"] = self._get_interface_list()
            self._facts["vlans"] = self._get_vlan_list()

        return self._facts

    def file_copy(self, src, dest=None, file_system=None):
        """[summary]

        Args:
            src (string): source file
            dest (string, optional): Destintion file. Defaults to None.
            file_system (string, optional): Describes device file system. Defaults to None.

        Raises:
            FileTransferError: raise exception if there is an error
        """
        self.open()
        self.enable()

        if file_system is None:
            file_system = self._get_file_system()

        if not self.file_copy_remote_exists(src, dest, file_system):
            fc = self._file_copy_instance(src, dest, file_system=file_system)

            try:
                fc.enable_scp()
                fc.establish_scp_conn()
                fc.transfer_file()
            except:  # noqa E722
                raise FileTransferError
            finally:
                fc.close_scp_chan()

            if not self.file_copy_remote_exists(src, dest, file_system):
                raise FileTransferError(
                    message=
                    "Attempted file copy, but could not validate file existed after transfer"
                )

    # TODO: Make this an internal method since exposing file_copy should be sufficient
    def file_copy_remote_exists(self, src, dest=None, file_system=None):
        self.enable()
        if file_system is None:
            file_system = self._get_file_system()

        fc = self._file_copy_instance(src, dest, file_system=file_system)
        if fc.check_file_exists() and fc.compare_md5():
            return True

        return False

    def install_os(self, image_name, **vendor_specifics):
        timeout = vendor_specifics.get("timeout", 3600)
        if not self._image_booted(image_name):
            self.set_boot_options(image_name, **vendor_specifics)
            self.reboot(confirm=True)
            self._wait_for_device_reboot(timeout=timeout)
            if not self._image_booted(image_name):
                raise OSInstallError(hostname=self.facts.get("hostname"),
                                     desired_boot=image_name)

            return True

        return False

    def open(self):
        """Opens ssh connection with Netmiko ConnectHandler to be used with FileTransfer"""
        if self._connected:
            try:
                self.native_ssh.find_prompt()
            except Exception:
                self._connected = False

        if not self._connected:
            self.native_ssh = ConnectHandler(
                device_type="arista_eos",
                ip=self.host,
                username=self.username,
                password=self.password,
                # port=self.port,
                # global_delay_factor=self.global_delay_factor,
                # secret=self.secret,
                verbose=False,
            )
            self._connected = True

    def reboot(self, confirm=False, timer=0):
        if timer != 0:
            raise RebootTimerError(self.device_type)

        if confirm:
            self.show("reload now")
        else:
            print("Need to confirm reboot with confirm=True")

    def rollback(self, rollback_to):
        try:
            self.show("configure replace %s force" % rollback_to)
        except (CommandError, CommandListError):
            raise RollbackError("Rollback unsuccessful. %s may not exist." %
                                rollback_to)

    @property
    def running_config(self):
        return self.show("show running-config", raw_text=True)

    def save(self, filename="startup-config"):
        self.show("copy running-config %s" % filename)
        return True

    def set_boot_options(self, image_name, **vendor_specifics):
        file_system = vendor_specifics.get("file_system")
        if file_system is None:
            file_system = self._get_file_system()

        file_system_files = self.show("dir {0}".format(file_system),
                                      raw_text=True)
        if re.search(image_name, file_system_files) is None:
            raise NTCFileNotFoundError(hostname=self.facts.get("hostname"),
                                       file=image_name,
                                       dir=file_system)

        self.show("install source {0}{1}".format(file_system, image_name))
        if self.boot_options["sys"] != image_name:
            raise CommandError(
                command="install source {0}".format(image_name),
                message="Setting install source did not yield expected results",
            )

    def show(self, command, raw_text=False):
        try:
            response_list = self.show_list([command], raw_text=raw_text)
        except CommandListError as e:
            raise CommandError(e.command, e.message)

        return response_list[0]

    def show_list(self, commands, raw_text=False):
        if raw_text:
            encoding = "text"
        else:
            encoding = "json"

        try:
            return self._parse_response(self.native.enable(commands,
                                                           encoding=encoding),
                                        raw_text=raw_text)
        except EOSCommandError as e:
            raise CommandListError(commands, e.commands[len(e.commands) - 1],
                                   e.message)

    @property
    def startup_config(self):
        return self.show("show startup-config", raw_text=True)