ttnn-visualizer 0.44.1__py3-none-any.whl → 0.45.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ttnn_visualizer/csv_queries.py +1 -54
- ttnn_visualizer/exceptions.py +7 -1
- ttnn_visualizer/models.py +1 -0
- ttnn_visualizer/remote_sqlite_setup.py +6 -82
- ttnn_visualizer/sftp_operations.py +11 -98
- ttnn_visualizer/ssh_client.py +352 -0
- ttnn_visualizer/static/assets/{allPaths-CFKU23gh.js → allPaths-CWDYwlGf.js} +1 -1
- ttnn_visualizer/static/assets/{allPathsLoader-CpaihUCo.js → allPathsLoader-CWMmYjN2.js} +2 -2
- ttnn_visualizer/static/assets/{index-B2fHW2_O.js → index-BgTcwiDZ.js} +177 -177
- ttnn_visualizer/static/assets/{index-BueCaPcI.css → index-cjyfcubn.css} +2 -2
- ttnn_visualizer/static/assets/{splitPathsBySizeLoader-BEb-7YZm.js → splitPathsBySizeLoader-BHSjwVae.js} +1 -1
- ttnn_visualizer/static/index.html +2 -2
- ttnn_visualizer/views.py +71 -177
- {ttnn_visualizer-0.44.1.dist-info → ttnn_visualizer-0.45.0.dist-info}/METADATA +4 -1
- {ttnn_visualizer-0.44.1.dist-info → ttnn_visualizer-0.45.0.dist-info}/RECORD +20 -19
- {ttnn_visualizer-0.44.1.dist-info → ttnn_visualizer-0.45.0.dist-info}/WHEEL +0 -0
- {ttnn_visualizer-0.44.1.dist-info → ttnn_visualizer-0.45.0.dist-info}/entry_points.txt +0 -0
- {ttnn_visualizer-0.44.1.dist-info → ttnn_visualizer-0.45.0.dist-info}/licenses/LICENSE +0 -0
- {ttnn_visualizer-0.44.1.dist-info → ttnn_visualizer-0.45.0.dist-info}/licenses/LICENSE_understanding.txt +0 -0
- {ttnn_visualizer-0.44.1.dist-info → ttnn_visualizer-0.45.0.dist-info}/top_level.txt +0 -0
ttnn_visualizer/csv_queries.py
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
import csv
|
5
5
|
import json
|
6
6
|
import os
|
7
|
-
import subprocess
|
8
7
|
import tempfile
|
9
8
|
from io import StringIO
|
10
9
|
from pathlib import Path
|
@@ -13,62 +12,10 @@ from typing import Dict, List, Optional, Union
|
|
13
12
|
import pandas as pd
|
14
13
|
import zstd
|
15
14
|
from tt_perf_report import perf_report
|
16
|
-
from ttnn_visualizer.exceptions import
|
17
|
-
AuthenticationException,
|
18
|
-
DataFormatError,
|
19
|
-
NoValidConnectionsError,
|
20
|
-
SSHException,
|
21
|
-
)
|
15
|
+
from ttnn_visualizer.exceptions import DataFormatError
|
22
16
|
from ttnn_visualizer.models import Instance, RemoteConnection
|
23
17
|
|
24
18
|
|
25
|
-
def handle_ssh_subprocess_error(
|
26
|
-
e: subprocess.CalledProcessError, remote_connection: RemoteConnection
|
27
|
-
):
|
28
|
-
"""
|
29
|
-
Convert subprocess SSH errors to appropriate SSH exceptions.
|
30
|
-
|
31
|
-
:param e: The subprocess.CalledProcessError
|
32
|
-
:param remote_connection: The RemoteConnection object for context
|
33
|
-
:raises: SSHException, AuthenticationException, or NoValidConnectionsError
|
34
|
-
"""
|
35
|
-
stderr = e.stderr.lower() if e.stderr else ""
|
36
|
-
|
37
|
-
# Check for authentication failures
|
38
|
-
if any(
|
39
|
-
auth_err in stderr
|
40
|
-
for auth_err in [
|
41
|
-
"permission denied",
|
42
|
-
"authentication failed",
|
43
|
-
"publickey",
|
44
|
-
"password",
|
45
|
-
"host key verification failed",
|
46
|
-
]
|
47
|
-
):
|
48
|
-
raise AuthenticationException(f"SSH authentication failed: {e.stderr}")
|
49
|
-
|
50
|
-
# Check for connection failures
|
51
|
-
elif any(
|
52
|
-
conn_err in stderr
|
53
|
-
for conn_err in [
|
54
|
-
"connection refused",
|
55
|
-
"network is unreachable",
|
56
|
-
"no route to host",
|
57
|
-
"name or service not known",
|
58
|
-
"connection timed out",
|
59
|
-
]
|
60
|
-
):
|
61
|
-
raise NoValidConnectionsError(f"SSH connection failed: {e.stderr}")
|
62
|
-
|
63
|
-
# Check for general SSH protocol errors
|
64
|
-
elif "ssh:" in stderr or "protocol" in stderr:
|
65
|
-
raise SSHException(f"SSH protocol error: {e.stderr}")
|
66
|
-
|
67
|
-
# Default to generic SSH exception
|
68
|
-
else:
|
69
|
-
raise SSHException(f"SSH command failed: {e.stderr}")
|
70
|
-
|
71
|
-
|
72
19
|
class LocalCSVQueryRunner:
|
73
20
|
def __init__(self, file_path: str, offset: int = 0):
|
74
21
|
self.file_path = file_path
|
ttnn_visualizer/exceptions.py
CHANGED
@@ -14,10 +14,12 @@ class RemoteConnectionException(Exception):
|
|
14
14
|
message,
|
15
15
|
status: ConnectionTestStates,
|
16
16
|
http_status_code: Optional[HTTPStatus] = None,
|
17
|
+
detail: Optional[str] = None,
|
17
18
|
):
|
18
19
|
super().__init__(message)
|
19
20
|
self.message = message
|
20
21
|
self.status = status
|
22
|
+
self.detail = detail
|
21
23
|
self._http_status_code = http_status_code
|
22
24
|
|
23
25
|
@property
|
@@ -37,12 +39,16 @@ class AuthenticationFailedException(RemoteConnectionException):
|
|
37
39
|
"""Exception for SSH authentication failures that should return HTTP 422"""
|
38
40
|
|
39
41
|
def __init__(
|
40
|
-
self,
|
42
|
+
self,
|
43
|
+
message,
|
44
|
+
status: ConnectionTestStates = ConnectionTestStates.FAILED,
|
45
|
+
detail: Optional[str] = None,
|
41
46
|
):
|
42
47
|
super().__init__(
|
43
48
|
message=message,
|
44
49
|
status=status,
|
45
50
|
http_status_code=HTTPStatus.UNPROCESSABLE_ENTITY, # 422
|
51
|
+
detail=detail,
|
46
52
|
)
|
47
53
|
|
48
54
|
|
ttnn_visualizer/models.py
CHANGED
@@ -3,100 +3,24 @@
|
|
3
3
|
# SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
|
4
4
|
|
5
5
|
import re
|
6
|
-
import subprocess
|
7
6
|
|
8
7
|
from ttnn_visualizer.decorators import remote_exception_handler
|
9
8
|
from ttnn_visualizer.enums import ConnectionTestStates
|
10
|
-
from ttnn_visualizer.exceptions import
|
11
|
-
AuthenticationException,
|
12
|
-
NoValidConnectionsError,
|
13
|
-
RemoteSqliteException,
|
14
|
-
SSHException,
|
15
|
-
)
|
9
|
+
from ttnn_visualizer.exceptions import RemoteSqliteException, SSHException
|
16
10
|
from ttnn_visualizer.models import RemoteConnection
|
17
|
-
|
18
|
-
|
19
|
-
def handle_ssh_subprocess_error(
|
20
|
-
e: subprocess.CalledProcessError, remote_connection: RemoteConnection
|
21
|
-
):
|
22
|
-
"""
|
23
|
-
Convert subprocess SSH errors to appropriate SSH exceptions.
|
24
|
-
|
25
|
-
:param e: The subprocess.CalledProcessError
|
26
|
-
:param remote_connection: The RemoteConnection object for context
|
27
|
-
:raises: SSHException, AuthenticationException, or NoValidConnectionsError
|
28
|
-
"""
|
29
|
-
stderr = e.stderr.lower() if e.stderr else ""
|
30
|
-
|
31
|
-
# Check for authentication failures
|
32
|
-
if any(
|
33
|
-
auth_err in stderr
|
34
|
-
for auth_err in [
|
35
|
-
"permission denied",
|
36
|
-
"authentication failed",
|
37
|
-
"publickey",
|
38
|
-
"password",
|
39
|
-
"host key verification failed",
|
40
|
-
]
|
41
|
-
):
|
42
|
-
raise AuthenticationException(f"SSH authentication failed: {e.stderr}")
|
43
|
-
|
44
|
-
# Check for connection failures
|
45
|
-
elif any(
|
46
|
-
conn_err in stderr
|
47
|
-
for conn_err in [
|
48
|
-
"connection refused",
|
49
|
-
"network is unreachable",
|
50
|
-
"no route to host",
|
51
|
-
"name or service not known",
|
52
|
-
"connection timed out",
|
53
|
-
]
|
54
|
-
):
|
55
|
-
raise NoValidConnectionsError(f"SSH connection failed: {e.stderr}")
|
56
|
-
|
57
|
-
# Check for general SSH protocol errors
|
58
|
-
elif "ssh:" in stderr or "protocol" in stderr:
|
59
|
-
raise SSHException(f"SSH protocol error: {e.stderr}")
|
60
|
-
|
61
|
-
# Default to generic SSH exception
|
62
|
-
else:
|
63
|
-
raise SSHException(f"SSH command failed: {e.stderr}")
|
64
|
-
|
11
|
+
from ttnn_visualizer.ssh_client import SSHClient
|
65
12
|
|
66
13
|
MINIMUM_SQLITE_VERSION = "3.38.0"
|
67
14
|
|
68
15
|
|
69
16
|
def _execute_ssh_command(remote_connection: RemoteConnection, command: str) -> str:
|
70
17
|
"""Execute an SSH command and return the output."""
|
71
|
-
|
72
|
-
|
73
|
-
# Handle non-standard SSH port
|
74
|
-
if remote_connection.port != 22:
|
75
|
-
ssh_cmd.extend(["-p", str(remote_connection.port)])
|
76
|
-
|
77
|
-
ssh_cmd.extend([f"{remote_connection.username}@{remote_connection.host}", command])
|
78
|
-
|
18
|
+
ssh_client = SSHClient(remote_connection)
|
79
19
|
try:
|
80
|
-
|
81
|
-
|
82
|
-
)
|
83
|
-
return result.stdout
|
84
|
-
except subprocess.CalledProcessError as e:
|
85
|
-
if e.returncode == 255: # SSH protocol errors
|
86
|
-
handle_ssh_subprocess_error(e, remote_connection)
|
87
|
-
# This line should never be reached as handle_ssh_subprocess_error raises an exception
|
88
|
-
raise RemoteSqliteException(
|
89
|
-
message=f"SSH command failed: {e.stderr}",
|
90
|
-
status=ConnectionTestStates.FAILED,
|
91
|
-
)
|
92
|
-
else:
|
93
|
-
raise RemoteSqliteException(
|
94
|
-
message=f"SSH command failed: {e.stderr}",
|
95
|
-
status=ConnectionTestStates.FAILED,
|
96
|
-
)
|
97
|
-
except subprocess.TimeoutExpired:
|
20
|
+
return ssh_client.execute_command(command, timeout=30)
|
21
|
+
except SSHException as e:
|
98
22
|
raise RemoteSqliteException(
|
99
|
-
message=
|
23
|
+
message=str(e),
|
100
24
|
status=ConnectionTestStates.FAILED,
|
101
25
|
)
|
102
26
|
|
@@ -24,6 +24,7 @@ from ttnn_visualizer.exceptions import (
|
|
24
24
|
)
|
25
25
|
from ttnn_visualizer.models import RemoteConnection, RemoteReportFolder
|
26
26
|
from ttnn_visualizer.sockets import FileProgress, FileStatus, emit_file_status
|
27
|
+
from ttnn_visualizer.ssh_client import SSHClient
|
27
28
|
from ttnn_visualizer.utils import update_last_synced
|
28
29
|
|
29
30
|
logger = logging.getLogger(__name__)
|
@@ -33,53 +34,6 @@ TEST_PROFILER_FILE = "profile_log_device.csv"
|
|
33
34
|
REPORT_DATA_DIRECTORY = Path(__file__).parent.absolute().joinpath("data")
|
34
35
|
|
35
36
|
|
36
|
-
def handle_ssh_subprocess_error(
|
37
|
-
e: subprocess.CalledProcessError, remote_connection: RemoteConnection
|
38
|
-
):
|
39
|
-
"""
|
40
|
-
Convert subprocess SSH errors to appropriate SSH exceptions.
|
41
|
-
|
42
|
-
:param e: The subprocess.CalledProcessError
|
43
|
-
:param remote_connection: The RemoteConnection object for context
|
44
|
-
:raises: SSHException, AuthenticationException, or NoValidConnectionsError
|
45
|
-
"""
|
46
|
-
stderr = e.stderr.lower() if e.stderr else ""
|
47
|
-
|
48
|
-
# Check for authentication failures
|
49
|
-
if any(
|
50
|
-
auth_err in stderr
|
51
|
-
for auth_err in [
|
52
|
-
"permission denied",
|
53
|
-
"authentication failed",
|
54
|
-
"publickey",
|
55
|
-
"password",
|
56
|
-
"host key verification failed",
|
57
|
-
]
|
58
|
-
):
|
59
|
-
raise AuthenticationException(f"SSH authentication failed: {e.stderr}")
|
60
|
-
|
61
|
-
# Check for connection failures
|
62
|
-
elif any(
|
63
|
-
conn_err in stderr
|
64
|
-
for conn_err in [
|
65
|
-
"connection refused",
|
66
|
-
"network is unreachable",
|
67
|
-
"no route to host",
|
68
|
-
"name or service not known",
|
69
|
-
"connection timed out",
|
70
|
-
]
|
71
|
-
):
|
72
|
-
raise NoValidConnectionsError(f"SSH connection failed: {e.stderr}")
|
73
|
-
|
74
|
-
# Check for general SSH protocol errors
|
75
|
-
elif "ssh:" in stderr or "protocol" in stderr:
|
76
|
-
raise SSHException(f"SSH protocol error: {e.stderr}")
|
77
|
-
|
78
|
-
# Default to generic SSH exception
|
79
|
-
else:
|
80
|
-
raise SSHException(f"SSH command failed: {e.stderr}")
|
81
|
-
|
82
|
-
|
83
37
|
def start_background_task(task, *args):
|
84
38
|
with current_app.app_context():
|
85
39
|
if current_app.config["USE_WEBSOCKETS"]:
|
@@ -663,36 +617,8 @@ def read_remote_file(
|
|
663
617
|
else:
|
664
618
|
path = Path(remote_connection.profilerPath)
|
665
619
|
|
666
|
-
|
667
|
-
|
668
|
-
# Build SSH command to read the file
|
669
|
-
ssh_cmd = ["ssh", "-o", "PasswordAuthentication=no"]
|
670
|
-
|
671
|
-
# Handle non-standard SSH port
|
672
|
-
if remote_connection.port != 22:
|
673
|
-
ssh_cmd.extend(["-p", str(remote_connection.port)])
|
674
|
-
|
675
|
-
ssh_cmd.extend(
|
676
|
-
[f"{remote_connection.username}@{remote_connection.host}", f"cat '{path}'"]
|
677
|
-
)
|
678
|
-
|
679
|
-
try:
|
680
|
-
result = subprocess.run(ssh_cmd, capture_output=True, check=True, timeout=30)
|
681
|
-
return result.stdout
|
682
|
-
except subprocess.CalledProcessError as e:
|
683
|
-
if e.returncode == 255: # SSH protocol errors
|
684
|
-
handle_ssh_subprocess_error(e, remote_connection)
|
685
|
-
return None
|
686
|
-
else:
|
687
|
-
# File not found or other command error
|
688
|
-
logger.error(f"File not found or cannot be read: {path}")
|
689
|
-
return None
|
690
|
-
except subprocess.TimeoutExpired:
|
691
|
-
logger.error(f"Timeout reading remote file: {path}")
|
692
|
-
return None
|
693
|
-
except Exception as e:
|
694
|
-
logger.error(f"Error reading remote file {path}: {e}")
|
695
|
-
return None
|
620
|
+
ssh_client = SSHClient(remote_connection)
|
621
|
+
return ssh_client.read_file(path, timeout=30)
|
696
622
|
|
697
623
|
|
698
624
|
@remote_exception_handler
|
@@ -713,24 +639,11 @@ def check_remote_path_exists(remote_connection: RemoteConnection, path_key: str)
|
|
713
639
|
"""Check if a remote path exists using SSH test command."""
|
714
640
|
path = getattr(remote_connection, path_key)
|
715
641
|
|
716
|
-
|
717
|
-
ssh_cmd = ["ssh", "-o", "PasswordAuthentication=no"]
|
718
|
-
|
719
|
-
# Handle non-standard SSH port
|
720
|
-
if remote_connection.port != 22:
|
721
|
-
ssh_cmd.extend(["-p", str(remote_connection.port)])
|
722
|
-
|
723
|
-
ssh_cmd.extend(
|
724
|
-
[f"{remote_connection.username}@{remote_connection.host}", f"test -d '{path}'"]
|
725
|
-
)
|
642
|
+
ssh_client = SSHClient(remote_connection)
|
726
643
|
|
727
644
|
try:
|
728
|
-
|
729
|
-
|
730
|
-
return True
|
731
|
-
except subprocess.CalledProcessError as e:
|
732
|
-
if e.returncode == 255: # SSH protocol errors
|
733
|
-
handle_ssh_subprocess_error(e, remote_connection)
|
645
|
+
if ssh_client.check_path_exists(path, timeout=10):
|
646
|
+
return True
|
734
647
|
else:
|
735
648
|
# Directory does not exist or is inaccessible
|
736
649
|
if path_key == "performancePath":
|
@@ -742,10 +655,10 @@ def check_remote_path_exists(remote_connection: RemoteConnection, path_key: str)
|
|
742
655
|
raise RemoteConnectionException(
|
743
656
|
message=message, status=ConnectionTestStates.FAILED
|
744
657
|
)
|
745
|
-
except
|
746
|
-
logger.error(f"
|
658
|
+
except SSHException as e:
|
659
|
+
logger.error(f"Error checking remote path: {path}")
|
747
660
|
raise RemoteConnectionException(
|
748
|
-
message=f"
|
661
|
+
message=f"Error checking remote path: {path}: {str(e)}",
|
749
662
|
status=ConnectionTestStates.FAILED,
|
750
663
|
)
|
751
664
|
|
@@ -902,11 +815,11 @@ def sync_remote_profiler_folders(
|
|
902
815
|
def sync_remote_performance_folders(
|
903
816
|
remote_connection: RemoteConnection,
|
904
817
|
path_prefix: str,
|
905
|
-
|
818
|
+
performance: RemoteReportFolder,
|
906
819
|
exclude_patterns: Optional[List[str]] = None,
|
907
820
|
sid=None,
|
908
821
|
):
|
909
|
-
remote_folder_path =
|
822
|
+
remote_folder_path = performance.remotePath
|
910
823
|
profile_folder = Path(remote_folder_path).name
|
911
824
|
destination_dir = Path(
|
912
825
|
REPORT_DATA_DIRECTORY,
|