github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/gce/google/disk.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package google
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	jujuos "github.com/juju/utils/os"
     9  	"github.com/juju/utils/series"
    10  	"google.golang.org/api/compute/v1"
    11  )
    12  
    13  // The different types of disk persistence supported by GCE.
    14  const (
    15  	diskPersistenceTypeScratch    = "SCRATCH"
    16  	diskPersistenceTypePersistent = "PERSISTENT"
    17  )
    18  
    19  type DiskType string
    20  
    21  // The types of disk supported by GCE
    22  const (
    23  	// persistent
    24  	DiskPersistentStandard DiskType = "pd-standard"
    25  	DiskPersistentSSD      DiskType = "pd-ssd"
    26  	// scratch
    27  	DiskLocalSSD DiskType = "local-ssd"
    28  )
    29  
    30  type DiskMode string
    31  
    32  // The different disk modes supported by GCE.
    33  const (
    34  	ModeRW DiskMode = "READ_WRITE"
    35  	ModeRO DiskMode = "READ_ONLY"
    36  )
    37  
    38  type DiskStatus string
    39  
    40  const (
    41  	StatusCreating  DiskStatus = "CREATING"
    42  	StatusFailed    DiskStatus = "FAILED"
    43  	StatusReady     DiskStatus = "READY"
    44  	StatusRestoring DiskStatus = "RESTORING"
    45  )
    46  
    47  // MinDiskSizeGB is the minimum/default size (in megabytes) for
    48  // GCE disks.
    49  //
    50  // Note: GCE does not currently have an official minimum disk size.
    51  // However, in testing we found the minimum size to be 10 GB for ubuntu
    52  // and 50 GB for windows due to the image size. See gceapi message.
    53  //
    54  // gceapi: Requested disk size cannot be smaller than the image size (10 GB)
    55  func MinDiskSizeGB(ser string) uint64 {
    56  	// See comment below that explains why we're ignoring the error
    57  	os, _ := series.GetOSFromSeries(ser)
    58  	switch os {
    59  	case jujuos.Ubuntu:
    60  		return 10
    61  	case jujuos.Windows:
    62  		return 50
    63  	// On default we just return a "sane" default since the error
    64  	// will be propagated through the api and appear in juju status anyway
    65  	default:
    66  		return 10
    67  	}
    68  }
    69  
    70  // gibToMib converts gibibytes to mebibytes.
    71  func gibToMib(g int64) uint64 {
    72  	return uint64(g) * 1024
    73  }
    74  
    75  // DiskSpec holds all the data needed to request a new disk on GCE.
    76  // Some fields are used only for attached disks (i.e. in association
    77  // with instances).
    78  type DiskSpec struct {
    79  	// Series is the OS series on which the disk size depends
    80  	Series string
    81  	// SizeHintGB is the requested disk size in Gigabytes. It must be
    82  	// greater than 0.
    83  	SizeHintGB uint64
    84  	// ImageURL is the location of the image to which the disk should
    85  	// be initialized.
    86  	ImageURL string
    87  	// Boot indicates that this is a boot disk. An instance may only
    88  	// have one boot disk. (attached only)
    89  	Boot bool
    90  	// Scratch indicates that the disk should be a "scratch" disk
    91  	// instead of a "persistent" disk (the default).
    92  	Scratch bool
    93  	// Readonly indicates that the disk should not support writes.
    94  	Readonly bool
    95  	// AutoDelete indicates that the attached disk should be removed
    96  	// when the instance to which it is attached is removed.
    97  	AutoDelete bool
    98  	// PersistenDiskType is exclusive to persistent disks and indicates which of the
    99  	// persistent types available this disk should be.
   100  	PersistentDiskType DiskType
   101  	// Name: Name of the resource; provided by the client when the resource
   102  	// is created. The name must be 1-63 characters long, and comply with
   103  	// RFC1035. Specifically, the name must be 1-63 characters long and
   104  	// match the regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means
   105  	// the first character must be a lowercase letter, and all following
   106  	// characters must be a dash, lowercase letter, or digit, except the
   107  	// last character, which cannot be a dash.
   108  	Name string
   109  	// Description holds a description of the disk, it currently holds
   110  	// modelUUID.
   111  	// This field is used instead of a tag or metadata because, at the moment of writing
   112  	// this feature, compute (v1) API does not support any way to add extra data
   113  	// to disks.
   114  	// Description was picked because it is not mutable (actually no field is) for disks.
   115  	// There is a metadata API but it is not supported for disks for the moment.
   116  	Description string
   117  }
   118  
   119  // TooSmall checks the spec's size hint and indicates whether or not
   120  // it is smaller than the minimum disk size.
   121  func (ds *DiskSpec) TooSmall() bool {
   122  	return ds.SizeHintGB < MinDiskSizeGB(ds.Series)
   123  }
   124  
   125  // SizeGB returns the disk size to use for a new disk. The size hint
   126  // is returned if it isn't too small (otherwise the min size is
   127  // returned).
   128  func (ds *DiskSpec) SizeGB() uint64 {
   129  	size := ds.SizeHintGB
   130  	if ds.TooSmall() {
   131  		size = MinDiskSizeGB(ds.Series)
   132  	}
   133  	return size
   134  }
   135  
   136  // newAttached builds a compute.AttachedDisk using the information in
   137  // the disk spec and returns it.
   138  //
   139  // Note: Not all AttachedDisk fields are set.
   140  func (ds *DiskSpec) newAttached() *compute.AttachedDisk {
   141  	// TODO(ericsnow) Fail if SizeHintGB is 0?
   142  	diskType := diskPersistenceTypePersistent
   143  	if ds.Scratch {
   144  		diskType = diskPersistenceTypeScratch
   145  	}
   146  	mode := ModeRW
   147  	if ds.Readonly {
   148  		mode = ModeRO
   149  	}
   150  
   151  	disk := compute.AttachedDisk{
   152  		Type:       diskType,
   153  		Boot:       ds.Boot,
   154  		Mode:       string(mode),
   155  		AutoDelete: ds.AutoDelete,
   156  		InitializeParams: &compute.AttachedDiskInitializeParams{
   157  			// DiskName (defaults to instance name)
   158  			DiskSizeGb: int64(ds.SizeGB()),
   159  			// DiskType (defaults to pd-standard, pd-ssd, local-ssd)
   160  			SourceImage: ds.ImageURL,
   161  		},
   162  		// Interface (defaults to SCSI)
   163  		// DeviceName (GCE sets this, persistent disk only)
   164  	}
   165  	return &disk
   166  }
   167  
   168  // newDetached creates a new detached persistent disk representation,
   169  // this DOES NOT create a disk in gce, just creates the spec.
   170  // reference in https://cloud.google.com/compute/docs/reference/latest/disks#resource
   171  func (ds *DiskSpec) newDetached() (*compute.Disk, error) {
   172  	if ds.Scratch {
   173  		return nil, errors.New("cannot create scratch volumes detached")
   174  	}
   175  	if ds.PersistentDiskType == DiskLocalSSD {
   176  		return nil, errors.New("cannot create local ssd disks detached")
   177  	}
   178  	return &compute.Disk{
   179  		Name:        ds.Name,
   180  		SizeGb:      int64(ds.SizeGB()),
   181  		SourceImage: ds.ImageURL,
   182  		Type:        string(ds.PersistentDiskType),
   183  		Description: ds.Description,
   184  	}, nil
   185  }
   186  
   187  // AttachedDisk represents a disk that is attached to an instance.
   188  type AttachedDisk struct {
   189  	// VolumeName is the name of the volume that is attached, this is unique
   190  	// and used by gce as an identifier.
   191  	VolumeName string
   192  	// DeviceName is the name of the device in the instance, typycally
   193  	// is reflected into the /dev/disk/by-id/google-*
   194  	DeviceName string
   195  	// Mode is the read/write mode of the disk.
   196  	Mode DiskMode
   197  }
   198  
   199  // Disk represents a gce disk.
   200  type Disk struct {
   201  	// Id is an unique identifier google adds to the disk, it usually
   202  	// is not used in the API.
   203  	Id uint64
   204  	// Name is a unique identifier string for each disk.
   205  	Name string
   206  	// Description holds the description field for a disk, we store env UUID here.
   207  	Description string
   208  	// Size is the size in mbit.
   209  	Size uint64
   210  	// Type is one of the available disk types supported by
   211  	// gce (persistent or ephemeral).
   212  	Type DiskType
   213  	// Zone indicates the zone in which the disk lives.
   214  	Zone string
   215  	// DiskStatus holds the status of he aforementioned disk.
   216  	Status DiskStatus
   217  }
   218  
   219  func NewDisk(cd *compute.Disk) *Disk {
   220  	d := &Disk{
   221  		Id:          cd.Id,
   222  		Name:        cd.Name,
   223  		Description: cd.Description,
   224  		Size:        gibToMib(cd.SizeGb),
   225  		Type:        DiskType(cd.Type),
   226  		Zone:        cd.Zone,
   227  		Status:      DiskStatus(cd.Status),
   228  	}
   229  	return d
   230  }