1
16
17
18
19
20
21
22 package rbd
23
24 import (
25 "encoding/json"
26 "fmt"
27 "io/ioutil"
28 "os"
29 "os/exec"
30 "path/filepath"
31 "strconv"
32 "strings"
33 "time"
34
35 "k8s.io/klog/v2"
36 "k8s.io/mount-utils"
37 utilexec "k8s.io/utils/exec"
38 utilpath "k8s.io/utils/path"
39
40 v1 "k8s.io/api/core/v1"
41 "k8s.io/apimachinery/pkg/api/resource"
42 "k8s.io/apimachinery/pkg/util/errors"
43 "k8s.io/apimachinery/pkg/util/wait"
44 volumehelpers "k8s.io/cloud-provider/volume/helpers"
45 nodeutil "k8s.io/component-helpers/node/util"
46 "k8s.io/kubernetes/pkg/volume"
47 volutil "k8s.io/kubernetes/pkg/volume/util"
48 )
49
50 const (
51 imageWatcherStr = "watcher="
52 kubeLockMagic = "kubelet_lock_magic_"
53
54
55 rbdImageWatcherInitDelay = 1 * time.Second
56 rbdImageWatcherFactor = 1.4
57 rbdImageWatcherSteps = 10
58 rbdImageSizeUnitMiB = 1024 * 1024
59 )
60
61
62 type rbdImageInfo struct {
63 pool string
64 name string
65 }
66
67 func getDevFromImageAndPool(pool, image string) (string, bool) {
68 device, found := getRbdDevFromImageAndPool(pool, image)
69 if found {
70 return device, true
71 }
72 device, found = getNbdDevFromImageAndPool(pool, image)
73 if found {
74 return device, true
75 }
76 return "", false
77 }
78
79
80 func getRbdDevFromImageAndPool(pool string, image string) (string, bool) {
81
82 sysPath := "/sys/bus/rbd/devices"
83 if dirs, err := ioutil.ReadDir(sysPath); err == nil {
84 for _, f := range dirs {
85
86
87
88 name := f.Name()
89
90 poolFile := filepath.Join(sysPath, name, "pool")
91 poolBytes, err := ioutil.ReadFile(poolFile)
92 if err != nil {
93 klog.V(4).Infof("error reading %s: %v", poolFile, err)
94 continue
95 }
96 if strings.TrimSpace(string(poolBytes)) != pool {
97 klog.V(4).Infof("device %s is not %q: %q", name, pool, string(poolBytes))
98 continue
99 }
100 imgFile := filepath.Join(sysPath, name, "name")
101 imgBytes, err := ioutil.ReadFile(imgFile)
102 if err != nil {
103 klog.V(4).Infof("error reading %s: %v", imgFile, err)
104 continue
105 }
106 if strings.TrimSpace(string(imgBytes)) != image {
107 klog.V(4).Infof("device %s is not %q: %q", name, image, string(imgBytes))
108 continue
109 }
110
111 devicePath := "/dev/rbd" + name
112 if _, err := os.Lstat(devicePath); err == nil {
113 return devicePath, true
114 }
115 }
116 }
117 return "", false
118 }
119
120 func getMaxNbds() (int, error) {
121
122
123
124 maxNbdsPath := "/sys/module/nbd/parameters/nbds_max"
125 _, err := os.Lstat(maxNbdsPath)
126 if err != nil {
127 return 0, fmt.Errorf("rbd-nbd: failed to retrieve max_nbds from %s err: %q", maxNbdsPath, err)
128 }
129
130 klog.V(4).Infof("found nbds max parameters file at %s", maxNbdsPath)
131
132 maxNbdBytes, err := ioutil.ReadFile(maxNbdsPath)
133 if err != nil {
134 return 0, fmt.Errorf("rbd-nbd: failed to read max_nbds from %s err: %q", maxNbdsPath, err)
135 }
136
137 maxNbds, err := strconv.Atoi(strings.TrimSpace(string(maxNbdBytes)))
138 if err != nil {
139 return 0, fmt.Errorf("rbd-nbd: failed to read max_nbds err: %q", err)
140 }
141
142 klog.V(4).Infof("rbd-nbd: max_nbds: %d", maxNbds)
143 return maxNbds, nil
144 }
145
146
147
148
149
150
151 func getNbdDevFromImageAndPool(pool string, image string) (string, bool) {
152
153 basePath := "/sys/block/nbd"
154
155 imgPath := fmt.Sprintf("%s/%s", pool, image)
156
157 maxNbds, maxNbdsErr := getMaxNbds()
158 if maxNbdsErr != nil {
159 klog.V(4).Infof("error reading nbds_max %v", maxNbdsErr)
160 return "", false
161 }
162
163 for i := 0; i < maxNbds; i++ {
164 nbdPath := basePath + strconv.Itoa(i)
165 _, err := os.Lstat(nbdPath)
166 if err != nil {
167 klog.V(4).Infof("error reading nbd info directory %s: %v", nbdPath, err)
168 continue
169 }
170 pidBytes, err := ioutil.ReadFile(filepath.Join(nbdPath, "pid"))
171 if err != nil {
172 klog.V(5).Infof("did not find valid pid file in dir %s: %v", nbdPath, err)
173 continue
174 }
175 cmdlineFileName := filepath.Join("/proc", strings.TrimSpace(string(pidBytes)), "cmdline")
176 rawCmdline, err := ioutil.ReadFile(cmdlineFileName)
177 if err != nil {
178 klog.V(4).Infof("failed to read cmdline file %s: %v", cmdlineFileName, err)
179 continue
180 }
181 cmdlineArgs := strings.FieldsFunc(string(rawCmdline), func(r rune) bool {
182 return r == '\u0000'
183 })
184
185
186
187 if len(cmdlineArgs) < 3 || cmdlineArgs[0] != "rbd-nbd" || cmdlineArgs[1] != "map" {
188 klog.V(4).Infof("nbd device %s is not used by rbd", nbdPath)
189 continue
190 }
191 if cmdlineArgs[2] != imgPath {
192 klog.V(4).Infof("rbd-nbd device %s did not match expected image path: %s with path found: %s",
193 nbdPath, imgPath, cmdlineArgs[2])
194 continue
195 }
196 devicePath := filepath.Join("/dev", "nbd"+strconv.Itoa(i))
197 if _, err := os.Lstat(devicePath); err != nil {
198 klog.Warningf("Stat device %s for imgpath %s failed %v", devicePath, imgPath, err)
199 continue
200 }
201 return devicePath, true
202 }
203 return "", false
204 }
205
206
207 func waitForPath(pool, image string, maxRetries int, useNbdDriver bool) (string, bool) {
208 for i := 0; i < maxRetries; i++ {
209 if i != 0 {
210 time.Sleep(time.Second)
211 }
212 if useNbdDriver {
213 if devicePath, found := getNbdDevFromImageAndPool(pool, image); found {
214 return devicePath, true
215 }
216 } else {
217 if devicePath, found := getRbdDevFromImageAndPool(pool, image); found {
218 return devicePath, true
219 }
220 }
221 }
222 return "", false
223 }
224
225
226
227 func execRbdMap(b rbdMounter, rbdCmd string, mon string) ([]byte, error) {
228
229
230 imgPath := fmt.Sprintf("%s/%s", b.Pool, b.Image)
231 if b.Secret != "" {
232 return b.exec.Command(rbdCmd,
233 "map", imgPath, "--id", b.ID, "-m", mon, "--key="+b.Secret).CombinedOutput()
234 }
235 return b.exec.Command(rbdCmd,
236 "map", imgPath, "--id", b.ID, "-m", mon, "-k", b.Keyring).CombinedOutput()
237 }
238
239
240 func checkRbdNbdTools(e utilexec.Interface) bool {
241 _, err := e.Command("modprobe", "nbd").CombinedOutput()
242 if err != nil {
243 klog.V(5).Infof("rbd-nbd: nbd modprobe failed with error %v", err)
244 return false
245 }
246 if _, err := e.Command("rbd-nbd", "--version").CombinedOutput(); err != nil {
247 klog.V(5).Infof("rbd-nbd: getting rbd-nbd version failed with error %v", err)
248 return false
249 }
250 klog.V(3).Infof("rbd-nbd tools were found.")
251 return true
252 }
253
254
255 func makePDNameInternal(host volume.VolumeHost, pool string, image string) string {
256
257 deprecatedDir := filepath.Join(host.GetPluginDir(rbdPluginName), "rbd", pool+"-image-"+image)
258 info, err := os.Stat(deprecatedDir)
259 if err == nil && info.IsDir() {
260
261 klog.V(5).Infof("Deprecated format path %s found", deprecatedDir)
262 return deprecatedDir
263 }
264
265 return filepath.Join(host.GetPluginDir(rbdPluginName), volutil.MountsInGlobalPDPath, pool+"-image-"+image)
266 }
267
268
269 func makeVDPDNameInternal(host volume.VolumeHost, pool string, image string) string {
270 return filepath.Join(host.GetVolumeDevicePluginDir(rbdPluginName), pool+"-image-"+image)
271 }
272
273
274 type rbdUtil struct{}
275
276 var _ diskManager = &rbdUtil{}
277
278
279 func (util *rbdUtil) MakeGlobalPDName(rbd rbd) string {
280 return makePDNameInternal(rbd.plugin.host, rbd.Pool, rbd.Image)
281 }
282
283
284 func (util *rbdUtil) MakeGlobalVDPDName(rbd rbd) string {
285 return makeVDPDNameInternal(rbd.plugin.host, rbd.Pool, rbd.Image)
286 }
287
288 func rbdErrors(runErr, resultErr error) error {
289 if err, ok := runErr.(*exec.Error); ok {
290 if err.Err == exec.ErrNotFound {
291 return fmt.Errorf("rbd: rbd cmd not found")
292 }
293 }
294 return resultErr
295 }
296
297
298
299
300
301
302
303
304
305
306 func (util *rbdUtil) kernelRBDMonitorsOpt(mons []string) string {
307 return strings.Join(mons, ",")
308 }
309
310
311 func (util *rbdUtil) rbdUnlock(b rbdMounter) error {
312 var err error
313 var output, locker string
314 var cmd []byte
315 var secretOpt []string
316
317 if b.Secret != "" {
318 secretOpt = []string{"--key=" + b.Secret}
319 } else {
320 secretOpt = []string{"-k", b.Keyring}
321 }
322 if len(b.adminID) == 0 {
323 b.adminID = b.ID
324 }
325 if len(b.adminSecret) == 0 {
326 b.adminSecret = b.Secret
327 }
328
329
330 hostName, err := nodeutil.GetHostname("")
331 if err != nil {
332 return err
333 }
334 lockID := kubeLockMagic + hostName
335
336 mon := util.kernelRBDMonitorsOpt(b.Mon)
337
338
339 args := []string{"lock", "list", b.Image, "--pool", b.Pool, "--id", b.ID, "-m", mon}
340 args = append(args, secretOpt...)
341 cmd, err = b.exec.Command("rbd", args...).CombinedOutput()
342 output = string(cmd)
343 klog.V(4).Infof("lock list output %q", output)
344 if err != nil {
345 return err
346 }
347 ind := strings.LastIndex(output, lockID) - 1
348 for i := ind; i >= 0; i-- {
349 if output[i] == '\n' {
350 locker = output[(i + 1):ind]
351 break
352 }
353 }
354
355
356 if len(locker) > 0 {
357 args := []string{"lock", "remove", b.Image, lockID, locker, "--pool", b.Pool, "--id", b.ID, "-m", mon}
358 args = append(args, secretOpt...)
359 _, err = b.exec.Command("rbd", args...).CombinedOutput()
360 if err == nil {
361 klog.V(4).Infof("rbd: successfully remove lock (locker_id: %s) on image: %s/%s with id %s mon %s", lockID, b.Pool, b.Image, b.ID, mon)
362 } else {
363 klog.Warningf("rbd: failed to remove lock (lockID: %s) on image: %s/%s with id %s mon %s: %v", lockID, b.Pool, b.Image, b.ID, mon, err)
364 }
365 }
366
367 return err
368 }
369
370
371 func (util *rbdUtil) AttachDisk(b rbdMounter) (string, error) {
372 var output []byte
373
374 globalPDPath := util.MakeGlobalPDName(*b.rbd)
375 if pathExists, pathErr := mount.PathExists(globalPDPath); pathErr != nil {
376 return "", fmt.Errorf("error checking if path exists: %v", pathErr)
377 } else if !pathExists {
378 if err := os.MkdirAll(globalPDPath, 0750); err != nil {
379 return "", err
380 }
381 }
382
383
384 devicePath, mapped := waitForPath(b.Pool, b.Image, 1 , false )
385
386
387 nbdToolsFound := false
388
389 if !mapped {
390 nbdToolsFound = checkRbdNbdTools(b.exec)
391 if nbdToolsFound {
392 devicePath, mapped = waitForPath(b.Pool, b.Image, 1 , true )
393 }
394 }
395
396 if !mapped {
397
398
399
400
401 backoff := wait.Backoff{
402 Duration: rbdImageWatcherInitDelay,
403 Factor: rbdImageWatcherFactor,
404 Steps: rbdImageWatcherSteps,
405 }
406 needValidUsed := true
407 if b.accessModes != nil {
408
409 if len(b.accessModes) == 1 && b.accessModes[0] == v1.ReadOnlyMany {
410 needValidUsed = false
411 }
412 }
413
414
415
416
417 if needValidUsed {
418 err := wait.ExponentialBackoff(backoff, func() (bool, error) {
419 used, rbdOutput, err := util.rbdStatus(&b)
420 if err != nil {
421 return false, fmt.Errorf("fail to check rbd image status with: (%v), rbd output: (%s)", err, rbdOutput)
422 }
423 return !used, nil
424 })
425
426 if err == wait.ErrWaitTimeout {
427 return "", fmt.Errorf("rbd image %s/%s is still being used", b.Pool, b.Image)
428 }
429
430 if err != nil {
431 return "", err
432 }
433 }
434
435 mon := util.kernelRBDMonitorsOpt(b.Mon)
436 klog.V(1).Infof("rbd: map mon %s", mon)
437
438 _, err := b.exec.Command("modprobe", "rbd").CombinedOutput()
439 if err != nil {
440 klog.Warningf("rbd: failed to load rbd kernel module:%v", err)
441 }
442 output, err = execRbdMap(b, "rbd", mon)
443 if err != nil {
444 if !nbdToolsFound {
445 klog.V(1).Infof("rbd: map error %v, rbd output: %s", err, string(output))
446 return "", fmt.Errorf("rbd: map failed %v, rbd output: %s", err, string(output))
447 }
448 klog.V(3).Infof("rbd: map failed with %v, %s. Retrying with rbd-nbd", err, string(output))
449 errList := []error{err}
450 outputList := output
451 output, err = execRbdMap(b, "rbd-nbd", mon)
452 if err != nil {
453 errList = append(errList, err)
454 outputList = append(outputList, output...)
455 return "", fmt.Errorf("rbd: map failed %v, rbd output: %s", errors.NewAggregate(errList), string(outputList))
456 }
457 devicePath, mapped = waitForPath(b.Pool, b.Image, 10 , true )
458 } else {
459 devicePath, mapped = waitForPath(b.Pool, b.Image, 10 , false )
460 }
461 if !mapped {
462 return "", fmt.Errorf("could not map image %s/%s, Timeout after 10s", b.Pool, b.Image)
463 }
464 }
465 return devicePath, nil
466 }
467
468
469
470
471 func (util *rbdUtil) DetachDisk(plugin *rbdPlugin, deviceMountPath string, device string) error {
472 if len(device) == 0 {
473 return fmt.Errorf("DetachDisk failed , device is empty")
474 }
475
476 exec := plugin.host.GetExec(plugin.GetPluginName())
477
478 var rbdCmd string
479
480
481
482 if strings.HasPrefix(device, "/dev/nbd") {
483 rbdCmd = "rbd-nbd"
484 } else {
485 rbdCmd = "rbd"
486 }
487
488
489 output, err := exec.Command(rbdCmd, "unmap", device).CombinedOutput()
490 if err != nil {
491 return rbdErrors(err, fmt.Errorf("rbd: failed to unmap device %s, error %v, rbd output: %s", device, err, string(output)))
492 }
493 klog.V(3).Infof("rbd: successfully unmap device %s", device)
494
495
496
497 rbdFile := filepath.Join(deviceMountPath, "rbd.json")
498 exists, err := utilpath.Exists(utilpath.CheckFollowSymlink, rbdFile)
499 if err != nil {
500 return err
501 }
502 if exists {
503 klog.V(3).Infof("rbd: old rbd.json is found under %s, cleaning it", deviceMountPath)
504 err = util.cleanOldRBDFile(plugin, rbdFile)
505 if err != nil {
506 klog.Errorf("rbd: failed to clean %s", rbdFile)
507 return err
508 }
509 klog.V(3).Infof("rbd: successfully remove %s", rbdFile)
510 }
511 return nil
512 }
513
514
515 func (util *rbdUtil) DetachBlockDisk(disk rbdDiskUnmapper, mapPath string) error {
516
517 if pathExists, pathErr := mount.PathExists(mapPath); pathErr != nil {
518 return fmt.Errorf("error checking if path exists: %v", pathErr)
519 } else if !pathExists {
520 klog.Warningf("Warning: Unmap skipped because path does not exist: %v", mapPath)
521 return nil
522 }
523
524 device, err := getBlockVolumeDevice(mapPath)
525 if err != nil {
526 return err
527 }
528
529 if len(device) == 0 {
530 return fmt.Errorf("DetachDisk failed , device is empty")
531 }
532
533 exec := disk.plugin.host.GetExec(disk.plugin.GetPluginName())
534
535 var rbdCmd string
536
537
538
539 if strings.HasPrefix(device, "/dev/nbd") {
540 rbdCmd = "rbd-nbd"
541 klog.V(4).Infof("rbd: using rbd-nbd for unmap function")
542 } else {
543 rbdCmd = "rbd"
544 klog.V(4).Infof("rbd: using rbd for unmap function")
545 }
546
547
548 output, err := exec.Command(rbdCmd, "unmap", device).CombinedOutput()
549 if err != nil {
550 return rbdErrors(err, fmt.Errorf("rbd: failed to unmap device %s, error %v, rbd output: %s", device, err, string(output)))
551 }
552 klog.V(3).Infof("rbd: successfully unmap device %s", device)
553
554 return nil
555 }
556
557
558
559 func (util *rbdUtil) cleanOldRBDFile(plugin *rbdPlugin, rbdFile string) error {
560 mounter := &rbdMounter{
561
562 rbd: newRBD("", "", "", "", false, plugin, util),
563 }
564 fp, err := os.Open(rbdFile)
565 if err != nil {
566 return fmt.Errorf("rbd: open err %s/%s", rbdFile, err)
567 }
568 defer fp.Close()
569
570 decoder := json.NewDecoder(fp)
571 if err = decoder.Decode(mounter); err != nil {
572 return fmt.Errorf("rbd: decode err: %v", err)
573 }
574
575
576
577
578 err = util.rbdUnlock(*mounter)
579 if err == nil {
580 os.Remove(rbdFile)
581 }
582 return err
583 }
584
585
586 func (util *rbdUtil) CreateImage(p *rbdVolumeProvisioner) (r *v1.RBDPersistentVolumeSource, size int, err error) {
587 var output []byte
588 capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
589
590 sz, err := volumehelpers.RoundUpToMiBInt(capacity)
591 if err != nil {
592 return nil, 0, err
593 }
594 volSz := fmt.Sprintf("%d", sz)
595 mon := util.kernelRBDMonitorsOpt(p.Mon)
596 if p.rbdMounter.imageFormat == rbdImageFormat2 {
597 klog.V(4).Infof("rbd: create %s size %s format %s (features: %s) using mon %s, pool %s id %s key <masked>", p.rbdMounter.Image, volSz, p.rbdMounter.imageFormat, p.rbdMounter.imageFeatures, mon, p.rbdMounter.Pool, p.rbdMounter.adminID)
598 } else {
599 klog.V(4).Infof("rbd: create %s size %s format %s using mon %s, pool %s id %s key <masked>", p.rbdMounter.Image, volSz, p.rbdMounter.imageFormat, mon, p.rbdMounter.Pool, p.rbdMounter.adminID)
600 }
601 args := []string{"create", p.rbdMounter.Image, "--size", volSz, "--pool", p.rbdMounter.Pool, "--id", p.rbdMounter.adminID, "-m", mon, "--key=" + p.rbdMounter.adminSecret, "--image-format", p.rbdMounter.imageFormat}
602 if p.rbdMounter.imageFormat == rbdImageFormat2 {
603
604
605 features := strings.Join(p.rbdMounter.imageFeatures, ",")
606 args = append(args, "--image-feature", features)
607 }
608 output, err = p.exec.Command("rbd", args...).CombinedOutput()
609
610 if err != nil {
611 klog.Warningf("failed to create rbd image, output %v", string(output))
612 return nil, 0, fmt.Errorf("failed to create rbd image: %v, command output: %s", err, string(output))
613 }
614
615 return &v1.RBDPersistentVolumeSource{
616 CephMonitors: p.rbdMounter.Mon,
617 RBDImage: p.rbdMounter.Image,
618 RBDPool: p.rbdMounter.Pool,
619 }, sz, nil
620 }
621
622
623 func (util *rbdUtil) DeleteImage(p *rbdVolumeDeleter) error {
624 var output []byte
625 found, rbdOutput, err := util.rbdStatus(p.rbdMounter)
626 if err != nil {
627 return fmt.Errorf("error %v, rbd output: %v", err, rbdOutput)
628 }
629 if found {
630 klog.Infof("rbd %s is still being used ", p.rbdMounter.Image)
631 return fmt.Errorf("rbd image %s/%s is still being used, rbd output: %v", p.rbdMounter.Pool, p.rbdMounter.Image, rbdOutput)
632 }
633
634 mon := util.kernelRBDMonitorsOpt(p.rbdMounter.Mon)
635 klog.V(4).Infof("rbd: rm %s using mon %s, pool %s id %s key <masked>", p.rbdMounter.Image, mon, p.rbdMounter.Pool, p.rbdMounter.adminID)
636 output, err = p.exec.Command("rbd",
637 "rm", p.rbdMounter.Image, "--pool", p.rbdMounter.Pool, "--id", p.rbdMounter.adminID, "-m", mon, "--key="+p.rbdMounter.adminSecret).CombinedOutput()
638 if err == nil {
639 return nil
640 }
641
642 klog.Errorf("failed to delete rbd image: %v, command output: %s", err, string(output))
643 return fmt.Errorf("error %v, rbd output: %v", err, string(output))
644 }
645
646
647 func (util *rbdUtil) ExpandImage(rbdExpander *rbdVolumeExpander, oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error) {
648 var output []byte
649 var err error
650
651
652 sz, err := volumehelpers.RoundUpToMiBInt(newSize)
653 if err != nil {
654 return oldSize, err
655 }
656
657 newVolSz := fmt.Sprintf("%d", sz)
658 newSizeQuant := resource.MustParse(fmt.Sprintf("%dMi", sz))
659
660
661 curSize, infoErr := util.rbdInfo(rbdExpander.rbdMounter)
662 if infoErr != nil {
663 return oldSize, fmt.Errorf("rbd info failed, error: %v", infoErr)
664 }
665 if curSize >= sz {
666 return newSizeQuant, nil
667 }
668
669
670 mon := util.kernelRBDMonitorsOpt(rbdExpander.rbdMounter.Mon)
671 klog.V(4).Infof("rbd: resize %s using mon %s, pool %s id %s key <masked>", rbdExpander.rbdMounter.Image, mon, rbdExpander.rbdMounter.Pool, rbdExpander.rbdMounter.adminID)
672 output, err = rbdExpander.exec.Command("rbd",
673 "resize", rbdExpander.rbdMounter.Image, "--size", newVolSz, "--pool", rbdExpander.rbdMounter.Pool, "--id", rbdExpander.rbdMounter.adminID, "-m", mon, "--key="+rbdExpander.rbdMounter.adminSecret).CombinedOutput()
674 if err == nil {
675 return newSizeQuant, nil
676 }
677
678 klog.Errorf("failed to resize rbd image: %v, command output: %s", err, string(output))
679 return oldSize, err
680 }
681
682
683 func (util *rbdUtil) rbdInfo(b *rbdMounter) (int, error) {
684 var err error
685 var output []byte
686
687
688 id := b.adminID
689 secret := b.adminSecret
690 if id == "" {
691 id = b.ID
692 secret = b.Secret
693 }
694
695 mon := util.kernelRBDMonitorsOpt(b.Mon)
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713 klog.V(4).Infof("rbd: info %s using mon %s, pool %s id %s key <masked>", b.Image, mon, b.Pool, id)
714 output, err = b.exec.Command("rbd",
715 "info", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret, "-k=/dev/null", "--format=json").Output()
716
717 if err, ok := err.(*exec.Error); ok {
718 if err.Err == exec.ErrNotFound {
719 klog.Errorf("rbd cmd not found")
720
721 return 0, err
722 }
723 }
724
725
726 if err != nil {
727 return 0, err
728 }
729
730 if len(output) == 0 {
731 return 0, fmt.Errorf("can not get image size info %s: %s", b.Image, string(output))
732 }
733
734 return getRbdImageSize(output)
735 }
736
737 func getRbdImageSize(output []byte) (int, error) {
738 info := struct {
739 Size int64 `json:"size"`
740 }{}
741 if err := json.Unmarshal(output, &info); err != nil {
742 return 0, fmt.Errorf("parse rbd info output failed: %s, %v", string(output), err)
743 }
744 return int(info.Size / rbdImageSizeUnitMiB), nil
745 }
746
747
748 func (util *rbdUtil) rbdStatus(b *rbdMounter) (bool, string, error) {
749 var err error
750 var output string
751 var cmd []byte
752
753
754 id := b.adminID
755 secret := b.adminSecret
756 if id == "" {
757 id = b.ID
758 secret = b.Secret
759 }
760
761 mon := util.kernelRBDMonitorsOpt(b.Mon)
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776 klog.V(4).Infof("rbd: status %s using mon %s, pool %s id %s key <masked>", b.Image, mon, b.Pool, id)
777 cmd, err = b.exec.Command("rbd",
778 "status", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret).CombinedOutput()
779 output = string(cmd)
780
781 if err, ok := err.(*exec.Error); ok {
782 if err.Err == exec.ErrNotFound {
783 klog.Errorf("rbd cmd not found")
784
785 return false, output, err
786 }
787 }
788
789
790 if err != nil {
791 return false, output, err
792 }
793
794 if strings.Contains(output, imageWatcherStr) {
795 klog.V(4).Infof("rbd: watchers on %s: %s", b.Image, output)
796 return true, output, nil
797 }
798 klog.Warningf("rbd: no watchers on %s", b.Image)
799 return false, output, nil
800 }
801
802
803 func getRbdImageInfo(deviceMountPath string) (*rbdImageInfo, error) {
804 deviceMountedPathSeps := strings.Split(filepath.Base(deviceMountPath), "-image-")
805 if len(deviceMountedPathSeps) != 2 {
806 return nil, fmt.Errorf("can't found devicePath for %s ", deviceMountPath)
807 }
808 return &rbdImageInfo{
809 pool: deviceMountedPathSeps[0],
810 name: deviceMountedPathSeps[1],
811 }, nil
812 }
813
View as plain text