1#!/usr/bin/env python3

2#

3# This file is part of Checkbox.

4#

5# Copyright (C) 2010-2013 by Cloud Computing Center for Mobile Applications

6# Industrial Technology Research Institute

7# Copyright 2024 Canonical Ltd.

8#

9# Authors:

10# Nelson Chu <Nelson.Chu@itri.org.tw>

11# Jeff Lane <jeff@ubuntu.com>

12# Sylvain Pineau <sylvain.pineau@canonical.com>

13# Massimiliano Girardi <massimiliano.girardi@canonical.com>

14#

15# Checkbox is free software: you can redistribute it and/or modify

16# it under the terms of the GNU General Public License version 3,

17# as published by the Free Software Foundation.

18#

19# Checkbox is distributed in the hope that it will be useful,

20# but WITHOUT ANY WARRANTY; without even the implied warranty of

21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

22# GNU General Public License for more details.

23#

24# You should have received a copy of the GNU General Public License

25# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.

26"""

27 disk_resource creates a resource per disk from the output of lsblk.

28- name: column KNAME, internal kernel name

29- path: column PATH, path to the device node

30- model: column MODEL, model identifier

31- size: column SIZE, size of the disk in bytes

32- rotational: column ROTA, True for rotational drives like hard disks

33"""

34

35 import json

36 from subprocess import check_output, CalledProcessError

37

38

39 def get_blockdevices_info() -> dict:

40 try:

41 lsblk_out_text = check_output(

42 [

43 "lsblk",

44 "--json",

45 "--bytes",

46 "--ascii",

47 "--noheadings",

48 "--output",

49 "KNAME,PATH,TYPE,SIZE,MODEL,ROTA,MOUNTPOINT",

50 ],

51 universal_newlines=True,

52 )

53 except CalledProcessError as e:

54 raise SystemExit(str(e))

55 lsblk_out = json.loads(lsblk_out_text)

56 return lsblk_out["blockdevices"]

57

58

59 def get_relevant_block_devices(block_devices: list) -> list:

60 """

61 Filters out every block device that we don't usually consider a disk

62 like, for example, loopback devices

63 """

64

65 def include(block_device):

66 if block_device["type"] not in ("disk", "crypt"):

67 return False

68 is_mmcblk = block_device["kname"].startswith("mmcblk")

69 whitelisted_mountpoints = {

70 "/",

71 "/writable",

72 "/hostfs",

73 "/ubuntu-seed",

74 "/ubuntu-boot",

75 "/ubuntu-save",

76 "/data",

77 "/boot",

78 }

79 if is_mmcblk and block_device["mountpoint"] in whitelisted_mountpoints:

80 return False

81 if "snapd/save" in (block_device.get("mountpoint") or ""):

82 return False

83 return True

84

85 return filter(include, block_devices)

86

87

88 def print_as_resource(block_device):

89 model = block_device.get("model", "Unknown")

90 print("name:", block_device["kname"])

91 print("path:", block_device["path"])

92 print("model:", model)

93 print("size:", block_device["size"])

94 print("rotational:", block_device["rota"])

95 print()

96

97

98 def main():

99 """

100 Uses lsblk to gather information about disks seen by the OS.

101 """

102 block_devices = get_blockdevices_info()

103 relevant_block_devices = get_relevant_block_devices(block_devices)

104 for block_device in relevant_block_devices:

105 print_as_resource(block_device)

106

107

108 if __name__ == "__main__":

109 main()