Coverage for nova/filesystem.py: 90%

44 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-24 11:16 +0000

1# Licensed under the Apache License, Version 2.0 (the "License"); you may 

2# not use this file except in compliance with the License. You may obtain 

3# a copy of the License at 

4# 

5# http://www.apache.org/licenses/LICENSE-2.0 

6# 

7# Unless required by applicable law or agreed to in writing, software 

8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

10# License for the specific language governing permissions and limitations 

11# under the License. 

12 

13"""Functions to address filesystem calls, particularly sysfs.""" 

14 

15import functools 

16import os 

17import time 

18 

19from oslo_log import log as logging 

20 

21from nova import exception 

22 

23LOG = logging.getLogger(__name__) 

24SYS = '/sys' 

25RETRY_LIMIT = 5 

26 

27 

28# a retry decorator to handle EBUSY 

29def retry_if_busy(func): 

30 """Decorator to retry a function if it raises DeviceBusy. 

31 

32 This decorator will retry the function RETRY_LIMIT=5 times if it raises 

33 DeviceBusy. It will sleep for 1 second on the first retry, 2 seconds on 

34 the second retry, and so on, up to RETRY_LIMIT seconds. If the function 

35 still raises DeviceBusy after RETRY_LIMIT retries, the exception will be 

36 raised. 

37 """ 

38 

39 @functools.wraps(func) 

40 def wrapper(*args, **kwargs): 

41 for i in range(RETRY_LIMIT): 41 ↛ exitline 41 didn't return from function 'wrapper' because the loop on line 41 didn't complete

42 try: 

43 return func(*args, **kwargs) 

44 except exception.DeviceBusy as e: 

45 # if we have retried RETRY_LIMIT times, raise the exception 

46 # otherwise, sleep and retry, i is 0-based so we need 

47 # to add 1 to it 

48 count = i + 1 

49 if count < RETRY_LIMIT: 

50 LOG.debug( 

51 f"File {e.kwargs['file_path']} is busy, " 

52 f"sleeping {count} seconds before retrying") 

53 time.sleep(count) 

54 continue 

55 raise 

56 return wrapper 

57 

58# NOTE(bauzas): this method is deliberately not wrapped in a privsep entrypoint 

59 

60 

61@retry_if_busy 

62def read_sys(path: str) -> str: 

63 """Reads the content of a file in the sys filesystem. 

64 

65 :param path: relative or absolute. If relative, will be prefixed by /sys. 

66 :returns: contents of that file. 

67 :raises: nova.exception.FileNotFound if we can't read that file. 

68 :raises: nova.exception.DeviceBusy if the file is busy. 

69 """ 

70 try: 

71 # The path can be absolute with a /sys prefix but that's fine. 

72 with open(os.path.join(SYS, path), mode='r') as data: 

73 return data.read() 

74 except OSError as exc: 

75 # errno 16 is EBUSY, which means the file is busy. 

76 if exc.errno == 16: 

77 raise exception.DeviceBusy(file_path=path) from exc 

78 # convert permission denied to file not found 

79 raise exception.FileNotFound(file_path=path) from exc 

80 except ValueError as exc: 

81 raise exception.FileNotFound(file_path=path) from exc 

82 

83 

84# NOTE(bauzas): this method is deliberately not wrapped in a privsep entrypoint 

85# In order to correctly use it, you need to decorate the caller with a specific 

86# privsep entrypoint. 

87@retry_if_busy 

88def write_sys(path: str, data: str) -> None: 

89 """Writes the content of a file in the sys filesystem with data. 

90 

91 :param path: relative or absolute. If relative, will be prefixed by /sys. 

92 :param data: the data to write. 

93 :returns: contents of that file. 

94 :raises: nova.exception.FileNotFound if we can't write that file. 

95 :raises: nova.exception.DeviceBusy if the file is busy. 

96 """ 

97 try: 

98 # The path can be absolute with a /sys prefix but that's fine. 

99 with open(os.path.join(SYS, path), mode='w') as fd: 

100 fd.write(data) 

101 except OSError as exc: 

102 # errno 16 is EBUSY, which means the file is busy. 

103 if exc.errno == 16: 

104 raise exception.DeviceBusy(file_path=path) from exc 

105 # convert permission denied to file not found 

106 raise exception.FileNotFound(file_path=path) from exc 

107 except ValueError as exc: 

108 raise exception.FileNotFound(file_path=path) from exc