github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"fmt"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/names.v2"
    12  
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/cmd/juju/common"
    15  	"github.com/juju/juju/core/status"
    16  )
    17  
    18  // VolumeInfo defines the serialization behaviour for storage volume.
    19  type VolumeInfo struct {
    20  	// from params.Volume. This is provider-supplied unique volume id.
    21  	ProviderVolumeId string `yaml:"provider-id,omitempty" json:"provider-id,omitempty"`
    22  
    23  	// Storage is the ID of the storage instance that the volume is
    24  	// assigned to, if any.
    25  	Storage string `yaml:"storage,omitempty" json:"storage,omitempty"`
    26  
    27  	// Attachments is the set of entities attached to the volume.
    28  	Attachments *VolumeAttachments `yaml:"attachments,omitempty" json:"attachments,omitempty"`
    29  
    30  	// Pool is the name of the storage pool that the volume came from.
    31  	Pool string `yaml:"pool,omitempty" json:"pool,omitempty"`
    32  
    33  	// from params.Volume
    34  	HardwareId string `yaml:"hardware-id,omitempty" json:"hardware-id,omitempty"`
    35  
    36  	// from params.Volume
    37  	WWN string `yaml:"wwn,omitempty" json:"wwn,omitempty"`
    38  
    39  	// from params.Volume
    40  	Size uint64 `yaml:"size" json:"size"`
    41  
    42  	// from params.Volume
    43  	Persistent bool `yaml:"persistent" json:"persistent"`
    44  
    45  	// Life is the lifecycle state of the volume.
    46  	Life string `yaml:"life,omitempty" json:"life,omitempty"`
    47  
    48  	// from params.Volume
    49  	Status EntityStatus `yaml:"status,omitempty" json:"status,omitempty"`
    50  }
    51  
    52  type EntityStatus struct {
    53  	Current status.Status `json:"current,omitempty" yaml:"current,omitempty"`
    54  	Message string        `json:"message,omitempty" yaml:"message,omitempty"`
    55  	Since   string        `json:"since,omitempty" yaml:"since,omitempty"`
    56  }
    57  
    58  type VolumeAttachments struct {
    59  	Machines   map[string]VolumeAttachment      `yaml:"machines,omitempty" json:"machines,omitempty"`
    60  	Containers map[string]VolumeAttachment      `yaml:"containers,omitempty" json:"containers,omitempty"`
    61  	Units      map[string]UnitStorageAttachment `yaml:"units,omitempty" json:"units,omitempty"`
    62  }
    63  
    64  type VolumeAttachment struct {
    65  	DeviceName string `yaml:"device,omitempty" json:"device,omitempty"`
    66  	DeviceLink string `yaml:"device-link,omitempty" json:"device-link,omitempty"`
    67  	BusAddress string `yaml:"bus-address,omitempty" json:"bus-address,omitempty"`
    68  	ReadOnly   bool   `yaml:"read-only" json:"read-only"`
    69  	Life       string `yaml:"life,omitempty" json:"life,omitempty"`
    70  	// TODO(axw) add machine volume attachment status when we have it
    71  }
    72  
    73  //generateListVolumeOutput returns a map of volume info
    74  func generateListVolumeOutput(ctx *cmd.Context, api StorageListAPI, ids []string) (map[string]VolumeInfo, error) {
    75  
    76  	results, err := api.ListVolumes(ids)
    77  	if err != nil {
    78  		return nil, errors.Trace(err)
    79  	}
    80  	// filter out valid output, if any
    81  	var valid []params.VolumeDetails
    82  	for _, result := range results {
    83  		if result.Error == nil {
    84  			valid = append(valid, result.Result...)
    85  			continue
    86  		}
    87  		// display individual error
    88  		fmt.Fprintf(ctx.Stderr, "%v\n", result.Error)
    89  	}
    90  	if len(valid) == 0 {
    91  		return nil, nil
    92  	}
    93  	return convertToVolumeInfo(valid)
    94  }
    95  
    96  // convertToVolumeInfo returns a map of volume IDs to volume info.
    97  func convertToVolumeInfo(all []params.VolumeDetails) (map[string]VolumeInfo, error) {
    98  	result := make(map[string]VolumeInfo)
    99  	for _, one := range all {
   100  		volumeTag, info, err := createVolumeInfo(one)
   101  		if err != nil {
   102  			return nil, errors.Trace(err)
   103  		}
   104  		result[volumeTag.Id()] = info
   105  	}
   106  	return result, nil
   107  }
   108  
   109  var idFromTag = func(s string) (string, error) {
   110  	tag, err := names.ParseTag(s)
   111  	if err != nil {
   112  		return "", errors.Annotatef(err, "invalid tag %v", tag)
   113  	}
   114  	return tag.Id(), nil
   115  }
   116  
   117  func createVolumeInfo(details params.VolumeDetails) (names.VolumeTag, VolumeInfo, error) {
   118  	volumeTag, err := names.ParseVolumeTag(details.VolumeTag)
   119  	if err != nil {
   120  		return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err)
   121  	}
   122  
   123  	var info VolumeInfo
   124  	info.ProviderVolumeId = details.Info.VolumeId
   125  	info.HardwareId = details.Info.HardwareId
   126  	info.WWN = details.Info.WWN
   127  	info.Pool = details.Info.Pool
   128  	info.Size = details.Info.Size
   129  	info.Persistent = details.Info.Persistent
   130  	info.Life = string(details.Life)
   131  	info.Status = EntityStatus{
   132  		details.Status.Status,
   133  		details.Status.Info,
   134  		// TODO(axw) we should support formatting as ISO time
   135  		common.FormatTime(details.Status.Since, false),
   136  	}
   137  
   138  	attachmentsFromDetails := func(
   139  		in map[string]params.VolumeAttachmentDetails,
   140  		out map[string]VolumeAttachment,
   141  	) error {
   142  		for tag, attachment := range in {
   143  			id, err := idFromTag(tag)
   144  			if err != nil {
   145  				return errors.Trace(err)
   146  			}
   147  			out[id] = VolumeAttachment{
   148  				attachment.DeviceName,
   149  				attachment.DeviceLink,
   150  				attachment.BusAddress,
   151  				attachment.ReadOnly,
   152  				string(attachment.Life),
   153  			}
   154  		}
   155  		return nil
   156  	}
   157  
   158  	if len(details.MachineAttachments) > 0 {
   159  		machineAttachments := make(map[string]VolumeAttachment)
   160  		if err := attachmentsFromDetails(details.MachineAttachments, machineAttachments); err != nil {
   161  			return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err)
   162  		}
   163  		info.Attachments = &VolumeAttachments{
   164  			Machines: machineAttachments,
   165  		}
   166  	}
   167  
   168  	if len(details.UnitAttachments) > 0 {
   169  		unitAttachments := make(map[string]VolumeAttachment)
   170  		if err := attachmentsFromDetails(details.UnitAttachments, unitAttachments); err != nil {
   171  			return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err)
   172  		}
   173  		if info.Attachments == nil {
   174  			info.Attachments = &VolumeAttachments{}
   175  		}
   176  		info.Attachments.Containers = unitAttachments
   177  	}
   178  
   179  	if details.Storage != nil {
   180  		storageTag, storageInfo, err := createStorageInfo(*details.Storage)
   181  		if err != nil {
   182  			return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err)
   183  		}
   184  		info.Storage = storageTag.Id()
   185  		if storageInfo.Attachments != nil {
   186  			if info.Attachments == nil {
   187  				info.Attachments = &VolumeAttachments{}
   188  			}
   189  			info.Attachments.Units = storageInfo.Attachments.Units
   190  		}
   191  	}
   192  
   193  	return volumeTag, info, nil
   194  }