github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/gce/google/conn_disks.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package google 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 "google.golang.org/api/compute/v1" 12 ) 13 14 // CreateDisks implements storage section of gceConnection. 15 func (gce *Connection) CreateDisks(zone string, disks []DiskSpec) ([]*Disk, error) { 16 results := make([]*Disk, len(disks)) 17 for i, disk := range disks { 18 d, err := disk.newDetached() 19 if err != nil { 20 return []*Disk{}, errors.Annotate(err, "cannot create disk spec") 21 } 22 if err := gce.createDisk(zone, d); err != nil { 23 return []*Disk{}, errors.Annotatef(err, "cannot create disk %q", disk.Name) 24 } 25 results[i] = NewDisk(d) 26 } 27 return results, nil 28 } 29 30 func (gce *Connection) createDisk(zone string, disk *compute.Disk) error { 31 return gce.raw.CreateDisk(gce.projectID, zone, disk) 32 } 33 34 // Disks implements storage section of gceConnection. 35 func (gce *Connection) Disks(zone string) ([]*Disk, error) { 36 computeDisks, err := gce.raw.ListDisks(gce.projectID, zone) 37 if err != nil { 38 return nil, errors.Annotatef(err, "cannot list disks for zone %q", zone) 39 } 40 disks := make([]*Disk, len(computeDisks)) 41 for i, disk := range computeDisks { 42 disks[i] = NewDisk(disk) 43 } 44 return disks, nil 45 } 46 47 // RemoveDisk implements storage section of gceConnection. 48 // TODO(perrito666) handle non existing disk, perhaps catch 404. 49 func (gce *Connection) RemoveDisk(zone, name string) error { 50 return gce.raw.RemoveDisk(gce.projectID, zone, name) 51 } 52 53 // Disk implements storage section of gceConnection. 54 func (gce *Connection) Disk(zone, name string) (*Disk, error) { 55 d, err := gce.raw.GetDisk(gce.projectID, zone, name) 56 if err != nil { 57 return nil, errors.Annotatef(err, "cannot get disk %q in zone %q", name, zone) 58 } 59 return NewDisk(d), nil 60 } 61 62 // deviceName will generate a device name from the passed 63 // <zone> and <diskId>, the device name must not be confused 64 // with the volume name, as it is used mainly to name the 65 // disk when attached to a linux OS. 66 func deviceName(zone string, diskId uint64) string { 67 return fmt.Sprintf("%s-%d", zone, diskId) 68 } 69 70 // AttachDisk implements storage section of gceConnection. 71 func (gce *Connection) AttachDisk(zone, volumeName, instanceId string, mode DiskMode) (*AttachedDisk, error) { 72 disk, err := gce.raw.GetDisk(gce.projectID, zone, volumeName) 73 if err != nil { 74 return nil, errors.Annotatef(err, "cannot obtain disk %q to attach it", volumeName) 75 } 76 attachedDisk := &compute.AttachedDisk{ 77 // Specifies a unique device name of your choice that 78 // is reflected into the /dev/disk/by-id/google-* 79 DeviceName: deviceName(zone, disk.Id), 80 Source: disk.SelfLink, 81 Mode: string(mode), 82 } 83 err = gce.raw.AttachDisk(gce.projectID, zone, instanceId, attachedDisk) 84 if err != nil { 85 return nil, errors.Annotate(err, "cannot attach disk") 86 } 87 return &AttachedDisk{ 88 VolumeName: volumeName, 89 DeviceName: attachedDisk.DeviceName, 90 Mode: mode, 91 }, nil 92 } 93 94 // DetachDisk implements storage section of gceConnection. 95 // disk existence is checked but not instance nor is attachment. 96 func (gce *Connection) DetachDisk(zone, instanceId, volumeName string) error { 97 disk, err := gce.raw.GetDisk(gce.projectID, zone, volumeName) 98 if err != nil { 99 return errors.Annotatef(err, "cannot obtain disk %q to detach it", volumeName) 100 } 101 dn := deviceName(zone, disk.Id) 102 err = gce.raw.DetachDisk(gce.projectID, zone, instanceId, dn) 103 if err != nil { 104 return errors.Annotatef(err, "cannot detach %q from %q", dn, instanceId) 105 } 106 return nil 107 } 108 109 // sourceToVolumeName will return the disk Name part of a 110 // source URL for a compute disk, compute is a bit inconsistent 111 // on its handling of disk resources, when used in requests it will 112 // take the disk.Name but when used as a parameter it will take 113 // the source url. 114 // The source url (short) format is: 115 // /projects/project/zones/zone/disks/disk 116 // the relevant part is disk. 117 func sourceToVolumeName(source string) string { 118 if source == "" { 119 return "" 120 } 121 parts := strings.Split(source, "/") 122 if len(parts) == 1 { 123 return source 124 } 125 lastItem := len(parts) - 1 126 return parts[lastItem] 127 } 128 129 // InstanceDisks implements storage section of gceConnection. 130 func (gce *Connection) InstanceDisks(zone, instanceId string) ([]*AttachedDisk, error) { 131 disks, err := gce.raw.InstanceDisks(gce.projectID, zone, instanceId) 132 if err != nil { 133 return nil, errors.Annotatef(err, "cannot get disks from instance") 134 } 135 att := make([]*AttachedDisk, len(disks)) 136 for i, disk := range disks { 137 att[i] = &AttachedDisk{ 138 VolumeName: sourceToVolumeName(disk.Source), 139 DeviceName: disk.DeviceName, 140 Mode: DiskMode(disk.Mode), 141 } 142 } 143 return att, nil 144 }