Collect disk information along with other info#3621
Collect disk information along with other info#3621parthaa wants to merge 3 commits intocandlepin:mainfrom
Conversation
Reviewer's GuideThis PR introduces a new DiskCollector that scans /sys/block for real block devices, reads and converts their sector counts into size_bytes facts, integrates this collector into the main facts pipeline, and adds comprehensive unit tests to validate its behavior. Entity relationship diagram for disk factserDiagram
DISK {
string device_name
int size_bytes
}
DISK ||--o| FACTS : provides
FACTS {
string fact_name
int fact_value
}
Class diagram for the new DiskCollector and its integrationclassDiagram
class collector.FactsCollector {
<<abstract>>
+arch: str
+prefix: str
+testing: bool
+collected_hw_info: Dict
}
class DiskCollector {
+hardware_methods: List[Callable]
+_get_block_devices() List[str]
+_get_device_size_bytes(device: str) int
+get_disk_size_info() Dict[str, Union[str, int]]
}
DiskCollector --|> collector.FactsCollector
class all.AllFactsCollector {
+collectors: List[collector.FactsCollector]
}
all.AllFactsCollector o-- DiskCollector
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
chris1984
left a comment
There was a problem hiding this comment.
Tested and works ok:
[root@cappreview facts]# subscription-manager facts | grep disk
disk.vda.size_bytes: 107374182400There was a problem hiding this comment.
Hey there - I've reviewed your changes - here's some feedback:
- Use os.path.join for constructing filesystem paths rather than string concatenation to improve portability and readability.
- The block device regex currently only matches sd*/vd*/nvme*/hd*/xvd*; consider extending it (and adding tests) to include other valid device types like mmcblk*.
- Defaulting prefix to None can lead to errors when concatenating paths; consider defaulting prefix to an empty string or validating it before use.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Use os.path.join for constructing filesystem paths rather than string concatenation to improve portability and readability.
- The block device regex currently only matches sd*/vd*/nvme*/hd*/xvd*; consider extending it (and adding tests) to include other valid device types like mmcblk*.
- Defaulting prefix to None can lead to errors when concatenating paths; consider defaulting prefix to an empty string or validating it before use.
## Individual Comments
### Comment 1
<location> `test/rhsmlib/facts/test_disk.py:70-73` </location>
<code_context>
+ expected = ['hda', 'nvme0n1', 'sda', 'vda', 'xvda'] # sorted
+ self.assertEqual(result, expected)
+
+ def test_get_device_size_bytes_missing_file(self):
+ """Test _get_device_size_bytes with missing size file"""
+ result = self.collector._get_device_size_bytes('nonexistent')
+ self.assertEqual(result, 0)
+
+ def test_get_device_size_bytes_invalid_content(self):
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding a test for permission errors when reading the size file.
Testing permission errors would help ensure _get_device_size_bytes handles all relevant file access issues robustly.
</issue_to_address>
### Comment 2
<location> `test/rhsmlib/facts/test_disk.py:148-157` </location>
<code_context>
+ self.assertEqual(result, {'disk.sda.size_bytes': 1000000000000})
+ mock_method.assert_called_once()
+
+ def test_collect(self):
+ """Test collect method returns FactsCollection"""
+ with patch.object(self.collector, 'get_all') as mock_get_all:
+ mock_get_all.return_value = {'disk.sda.size_bytes': 1000000000000}
+
+ result = self.collector.collect()
+
+ # Check that it returns a FactsCollection object
+ from rhsmlib.facts.collection import FactsCollection
+ self.assertIsInstance(result, FactsCollection)
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** Consider testing collect() with an empty result.
Add a test where get_all returns an empty dictionary to verify that collect() correctly returns a FactsCollection and handles empty input.
```suggestion
def test_collect(self):
"""Test collect method returns FactsCollection"""
with patch.object(self.collector, 'get_all') as mock_get_all:
mock_get_all.return_value = {'disk.sda.size_bytes': 1000000000000}
result = self.collector.collect()
# Check that it returns a FactsCollection object
from rhsmlib.facts.collection import FactsCollection
self.assertIsInstance(result, FactsCollection)
def test_collect_empty(self):
"""Test collect method returns FactsCollection for empty input"""
with patch.object(self.collector, 'get_all') as mock_get_all:
mock_get_all.return_value = {}
result = self.collector.collect()
from rhsmlib.facts.collection import FactsCollection
self.assertIsInstance(result, FactsCollection)
# Optionally, check that the FactsCollection is empty
self.assertEqual(len(result), 0)
```
</issue_to_address>
### Comment 3
<location> `src/rhsmlib/facts/disk.py:40` </location>
<code_context>
def _get_block_devices(self) -> List[str]:
"""Get list of block devices from /sys/block/"""
block_devices: List[str] = []
sys_block_path: str = self.prefix + "/sys/block"
try:
if os.path.exists(sys_block_path):
for device in os.listdir(sys_block_path):
# Skip loop devices, ram devices, and other virtual devices
# Focus on actual disk devices (sd*, vd*, nvme*, hd*, xvd*)
if re.match(r'^(sd[a-z]+|vd[a-z]+|nvme[0-9]+n[0-9]+|hd[a-z]+|xvd[a-z]+)$', device):
block_devices.append(device)
except OSError as e:
log.debug(f"Could not read /sys/block directory: {e}")
return sorted(block_devices)
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use f-string instead of string concatenation ([`use-fstring-for-concatenation`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-fstring-for-concatenation/))
```suggestion
sys_block_path: str = f"{self.prefix}/sys/block"
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@Aftermath Can this get a review or merge? |
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Developer should review, I will leave to @jirihnidek. |
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
m-horky
left a comment
There was a problem hiding this comment.
I have some requests. Please note that addressing them doesn't mean this PR will get merged, we need to pass this through product. For transparency, this PR seems to address RHEL-124924/SAT-27982.
Trees under /sys/block/ seem to have SELinux type of sysfs_t, so we should be good with existing SELinux policy. This will definitely make it easier to get merged and released.
| @@ -0,0 +1,80 @@ | |||
| # Copyright (c) 2023 Red Hat, Inc. | |||
| if size_bytes > 0: | ||
| result[f"disk.{device}.size_bytes"] = size_bytes | ||
|
|
||
| return result No newline at end of file |
| @@ -0,0 +1,173 @@ | |||
| # Copyright (c) 2023 Red Hat, Inc. | |||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() No newline at end of file |
| # The size file contains the number of 512-byte sectors | ||
| sectors = int(f.read().strip()) | ||
| return sectors * 512 |
There was a problem hiding this comment.
512 might be wrong. You should check the content of /sys/block/*/queue/logical_block_size instead.
adds disk..size_bytes fact along with other facts. This is useful for customers.
# subscription-manager facts | grep disk disk.vda.size_bytes: 1500000 disks.sda.size_bytes: 1700000