Skip to content

Commit 17c9c99

Browse files
xmlqiuzhiqian
authored andcommitted
feat: Add gatherinfo and posthardware commands to lastore-tools
1 parent 0b175a4 commit 17c9c99

3 files changed

Lines changed: 527 additions & 1 deletion

File tree

src/lastore-tools/gather_info.go

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/base64"
6+
"encoding/json"
7+
"fmt"
8+
"io/ioutil"
9+
"net/http"
10+
"os"
11+
"os/exec"
12+
"path"
13+
"strconv"
14+
"strings"
15+
"time"
16+
17+
"github.com/codegangsta/cli"
18+
"github.com/jouyouyun/hardware/dmi"
19+
"github.com/linuxdeepin/lastore-daemon/src/internal/config"
20+
"github.com/linuxdeepin/lastore-daemon/src/internal/updateplatform"
21+
)
22+
23+
type Response struct {
24+
Result bool `json:"result"`
25+
Code int `json:"code"`
26+
Data Data `json:"data"`
27+
}
28+
29+
type Data struct {
30+
Fill bool `json:"fill"`
31+
Custom bool `json:"custom"`
32+
}
33+
34+
type DiskInfo struct {
35+
DiskNo string `json:"diskNo"`
36+
TotalCapacity int64 `json:"totalCapacity"`
37+
FreeCapacity int64 `json:"freeCapacity"`
38+
}
39+
40+
type PostMemoryInfo struct {
41+
MemoryNo string `json:"memoryNo"`
42+
Capacity int64 `json:"capacity"`
43+
}
44+
45+
type SystemInfo struct {
46+
SN string `json:"sn"`
47+
Disk []DiskInfo `json:"disk"`
48+
Memory []PostMemoryInfo `json:"memory"`
49+
}
50+
51+
var CMDPostHardwareInfo = cli.Command{
52+
Name: "posthardware",
53+
Usage: `post hardware info`,
54+
Action: MainPostHardwareInfo,
55+
Flags: []cli.Flag{
56+
cli.StringFlag{
57+
Name: "type,t",
58+
Value: "",
59+
Usage: "ui|post",
60+
},
61+
},
62+
}
63+
64+
var CMDGatherInfo = cli.Command{
65+
Name: "gatherinfo",
66+
Usage: `gather user info`,
67+
Action: MainGatherInfo,
68+
Flags: []cli.Flag{
69+
cli.StringFlag{
70+
Name: "type,t",
71+
Value: "",
72+
Usage: "ui|post",
73+
},
74+
},
75+
}
76+
77+
type BlockDevice struct {
78+
Name string `json:"name"`
79+
Removable bool `json:"rm"`
80+
Size int64 `json:"size"`
81+
Type string `json:"type"`
82+
Fsuse string `json:"fsuse%"`
83+
Children []Partition `json:"children"`
84+
}
85+
86+
type Partition struct {
87+
Name string `json:"name"`
88+
Removable bool `json:"rm"`
89+
Size int64 `json:"size"`
90+
Fsuse string `json:"fsuse%"`
91+
}
92+
93+
type BlockDevices struct {
94+
Blockdevices []BlockDevice `json:"blockdevices"`
95+
}
96+
97+
var gbToByteNumber int64 = 1024 * 1024 * 1024
98+
99+
func convertToHardwareBytes(totalCapacity int64) int64 {
100+
if totalCapacity/gbToByteNumber < 256 {
101+
return 256 * gbToByteNumber
102+
} else if totalCapacity/gbToByteNumber >= 256 && totalCapacity/gbToByteNumber < 512 {
103+
return 512 * gbToByteNumber
104+
} else if totalCapacity/gbToByteNumber >= 512 && totalCapacity/gbToByteNumber < 1024 {
105+
return 1024 * gbToByteNumber
106+
} else if totalCapacity/gbToByteNumber >= 1024 && totalCapacity/gbToByteNumber < 2048 {
107+
return 2048 * gbToByteNumber
108+
} else if totalCapacity/gbToByteNumber >= 2048 && totalCapacity/gbToByteNumber < 4096 {
109+
return 4096 * gbToByteNumber
110+
}
111+
return totalCapacity
112+
}
113+
114+
func getDiskSize() ([]DiskInfo, error) {
115+
var diskInfos []DiskInfo
116+
out, err := exec.Command("lsblk", "-J", "-bno", "NAME,RM,TYPE,SIZE,FSUSE%").Output()
117+
if err != nil {
118+
return diskInfos, err
119+
}
120+
var blockDevices BlockDevices
121+
err = json.Unmarshal(out, &blockDevices)
122+
if err != nil {
123+
return diskInfos, err
124+
}
125+
for _, blockDevice := range blockDevices.Blockdevices {
126+
// ignore removable disk(such as usb disk)
127+
if blockDevice.Removable {
128+
continue
129+
}
130+
if blockDevice.Type != "disk" {
131+
continue
132+
}
133+
134+
var info DiskInfo
135+
info.DiskNo = blockDevice.Name
136+
info.TotalCapacity = convertToHardwareBytes(blockDevice.Size)
137+
if len(blockDevice.Children) == 0 {
138+
if len(blockDevice.Fsuse) == 0 {
139+
info.FreeCapacity = blockDevice.Size
140+
} else {
141+
fsuse := strings.ReplaceAll(blockDevice.Fsuse, "%", "")
142+
var use int64
143+
use, err = strconv.ParseInt(fsuse, 10, 64)
144+
if err != nil {
145+
info.FreeCapacity = blockDevice.Size
146+
} else {
147+
info.FreeCapacity = blockDevice.Size * (100 - use) / 100
148+
}
149+
}
150+
} else {
151+
var freeCapacity int64
152+
for _, child := range blockDevice.Children {
153+
if len(child.Fsuse) == 0 {
154+
freeCapacity += child.Size
155+
} else {
156+
fsuse := strings.ReplaceAll(child.Fsuse, "%", "")
157+
var use int64
158+
use, err = strconv.ParseInt(fsuse, 10, 64)
159+
if err != nil {
160+
continue
161+
} else {
162+
freeCapacity += child.Size * (100 - use) / 100
163+
}
164+
}
165+
}
166+
if freeCapacity == 0 {
167+
info.FreeCapacity = blockDevice.Size
168+
} else {
169+
info.FreeCapacity = freeCapacity
170+
}
171+
}
172+
diskInfos = append(diskInfos, info)
173+
}
174+
return diskInfos, nil
175+
}
176+
177+
func getMemorySizeByDmi() ([]PostMemoryInfo, error) {
178+
cmd := exec.Command("dmidecode", "-t", "17")
179+
cmd.Env = append(os.Environ(), "LC_ALL=C")
180+
output, err := cmd.Output()
181+
if err != nil {
182+
return nil, fmt.Errorf("failed to execute dmidecode: %w", err)
183+
}
184+
185+
return parseMemoryModuleOutput(string(output))
186+
}
187+
188+
func parseMemoryModuleOutput(output string) ([]PostMemoryInfo, error) {
189+
lines := strings.Split(output, "\n")
190+
moduleCount := 0
191+
192+
var result []PostMemoryInfo
193+
for _, line := range lines {
194+
trimmedLine := strings.TrimSpace(line)
195+
196+
// Detect new memory module
197+
if strings.HasPrefix(trimmedLine, "Memory Device") {
198+
moduleCount++
199+
continue
200+
}
201+
202+
// Extract key information
203+
if strings.HasPrefix(trimmedLine, "Size:") {
204+
size := strings.TrimSpace(strings.TrimPrefix(trimmedLine, "Size:"))
205+
sizeValue, err := ParseSizeToBytes(size)
206+
if err != nil {
207+
continue
208+
}
209+
result = append(result, PostMemoryInfo{
210+
MemoryNo: fmt.Sprintf("%d", moduleCount),
211+
Capacity: sizeValue,
212+
})
213+
}
214+
}
215+
return result, nil
216+
}
217+
218+
func ParseSizeToBytes(sizeStr string) (int64, error) {
219+
numEndIndex := 0
220+
for i := 0; i < len(sizeStr); i++ {
221+
if (sizeStr[i] < '0' || sizeStr[i] > '9') && sizeStr[i] != '.' {
222+
numEndIndex = i
223+
break
224+
}
225+
}
226+
227+
// Parse the numeric part
228+
size, err := strconv.ParseFloat(strings.TrimSpace(sizeStr[:numEndIndex]), 64)
229+
if err != nil {
230+
return 0, fmt.Errorf("failed to parse size: %w", err)
231+
}
232+
233+
// Convert to bytes based on the unit
234+
unit := strings.ToUpper(strings.TrimSpace(sizeStr[numEndIndex:]))
235+
var multiplier int64
236+
237+
switch unit {
238+
case "B":
239+
multiplier = 1
240+
case "KB":
241+
multiplier = 1024
242+
case "MB":
243+
multiplier = 1024 * 1024
244+
case "GB":
245+
multiplier = 1024 * 1024 * 1024
246+
case "TB":
247+
multiplier = 1024 * 1024 * 1024 * 1024
248+
default:
249+
return 0, fmt.Errorf("unsupported unit: %s", unit)
250+
}
251+
252+
// Calculate the number of bytes and return it
253+
return int64(size * float64(multiplier)), nil
254+
}
255+
256+
func getWhetherGatherInfo(c *config.Config) (*http.Response, error) {
257+
url := c.PlatformUrl
258+
policyUrl := url + "/api/v1/terminal/info/check"
259+
client := &http.Client{
260+
Timeout: 4 * time.Second,
261+
}
262+
logger.Infof("%v", policyUrl)
263+
request, err := http.NewRequest("GET", policyUrl, nil)
264+
if err != nil {
265+
return nil, fmt.Errorf("%v new request failed: %v ", "/api/v1/terminal/info/check", err.Error())
266+
}
267+
request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(updateplatform.UpdateTokenConfigFile(c.IncludeDiskInfo))))
268+
return client.Do(request)
269+
}
270+
271+
func postHardwareInfo(c *config.Config) error {
272+
// get S/N
273+
var sn string
274+
dmi, err := dmi.GetDMI()
275+
if err != nil {
276+
logger.Warning("cannot get dmi")
277+
} else {
278+
sn = dmi.BoardSerial
279+
}
280+
281+
// get disk info
282+
diskInfos, err := getDiskSize()
283+
if err != nil {
284+
return fmt.Errorf("cannot get disk infos: %w", err)
285+
}
286+
logger.Infof("disk info :%+v", diskInfos)
287+
memoryInfos, err := getMemorySizeByDmi()
288+
if err != nil {
289+
return fmt.Errorf("cannot get memory infos: %w", err)
290+
}
291+
292+
systemInfo := SystemInfo{
293+
SN: sn,
294+
Disk: diskInfos,
295+
Memory: memoryInfos,
296+
}
297+
298+
jsonSystemInfo, err := json.Marshal(systemInfo)
299+
if err != nil {
300+
return fmt.Errorf("marshal system info failed: %w", err)
301+
}
302+
303+
url := c.PlatformUrl
304+
policyUrl := url + "/api/v1/terminal/hardware"
305+
client := &http.Client{
306+
Timeout: 4 * time.Second,
307+
}
308+
request, err := http.NewRequest("POST", policyUrl, bytes.NewBuffer(jsonSystemInfo))
309+
if err != nil {
310+
return fmt.Errorf("%v new request failed: %v ", "/api/v1/terminal/hardware", err.Error())
311+
}
312+
request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(updateplatform.UpdateTokenConfigFile(c.IncludeDiskInfo))))
313+
314+
resp, err := client.Do(request)
315+
if err != nil {
316+
return err
317+
}
318+
defer func() {
319+
_ = resp.Body.Close()
320+
}()
321+
322+
body, err := ioutil.ReadAll(resp.Body)
323+
if err != nil {
324+
return fmt.Errorf("read response body failed: %w", err)
325+
} else {
326+
logger.Infof("post hardware info response: status=%d, body=%s", resp.StatusCode, string(body))
327+
}
328+
329+
if resp.StatusCode >= 400 {
330+
return fmt.Errorf("post hardware info failed with status %d: %s", resp.StatusCode, string(body))
331+
}
332+
return nil
333+
}
334+
335+
func MainPostHardwareInfo(c *cli.Context) error {
336+
config := config.NewConfig(path.Join("/var/lib/lastore", "config.json"))
337+
// run with root
338+
return postHardwareInfo(config)
339+
}
340+
341+
func MainGatherInfo(c *cli.Context) error {
342+
config := config.NewConfig(path.Join("/var/lib/lastore", "config.json"))
343+
response, err := getWhetherGatherInfo(config)
344+
if err != nil {
345+
return fmt.Errorf("get whether gather info failed: %w", err)
346+
}
347+
defer func() {
348+
_ = response.Body.Close()
349+
}()
350+
body, err := ioutil.ReadAll(response.Body)
351+
if err != nil {
352+
return fmt.Errorf("read response body failed: %w", err)
353+
}
354+
logger.Infof("gather info body:%v", string(body))
355+
if response.StatusCode == 200 {
356+
var result Response
357+
err = json.Unmarshal(body, &result)
358+
if err != nil {
359+
return fmt.Errorf("unmarshal response body failed: %w", err)
360+
}
361+
if result.Data.Fill && result.Data.Custom {
362+
cmd := exec.Command("/usr/bin/dde-gather-info")
363+
var outBuf bytes.Buffer
364+
cmd.Stdout = &outBuf
365+
var errBuf bytes.Buffer
366+
cmd.Stderr = &errBuf
367+
err = cmd.Run()
368+
if err != nil {
369+
logger.Infof("Error executing command: %s\n", errBuf.String())
370+
logger.Infof("Output: %s\n", outBuf.String())
371+
return nil
372+
}
373+
}
374+
}
375+
return nil
376+
}

0 commit comments

Comments
 (0)