forked from jaypipes/ghw
-
Notifications
You must be signed in to change notification settings - Fork 0
/
block.go
258 lines (236 loc) · 7.48 KB
/
block.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//
package ghw
import (
"fmt"
"math"
"strings"
)
// DriveType describes the general category of drive device
type DriveType int
const (
DRIVE_TYPE_UNKNOWN DriveType = iota
DRIVE_TYPE_HDD // Hard disk drive
DRIVE_TYPE_FDD // Floppy disk drive
DRIVE_TYPE_ODD // Optical disk drive
DRIVE_TYPE_SSD // Solid-state drive
)
var (
driveTypeString = map[DriveType]string{
DRIVE_TYPE_UNKNOWN: "Unknown",
DRIVE_TYPE_HDD: "HDD",
DRIVE_TYPE_FDD: "FDD",
DRIVE_TYPE_ODD: "ODD",
DRIVE_TYPE_SSD: "SSD",
}
)
func (dt DriveType) String() string {
return driveTypeString[dt]
}
// NOTE(jaypipes): since serialized output is as "official" as we're going to
// get, let's lowercase the string output when serializing, in order to
// "normalize" the expected serialized output
func (dt DriveType) MarshalJSON() ([]byte, error) {
return []byte("\"" + strings.ToLower(dt.String()) + "\""), nil
}
// StorageController is a category of block storage controller/driver. It
// represents more of the physical hardware interface than the storage
// protocol, which represents more of the software interface.
//
// See discussion on https://github.com/jaypipes/ghw/issues/117
type StorageController int
const (
STORAGE_CONTROLLER_UNKNOWN StorageController = iota
STORAGE_CONTROLLER_IDE // Integrated Drive Electronics
STORAGE_CONTROLLER_SCSI // Small computer system interface
STORAGE_CONTROLLER_NVME // Non-volatile Memory Express
STORAGE_CONTROLLER_VIRTIO // Virtualized storage controller/driver
STORAGE_CONTROLLER_MMC // Multi-media controller (used for mobile phone storage devices)
)
var (
storageControllerString = map[StorageController]string{
STORAGE_CONTROLLER_UNKNOWN: "Unknown",
STORAGE_CONTROLLER_IDE: "IDE",
STORAGE_CONTROLLER_SCSI: "SCSI",
STORAGE_CONTROLLER_NVME: "NVMe",
STORAGE_CONTROLLER_VIRTIO: "virtio",
STORAGE_CONTROLLER_MMC: "MMC",
}
)
func (sc StorageController) String() string {
return storageControllerString[sc]
}
// NOTE(jaypipes): since serialized output is as "official" as we're going to
// get, let's lowercase the string output when serializing, in order to
// "normalize" the expected serialized output
func (sc StorageController) MarshalJSON() ([]byte, error) {
return []byte("\"" + strings.ToLower(sc.String()) + "\""), nil
}
// Disk describes a single disk drive on the host system. Disk drives provide
// raw block storage resources.
type Disk struct {
Name string `json:"name"`
SizeBytes uint64 `json:"size_bytes"`
PhysicalBlockSizeBytes uint64 `json:"physical_block_size_bytes"`
DriveType DriveType `json:"drive_type"`
IsRemovable bool `json:"removable"`
StorageController StorageController `json:"storage_controller"`
// NOTE(jaypipes): BusType is DEPRECATED. Use the DriveType and
// StorageController fields instead
BusType BusType `json:"-"`
BusPath string `json:"bus_path"`
// TODO(jaypipes): Convert this to a TopologyNode struct pointer and then
// add to serialized output as "numa_node,omitempty"
NUMANodeID int `json:"-"`
Vendor string `json:"vendor"`
Model string `json:"model"`
SerialNumber string `json:"serial_number"`
WWN string `json:"wwn"`
Partitions []*Partition `json:"partitions"`
// TODO(jaypipes): Add PCI field for accessing PCI device information
// PCI *PCIDevice `json:"pci"`
}
// Partition describes a logical division of a Disk.
type Partition struct {
Disk *Disk `json:"-"`
Name string `json:"name"`
Label string `json:"label"`
MountPoint string `json:"mount_point"`
SizeBytes uint64 `json:"size_bytes"`
UsableBytes uint64 `json:"usable_bytes"`
AvailableBytes uint64 `json:"available_bytes"`
Type string `json:"type"`
IsReadOnly bool `json:"read_only"`
}
// BlockInfo describes all disk drives and partitions in the host system.
type BlockInfo struct {
// TODO(jaypipes): Deprecate this field and replace with TotalSizeBytes
TotalPhysicalBytes uint64 `json:"total_size_bytes"`
Disks []*Disk `json:"disks"`
Partitions []*Partition `json:"-"`
}
// Block returns a BlockInfo struct that describes the block storage resources
// of the host system.
func Block(opts ...*WithOption) (*BlockInfo, error) {
mergeOpts := mergeOptions(opts...)
ctx := &context{
chroot: *mergeOpts.Chroot,
}
info := &BlockInfo{}
if err := ctx.blockFillInfo(info); err != nil {
return nil, err
}
return info, nil
}
func (i *BlockInfo) String() string {
tpbs := UNKNOWN
if i.TotalPhysicalBytes > 0 {
tpb := i.TotalPhysicalBytes
unit, unitStr := unitWithString(int64(tpb))
tpb = uint64(math.Ceil(float64(tpb) / float64(unit)))
tpbs = fmt.Sprintf("%d%s", tpb, unitStr)
}
dplural := "disks"
if len(i.Disks) == 1 {
dplural = "disk"
}
return fmt.Sprintf("block storage (%d %s, %s physical storage)",
len(i.Disks), dplural, tpbs)
}
func (d *Disk) String() string {
sizeStr := UNKNOWN
if d.SizeBytes > 0 {
size := d.SizeBytes
unit, unitStr := unitWithString(int64(size))
size = uint64(math.Ceil(float64(size) / float64(unit)))
sizeStr = fmt.Sprintf("%d%s", size, unitStr)
}
atNode := ""
if d.NUMANodeID >= 0 {
atNode = fmt.Sprintf(" (node #%d)", d.NUMANodeID)
}
vendor := ""
if d.Vendor != "" {
vendor = " vendor=" + d.Vendor
}
model := ""
if d.Model != UNKNOWN {
model = " model=" + d.Model
}
serial := ""
if d.SerialNumber != UNKNOWN {
serial = " serial=" + d.SerialNumber
}
wwn := ""
if d.WWN != UNKNOWN {
wwn = " WWN=" + d.WWN
}
removable := ""
if d.IsRemovable {
removable = " removable=true"
}
return fmt.Sprintf(
"%s %s (%s) %s [@%s%s]%s%s%s%s%s",
d.Name,
d.DriveType.String(),
sizeStr,
d.StorageController.String(),
d.BusPath,
atNode,
vendor,
model,
serial,
wwn,
removable,
)
}
func (p *Partition) String() string {
typeStr := ""
if p.Type != "" {
typeStr = fmt.Sprintf("[%s]", p.Type)
}
mountStr := ""
if p.MountPoint != "" {
mountStr = fmt.Sprintf(" mounted@%s", p.MountPoint)
}
sizeStr := UNKNOWN
if p.SizeBytes > 0 {
size := p.SizeBytes
unit, unitStr := unitWithString(int64(size))
size = uint64(math.Ceil(float64(size) / float64(unit)))
sizeStr = fmt.Sprintf("%d%s", size, unitStr)
}
availableStr := UNKNOWN
if p.AvailableBytes > 0 {
size := p.AvailableBytes
unit, unitStr := unitWithString(int64(size))
size = uint64(math.Ceil(float64(size) / float64(unit)))
availableStr = fmt.Sprintf("%d%s", size, unitStr)
}
return fmt.Sprintf(
"%s (%s, available: %s) %s%s",
p.Name,
sizeStr,
availableStr,
typeStr,
mountStr,
)
}
// simple private struct used to encapsulate block information in a top-level
// "block" YAML/JSON map/object key
type blockPrinter struct {
Info *BlockInfo `json:"block" yaml:"block"`
}
// YAMLString returns a string with the block information formatted as YAML
// under a top-level "block:" key
func (i *BlockInfo) YAMLString() string {
return safeYAML(blockPrinter{i})
}
// JSONString returns a string with the block information formatted as JSON
// under a top-level "block:" key
func (i *BlockInfo) JSONString(indent bool) string {
return safeJSON(blockPrinter{i}, indent)
}