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  }