github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/storage/volume.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storage
     5  
     6  import (
     7  	"github.com/juju/cmd"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/names"
    10  
    11  	"github.com/juju/juju/apiserver/params"
    12  	jujucmd "github.com/juju/juju/cmd"
    13  	"github.com/juju/juju/cmd/envcmd"
    14  	"github.com/juju/juju/cmd/juju/common"
    15  )
    16  
    17  const volumeCmdDoc = `
    18  "juju storage volume" is used to manage storage volumes in
    19   the Juju environment.
    20  `
    21  
    22  const volumeCmdPurpose = "manage storage volumes"
    23  
    24  // NewVolumeSuperCommand creates the storage volume super subcommand and
    25  // registers the subcommands that it supports.
    26  func NewVolumeSuperCommand() cmd.Command {
    27  	poolcmd := jujucmd.NewSubSuperCommand(cmd.SuperCommandParams{
    28  		Name:        "volume",
    29  		Doc:         volumeCmdDoc,
    30  		UsagePrefix: "juju storage",
    31  		Purpose:     volumeCmdPurpose,
    32  	})
    33  	poolcmd.Register(envcmd.Wrap(&VolumeListCommand{}))
    34  	return poolcmd
    35  }
    36  
    37  // VolumeCommandBase is a helper base structure for volume commands.
    38  type VolumeCommandBase struct {
    39  	StorageCommandBase
    40  }
    41  
    42  // VolumeInfo defines the serialization behaviour for storage volume.
    43  type VolumeInfo struct {
    44  	// from params.Volume. This is provider-supplied unique volume id.
    45  	VolumeId string `yaml:"id" json:"id"`
    46  
    47  	// from params.Volume
    48  	HardwareId string `yaml:"hardwareid" json:"hardwareid"`
    49  
    50  	// from params.Volume
    51  	Size uint64 `yaml:"size" json:"size"`
    52  
    53  	// from params.Volume
    54  	Persistent bool `yaml:"persistent" json:"persistent"`
    55  
    56  	// from params.VolumeAttachments
    57  	DeviceName string `yaml:"device,omitempty" json:"device,omitempty"`
    58  
    59  	// from params.VolumeAttachments
    60  	ReadOnly bool `yaml:"read-only" json:"read-only"`
    61  
    62  	// from params.Volume. This is juju volume id.
    63  	Volume string `yaml:"volume,omitempty" json:"volume,omitempty"`
    64  
    65  	// from params.Volume.
    66  	Status EntityStatus `yaml:"status,omitempty" json:"status,omitempty"`
    67  }
    68  
    69  type EntityStatus struct {
    70  	Current params.Status `json:"current,omitempty" yaml:"current,omitempty"`
    71  	Message string        `json:"message,omitempty" yaml:"message,omitempty"`
    72  	Since   string        `json:"since,omitempty" yaml:"since,omitempty"`
    73  }
    74  
    75  // convertToVolumeInfo returns map of maps with volume info
    76  // keyed first on machine_id and then on volume_id.
    77  func convertToVolumeInfo(all []params.VolumeItem) (map[string]map[string]map[string]VolumeInfo, error) {
    78  	result := map[string]map[string]map[string]VolumeInfo{}
    79  	for _, one := range all {
    80  		if err := convertVolumeItem(one, result); err != nil {
    81  			return nil, errors.Trace(err)
    82  		}
    83  	}
    84  	return result, nil
    85  }
    86  
    87  func convertVolumeItem(item params.VolumeItem, all map[string]map[string]map[string]VolumeInfo) error {
    88  	if len(item.Attachments) != 0 {
    89  		// add info for volume attachments
    90  		return convertVolumeAttachments(item, all)
    91  	}
    92  	unattached, unit, storage := createInfo(item.Volume)
    93  	addOneToAll("unattached", unit, storage, unattached, all)
    94  	return nil
    95  }
    96  
    97  var idFromTag = func(s string) (string, error) {
    98  	tag, err := names.ParseTag(s)
    99  	if err != nil {
   100  		return "", errors.Annotatef(err, "invalid tag %v", tag)
   101  	}
   102  	return tag.Id(), nil
   103  }
   104  
   105  func convertVolumeAttachments(item params.VolumeItem, all map[string]map[string]map[string]VolumeInfo) error {
   106  	for _, one := range item.Attachments {
   107  		machine, err := idFromTag(one.MachineTag)
   108  		if err != nil {
   109  			return errors.Trace(err)
   110  		}
   111  		info, unit, storage := createInfo(item.Volume)
   112  		info.DeviceName = one.Info.DeviceName
   113  		info.ReadOnly = one.Info.ReadOnly
   114  
   115  		addOneToAll(machine, unit, storage, info, all)
   116  	}
   117  	return nil
   118  }
   119  
   120  func addOneToAll(machineId, unitId, storageId string, item VolumeInfo, all map[string]map[string]map[string]VolumeInfo) {
   121  	machineVolumes, ok := all[machineId]
   122  	if !ok {
   123  		machineVolumes = map[string]map[string]VolumeInfo{}
   124  		all[machineId] = machineVolumes
   125  	}
   126  	unitVolumes, ok := machineVolumes[unitId]
   127  	if !ok {
   128  		unitVolumes = map[string]VolumeInfo{}
   129  		machineVolumes[unitId] = unitVolumes
   130  	}
   131  	unitVolumes[storageId] = item
   132  }
   133  
   134  func createInfo(volume params.VolumeInstance) (info VolumeInfo, unit, storage string) {
   135  	info.VolumeId = volume.VolumeId
   136  	info.HardwareId = volume.HardwareId
   137  	info.Size = volume.Size
   138  	info.Persistent = volume.Persistent
   139  	info.Status = EntityStatus{
   140  		volume.Status.Status,
   141  		volume.Status.Info,
   142  		// TODO(axw) we should support formatting as ISO time
   143  		common.FormatTime(volume.Status.Since, false),
   144  	}
   145  
   146  	if v, err := idFromTag(volume.VolumeTag); err == nil {
   147  		info.Volume = v
   148  	}
   149  	var err error
   150  	if storage, err = idFromTag(volume.StorageTag); err != nil {
   151  		storage = "unassigned"
   152  	}
   153  	if unit, err = idFromTag(volume.UnitTag); err != nil {
   154  		unit = "unattached"
   155  	}
   156  	return
   157  }