github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  }