ttnn-visualizer 0.41.0__py3-none-any.whl → 0.42.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.
@@ -1 +1 @@
1
- import{_ as o,a as _,b as i,p as c,I as u}from"./index-DFVwehlj.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-BKzgFDAn.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-BvSuWPlB.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
1
+ import{_ as o,a as _,b as i,p as c,I as u}from"./index-Ybr1HJxx.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-BKzgFDAn.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-BvSuWPlB.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
@@ -34,7 +34,7 @@
34
34
  /* SERVER_CONFIG */
35
35
  </script>
36
36
 
37
- <script type="module" crossorigin src="/static/assets/index-DFVwehlj.js"></script>
37
+ <script type="module" crossorigin src="/static/assets/index-Ybr1HJxx.js"></script>
38
38
  <link rel="stylesheet" crossorigin href="/static/assets/index-C1rJBrMl.css">
39
39
  </head>
40
40
  <body>
@@ -16,7 +16,6 @@ from ttnn_visualizer.models import (
16
16
  )
17
17
  from ttnn_visualizer.queries import DatabaseQueries
18
18
  from ttnn_visualizer.queries import LocalQueryRunner
19
- from ttnn_visualizer.queries import RemoteQueryRunner
20
19
 
21
20
 
22
21
  class TestQueryTable(unittest.TestCase):
@@ -222,14 +221,6 @@ class TestDatabaseQueries(unittest.TestCase):
222
221
  str(context.exception),
223
222
  )
224
223
 
225
- @patch("ttnn_visualizer.queries.get_client")
226
- def test_init_with_valid_remote_instance(self, _mock_client):
227
- mock_instance = Mock()
228
- mock_instance.remote_connection = Mock(useRemoteQuerying=True)
229
- mock_instance.remote_connection.sqliteBinaryPath = "/usr/bin/sqlite3"
230
- mock_instance.remote_profiler_folder = Mock(remotePath="/remote/path")
231
- db_queries = DatabaseQueries(instance=mock_instance)
232
- self.assertIsInstance(db_queries.query_runner, RemoteQueryRunner)
233
224
 
234
225
  def test_init_with_valid_local_instance(self):
235
226
  with tempfile.NamedTemporaryFile(suffix=".sqlite") as temp_db_file:
@@ -381,64 +372,5 @@ class TestDatabaseQueries(unittest.TestCase):
381
372
  self.assertEqual(result.operation_id, 2)
382
373
 
383
374
 
384
- class TestRemoteQueryRunner(unittest.TestCase):
385
-
386
- def setUp(self):
387
- self.mock_instance = Mock()
388
- self.mock_instance.remote_connection.sqliteBinaryPath = "/usr/bin/sqlite3"
389
- self.mock_instance.remote_connection.host = "mockhost"
390
- self.mock_instance.remote_connection.user = "mockuser"
391
- self.mock_instance.remote_profiler_folder.remotePath = "/remote/db"
392
-
393
- @patch("ttnn_visualizer.queries.get_client")
394
- def test_init_with_mock_get_client(self, mock_get_client):
395
- # Mock the SSHClient returned by get_client
396
- mock_ssh_client = Mock()
397
- mock_get_client.return_value = mock_ssh_client
398
-
399
- runner = RemoteQueryRunner(instance=self.mock_instance)
400
- self.assertEqual(runner.ssh_client, mock_ssh_client)
401
- mock_get_client.assert_called_once_with(
402
- remote_connection=self.mock_instance.remote_connection
403
- )
404
-
405
- @patch("ttnn_visualizer.queries.get_client")
406
- def test_execute_query(self, mock_get_client):
407
- # Mock the SSH client
408
- mock_ssh_client = Mock()
409
- mock_get_client.return_value = mock_ssh_client
410
-
411
- mock_stdout = Mock()
412
- mock_stdout.read.return_value = b'[{"col1": "value1", "col2": "value2"}]'
413
- mock_stderr = Mock()
414
- mock_stderr.read.return_value = b""
415
- mock_ssh_client.exec_command.return_value = (None, mock_stdout, mock_stderr)
416
-
417
- runner = RemoteQueryRunner(instance=self.mock_instance)
418
-
419
- query = "SELECT * FROM table WHERE id = ?"
420
- params = [1]
421
- results = runner.execute_query(query, params)
422
-
423
- # Validate results
424
- self.assertEqual(results, [("value1", "value2")])
425
- mock_get_client.assert_called_once()
426
- mock_ssh_client.exec_command.assert_called_once()
427
-
428
- @patch("ttnn_visualizer.queries.get_client")
429
- def test_close(self, mock_get_client):
430
- # Mock the SSH client
431
- mock_ssh_client = Mock()
432
- mock_get_client.return_value = mock_ssh_client
433
-
434
- runner = RemoteQueryRunner(instance=self.mock_instance)
435
-
436
- runner.close()
437
- mock_ssh_client.close.assert_called_once()
438
-
439
- def tearDown(self):
440
- pass
441
-
442
-
443
375
  if __name__ == "__main__":
444
376
  unittest.main()
ttnn_visualizer/views.py CHANGED
@@ -65,13 +65,92 @@ from ttnn_visualizer.sftp_operations import (
65
65
  sync_remote_performance_folders,
66
66
  get_cluster_desc,
67
67
  )
68
- from ttnn_visualizer.ssh_client import get_client
68
+ from ttnn_visualizer.exceptions import SSHException, AuthenticationException, NoValidConnectionsError
69
+ import subprocess
69
70
  from ttnn_visualizer.utils import (
70
71
  get_cluster_descriptor_path,
71
72
  read_last_synced_file,
72
73
  timer,
73
74
  )
74
75
 
76
+
77
+ def handle_ssh_subprocess_error(e: subprocess.CalledProcessError, remote_connection):
78
+ """
79
+ Convert subprocess SSH errors to appropriate SSH exceptions.
80
+
81
+ :param e: The subprocess.CalledProcessError
82
+ :param remote_connection: The RemoteConnection object for context
83
+ :raises: SSHException, AuthenticationException, or NoValidConnectionsError
84
+ """
85
+ stderr = e.stderr.lower() if e.stderr else ""
86
+
87
+ # Check for authentication failures
88
+ if any(auth_err in stderr for auth_err in [
89
+ "permission denied",
90
+ "authentication failed",
91
+ "publickey",
92
+ "password",
93
+ "host key verification failed"
94
+ ]):
95
+ raise AuthenticationException(f"SSH authentication failed: {e.stderr}")
96
+
97
+ # Check for connection failures
98
+ elif any(conn_err in stderr for conn_err in [
99
+ "connection refused",
100
+ "network is unreachable",
101
+ "no route to host",
102
+ "name or service not known",
103
+ "connection timed out"
104
+ ]):
105
+ raise NoValidConnectionsError(f"SSH connection failed: {e.stderr}")
106
+
107
+ # Check for general SSH protocol errors
108
+ elif "ssh:" in stderr or "protocol" in stderr:
109
+ raise SSHException(f"SSH protocol error: {e.stderr}")
110
+
111
+ # Default to generic SSH exception
112
+ else:
113
+ raise SSHException(f"SSH command failed: {e.stderr}")
114
+
115
+
116
+ def test_ssh_connection(connection) -> bool:
117
+ """Test SSH connection by running a simple command."""
118
+ ssh_cmd = ["ssh"]
119
+
120
+ # Handle non-standard SSH port
121
+ if connection.port != 22:
122
+ ssh_cmd.extend(["-p", str(connection.port)])
123
+
124
+ ssh_cmd.extend([
125
+ f"{connection.username}@{connection.host}",
126
+ "echo 'SSH connection test'"
127
+ ])
128
+
129
+ try:
130
+ result = subprocess.run(
131
+ ssh_cmd,
132
+ capture_output=True,
133
+ text=True,
134
+ check=True,
135
+ timeout=10
136
+ )
137
+ return True
138
+ except subprocess.CalledProcessError as e:
139
+ if e.returncode == 255: # SSH protocol errors
140
+ handle_ssh_subprocess_error(e, connection)
141
+ else:
142
+ raise RemoteConnectionException(
143
+ message=f"SSH connection test failed: {e.stderr}",
144
+ status=ConnectionTestStates.FAILED
145
+ )
146
+ return False
147
+ except subprocess.TimeoutExpired:
148
+ raise RemoteConnectionException(
149
+ message="SSH connection test timed out",
150
+ status=ConnectionTestStates.FAILED
151
+ )
152
+ return False
153
+
75
154
  logger = logging.getLogger(__name__)
76
155
 
77
156
  api = Blueprint("api", __name__)
@@ -648,6 +727,22 @@ def get_npe_manifest(instance: Instance):
648
727
  return jsonify(content)
649
728
 
650
729
 
730
+ @api.route("/performance/npe/timeline", methods=["GET"])
731
+ @with_instance
732
+ def get_npe_timeline(instance: Instance):
733
+ if not instance.performance_path:
734
+ return Response(status=HTTPStatus.NOT_FOUND)
735
+
736
+ filename = request.args.get("filename", default=None)
737
+
738
+ try:
739
+ content = NPEQueries.get_npe_timeline(instance, filename=filename)
740
+ except FileNotFoundError:
741
+ return jsonify({})
742
+
743
+ return jsonify(content)
744
+
745
+
651
746
  @api.route("/performance/device-log/zone/<zone>", methods=["GET"])
652
747
  @with_instance
653
748
  def get_zone_statistics(zone, instance: Instance):
@@ -912,7 +1007,7 @@ def test_remote_folder():
912
1007
 
913
1008
  # Test SSH Connection
914
1009
  try:
915
- get_client(connection)
1010
+ test_ssh_connection(connection)
916
1011
  add_status(ConnectionTestStates.OK.value, "SSH connection established")
917
1012
  except RemoteConnectionException as e:
918
1013
  add_status(ConnectionTestStates.FAILED.value, e.message)
@@ -109,7 +109,6 @@ The following separate and independent dependencies are utilized by this project
109
109
  - Flask – BSD‑3‑Clause – https://github.com/pallets/flask/blob/main/LICENSE.txt
110
110
  - gunicorn – MIT – https://github.com/benoitc/gunicorn/blob/master/LICENSE
111
111
  - uvicorn – BSD‑3‑Clause – https://github.com/encode/uvicorn/blob/master/LICENSE.md
112
- - paramiko~=3.4.0 – LGPL‑2.1+ – https://github.com/paramiko/paramiko/blob/main/LICENSE
113
112
  - flask_cors – MIT – https://github.com/corydolphin/flask-cors/blob/main/LICENSE
114
113
  - pydantic – MIT – https://github.com/pydantic/pydantic-settings/blob/main/LICENSE
115
114
  - setuptools – MIT – https://github.com/pypa/setuptools/blob/main/LICENSE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ttnn_visualizer
3
- Version: 0.41.0
3
+ Version: 0.42.0
4
4
  Summary: TT-NN Visualizer
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -16,7 +16,6 @@ Requires-Dist: Flask-SocketIO==5.4.1
16
16
  Requires-Dist: Flask-SQLAlchemy==3.1.1
17
17
  Requires-Dist: pandas==2.2.3
18
18
  Requires-Dist: gunicorn~=22.0.0
19
- Requires-Dist: paramiko~=3.4.0
20
19
  Requires-Dist: pydantic==2.7.3
21
20
  Requires-Dist: pydantic-core==2.18.4
22
21
  Requires-Dist: setuptools==65.5.0
@@ -24,7 +23,7 @@ Requires-Dist: gevent===24.10.2
24
23
  Requires-Dist: python-dotenv==1.0.1
25
24
  Requires-Dist: sqlalchemy==2.0.34
26
25
  Requires-Dist: PyYAML==6.0.2
27
- Requires-Dist: tt-perf-report==1.0.6
26
+ Requires-Dist: tt-perf-report==1.0.7
28
27
  Requires-Dist: zstd==1.5.7.0
29
28
 
30
29
 
@@ -1,46 +1,45 @@
1
1
  ttnn_visualizer/__init__.py,sha256=tVr9rTN1gXorz5XQQYmcRkzeEYjDGAKez1gP0oOIYeY,94
2
2
  ttnn_visualizer/app.py,sha256=QCgd3nyc_I6iwJEAXGmNCvIXvR6oLSGKmLyV_7ECsH8,7222
3
- ttnn_visualizer/csv_queries.py,sha256=zoTPT4sqxBOXgdlrcGkz6epc1dqAaWPsxcyJP3wJcCs,22955
4
- ttnn_visualizer/decorators.py,sha256=YthndmmOfNlCMyjhChA_Lztcx1npFk7g0u4-7XT7Gnc,4571
3
+ ttnn_visualizer/csv_queries.py,sha256=WzY1D_2fzrRzgdLnXf5w_aBwsW695Mkq5h2GXGeshyM,25676
4
+ ttnn_visualizer/decorators.py,sha256=kveD2h6cgXD-rt03uAYWmZ9RKYphgvoGk53g2Pgn_RM,4799
5
5
  ttnn_visualizer/enums.py,sha256=SEIqp1tlc_zw2vQ8nHH9YTaV0m3Cb8fjn_goqz5wurE,203
6
- ttnn_visualizer/exceptions.py,sha256=iwDejaYUkRwHhpv8bc16X_2ax-2QmFqgaajbHvQImN0,1055
6
+ ttnn_visualizer/exceptions.py,sha256=wzD_RuW1L6E2WdfM1pf34e25gisIe9jIMatf3HgyhIA,1375
7
7
  ttnn_visualizer/extensions.py,sha256=AW2aujcvayePfrwovVcsXIUkCFe_k2T4Vk3OblZd4Bk,380
8
8
  ttnn_visualizer/file_uploads.py,sha256=aXolaGeisY3H89y_GxfciPvkSU1XmcvWqpf8vDqKrgM,5011
9
9
  ttnn_visualizer/instances.py,sha256=99L80yT0BXyhEkBbGFC_tNAg8LlGWUqWQrhkK-4-Yxc,11261
10
10
  ttnn_visualizer/models.py,sha256=ut9V9fTzbbjk_WNKKP8ILrVXYnIv7qQaDvNOEEX3BT8,7778
11
- ttnn_visualizer/queries.py,sha256=KC4WtI6Pf3LL_LqHXQTC5zVd-HvjJLWY4Stndd0z0qs,13681
12
- ttnn_visualizer/remote_sqlite_setup.py,sha256=FXZTT4_-7zPG6rooXJ7o3a12r86hRzKt87qfUiKdMcs,3269
13
- ttnn_visualizer/requirements.txt,sha256=AoRskxD-FUPk1j_xruPxweDgKb3RjFv8lCQNqpf6Rrs,370
11
+ ttnn_visualizer/queries.py,sha256=MB57Ci3gyXn9jdXdR6KaMBHoHYt5jgdk3wP7jSDGWFA,9891
12
+ ttnn_visualizer/remote_sqlite_setup.py,sha256=0JfSQLA_ST6gh7lyHhxUdsEsmw8moremC37myES6Al8,5892
13
+ ttnn_visualizer/requirements.txt,sha256=RuHRxxkHKSAAMF7JbvifDMOOFTfQH19AW3qJFL3I69M,339
14
14
  ttnn_visualizer/serializers.py,sha256=7kjtxp3NEpTlBKUNsxdtUcOM23pAyIt5MR-XTl85bVw,8028
15
15
  ttnn_visualizer/settings.py,sha256=r7UdqFuXEVg0qw1F2_zRFM_687PctqpgoUEKjvTVhwU,4578
16
- ttnn_visualizer/sftp_operations.py,sha256=Sn13vkkQoRifCO3SCNGo6ORlwaHB4MO4uSxxo_7m8Dk,17906
16
+ ttnn_visualizer/sftp_operations.py,sha256=p06_dYHoM-qbAZuUUR9CN7fWy4U_QpIaXBxPsjxJfgI,32074
17
17
  ttnn_visualizer/sockets.py,sha256=GAvReqKaT0YQyeb0kf7ndtL1odLldHCpbbKkr0INXRM,3624
18
- ttnn_visualizer/ssh_client.py,sha256=dTkxzAFCGKyiZL1_LHx23Gn4y0oW4yKfGKjTxuqcx8I,2679
19
18
  ttnn_visualizer/utils.py,sha256=9Ier5XBD4vHzvtIxfbIRnsP1Is03J2usUmhfB5gSqqc,6186
20
- ttnn_visualizer/views.py,sha256=KLypbTrgkCW-dlW6-v4O6eO7lDo6ryobZ9AkDvw0kjU,41790
19
+ ttnn_visualizer/views.py,sha256=WywffIzF5ahslw6qBDHyfL5kIzDKxbUf_yQXxeMUVgA,44784
21
20
  ttnn_visualizer/bin/docker-entrypoint-web,sha256=uuv6aubpMCfOcuvDBxwBDITE8PN39teuwyJ2zA5KWuw,413
22
21
  ttnn_visualizer/bin/pip3-install,sha256=nbSRT4GfJQIQ9KTNO3j-6b5WM4lrx9XA4GBlAURRMws,502
23
- ttnn_visualizer/static/index.html,sha256=MluQqpMFiWIeZZkmmB_4qxyRF5wSYBmSz3mBmjUNRuA,1135
24
- ttnn_visualizer/static/assets/allPaths-4_pFqSAW.js,sha256=zZjbLQz-Za-yCxzBEp-Kft7qWr61bB5XuaJpXN14-qM,309
25
- ttnn_visualizer/static/assets/allPathsLoader-CpLPTLlt.js,sha256=Y1dM0b_Tk2ZaIcGwye3wj4d-G8mBM4qHMwEP4P2VQdM,550
22
+ ttnn_visualizer/static/index.html,sha256=gM7cQnEuPT29ZMKqJcEqjW9POf4AigZftNS8uw68ViY,1135
23
+ ttnn_visualizer/static/assets/allPaths-wwXsGKJ2.js,sha256=CxiByiF9vD2BvhrONDu-MQVoLKj-KhOcYm1XBZdSXAw,309
24
+ ttnn_visualizer/static/assets/allPathsLoader-BK9jqlVe.js,sha256=Bc4W_X5zso_Yf-8g1aZBA9wg0NfKXGjD64lDJWqAyzE,550
26
25
  ttnn_visualizer/static/assets/index-BKzgFDAn.js,sha256=Pu504I3PJMoop2k0KQxrd1qKuS3zR5KtvTTdz9IhfeY,283793
27
26
  ttnn_visualizer/static/assets/index-BvSuWPlB.js,sha256=43E6se9tLaZkX7kCG4waDG_vyL4yED6SEaDKLt3Nw_s,292382
28
27
  ttnn_visualizer/static/assets/index-C1rJBrMl.css,sha256=f1PX54ZLwJIs0PqePOu-B3YP3joUxhrkkZ1Butqil9M,623666
29
- ttnn_visualizer/static/assets/index-DFVwehlj.js,sha256=g0Ub3DCVUQ2e3kzq_i15x3nEk_wOC1OFpNDB9OreuCs,7785076
28
+ ttnn_visualizer/static/assets/index-Ybr1HJxx.js,sha256=fSByVEefEo8y-kGOi8sS1n6_thxMXcapSWptHAd3z3Y,7785792
30
29
  ttnn_visualizer/static/assets/site-BTBrvHC5.webmanifest,sha256=Uy_XmnGuYFVf-OZuma2NvgEPdrCrevb3HZvaxSIHoA0,456
31
- ttnn_visualizer/static/assets/splitPathsBySizeLoader-D-RvsTqO.js,sha256=_wi4njEV-DPGOQVuKGxWgusQEMkREePV7M8NmDZ2TNc,472
30
+ ttnn_visualizer/static/assets/splitPathsBySizeLoader-CauQGZHk.js,sha256=431X0PrDog4lr5A_hi5ObsXMZe6KpjgIDgTc65IwZWM,472
32
31
  ttnn_visualizer/static/favicon/android-chrome-192x192.png,sha256=BZWA09Zxaa3fXbaeS6nhWo2e-DUSjm9ElzNQ_xTB5XU,6220
33
32
  ttnn_visualizer/static/favicon/android-chrome-512x512.png,sha256=HBiJSZyguB3o8fMJuqIGcpeBy_9JOdImme3wD02UYCw,62626
34
33
  ttnn_visualizer/static/favicon/favicon-32x32.png,sha256=Zw201qUsczQv1UvoQvJf5smQ2ss10xaTeWxmQNYCGtY,480
35
34
  ttnn_visualizer/static/favicon/favicon.svg,sha256=wDPY3VrekJ_DE1TnJ2vUy602K3S4Xe9TgrdZ7jXK9c8,633
36
35
  ttnn_visualizer/static/sample-data/cluster-desc.yaml,sha256=LMxOmsRUXtVVU5ogzYkXUozB3dg2IzqIRJQpV_O5qMU,29618
37
36
  ttnn_visualizer/tests/__init__.py,sha256=tVr9rTN1gXorz5XQQYmcRkzeEYjDGAKez1gP0oOIYeY,94
38
- ttnn_visualizer/tests/test_queries.py,sha256=74JgzuA3Kj1DPjLexI-YCOl8sgMi9L1I1wkNdzeHih8,16602
37
+ ttnn_visualizer/tests/test_queries.py,sha256=QLfV_MWnM68WOWKKa1TPzn5aKQJbysQrGPIJ5aoxl7k,13882
39
38
  ttnn_visualizer/tests/test_serializers.py,sha256=XyS5f8JK234CtizslrQ49hhxaz7RmhkEbVTaxOvdSd4,18490
40
- ttnn_visualizer-0.41.0.dist-info/LICENSE,sha256=hcHiC16In8fLrs56SK2KzaBvs1OwBfVMJBxi7Rc2w0g,14899
41
- ttnn_visualizer-0.41.0.dist-info/LICENSE_understanding.txt,sha256=pymi-yb_RvYM9p2ZA4iSNsImcvhDBBxlGuJCY9dTq7M,233
42
- ttnn_visualizer-0.41.0.dist-info/METADATA,sha256=LeTsyqk6ByO0zDD2QL14ujQFxQf-r58QtFaX1dshLOU,7393
43
- ttnn_visualizer-0.41.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
44
- ttnn_visualizer-0.41.0.dist-info/entry_points.txt,sha256=QpuUpkmQ_mEHJTMqOBdU0MH2Z4WF_9iFsGACeyyAO1E,61
45
- ttnn_visualizer-0.41.0.dist-info/top_level.txt,sha256=M1EGkvDOuIfbhDbcUdz2-TSdmCtDoQ2Uyag9k5JLDSY,16
46
- ttnn_visualizer-0.41.0.dist-info/RECORD,,
39
+ ttnn_visualizer-0.42.0.dist-info/LICENSE,sha256=-Y7CZK1-MxZZ7l0Rb414S0SZ9tdCby-bMGl_LmFFicM,14806
40
+ ttnn_visualizer-0.42.0.dist-info/LICENSE_understanding.txt,sha256=pymi-yb_RvYM9p2ZA4iSNsImcvhDBBxlGuJCY9dTq7M,233
41
+ ttnn_visualizer-0.42.0.dist-info/METADATA,sha256=PpiZyDS_Vxcen6rGwhwgx1YFrfmCb35x7Jn_mxijYgU,7362
42
+ ttnn_visualizer-0.42.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
43
+ ttnn_visualizer-0.42.0.dist-info/entry_points.txt,sha256=QpuUpkmQ_mEHJTMqOBdU0MH2Z4WF_9iFsGACeyyAO1E,61
44
+ ttnn_visualizer-0.42.0.dist-info/top_level.txt,sha256=M1EGkvDOuIfbhDbcUdz2-TSdmCtDoQ2Uyag9k5JLDSY,16
45
+ ttnn_visualizer-0.42.0.dist-info/RECORD,,
@@ -1,85 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
2
- #
3
- # SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
4
-
5
- import paramiko
6
- import os
7
- from pathlib import Path
8
- from paramiko.agent import Agent
9
- from paramiko.ssh_exception import SSHException
10
-
11
- from ttnn_visualizer.decorators import remote_exception_handler
12
- from ttnn_visualizer.models import RemoteConnection
13
- import logging
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- def initialize_ssh_client():
19
- ssh = paramiko.SSHClient()
20
- ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
21
- ssh.load_system_host_keys()
22
- return ssh
23
-
24
-
25
- def get_connection_args(remote_connection: RemoteConnection) -> dict:
26
- use_agent = os.getenv("USE_SSH_AGENT", "true").lower() == "true"
27
- ssh_config_path = Path(os.getenv("SSH_CONFIG_PATH", "~/.ssh/config")).expanduser()
28
-
29
- if use_agent:
30
- agent = Agent()
31
- keys = agent.get_keys()
32
- if not keys:
33
- logger.error("No keys found in agent")
34
- raise SSHException("No keys found")
35
- return {"look_for_keys": True}
36
-
37
- config = paramiko.SSHConfig.from_path(ssh_config_path).lookup(
38
- remote_connection.host
39
- )
40
- if not config:
41
- raise SSHException(f"Host not found in SSH config {remote_connection.host}")
42
-
43
- return {"key_filename": config["identityfile"].pop(), "look_for_keys": False} # type: ignore
44
-
45
-
46
- @remote_exception_handler
47
- def get_client(remote_connection: RemoteConnection) -> paramiko.SSHClient:
48
- ssh = initialize_ssh_client()
49
- connection_args = get_connection_args(remote_connection)
50
-
51
- ssh.connect(
52
- remote_connection.host,
53
- port=remote_connection.port,
54
- username=remote_connection.username,
55
- **connection_args,
56
- )
57
- return ssh
58
-
59
-
60
- def check_permissions(client, directory):
61
- """Check if write and delete permissions are available in the remote directory."""
62
- test_file = Path(directory) / "test_permission_file.txt"
63
- touch_command = f"touch {test_file}"
64
- remove_command = f"rm {test_file}"
65
-
66
- stdin, stdout, stderr = client.exec_command(touch_command)
67
- error = stderr.read().decode().strip()
68
- if error:
69
- raise Exception(f"No permission to create files in {directory}: {error}")
70
-
71
- stdin, stdout, stderr = client.exec_command(remove_command)
72
- error = stderr.read().decode().strip()
73
- if error:
74
- raise Exception(f"No permission to delete files in {directory}: {error}")
75
-
76
- return True
77
-
78
-
79
- def check_gzip_exists(client):
80
- """Check if gzip and tar exist on the remote server."""
81
- stdin, stdout, stderr = client.exec_command("which gzip && which tar")
82
- result = stdout.read().decode().strip()
83
- if not result:
84
- return False
85
- return True