@@ -4,8 +4,10 @@ import (
4
4
"context"
5
5
"encoding/json"
6
6
"fmt"
7
+ "os/exec"
7
8
"path/filepath"
8
9
"regexp"
10
+ "strconv"
9
11
"strings"
10
12
11
13
"github.com/pkg/errors"
@@ -25,6 +27,8 @@ type DeviceManager interface {
25
27
Mountpoint (ctx context.Context , device string ) (string , error )
26
28
// Seektime checks device seektime
27
29
Seektime (ctx context.Context , device string ) (zos.DeviceType , error )
30
+ // ClearCache clears the cached devices to refresh
31
+ ClearCache ()
28
32
}
29
33
30
34
// Devices represents a list of cached in memory devices
@@ -38,9 +42,7 @@ const (
38
42
BtrfsFSType FSType = "btrfs"
39
43
)
40
44
41
- var (
42
- subvolFindmntOption = regexp .MustCompile (`(^|,)subvol=/($|,)` )
43
- )
45
+ var subvolFindmntOption = regexp .MustCompile (`(^|,)subvol=/($|,)` )
44
46
45
47
// blockDevices lsblk output
46
48
type blockDevices struct {
@@ -61,6 +63,14 @@ type DeviceInfo struct {
61
63
Children []DeviceInfo `json:"children,omitempty"`
62
64
}
63
65
66
+ type DiskSpace struct {
67
+ Number int `json:"number"`
68
+ Start string `json:"start"`
69
+ End string `json:"end"`
70
+ Type string `json:"type"`
71
+ Size string `json:"size"`
72
+ }
73
+
64
74
func (i * DeviceInfo ) Name () string {
65
75
return filepath .Base (i .Path )
66
76
}
@@ -83,6 +93,81 @@ func (d *DeviceInfo) IsPXEPartition() bool {
83
93
return d .Label == "ZOSPXE"
84
94
}
85
95
96
+ func (d * DeviceInfo ) IsPartitioned () bool {
97
+ return len (d .Children ) != 0
98
+ }
99
+
100
+ func (d * DeviceInfo ) GetUnallocatedSpaces (ctx context.Context ) ([]DiskSpace , error ) {
101
+ args := []string {
102
+ "--json" , d .Path , "unit" , "B" , "print" , "free" ,
103
+ }
104
+ output , err := exec .CommandContext (ctx , "parted" , args ... ).CombinedOutput ()
105
+ if err != nil {
106
+ return nil , fmt .Errorf ("parted command error: %w" , err )
107
+ }
108
+
109
+ var diskData struct {
110
+ Disk struct {
111
+ Partitions []DiskSpace `json:"partitions"`
112
+ } `json:"disk"`
113
+ }
114
+ if err := json .Unmarshal (output , & diskData ); err != nil {
115
+ return nil , fmt .Errorf ("failed to parse parted output: %v" , err )
116
+ }
117
+
118
+ validSpaces := []DiskSpace {}
119
+ for _ , part := range diskData .Disk .Partitions {
120
+ if isValidAsDevice (part ) {
121
+ validSpaces = append (validSpaces , part )
122
+ }
123
+ }
124
+
125
+ return validSpaces , nil
126
+ }
127
+
128
+ func (d * DeviceInfo ) AllocateEmptySpace (ctx context.Context , space DiskSpace ) error {
129
+ args := []string {
130
+ d .Path , "mkpart" , "primary" , string (BtrfsFSType ), space .Start , space .End ,
131
+ }
132
+
133
+ output , err := exec .CommandContext (ctx , "parted" , args ... ).CombinedOutput ()
134
+ if err != nil {
135
+ return fmt .Errorf ("parted command error: %w" , err )
136
+ }
137
+ log .Debug ().Str ("output" , string (output )).Msg ("allocate empty space parted command" )
138
+
139
+ return nil
140
+ }
141
+
142
+ func (d * DeviceInfo ) RefreshDeviceInfo (ctx context.Context ) (DeviceInfo , error ) {
143
+ // notify the kernel with the changed
144
+ if err := Partprobe (ctx ); err != nil {
145
+ return DeviceInfo {}, err
146
+ }
147
+
148
+ // remove the cache
149
+ d .mgr .ClearCache ()
150
+
151
+ return d .mgr .Device (ctx , d .Path )
152
+ }
153
+
154
+ func isValidAsDevice (space DiskSpace ) bool {
155
+ // minimum acceptable device size could be used by zos
156
+ const minDeviceSizeBytes = 5 * 1024 * 1024 * 1024 // 5 GiB
157
+
158
+ spaceSize , err := strconv .ParseUint (strings .TrimSuffix (space .Size , "B" ), 10 , 64 )
159
+ if err != nil {
160
+ log .Debug ().Err (err ).Msg ("failed converting space size" )
161
+ return false
162
+ }
163
+
164
+ if space .Type == "free" &&
165
+ spaceSize >= minDeviceSizeBytes {
166
+ return true
167
+ }
168
+ return false
169
+ }
170
+
86
171
// lsblkDeviceManager uses the lsblk utility to scann the disk for devices, and
87
172
// caches the result.
88
173
//
@@ -106,6 +191,10 @@ func defaultDeviceManager(exec executer) DeviceManager {
106
191
return m
107
192
}
108
193
194
+ func (l * lsblkDeviceManager ) ClearCache () {
195
+ l .cache = nil
196
+ }
197
+
109
198
// Devices gets available block devices
110
199
func (l * lsblkDeviceManager ) Seektime (ctx context.Context , device string ) (zos.DeviceType , error ) {
111
200
log .Debug ().Str ("device" , device ).Msg ("checking seektim for device" )
@@ -160,7 +249,6 @@ func (l *lsblkDeviceManager) Device(ctx context.Context, path string) (device De
160
249
}
161
250
162
251
return device , fmt .Errorf ("device not found" )
163
-
164
252
}
165
253
166
254
func (l * lsblkDeviceManager ) lsblk (ctx context.Context ) ([]DeviceInfo , error ) {
@@ -233,7 +321,6 @@ func (l *lsblkDeviceManager) Mountpoint(ctx context.Context, device string) (str
233
321
}
234
322
235
323
return "" , nil
236
-
237
324
}
238
325
239
326
func (l * lsblkDeviceManager ) raw (ctx context.Context ) ([]DeviceInfo , error ) {
0 commit comments