forked from kata-containers/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
298 lines (229 loc) · 6.88 KB
/
utils.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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package utils
import (
"crypto/rand"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
)
const cpBinaryName = "cp"
const fileMode0755 = os.FileMode(0755)
// MibToBytesShift the number to shift needed to convert MiB to Bytes
const MibToBytesShift = 20
// MaxSocketPathLen is the effective maximum Unix domain socket length.
//
// See unix(7).
const MaxSocketPathLen = 107
// VHostVSockDevicePath path to vhost-vsock device
var VHostVSockDevicePath = "/dev/vhost-vsock"
// FileCopy copys files from srcPath to dstPath
func FileCopy(srcPath, dstPath string) error {
if srcPath == "" {
return fmt.Errorf("Source path cannot be empty")
}
if dstPath == "" {
return fmt.Errorf("Destination path cannot be empty")
}
binPath, err := exec.LookPath(cpBinaryName)
if err != nil {
return err
}
cmd := exec.Command(binPath, srcPath, dstPath)
return cmd.Run()
}
// GenerateRandomBytes generate n random bytes
func GenerateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
if err != nil {
return nil, err
}
return b, nil
}
// ReverseString reverses whole string
func ReverseString(s string) string {
r := []rune(s)
length := len(r)
for i, j := 0, length-1; i < length/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
// CleanupFds closed bundles of open fds in batch
func CleanupFds(fds []*os.File, numFds int) {
maxFds := len(fds)
if numFds < maxFds {
maxFds = numFds
}
for i := 0; i < maxFds; i++ {
_ = fds[i].Close()
}
}
// WriteToFile opens a file in write only mode and writes bytes to it
func WriteToFile(path string, data []byte) error {
f, err := os.OpenFile(path, os.O_WRONLY, fileMode0755)
if err != nil {
return err
}
defer f.Close()
if _, err := f.Write(data); err != nil {
return err
}
return nil
}
//CalculateMilliCPUs converts CPU quota and period to milli-CPUs
func CalculateMilliCPUs(quota int64, period uint64) uint32 {
// If quota is -1, it means the CPU resource request is
// unconstrained. In that case, we don't currently assign
// additional CPUs.
if quota >= 0 && period != 0 {
return uint32((uint64(quota) * 1000) / period)
}
return 0
}
//CalculateVCpusFromMilliCpus converts from mCPU to CPU, taking the ceiling
// value when necessary
func CalculateVCpusFromMilliCpus(mCPU uint32) uint32 {
return (mCPU + 999) / 1000
}
// ConstraintsToVCPUs converts CPU quota and period to vCPUs
func ConstraintsToVCPUs(quota int64, period uint64) uint {
if quota != 0 && period != 0 {
// Use some math magic to round up to the nearest whole vCPU
// (that is, a partial part of a quota request ends up assigning
// a whole vCPU, for instance, a request of 1.5 'cpu quotas'
// will give 2 vCPUs).
// This also has the side effect that we will always allocate
// at least 1 vCPU.
return uint((uint64(quota) + (period - 1)) / period)
}
return 0
}
// GetVirtDriveName returns the disk name format for virtio-blk
// Reference: https://github.com/torvalds/linux/blob/master/drivers/block/virtio_blk.c @c0aa3e0916d7e531e69b02e426f7162dfb1c6c0
func GetVirtDriveName(index int) (string, error) {
if index < 0 {
return "", fmt.Errorf("Index cannot be negative for drive")
}
// Prefix used for virtio-block devices
const prefix = "vd"
//Refer to DISK_NAME_LEN: https://github.com/torvalds/linux/blob/08c521a2011ff492490aa9ed6cc574be4235ce2b/include/linux/genhd.h#L61
diskNameLen := 32
base := 26
suffLen := diskNameLen - len(prefix)
diskLetters := make([]byte, suffLen)
var i int
for i = 0; i < suffLen && index >= 0; i++ {
letter := byte('a' + (index % base))
diskLetters[i] = letter
index = index/base - 1
}
if index >= 0 {
return "", fmt.Errorf("Index not supported")
}
diskName := prefix + ReverseString(string(diskLetters[:i]))
return diskName, nil
}
const maxSCSIDevices = 65535
// GetSCSIIdLun gets the SCSI id and lun, based on the index of the drive being inserted.
// qemu code suggests that scsi-id can take values from 0 to 255 inclusive, while lun can
// take values from 0 to 16383 inclusive. But lun values over 255 do not seem to follow
// consistent SCSI addressing. Hence we limit to 255.
func GetSCSIIdLun(index int) (int, int, error) {
if index < 0 {
return -1, -1, fmt.Errorf("Index cannot be negative")
}
if index > maxSCSIDevices {
return -1, -1, fmt.Errorf("Index cannot be greater than %d, maximum of %d devices are supported", maxSCSIDevices, maxSCSIDevices)
}
return index / 256, index % 256, nil
}
// GetSCSIAddress gets scsiID and lun from index, and combined them into a scsi ID
func GetSCSIAddress(index int) (string, error) {
scsiID, lun, err := GetSCSIIdLun(index)
if err != nil {
return "", err
}
return fmt.Sprintf("%d:%d", scsiID, lun), nil
}
// MakeNameID is generic function for creating a named-id for passing on the hypervisor commandline
func MakeNameID(namedType, id string, maxLen int) string {
nameID := fmt.Sprintf("%s-%s", namedType, id)
if len(nameID) > maxLen {
nameID = nameID[:maxLen]
}
return nameID
}
// BuildSocketPath concatenates the provided elements into a path and returns
// it. If the resulting path is longer than the maximum permitted socket path
// on Linux, it will return an error.
func BuildSocketPath(elements ...string) (string, error) {
result := filepath.Join(elements...)
if result == "" {
return "", errors.New("empty path")
}
l := len(result)
if l > MaxSocketPathLen {
return "", fmt.Errorf("path too long (got %v, max %v): %s", l, MaxSocketPathLen, result)
}
return result, nil
}
// SupportsVsocks returns true if vsocks are supported, otherwise false
func SupportsVsocks() bool {
if _, err := os.Stat(VHostVSockDevicePath); err != nil {
return false
}
return true
}
// StartCmd pointer to a function to start a command.
// Defined this way to allow mock testing.
var StartCmd = func(c *exec.Cmd) error {
return c.Start()
}
// AlignMem align memory provided to a block size
func (m MemUnit) AlignMem(blockSize MemUnit) MemUnit {
memSize := m
if m < blockSize {
memSize = blockSize
}
remainder := memSize % blockSize
if remainder != 0 {
// Align memory to memoryBlockSizeMB
memSize += blockSize - remainder
}
return memSize
}
type MemUnit uint64
func (m MemUnit) ToMiB() uint64 {
return m.ToBytes() / (1 * MiB).ToBytes()
}
func (m MemUnit) ToBytes() uint64 {
return uint64(m)
}
const (
Byte MemUnit = 1
KiB = Byte << 10
MiB = KiB << 10
GiB = MiB << 10
)
// Binary to use to log program output
const LoggerBinaryName = "systemd-cat"
type ProgramLogger struct {
cmd *exec.Cmd
}
func NewProgramLogger(loggerLabel string) ProgramLogger {
return ProgramLogger{cmd: exec.Command(LoggerBinaryName, "-t", loggerLabel)}
}
func (p *ProgramLogger) StartLogger(output io.ReadCloser) error {
p.cmd.Stdin = output
return StartCmd(p.cmd)
}
func (p ProgramLogger) String() string {
return p.cmd.Path
}