1#!/usr/bin/env python3

2# encoding: UTF-8

3# Copyright (c) 2018 Canonical Ltd.

4#

5# Authors:

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

7#

8# This program is free software: you can redistribute it and/or modify

9# it under the terms of the GNU General Public License as published by

10# the Free Software Foundation, either version 3 of the License, or

11# (at your option) any later version.

12#

13# This program is distributed in the hope that it will be useful,

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

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

16# GNU General Public License for more details.

17#

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

19# along with this program. If not, see <http://www.gnu.org/licenses/>.

20

21 from collections import OrderedDict

22 import json

23 import re

24 import subprocess

25 import sys

26

27

28 def _run_cmd(cmd):

29 try:

30 return subprocess.check_output(

31 cmd, shell=True, universal_newlines=True

32 )

33 except subprocess.CalledProcessError as e:

34 print(e.output, file=sys.stderr)

35 return None

36

37

38 class INXI:

39 """

40 INXI JSON wrapper

41 """

42

43 def __init__(self, inxi_output):

44 self._inxi = {}

45 if inxi_output:

46 self._inxi = inxi_output

47 self._system_info = OrderedDict()

48

49 def _find_section(self, key):

50 for k, v in self._inxi.items():

51 if key in k:

52 return self._inxi[k]

53 else:

54 return []

55

56 def _find_records(self, section, key):

57 records = []

58 for record in section:

59 for k in record.keys():

60 if key in k:

61 records.append(OrderedDict(sorted(record.items())))

62 return records

63

64 def _prepare_system_info(self):

65 system_data = []

66 system = self._find_section("System")

67 system_info = self._find_records(system, "#Kernel")

68 system_details = ""

69 for i in system_info:

70 for k, v in i.items():

71 if "#Kernel" in k:

72 system_details += "{}: {}\n".format(

73 re.sub(".*#", "", k), v

74 )

75 elif "#Distro" in k:

76 system_details += "{}: {}".format(re.sub(".*#", "", k), v)

77 if system_details:

78 system_data = system_details.splitlines()

79 if system_data:

80 self._system_info["System"] = system_data

81

82 def _prepare_machine_info(self):

83 machine_data = []

84 machine = self._find_section("Machine")

85 machine_info = self._find_records(machine, "#Type")

86 machine_details = ""

87 for i in machine_info:

88 for k, v in i.items():

89 if "#serial" in k:

90 break

91 elif "#product" in k:

92 machine_details += "\n"

93 machine_details += "{}: {} ".format(re.sub(".*#", "", k), v)

94 if machine_details:

95 machine_data = machine_details.splitlines()

96 machine_info = self._find_records(machine, "#UEFI")

97 machine_UEFI = ""

98 for i in machine_info:

99 keep = False

100 for k, v in i.items():

101 if "#UEFI" in k:

102 keep = True

103 if not keep:

104 continue

105 machine_UEFI += "{}: {} ".format(re.sub(".*#", "", k), v)

106 if machine_UEFI:

107 machine_data.append(machine_UEFI)

108 if machine_data:

109 self._system_info["Machine"] = machine_data

110

111 def _prepare_cpu_info(self):

112 cpu_data = []

113 cpu = self._find_section("CPU")

114 cpu_info = self._find_records(cpu, "#Topology")

115 cpu_details = ""

116 for i in cpu_info:

117 for k, v in i.items():

118 if "#bits" in k or "#type" in k:

119 continue

120 elif "#model" in k:

121 cpu_details += "\nModel: {} ".format(v)

122 else:

123 cpu_details += "{}: {} ".format(re.sub(".*#", "", k), v)

124 if "#arch" in k:

125 break

126 if cpu_details:

127 cpu_data = cpu_details.splitlines()

128 if cpu_data:

129 self._system_info["CPU"] = cpu_data

130

131 def _prepare_memory_info(self):

132 memory_data = []

133 memory = self._find_section("Memory")

134 memory_info = self._find_records(memory, "#total")

135 memory_details = ""

136 for i in memory_info:

137 for k, v in i.items():

138 if "#total" in k:

139 memory_details += v

140 break

141 if memory_details:

142 memory_data.append(memory_details)

143 if memory_data:

144 self._system_info["Memory"] = memory_data

145

146 def _prepare_generic_info(self, section_name, key):

147 generic_data = []

148 generic = self._find_section(section_name)

149 generic_info = self._find_records(generic, key)

150 for i in generic_info:

151 generic_details = ""

152 ignore = False

153 device_mode = False

154 for k, v in i.items():

155 # Ignore USB/removable devices (except Network)

156 if "#type" in k and section_name != "Network":

157 ignore = True

158 if key in k and "#ID" not in k:

159 generic_details += v

160 device_mode = True

161 if not device_mode and (

162 "#vendor" in k or "#model" in k or "#size" in k

163 ):

164 generic_details += "{} ".format(v)

165 if "#chip ID" in k:

166 generic_details += " [{}]".format(v)

167 if generic_details and not ignore:

168 generic_data.append(generic_details)

169 if generic_data:

170 self._system_info[section_name] = generic_data

171

172 def _prepare_raid_info(self):

173 raid_data = []

174 raid = self._find_section("RAID")

175 raid_info = self._find_records(raid, "#Hardware")

176 for i in raid_info:

177 raid_details = ""

178 for k, v in i.items():

179 if "#Hardware" in k:

180 raid_details += v

181 if "#chip ID" in k:

182 raid_details += " [{}]".format(v)

183 if raid_details:

184 raid_data.append(raid_details)

185 if raid_data:

186 self._system_info["RAID"] = raid_data

187 raid_info = self._find_records(raid, "#Device")

188 for i in raid_info:

189 raid_details = ""

190 for k, v in i.items():

191 if "#Device" in k:

192 raid_details += v

193 if "#type" in k:

194 raid_details += " {}: {} ".format(re.sub(".*#", "", k), v)

195 if raid_details:

196 raid_data.append(raid_details)

197 if raid_data:

198 self._system_info["RAID"] = raid_data

199

200 def get_sys_info(self):

201 self._prepare_system_info()

202 self._prepare_machine_info()

203 self._prepare_cpu_info()

204 self._prepare_memory_info()

205 self._prepare_generic_info("Audio", "#Device")

206 self._prepare_generic_info("Graphics", "#Device")

207 self._prepare_generic_info("Network", "#Device")

208 self._prepare_generic_info("Drives", "#ID")

209 self._prepare_raid_info()

210 return self._system_info

211

212

213 def main():

214 inxi_output = _run_cmd(

215 "inxi_snapshot --usb-sys -A -G -C -M -m -N -D -S -R -xx "

216 "--output json --output-file print"

217 )

218 if not inxi_output:

219 inxi_output = "{}"

220 inxi = INXI(json.loads(inxi_output))

221 sys_info = inxi.get_sys_info()

222 extra_sections = {

223 "Bluetooth": "BLUETOOTH",

224 "Touchscreen": "TOUCHSCREEN",

225 "Webcam": "CAPTURE",

226 "Touchpad": "TOUCHPAD",

227 }

228 for section, udev_category in extra_sections.items():

229 section_info = _run_cmd(

230 "udev_resource.py -l {} -s".format(udev_category)

231 )

232 if section_info:

233 sys_info[section] = section_info.splitlines()

234 print(json.dumps(sys_info, indent=4))

235

236

237 if __name__ == "__main__":

238 raise SystemExit(main())