provider/opc: Add Storage Volume Snapshots

Adds `storage_volume_snapshot` resource, and allows for creating a storage_volume from a storage volume snapshot.
Also adds documentation for additions, and tests.

```
TF_ACC=1 go test ./builtin/providers/opc -v -run=TestAccOPCStorageVolume -timeout 120m
=== RUN   TestAccOPCStorageVolumeSnapshot_importBasic
--- PASS: TestAccOPCStorageVolumeSnapshot_importBasic (24.72s)
=== RUN   TestAccOPCStorageVolumeSnapshot_basic
--- PASS: TestAccOPCStorageVolumeSnapshot_basic (24.01s)
=== RUN   TestAccOPCStorageVolume_Basic
--- PASS: TestAccOPCStorageVolume_Basic (12.99s)
=== RUN   TestAccOPCStorageVolume_Complete
--- PASS: TestAccOPCStorageVolume_Complete (13.91s)
=== RUN   TestAccOPCStorageVolume_MaxSize
--- PASS: TestAccOPCStorageVolume_MaxSize (14.47s)
=== RUN   TestAccOPCStorageVolume_Update
--- PASS: TestAccOPCStorageVolume_Update (22.36s)
=== RUN   TestAccOPCStorageVolume_FromSnapshot
--- PASS: TestAccOPCStorageVolume_FromSnapshot (32.28s)
```
This commit is contained in:
Jake Champlin 2017-04-06 02:07:38 -04:00
parent 871d33d90d
commit e569fd3f6c
No known key found for this signature in database
GPG Key ID: DC31F41958EF4AC2
12 changed files with 923 additions and 30 deletions

View File

@ -0,0 +1,29 @@
package opc
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCStorageVolumeSnapshot_importBasic(t *testing.T) {
resourceName := "opc_compute_storage_volume_snapshot.test"
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: opcResourceCheck(resourceName, testAccCheckStorageVolumeSnapshotDestroyed),
Steps: []resource.TestStep{
{
Config: testAccStorageVolumeSnapshot_basic(rInt),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -68,6 +68,7 @@ func Provider() terraform.ResourceProvider {
"opc_compute_sec_rule": resourceOPCSecRule(), "opc_compute_sec_rule": resourceOPCSecRule(),
"opc_compute_ssh_key": resourceOPCSSHKey(), "opc_compute_ssh_key": resourceOPCSSHKey(),
"opc_compute_storage_volume": resourceOPCStorageVolume(), "opc_compute_storage_volume": resourceOPCStorageVolume(),
"opc_compute_storage_volume_snapshot": resourceOPCStorageVolumeSnapshot(),
"opc_compute_vnic_set": resourceOPCVNICSet(), "opc_compute_vnic_set": resourceOPCVNICSet(),
"opc_compute_security_protocol": resourceOPCSecurityProtocol(), "opc_compute_security_protocol": resourceOPCSecurityProtocol(),
"opc_compute_ip_address_prefix_set": resourceOPCIPAddressPrefixSet(), "opc_compute_ip_address_prefix_set": resourceOPCIPAddressPrefixSet(),

View File

@ -42,6 +42,26 @@ func resourceOPCStorageVolume() *schema.Resource {
}, true), }, true),
}, },
"snapshot": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"snapshot_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"snapshot_account": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"bootable": { "bootable": {
Type: schema.TypeList, Type: schema.TypeList,
Optional: true, Optional: true,
@ -128,7 +148,19 @@ func resourceOPCStorageVolumeCreate(d *schema.ResourceData, meta interface{}) er
Tags: getStringList(d, "tags"), Tags: getStringList(d, "tags"),
} }
expandOPCStorageVolumeOptionalFields(d, input) expandOPCStorageVolumeOptionalFields(d, &input)
if v, ok := d.GetOk("snapshot"); ok {
input.Snapshot = v.(string)
}
if v, ok := d.GetOk("snapshot_account"); ok {
input.SnapshotAccount = v.(string)
}
if v, ok := d.GetOk("snapshot_id"); ok {
input.SnapshotID = v.(string)
}
info, err := client.CreateStorageVolume(&input) info, err := client.CreateStorageVolume(&input)
if err != nil { if err != nil {
@ -188,6 +220,9 @@ func resourceOPCStorageVolumeRead(d *schema.ResourceData, meta interface{}) erro
d.Set("name", result.Name) d.Set("name", result.Name)
d.Set("description", result.Description) d.Set("description", result.Description)
d.Set("storage", result.Properties[0]) d.Set("storage", result.Properties[0])
d.Set("snapshot", result.Snapshot)
d.Set("snapshot_id", result.SnapshotID)
d.Set("snapshot_account", result.SnapshotAccount)
size, err := strconv.Atoi(result.Size) size, err := strconv.Atoi(result.Size)
if err != nil { if err != nil {
return err return err
@ -220,11 +255,11 @@ func resourceOPCStorageVolumeDelete(d *schema.ResourceData, meta interface{}) er
return nil return nil
} }
func expandOPCStorageVolumeOptionalFields(d *schema.ResourceData, input compute.CreateStorageVolumeInput) { func expandOPCStorageVolumeOptionalFields(d *schema.ResourceData, input *compute.CreateStorageVolumeInput) {
value, exists := d.GetOk("bootable") bootValue, bootExists := d.GetOk("bootable")
input.Bootable = exists input.Bootable = bootExists
if exists { if bootExists {
configs := value.([]interface{}) configs := bootValue.([]interface{})
config := configs[0].(map[string]interface{}) config := configs[0].(map[string]interface{})
input.ImageList = config["image_list"].(string) input.ImageList = config["image_list"].(string)

View File

@ -0,0 +1,227 @@
package opc
import (
"fmt"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceOPCStorageVolumeSnapshot() *schema.Resource {
return &schema.Resource{
Create: resourceOPCStorageVolumeSnapshotCreate,
Read: resourceOPCStorageVolumeSnapshotRead,
Delete: resourceOPCStorageVolumeSnapshotDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
// Required Attributes
"volume": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
// Optional Attributes
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
// Optional, but also computed if unspecified
"name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"parent_volume_bootable": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "false",
},
"collocated": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},
"tags": tagsForceNewSchema(),
// Computed Attributes
"account": {
Type: schema.TypeString,
Computed: true,
},
"machine_image_name": {
Type: schema.TypeString,
Computed: true,
},
"size": {
Type: schema.TypeString,
Computed: true,
},
"property": {
Type: schema.TypeString,
Computed: true,
},
"platform": {
Type: schema.TypeString,
Computed: true,
},
"snapshot_timestamp": {
Type: schema.TypeString,
Computed: true,
},
"snapshot_id": {
Type: schema.TypeString,
Computed: true,
},
"start_timestamp": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"status_detail": {
Type: schema.TypeString,
Computed: true,
},
"status_timestamp": {
Type: schema.TypeString,
Computed: true,
},
"uri": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceOPCStorageVolumeSnapshotCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).StorageVolumeSnapshots()
// Get required attribute
input := &compute.CreateStorageVolumeSnapshotInput{
Volume: d.Get("volume").(string),
}
if v, ok := d.GetOk("description"); ok {
input.Description = v.(string)
}
if v, ok := d.GetOk("name"); ok {
input.Name = v.(string)
}
if v, ok := d.GetOk("parent_volume_bootable"); ok {
input.ParentVolumeBootable = v.(string)
}
collocated := d.Get("collocated").(bool)
if collocated {
input.Property = compute.SnapshotPropertyCollocated
}
tags := getStringList(d, "tags")
if len(tags) > 0 {
input.Tags = tags
}
info, err := client.CreateStorageVolumeSnapshot(input)
if err != nil {
return fmt.Errorf("Error creating snapshot '%s': %v", input.Name, err)
}
d.SetId(info.Name)
return resourceOPCStorageVolumeSnapshotRead(d, meta)
}
func resourceOPCStorageVolumeSnapshotRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).StorageVolumeSnapshots()
name := d.Id()
input := &compute.GetStorageVolumeSnapshotInput{
Name: name,
}
result, err := client.GetStorageVolumeSnapshot(input)
if err != nil {
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading storage volume snapshot '%s': %v", name, err)
}
if result == nil {
d.SetId("")
return nil
}
d.Set("volume", result.Volume)
d.Set("description", result.Description)
d.Set("name", result.Name)
d.Set("parent_volume_bootable", result.ParentVolumeBootable)
d.Set("property", result.Property)
d.Set("platform", result.Platform)
d.Set("account", result.Account)
d.Set("machine_image_name", result.MachineImageName)
d.Set("size", result.Size)
d.Set("snapshot_timestamp", result.SnapshotTimestamp)
d.Set("snapshot_id", result.SnapshotID)
d.Set("start_timestamp", result.StartTimestamp)
d.Set("status", result.Status)
d.Set("status_detail", result.StatusDetail)
d.Set("status_timestamp", result.StatusTimestamp)
d.Set("uri", result.URI)
if result.Property != compute.SnapshotPropertyCollocated {
d.Set("collocated", false)
} else {
d.Set("collocated", true)
}
if err := setStringList(d, "tags", result.Tags); err != nil {
return err
}
return nil
}
func resourceOPCStorageVolumeSnapshotDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).StorageVolumeSnapshots()
name := d.Id()
input := &compute.DeleteStorageVolumeSnapshotInput{
Name: name,
}
if err := client.DeleteStorageVolumeSnapshot(input); err != nil {
return fmt.Errorf("Error deleting storage volume snapshot '%s': %v", name, err)
}
return nil
}

View File

@ -0,0 +1,88 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCStorageVolumeSnapshot_basic(t *testing.T) {
snapshotName := "opc_compute_storage_volume_snapshot.test"
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: opcResourceCheck(snapshotName, testAccCheckStorageVolumeSnapshotDestroyed),
Steps: []resource.TestStep{
{
Config: testAccStorageVolumeSnapshot_basic(rInt),
Check: resource.ComposeTestCheckFunc(opcResourceCheck(snapshotName, testAccCheckStorageVolumeSnapshotExists),
resource.TestCheckResourceAttr(snapshotName, "name", fmt.Sprintf("test-acc-stor-vol-%d", rInt)),
resource.TestCheckResourceAttr(snapshotName, "parent_volume_bootable", "false"),
resource.TestCheckResourceAttr(snapshotName, "collocated", "true"),
resource.TestCheckResourceAttr(snapshotName, "size", "5"),
),
},
},
})
}
func testAccCheckStorageVolumeSnapshotExists(state *OPCResourceState) error {
client := state.Client.StorageVolumeSnapshots()
snapshotName := state.Attributes["name"]
input := &compute.GetStorageVolumeSnapshotInput{
Name: snapshotName,
}
info, err := client.GetStorageVolumeSnapshot(input)
if err != nil {
return fmt.Errorf("Error retrieving state of snapshot '%s': %v", snapshotName, err)
}
if info == nil {
return fmt.Errorf("No info found for snapshot '%s'", snapshotName)
}
return nil
}
func testAccCheckStorageVolumeSnapshotDestroyed(state *OPCResourceState) error {
client := state.Client.StorageVolumeSnapshots()
snapshotName := state.Attributes["name"]
input := &compute.GetStorageVolumeSnapshotInput{
Name: snapshotName,
}
info, err := client.GetStorageVolumeSnapshot(input)
if err != nil {
return fmt.Errorf("Error retrieving state of snapshot '%s': %v", snapshotName, err)
}
if info != nil {
return fmt.Errorf("Snapshot '%s' still exists", snapshotName)
}
return nil
}
func testAccStorageVolumeSnapshot_basic(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_storage_volume" "foo" {
name = "test-acc-stor-vol-%d"
description = "testAccStorageVolumeSnapshot_basic"
size = 5
}
resource "opc_compute_storage_volume_snapshot" "test" {
name = "test-acc-stor-vol-%d"
description = "storage volume snapshot"
collocated = true
volume = "${opc_compute_storage_volume.foo.name}"
}
`, rInt, rInt)
}

View File

@ -116,6 +116,29 @@ func TestAccOPCStorageVolume_Bootable(t *testing.T) {
}) })
} }
func TestAccOPCStorageVolume_FromSnapshot(t *testing.T) {
volumeResourceName := "opc_compute_storage_volume.test"
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: opcResourceCheck(volumeResourceName, testAccCheckStorageVolumeDestroyed),
Steps: []resource.TestStep{
{
Config: testAccStorageVolumeFromSnapshot(rInt),
Check: resource.ComposeTestCheckFunc(
opcResourceCheck(volumeResourceName, testAccCheckStorageVolumeExists),
resource.TestCheckResourceAttr(volumeResourceName, "name", fmt.Sprintf("test-acc-stor-vol-final-%d", rInt)),
resource.TestCheckResourceAttrSet(volumeResourceName, "snapshot"),
resource.TestCheckResourceAttrSet(volumeResourceName, "snapshot_id"),
resource.TestCheckResourceAttr(volumeResourceName, "size", "5"),
),
},
},
})
}
func testAccCheckStorageVolumeExists(state *OPCResourceState) error { func testAccCheckStorageVolumeExists(state *OPCResourceState) error {
sv := state.Client.StorageVolumes() sv := state.Client.StorageVolumes()
volumeName := state.Attributes["name"] volumeName := state.Attributes["name"]
@ -204,3 +227,29 @@ resource "opc_compute_storage_volume" "test" {
size = 2048 size = 2048
} }
` `
func testAccStorageVolumeFromSnapshot(rInt int) string {
return fmt.Sprintf(`
// Initial Storage Volume to create snapshot with
resource "opc_compute_storage_volume" "foo" {
name = "test-acc-stor-vol-%d"
description = "Acc Test intermediary storage volume for snapshot"
size = 5
}
resource "opc_compute_storage_volume_snapshot" "foo" {
description = "testing-acc"
name = "test-acc-stor-snapshot-%d"
collocated = true
volume = "${opc_compute_storage_volume.foo.name}"
}
// Create storage volume from snapshot
resource "opc_compute_storage_volume" "test" {
name = "test-acc-stor-vol-final-%d"
description = "storage volume from snapshot"
size = 5
snapshot_id = "${opc_compute_storage_volume_snapshot.foo.snapshot_id}"
}
`, rInt, rInt, rInt)
}

View File

@ -0,0 +1,152 @@
package compute
const (
IPAddressAssociationDescription = "ip address association"
IPAddressAssociationContainerPath = "/network/v1/ipassociation/"
IPAddressAssociationResourcePath = "/network/v1/ipassociation"
)
type IPAddressAssociationsClient struct {
ResourceClient
}
// IPAddressAssociations() returns an IPAddressAssociationsClient that can be used to access the
// necessary CRUD functions for IP Address Associations.
func (c *Client) IPAddressAssociations() *IPAddressAssociationsClient {
return &IPAddressAssociationsClient{
ResourceClient: ResourceClient{
Client: c,
ResourceDescription: IPAddressAssociationDescription,
ContainerPath: IPAddressAssociationContainerPath,
ResourceRootPath: IPAddressAssociationResourcePath,
},
}
}
// IPAddressAssociationInfo contains the exported fields necessary to hold all the information about an
// IP Address Association
type IPAddressAssociationInfo struct {
// The name of the NAT IP address reservation.
IPAddressReservation string `json:"ipAddressReservation"`
// Name of the virtual NIC associated with this NAT IP reservation.
Vnic string `json:"vnic"`
// The name of the IP Address Association
Name string `json:"name"`
// Description of the IP Address Association
Description string `json:"description"`
// Slice of tags associated with the IP Address Association
Tags []string `json:"tags"`
// Uniform Resource Identifier for the IP Address Association
Uri string `json:"uri"`
}
type CreateIPAddressAssociationInput struct {
// The name of the IP Address Association to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive.
// Required
Name string `json:"name"`
// The name of the NAT IP address reservation.
// Optional
IPAddressReservation string `json:"ipAddressReservation,omitempty"`
// Name of the virtual NIC associated with this NAT IP reservation.
// Optional
Vnic string `json:"vnic,omitempty"`
// Description of the IPAddressAssociation
// Optional
Description string `json:"description"`
// String slice of tags to apply to the IP Address Association object
// Optional
Tags []string `json:"tags"`
}
// Create a new IP Address Association from an IPAddressAssociationsClient and an input struct.
// Returns a populated Info struct for the IP Address Association, and any errors
func (c *IPAddressAssociationsClient) CreateIPAddressAssociation(input *CreateIPAddressAssociationInput) (*IPAddressAssociationInfo, error) {
input.Name = c.getQualifiedName(input.Name)
input.IPAddressReservation = c.getQualifiedName(input.IPAddressReservation)
input.Vnic = c.getQualifiedName(input.Vnic)
var ipInfo IPAddressAssociationInfo
if err := c.createResource(&input, &ipInfo); err != nil {
return nil, err
}
return c.success(&ipInfo)
}
type GetIPAddressAssociationInput struct {
// The name of the IP Address Association to query for. Case-sensitive
// Required
Name string `json:"name"`
}
// Returns a populated IPAddressAssociationInfo struct from an input struct
func (c *IPAddressAssociationsClient) GetIPAddressAssociation(input *GetIPAddressAssociationInput) (*IPAddressAssociationInfo, error) {
input.Name = c.getQualifiedName(input.Name)
var ipInfo IPAddressAssociationInfo
if err := c.getResource(input.Name, &ipInfo); err != nil {
return nil, err
}
return c.success(&ipInfo)
}
// UpdateIPAddressAssociationInput defines what to update in a ip address association
type UpdateIPAddressAssociationInput struct {
// The name of the IP Address Association to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive.
// Required
Name string `json:"name"`
// The name of the NAT IP address reservation.
// Optional
IPAddressReservation string `json:"ipAddressReservation,omitempty"`
// Name of the virtual NIC associated with this NAT IP reservation.
// Optional
Vnic string `json:"vnic,omitempty"`
// Description of the IPAddressAssociation
// Optional
Description string `json:"description"`
// String slice of tags to apply to the IP Address Association object
// Optional
Tags []string `json:"tags"`
}
// UpdateIPAddressAssociation update the ip address association
func (c *IPAddressAssociationsClient) UpdateIPAddressAssociation(updateInput *UpdateIPAddressAssociationInput) (*IPAddressAssociationInfo, error) {
updateInput.Name = c.getQualifiedName(updateInput.Name)
updateInput.IPAddressReservation = c.getQualifiedName(updateInput.IPAddressReservation)
updateInput.Vnic = c.getQualifiedName(updateInput.Vnic)
var ipInfo IPAddressAssociationInfo
if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil {
return nil, err
}
return c.success(&ipInfo)
}
type DeleteIPAddressAssociationInput struct {
// The name of the IP Address Association to query for. Case-sensitive
// Required
Name string `json:"name"`
}
func (c *IPAddressAssociationsClient) DeleteIPAddressAssociation(input *DeleteIPAddressAssociationInput) error {
return c.deleteResource(input.Name)
}
// Unqualifies any qualified fields in the IPAddressAssociationInfo struct
func (c *IPAddressAssociationsClient) success(info *IPAddressAssociationInfo) (*IPAddressAssociationInfo, error) {
c.unqualify(&info.Name)
c.unqualify(&info.Vnic)
c.unqualify(&info.IPAddressReservation)
return info, nil
}

View File

@ -0,0 +1,250 @@
package compute
import (
"fmt"
"strings"
)
const (
StorageVolumeSnapshotDescription = "storage volume snapshot"
StorageVolumeSnapshotContainerPath = "/storage/snapshot/"
StorageVolumeSnapshotResourcePath = "/storage/snapshot"
WaitForSnapshotCreateTimeout = 1200
WaitForSnapshotDeleteTimeout = 1500
// Collocated Snapshot Property
SnapshotPropertyCollocated = "/oracle/private/storage/snapshot/collocated"
)
// StorageVolumeSnapshotClient is a client for the Storage Volume Snapshot functions of the Compute API.
type StorageVolumeSnapshotClient struct {
ResourceClient
}
func (c *Client) StorageVolumeSnapshots() *StorageVolumeSnapshotClient {
return &StorageVolumeSnapshotClient{
ResourceClient: ResourceClient{
Client: c,
ResourceDescription: StorageVolumeSnapshotDescription,
ContainerPath: StorageVolumeSnapshotContainerPath,
ResourceRootPath: StorageVolumeSnapshotResourcePath,
},
}
}
// StorageVolumeSnapshotInfo represents the information retrieved from the service about a storage volume snapshot
type StorageVolumeSnapshotInfo struct {
// Account to use for snapshots
Account string `json:"account"`
// Description of the snapshot
Description string `json:"description"`
// The name of the machine image that's used in the boot volume from which this snapshot is taken
MachineImageName string `json:"machineimage_name"`
// Name of the snapshot
Name string `json:"name"`
// String indicating whether the parent volume is bootable or not
ParentVolumeBootable string `json:"parent_volume_bootable"`
// Platform the snapshot is compatible with
Platform string `json:"platform"`
// String determining whether the snapshot is remote or collocated
Property string `json:"property"`
// The size of the snapshot in GB
Size string `json:"size"`
// The ID of the snapshot. Generated by the server
SnapshotID string `json:"snapshot_id"`
// The timestamp of the storage snapshot
SnapshotTimestamp string `json:"snapshot_timestamp"`
// Timestamp for when the operation started
StartTimestamp string `json:"start_timestamp"`
// Status of the snapshot
Status string `json:"status"`
// Status Detail of the storage snapshot
StatusDetail string `json:"status_detail"`
// Indicates the time that the current view of the storage volume snapshot was generated.
StatusTimestamp string `json:"status_timestamp"`
// Array of tags for the snapshot
Tags []string `json:"tags,omitempty"`
// Uniform Resource Identifier
URI string `json:"uri"`
// Name of the parent storage volume for the snapshot
Volume string `json:"volume"`
}
// CreateStorageVolumeSnapshotInput represents the body of an API request to create a new storage volume snapshot
type CreateStorageVolumeSnapshotInput struct {
// Description of the snapshot
// Optional
Description string `json:"description,omitempty"`
// Name of the snapshot
// Optional, will be generated if not specified
Name string `json:"name,omitempty"`
// Whether or not the parent volume is bootable
// Optional
ParentVolumeBootable string `json:"parent_volume_bootable,omitempty"`
// Whether collocated or remote
// Optional, will be remote if unspecified
Property string `json:"property,omitempty"`
// Array of tags for the snapshot
// Optional
Tags []string `json:"tags,omitempty"`
// Name of the volume to create the snapshot from
// Required
Volume string `json:"volume"`
// Timeout (in seconds) to wait for snapshot to be completed. Will use default if unspecified
Timeout int
}
// CreateStorageVolumeSnapshot creates a snapshot based on the supplied information struct
func (c *StorageVolumeSnapshotClient) CreateStorageVolumeSnapshot(input *CreateStorageVolumeSnapshotInput) (*StorageVolumeSnapshotInfo, error) {
if input.Name != "" {
input.Name = c.getQualifiedName(input.Name)
}
input.Volume = c.getQualifiedName(input.Volume)
var storageSnapshotInfo StorageVolumeSnapshotInfo
if err := c.createResource(&input, &storageSnapshotInfo); err != nil {
return nil, err
}
timeout := WaitForSnapshotCreateTimeout
if input.Timeout != 0 {
timeout = input.Timeout
}
// The name of the snapshot could have been generated. Use the response name as input
return c.waitForStorageSnapshotAvailable(storageSnapshotInfo.Name, timeout)
}
// GetStorageVolumeSnapshotInput represents the body of an API request to get information on a storage volume snapshot
type GetStorageVolumeSnapshotInput struct {
// Name of the snapshot
Name string `json:"name"`
}
// GetStorageVolumeSnapshot makes an API request to populate information on a storage volume snapshot
func (c *StorageVolumeSnapshotClient) GetStorageVolumeSnapshot(input *GetStorageVolumeSnapshotInput) (*StorageVolumeSnapshotInfo, error) {
var storageSnapshot StorageVolumeSnapshotInfo
input.Name = c.getQualifiedName(input.Name)
if err := c.getResource(input.Name, &storageSnapshot); err != nil {
if WasNotFoundError(err) {
return nil, nil
}
return nil, err
}
return c.success(&storageSnapshot)
}
// DeleteStorageVolumeSnapshotInput represents the body of an API request to delete a storage volume snapshot
type DeleteStorageVolumeSnapshotInput struct {
// Name of the snapshot to delete
Name string `json:"name"`
// Timeout in seconds to wait for deletion, will use default if unspecified
Timeout int
}
// DeleteStoragevolumeSnapshot makes an API request to delete a storage volume snapshot
func (c *StorageVolumeSnapshotClient) DeleteStorageVolumeSnapshot(input *DeleteStorageVolumeSnapshotInput) error {
input.Name = c.getQualifiedName(input.Name)
if err := c.deleteResource(input.Name); err != nil {
return err
}
timeout := WaitForSnapshotDeleteTimeout
if input.Timeout != 0 {
timeout = input.Timeout
}
return c.waitForStorageSnapshotDeleted(input.Name, timeout)
}
func (c *StorageVolumeSnapshotClient) success(result *StorageVolumeSnapshotInfo) (*StorageVolumeSnapshotInfo, error) {
c.unqualify(&result.Name)
c.unqualify(&result.Volume)
sizeInGigaBytes, err := sizeInGigaBytes(result.Size)
if err != nil {
return nil, err
}
result.Size = sizeInGigaBytes
return result, nil
}
// Waits for a storage snapshot to become available
func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotAvailable(name string, timeout int) (*StorageVolumeSnapshotInfo, error) {
var result *StorageVolumeSnapshotInfo
err := c.waitFor(
fmt.Sprintf("storage volume snapshot %s to become available", c.getQualifiedName(name)),
timeout,
func() (bool, error) {
req := &GetStorageVolumeSnapshotInput{
Name: name,
}
res, err := c.GetStorageVolumeSnapshot(req)
if err != nil {
return false, err
}
if res != nil {
result = res
if strings.ToLower(result.Status) == "completed" {
return true, nil
} else if strings.ToLower(result.Status) == "error" {
return false, fmt.Errorf("Snapshot '%s' failed to create successfully. Status: %s Status Detail: %s", result.Name, result.Status, result.StatusDetail)
}
}
return false, nil
})
return result, err
}
// Waits for a storage snapshot to be deleted
func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotDeleted(name string, timeout int) error {
return c.waitFor(
fmt.Sprintf("storage volume snapshot %s to be deleted", c.getQualifiedName(name)),
timeout,
func() (bool, error) {
req := &GetStorageVolumeSnapshotInput{
Name: name,
}
res, err := c.GetStorageVolumeSnapshot(req)
if res == nil {
return true, nil
}
if err != nil {
return false, err
}
return res == nil, nil
})
}

View File

@ -188,6 +188,7 @@ type GetStorageVolumeInput struct {
func (c *StorageVolumeClient) success(result *StorageVolumeInfo) (*StorageVolumeInfo, error) { func (c *StorageVolumeClient) success(result *StorageVolumeInfo) (*StorageVolumeInfo, error) {
c.unqualify(&result.Name) c.unqualify(&result.Name)
c.unqualify(&result.Snapshot)
sizeInMegaBytes, err := sizeInGigaBytes(result.Size) sizeInMegaBytes, err := sizeInGigaBytes(result.Size)
if err != nil { if err != nil {

6
vendor/vendor.json vendored
View File

@ -1970,10 +1970,10 @@
"revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5" "revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5"
}, },
{ {
"checksumSHA1": "QKusHEboSl00AnORqkjv0gZEhqw=", "checksumSHA1": "mVLpbxsm+8TlXKgkezrh3c5I7+4=",
"path": "github.com/hashicorp/go-oracle-terraform/compute", "path": "github.com/hashicorp/go-oracle-terraform/compute",
"revision": "15f277fb824b7af18c6bef8d30d84174154f989b", "revision": "381402af3554bcca5fa7eeda94d47003e6ba7ee7",
"revisionTime": "2017-04-05T20:02:51Z" "revisionTime": "2017-04-06T04:33:22Z"
}, },
{ {
"checksumSHA1": "DzK7lYwHt5Isq5Zf73cnQqBO2LI=", "checksumSHA1": "DzK7lYwHt5Isq5Zf73cnQqBO2LI=",

View File

@ -51,6 +51,9 @@ The following arguments are supported:
* `storage_type` - (Optional) - The Type of Storage to provision. Possible values are `/oracle/public/storage/latency` or `/oracle/public/storage/default`. Defaults to `/oracle/public/storage/default`. * `storage_type` - (Optional) - The Type of Storage to provision. Possible values are `/oracle/public/storage/latency` or `/oracle/public/storage/default`. Defaults to `/oracle/public/storage/default`.
* `bootable` - (Optional) A `bootable` block as defined below. * `bootable` - (Optional) A `bootable` block as defined below.
* `tags` - (Optional) Comma-separated strings that tag the storage volume. * `tags` - (Optional) Comma-separated strings that tag the storage volume.
* `snapshot` - (Optional) Name of the storage volume snapshot if this storage volume is a clone.
* `snapshot_account` - (Optional) Account of the parent snapshot from which the storage volume is restored.
* `snapshot_id` - (Optional) Id of the parent snapshot from which the storage volume is restored or cloned.
`bootable` supports the following: `bootable` supports the following:
* `image_list` - (Required) Defines an image list. * `image_list` - (Required) Defines an image list.

View File

@ -0,0 +1,58 @@
---
layout: "opc"
page_title: "Oracle: opc_compute_storage_volume_snapshot"
sidebar_current: "docs-opc-resource-storage-volume-snapshot"
description: |-
Creates and manages a storage volume snapshot in an OPC identity domain.
---
# opc\_compute\_storage\_volume_snapshot
The ``opc_compute_storage_volume_snapshot`` resource creates and manages a storage volume snapshot in an OPC identity domain.
## Example Usage
```
resource "opc_compute_storage_volume_snapshot" "test" {
name = "storageVolume1"
description = "Description for the Storage Volume"
tags = ["bar", "foo"]
collocated = true
}
```
## Argument Reference
The following arguments are supported:
* `volume` (Required) The name of the storage volume to create the snapshot from.
* `description` (Optional) The description of the storage volume snapshot.
* `name` (Optional) The name of the storage volume snapshot. Will be generated if unspecified.
* `parent_volume_bootable` (Optional) A string value of whether or not the parent volume is 'bootable' or not. Defaults to `"false"`.
* `collocated` (Optional) Boolean specifying whether the snapshot is collocated or remote. Defaults to `false`.
* `tags` - (Optional) Comma-separated strings that tag the storage volume.
## Attributes Reference
In addition to the attributes above, the following attributes are exported:
* `account` - Account to use for snapshots.
* `machine_image_name` - The name of the machine image that's used in the boot volume from which this snapshot is taken.
* `size` - The size of the snapshot in GB.
* `property` - Where the snapshot is stored, whether collocated, or in the Oracle Storage Cloud Service instance.
* `platform` - The OS platform this snapshot is compatible with
* `snapshot_timestamp` - Timestamp of the storage snapshot, generated by storage server. The snapshot will contain data written to the original volume before this time.
* `snapshot_id` - The Oracle ID of the snapshot.
* `start_timestamp` - Timestamp when the snapshot was started.
* `status` - Status of the snapshot.
* `status_detail` - Details about the latest state of the storage volume snapshot.
* `status_timestamp` - Indicates the time that the current view of the storage volume snapshot was generated.
* `uri` - Uniform Resource Identifier
## Import
Storage Volume Snapshot's can be imported using the `resource name`, e.g.
```
terraform import opc_compute_storage_volume_snapshot.volume1 example
```