From 13e8dde7933b65a3ee5e6e1903ba14a102658d81 Mon Sep 17 00:00:00 2001 From: isabelmsft Date: Mon, 29 Jul 2024 21:16:29 +0000 Subject: [PATCH 1/2] add dbus file stat --- host_modules/file_service.py | 45 ++++++++++++++++++++++ scripts/sonic-host-server | 5 ++- tests/host_modules/file_stat_test.py | 57 ++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 host_modules/file_service.py create mode 100644 tests/host_modules/file_stat_test.py diff --git a/host_modules/file_service.py b/host_modules/file_service.py new file mode 100644 index 00000000..7af4d4d2 --- /dev/null +++ b/host_modules/file_service.py @@ -0,0 +1,45 @@ +"""File stat handler""" + +from host_modules import host_service +import subprocess + +MOD_NAME = 'file' +EXIT_FAILURE = 1 + +import os + +class FileService(host_service.HostModule): + """ + Dbus endpoint that executes the file command + """ + @host_service.method(host_service.bus_name(MOD_NAME), in_signature='s', out_signature='ia{ss}') + def get_file_stat(self, path): + if not path: + return EXIT_FAILURE, {'error': 'Dbus get_file_stat called with no path specified'} + + try: + file_stat = os.stat(path) + + # Get last modified time in nanoseconds since epoch + last_modified = int(file_stat.st_mtime * 1e9) # Convert seconds to nanoseconds + + # Get permissions in octal format + permissions = oct(file_stat.st_mode)[-3:] + + # Get file size in bytes + size = file_stat.st_size + + # Get current umask + current_umask = os.umask(0) + os.umask(current_umask) # Reset umask to previous value + + return 0, { + 'path': path, + 'last_modified': str(last_modified), # Converting to string to maintain consistency + 'permissions': permissions, + 'size': str(size), # Converting to string to maintain consistency + 'umask': oct(current_umask)[-3:] + } + + except Exception as e: + return EXIT_FAILURE, {'error': str(e)} \ No newline at end of file diff --git a/scripts/sonic-host-server b/scripts/sonic-host-server index da533a84..53f7b038 100755 --- a/scripts/sonic-host-server +++ b/scripts/sonic-host-server @@ -12,7 +12,7 @@ import dbus.service import dbus.mainloop.glib from gi.repository import GObject -from host_modules import config_engine, gcu, host_service, showtech, systemd_service +from host_modules import config_engine, gcu, host_service, showtech, systemd_service, file_stat def register_dbus(): @@ -22,7 +22,8 @@ def register_dbus(): 'gcu': gcu.GCU('gcu'), 'host_service': host_service.HostService('host_service'), 'showtech': showtech.Showtech('showtech'), - 'systemd': systemd_service.SystemdService('systemd') + 'systemd': systemd_service.SystemdService('systemd'), + 'file_stat': file_stat.FileService('file') } for mod_name, handler_class in mod_dict.items(): handlers[mod_name] = handler_class diff --git a/tests/host_modules/file_stat_test.py b/tests/host_modules/file_stat_test.py new file mode 100644 index 00000000..32609486 --- /dev/null +++ b/tests/host_modules/file_stat_test.py @@ -0,0 +1,57 @@ +import sys +import os +import pytest +from unittest import mock +from host_modules import file_service + +class TestFileService(object): + @mock.patch("dbus.SystemBus") + @mock.patch("dbus.service.BusName") + @mock.patch("dbus.service.Object.__init__") + @mock.patch("os.stat") + @mock.patch("os.umask") + def test_get_file_stat_valid(self, mock_umask, mock_stat, MockInit, MockBusName, MockSystemBus): + mock_stat_result = mock.Mock() + mock_stat_result.st_mtime = 1609459200.0 # 2021-01-01 00:00:00 in nanoseconds + mock_stat_result.st_mode = 0o100644 # Regular file with permissions + mock_stat_result.st_size = 1024 + mock_stat.return_value = mock_stat_result + + mock_umask.return_value = 0o022 # Default umask + + file_service_stub = file_service.FileService(file_service.MOD_NAME) + path = "/valid/path" + ret, msg = file_service_stub.get_file_stat(path) + + assert ret == 0 + assert msg['path'] == path + assert msg['last_modified'] == "1609459200000000000" + assert msg['permissions'] == "644" + assert msg['size'] == "1024" + assert msg['umask'] == "o22" + + @mock.patch("dbus.SystemBus") + @mock.patch("dbus.service.BusName") + @mock.patch("dbus.service.Object.__init__") + @mock.patch("os.stat") + def test_get_file_stat_invalid_path(self, mock_stat, MockInit, MockBusName, MockSystemBus): + mock_stat.side_effect = FileNotFoundError("[Errno 2] No such file or directory") + + file_service_stub = file_service.FileService(file_service.MOD_NAME) + path = "/invalid/path" + ret, msg = file_service_stub.get_file_stat(path) + + assert ret == 1 + assert 'error' in msg + assert "No such file or directory" in msg['error'] + + @mock.patch("dbus.SystemBus") + @mock.patch("dbus.service.BusName") + @mock.patch("dbus.service.Object.__init__") + def test_get_file_stat_empty_path(self, MockInit, MockBusName, MockSystemBus): + file_service_stub = file_service.FileService(file_service.MOD_NAME) + path = "" + ret, msg = file_service_stub.get_file_stat(path) + + assert ret == 1 + assert "Dbus get_file_stat called with no path specified" in msg['error'] \ No newline at end of file From 12fd0915462f5c1160dedf4c50bc46225ba2407d Mon Sep 17 00:00:00 2001 From: isabelmsft Date: Mon, 29 Jul 2024 21:48:47 +0000 Subject: [PATCH 2/2] update --- scripts/sonic-host-server | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/sonic-host-server b/scripts/sonic-host-server index 53f7b038..7028f28a 100755 --- a/scripts/sonic-host-server +++ b/scripts/sonic-host-server @@ -12,7 +12,7 @@ import dbus.service import dbus.mainloop.glib from gi.repository import GObject -from host_modules import config_engine, gcu, host_service, showtech, systemd_service, file_stat +from host_modules import config_engine, gcu, host_service, showtech, systemd_service, file_service def register_dbus(): @@ -23,7 +23,7 @@ def register_dbus(): 'host_service': host_service.HostService('host_service'), 'showtech': showtech.Showtech('showtech'), 'systemd': systemd_service.SystemdService('systemd'), - 'file_stat': file_stat.FileService('file') + 'file_stat': file_service.FileService('file') } for mod_name, handler_class in mod_dict.items(): handlers[mod_name] = handler_class