github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/diskformatter/diskformatter.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package diskformatter defines a worker that watches for block devices 5 // assigned to storage instances owned by the unit that runs this worker, 6 // and creates filesystems on them as necessary. Each unit agent runs this 7 // worker. 8 package diskformatter 9 10 import ( 11 "bytes" 12 "os/exec" 13 14 "github.com/juju/errors" 15 "github.com/juju/loggo" 16 "github.com/juju/names" 17 18 "github.com/juju/juju/api/watcher" 19 "github.com/juju/juju/apiserver/params" 20 "github.com/juju/juju/storage" 21 "github.com/juju/juju/worker" 22 ) 23 24 var logger = loggo.GetLogger("juju.worker.diskformatter") 25 26 // defaultFilesystemType is the default filesystem type to 27 // create on a managed block device for a "filesystem" type 28 // storage instance. 29 const defaultFilesystemType = "ext4" 30 31 // BlockDeviceAccessor is an interface used to watch and retrieve details of 32 // the block devices assigned to storage instances owned by the unit. 33 type BlockDeviceAccessor interface { 34 WatchBlockDevices() (watcher.StringsWatcher, error) 35 BlockDevices([]names.DiskTag) (params.BlockDeviceResults, error) 36 BlockDeviceStorageInstances([]names.DiskTag) (params.StorageInstanceResults, error) 37 } 38 39 // NewWorker returns a new worker that creates filesystems on block devices 40 // assigned to this unit's storage instances. 41 func NewWorker( 42 accessor BlockDeviceAccessor, 43 ) worker.Worker { 44 return worker.NewStringsWorker(newDiskFormatter(accessor)) 45 } 46 47 func newDiskFormatter(accessor BlockDeviceAccessor) worker.StringsWatchHandler { 48 return &diskFormatter{accessor} 49 } 50 51 type diskFormatter struct { 52 accessor BlockDeviceAccessor 53 } 54 55 func (f *diskFormatter) SetUp() (watcher.StringsWatcher, error) { 56 return f.accessor.WatchBlockDevices() 57 } 58 59 func (f *diskFormatter) TearDown() error { 60 return nil 61 } 62 63 func (f *diskFormatter) Handle(diskNames []string) error { 64 tags := make([]names.DiskTag, len(diskNames)) 65 for i, name := range diskNames { 66 tags[i] = names.NewDiskTag(name) 67 } 68 69 // attachedBlockDevices returns the block devices that are 70 // assigned to the caller, and are known to be attached and 71 // visible to their associated machines. 72 blockDevices, err := f.attachedBlockDevices(tags) 73 if err != nil { 74 return err 75 } 76 77 blockDeviceTags := make([]names.DiskTag, len(blockDevices)) 78 for i, dev := range blockDevices { 79 blockDeviceTags[i] = names.NewDiskTag(dev.Name) 80 } 81 82 // Map block devices to the storage instances they are assigned to. 83 results, err := f.accessor.BlockDeviceStorageInstances(blockDeviceTags) 84 if err != nil { 85 return errors.Annotate(err, "cannot get assigned storage instances") 86 } 87 88 for i, result := range results.Results { 89 if result.Error != nil { 90 logger.Errorf( 91 "could not determine storage instance for block device %q: %v", 92 blockDevices[i].Name, result.Error, 93 ) 94 continue 95 } 96 if blockDevices[i].FilesystemType != "" { 97 logger.Debugf("block device %q already has a filesystem", blockDevices[i].Name) 98 continue 99 } 100 storageInstance := result.Result 101 if storageInstance.Kind != storage.StorageKindFilesystem { 102 logger.Debugf("storage instance %q does not need a filesystem", storageInstance.Id) 103 continue 104 } 105 devicePath, err := storage.BlockDevicePath(blockDevices[i]) 106 if err != nil { 107 logger.Errorf("cannot get path for block device %q: %v", blockDevices[i].Name, err) 108 continue 109 } 110 if err := createFilesystem(devicePath); err != nil { 111 logger.Errorf("failed to create filesystem on block device %q: %v", blockDevices[i].Name, err) 112 continue 113 } 114 } 115 116 return nil 117 } 118 119 func (f *diskFormatter) attachedBlockDevices(tags []names.DiskTag) ([]storage.BlockDevice, error) { 120 results, err := f.accessor.BlockDevices(tags) 121 if err != nil { 122 return nil, errors.Annotate(err, "cannot get block devices") 123 } 124 blockDevices := make([]storage.BlockDevice, 0, len(tags)) 125 for i := range results.Results { 126 result := results.Results[i] 127 if result.Error != nil { 128 if !errors.IsNotFound(result.Error) { 129 logger.Errorf("could not get details for block device %q", tags[i]) 130 } 131 continue 132 } 133 blockDevices = append(blockDevices, result.Result) 134 } 135 return blockDevices, nil 136 } 137 138 func createFilesystem(devicePath string) error { 139 logger.Debugf("attempting to create filesystem on %q", devicePath) 140 if err := maybeCreateFilesystem(devicePath); err != nil { 141 return err 142 } 143 logger.Infof("created filesystem on %q", devicePath) 144 return nil 145 } 146 147 func maybeCreateFilesystem(path string) error { 148 mkfscmd := "mkfs." + defaultFilesystemType 149 output, err := exec.Command(mkfscmd, path).CombinedOutput() 150 if err != nil { 151 return errors.Annotatef(err, "%s failed (%q)", mkfscmd, bytes.TrimSpace(output)) 152 } 153 return nil 154 }