github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	supercmd := jujucmd.NewSubSuperCommand(cmd.SuperCommandParams{
    28  		Name:        "volume",
    29  		Doc:         volumeCmdDoc,
    30  		UsagePrefix: "juju storage",
    31  		Purpose:     volumeCmdPurpose,
    32  	})
    33  	supercmd.Register(envcmd.Wrap(&VolumeListCommand{}))
    34  	return supercmd
    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  	ProviderVolumeId string `yaml:"provider-id,omitempty" json:"provider-id,omitempty"`
    46  
    47  	// Storage is the ID of the storage instance that the volume is
    48  	// assigned to, if any.
    49  	Storage string `yaml:"storage,omitempty" json:"storage,omitempty"`
    50  
    51  	// Attachments is the set of entities attached to the volume.
    52  	Attachments *VolumeAttachments `yaml:"attachments,omitempty" json:"attachments,omitempty"`
    53  
    54  	// from params.Volume
    55  	HardwareId string `yaml:"hardware-id,omitempty" json:"hardware-id,omitempty"`
    56  
    57  	// from params.Volume
    58  	Size uint64 `yaml:"size" json:"size"`
    59  
    60  	// from params.Volume
    61  	Persistent bool `yaml:"persistent" json:"persistent"`
    62  
    63  	// from params.Volume
    64  	Status EntityStatus `yaml:"status,omitempty" json:"status,omitempty"`
    65  }
    66  
    67  type EntityStatus struct {
    68  	Current params.Status `json:"current,omitempty" yaml:"current,omitempty"`
    69  	Message string        `json:"message,omitempty" yaml:"message,omitempty"`
    70  	Since   string        `json:"since,omitempty" yaml:"since,omitempty"`
    71  }
    72  
    73  type VolumeAttachments struct {
    74  	Machines map[string]MachineVolumeAttachment `yaml:"machines,omitempty" json:"machines,omitempty"`
    75  	Units    map[string]UnitStorageAttachment   `yaml:"units,omitempty" json:"units,omitempty"`
    76  }
    77  
    78  type MachineVolumeAttachment struct {
    79  	DeviceName string `yaml:"device,omitempty" json:"device,omitempty"`
    80  	DeviceLink string `yaml:"device-link,omitempty" json:"device-link,omitempty"`
    81  	BusAddress string `yaml:"bus-address,omitempty" json:"bus-address,omitempty"`
    82  	ReadOnly   bool   `yaml:"read-only" json:"read-only"`
    83  	// TODO(axw) add machine volume attachment status when we have it
    84  }
    85  
    86  // convertToVolumeInfo returns a map of volume IDs to volume info.
    87  func convertToVolumeInfo(all []params.VolumeDetailsResult) (map[string]VolumeInfo, error) {
    88  	result := make(map[string]VolumeInfo)
    89  	for _, one := range all {
    90  		volumeTag, info, err := createVolumeInfo(one)
    91  		if err != nil {
    92  			return nil, errors.Trace(err)
    93  		}
    94  		result[volumeTag.Id()] = info
    95  	}
    96  	return result, nil
    97  }
    98  
    99  var idFromTag = func(s string) (string, error) {
   100  	tag, err := names.ParseTag(s)
   101  	if err != nil {
   102  		return "", errors.Annotatef(err, "invalid tag %v", tag)
   103  	}
   104  	return tag.Id(), nil
   105  }
   106  
   107  func createVolumeInfo(result params.VolumeDetailsResult) (names.VolumeTag, VolumeInfo, error) {
   108  	details := result.Details
   109  	if details == nil {
   110  		details = volumeDetailsFromLegacy(result)
   111  	}
   112  
   113  	volumeTag, err := names.ParseVolumeTag(details.VolumeTag)
   114  	if err != nil {
   115  		return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err)
   116  	}
   117  
   118  	var info VolumeInfo
   119  	info.ProviderVolumeId = details.Info.VolumeId
   120  	info.HardwareId = details.Info.HardwareId
   121  	info.Size = details.Info.Size
   122  	info.Persistent = details.Info.Persistent
   123  	info.Status = EntityStatus{
   124  		details.Status.Status,
   125  		details.Status.Info,
   126  		// TODO(axw) we should support formatting as ISO time
   127  		common.FormatTime(details.Status.Since, false),
   128  	}
   129  
   130  	if len(details.MachineAttachments) > 0 {
   131  		machineAttachments := make(map[string]MachineVolumeAttachment)
   132  		for machineTag, attachment := range details.MachineAttachments {
   133  			machineId, err := idFromTag(machineTag)
   134  			if err != nil {
   135  				return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err)
   136  			}
   137  			machineAttachments[machineId] = MachineVolumeAttachment{
   138  				attachment.DeviceName,
   139  				attachment.DeviceLink,
   140  				attachment.BusAddress,
   141  				attachment.ReadOnly,
   142  			}
   143  		}
   144  		info.Attachments = &VolumeAttachments{
   145  			Machines: machineAttachments,
   146  		}
   147  	}
   148  
   149  	if details.Storage != nil {
   150  		storageTag, storageInfo, err := createStorageInfo(*details.Storage)
   151  		if err != nil {
   152  			return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err)
   153  		}
   154  		info.Storage = storageTag.Id()
   155  		if storageInfo.Attachments != nil {
   156  			info.Attachments.Units = storageInfo.Attachments.Units
   157  		}
   158  	}
   159  
   160  	return volumeTag, info, nil
   161  }
   162  
   163  // volumeDetailsFromLegacy converts from legacy data structures
   164  // to params.VolumeDetails. This exists only for backwards-
   165  // compatibility. Please think long and hard before changing it.
   166  func volumeDetailsFromLegacy(result params.VolumeDetailsResult) *params.VolumeDetails {
   167  	details := &params.VolumeDetails{
   168  		VolumeTag: result.LegacyVolume.VolumeTag,
   169  		Status:    result.LegacyVolume.Status,
   170  	}
   171  	details.Info.VolumeId = result.LegacyVolume.VolumeId
   172  	details.Info.HardwareId = result.LegacyVolume.HardwareId
   173  	details.Info.Size = result.LegacyVolume.Size
   174  	details.Info.Persistent = result.LegacyVolume.Persistent
   175  	if len(result.LegacyAttachments) > 0 {
   176  		attachments := make(map[string]params.VolumeAttachmentInfo)
   177  		for _, attachment := range result.LegacyAttachments {
   178  			attachments[attachment.MachineTag] = attachment.Info
   179  		}
   180  		details.MachineAttachments = attachments
   181  	}
   182  	if result.LegacyVolume.StorageTag != "" {
   183  		details.Storage = &params.StorageDetails{
   184  			StorageTag: result.LegacyVolume.StorageTag,
   185  			Status:     details.Status,
   186  		}
   187  		if result.LegacyVolume.UnitTag != "" {
   188  			// Servers with legacy storage do not support shared
   189  			// storage, so there will only be one attachment, and
   190  			// the owner is always a unit.
   191  			details.Storage.OwnerTag = result.LegacyVolume.UnitTag
   192  			if len(result.LegacyAttachments) == 1 {
   193  				details.Storage.Attachments = map[string]params.StorageAttachmentDetails{
   194  					result.LegacyVolume.UnitTag: params.StorageAttachmentDetails{
   195  						StorageTag: result.LegacyVolume.StorageTag,
   196  						UnitTag:    result.LegacyVolume.UnitTag,
   197  						MachineTag: result.LegacyAttachments[0].MachineTag,
   198  						// Don't set Location, because we can't infer that
   199  						// from the legacy volume details.
   200  						Location: "",
   201  					},
   202  				}
   203  			}
   204  		}
   205  	}
   206  	return details
   207  }