Module netmiko.mikrotik

Sub-modules

netmiko.mikrotik.mikrotik_ssh

Classes

class MikrotikRouterOsFileTransfer (ssh_conn: BaseConnection,
source_file: str,
dest_file: str,
file_system: str | None = 'flash',
direction: str = 'put',
socket_timeout: float = 10.0,
progress: Callable[..., Any] | None = None,
progress4: Callable[..., Any] | None = None,
hash_supported: bool = False)
Expand source code
class MikrotikRouterOsFileTransfer(BaseFileTransfer):
    """Mikrotik Router Os File Transfer driver."""

    def __init__(
        self,
        ssh_conn: BaseConnection,
        source_file: str,
        dest_file: str,
        file_system: Optional[str] = "flash",
        direction: str = "put",
        socket_timeout: float = 10.0,
        progress: Optional[Callable[..., Any]] = None,
        progress4: Optional[Callable[..., Any]] = None,
        hash_supported: bool = False,
    ) -> None:
        super().__init__(
            ssh_conn=ssh_conn,
            source_file=source_file,
            dest_file=dest_file,
            file_system=file_system,
            direction=direction,
            socket_timeout=socket_timeout,
            progress=progress,
            progress4=progress4,
            hash_supported=hash_supported,
        )

    def check_file_exists(self, remote_cmd: str = "") -> bool:
        """Check if the dest_file already exists on the file system."""
        if self.direction == "put":
            if not remote_cmd:
                remote_cmd = f'/file print detail where name="{self.file_system}/{self.dest_file}"'
            remote_out = self.ssh_ctl_chan._send_command_timing_str(remote_cmd)
            # Possible outputs:
            # - Normal entry containing 'size' and the filename
            #   0 name="flash/test9.txt" type=".txt file" size=19 creation-time=...
            # - Some firmware versions print flags then the entry
            #   Flags: S - shared
            #   0 name="flash/test9.txt" type=".txt file" size=19 creation-time=...
            # - Or only the flags (no matching file), or entirely blank on failure
            #   Flags: S - shared
            #   <no matching file line>
            if "size" in remote_out and f"{self.file_system}/{self.dest_file}" in remote_out:
                return True
            elif not remote_out.strip() or ("Flags:" in remote_out and "size" not in remote_out):
                return False
            raise ValueError("Unexpected output from check_file_exists")
        elif self.direction == "get":
            return os.path.exists(self.dest_file)
        else:
            raise ValueError("Unexpected value for self.direction")

    def remote_space_available(self, search_pattern: str = "") -> int:
        """Return space available on remote device."""
        remote_cmd = "system resource print without-paging"
        sys_res = self.ssh_ctl_chan._send_command_timing_str(remote_cmd).splitlines()
        for res in sys_res:
            if "free-memory" in res:
                space_str = res.strip().replace("free-memory: ", "")
                return self._format_to_bytes(space_str)
        raise ValueError("Unexpected output from remote_space_available")

    def remote_file_size(self, remote_cmd: str = "", remote_file: Optional[str] = None) -> int:
        """Get the file size of the remote file."""
        if remote_file is None:
            if self.direction == "put":
                remote_file = self.dest_file
            elif self.direction == "get":
                remote_file = self.source_file
            else:
                raise ValueError("Invalid value for file transfer direction.")

        if not remote_cmd:
            remote_cmd = f'/file print detail where name="{self.file_system}/{remote_file}"'
        remote_out = self.ssh_ctl_chan._send_command_timing_str(remote_cmd)
        try:
            size = remote_out.split("size=")[1].split(" ")[0]
            return self._format_to_bytes(size)
        except (KeyError, IndexError):
            raise ValueError("Unable to find file on remote system")

    def file_md5(self, file_name: str, add_newline: bool = False) -> str:
        raise AttributeError("RouterOS does not natively support an MD5-hash operation.")

    @staticmethod
    def process_md5(md5_output: str, pattern: str = "") -> str:
        raise AttributeError("RouterOS does not natively support an MD5-hash operation.")

    def compare_md5(self) -> bool:
        raise AttributeError("RouterOS does not natively support an MD5-hash operation.")

    def remote_md5(self, base_cmd: str = "", remote_file: Optional[str] = None) -> str:
        raise AttributeError("RouterOS does not natively support an MD5-hash operation.")

    def verify_file(self) -> bool:
        """
        Verify the file has been transferred correctly based on filesize.
        This method is very approximate as Mikrotik rounds file sizes to KiB, MiB, GiB...
        Therefore multiple conversions from/to bytes are needed
        """
        if self.direction == "put":
            local_size = self._format_bytes(os.stat(self.source_file).st_size)
            remote_size = self._format_bytes(self.remote_file_size(remote_file=self.dest_file))
            return local_size == remote_size
        elif self.direction == "get":
            local_size = self._format_bytes(os.stat(self.dest_file).st_size)
            remote_size = self._format_bytes(self.remote_file_size(remote_file=self.source_file))
            return local_size == remote_size
        else:
            raise ValueError("Unexpected value of self.direction")

    @staticmethod
    def _format_to_bytes(size: str) -> int:
        """
        Internal function to convert Mikrotik size to bytes
        """
        if size.endswith("KiB"):
            return round(int(float(size.replace("KiB", "")) * 1024))
        if size.endswith("MiB"):
            return round(int(float(size.replace("MiB", "")) * 1048576))
        if size.endswith("GiB"):
            return round(int(float(size.replace("GiB", "")) * 1073741824))
        return round(int(size))

    @staticmethod
    def _format_bytes(size: int) -> str:
        """
        Internal function to convert bytes to KiB, MiB or GiB
        Extremely approximate
        """
        n = 0
        levels = {0: "", 1: "Ki", 2: "Mi", 3: "Gi"}
        while size > 4096 and n < 3:
            size = round(size / 1024)
            n += 1
        return f"{size}{levels[n]}B"

Mikrotik Router Os File Transfer driver.

Ancestors

Methods

def check_file_exists(self, remote_cmd: str = '') ‑> bool
Expand source code
def check_file_exists(self, remote_cmd: str = "") -> bool:
    """Check if the dest_file already exists on the file system."""
    if self.direction == "put":
        if not remote_cmd:
            remote_cmd = f'/file print detail where name="{self.file_system}/{self.dest_file}"'
        remote_out = self.ssh_ctl_chan._send_command_timing_str(remote_cmd)
        # Possible outputs:
        # - Normal entry containing 'size' and the filename
        #   0 name="flash/test9.txt" type=".txt file" size=19 creation-time=...
        # - Some firmware versions print flags then the entry
        #   Flags: S - shared
        #   0 name="flash/test9.txt" type=".txt file" size=19 creation-time=...
        # - Or only the flags (no matching file), or entirely blank on failure
        #   Flags: S - shared
        #   <no matching file line>
        if "size" in remote_out and f"{self.file_system}/{self.dest_file}" in remote_out:
            return True
        elif not remote_out.strip() or ("Flags:" in remote_out and "size" not in remote_out):
            return False
        raise ValueError("Unexpected output from check_file_exists")
    elif self.direction == "get":
        return os.path.exists(self.dest_file)
    else:
        raise ValueError("Unexpected value for self.direction")

Check if the dest_file already exists on the file system.

def verify_file(self) ‑> bool
Expand source code
def verify_file(self) -> bool:
    """
    Verify the file has been transferred correctly based on filesize.
    This method is very approximate as Mikrotik rounds file sizes to KiB, MiB, GiB...
    Therefore multiple conversions from/to bytes are needed
    """
    if self.direction == "put":
        local_size = self._format_bytes(os.stat(self.source_file).st_size)
        remote_size = self._format_bytes(self.remote_file_size(remote_file=self.dest_file))
        return local_size == remote_size
    elif self.direction == "get":
        local_size = self._format_bytes(os.stat(self.dest_file).st_size)
        remote_size = self._format_bytes(self.remote_file_size(remote_file=self.source_file))
        return local_size == remote_size
    else:
        raise ValueError("Unexpected value of self.direction")

Verify the file has been transferred correctly based on filesize. This method is very approximate as Mikrotik rounds file sizes to KiB, MiB, GiB… Therefore multiple conversions from/to bytes are needed

Inherited members

class MikrotikRouterOsSSH (**kwargs: Any)
Expand source code
class MikrotikRouterOsSSH(MikrotikBase):
    """Mikrotik RouterOS SSH driver."""

    pass

Mikrotik RouterOS SSH driver.

Ancestors

Inherited members

class MikrotikSwitchOsSSH (**kwargs: Any)
Expand source code
class MikrotikSwitchOsSSH(MikrotikBase):
    """Mikrotik SwitchOS SSH driver."""

    pass

Mikrotik SwitchOS SSH driver.

Ancestors

Inherited members