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 }