Merge pull request #6693 from jtopjian/openstack-blockstorage-v2
provider/openstack: openstack_blockstorage_volume_v2 resource
This commit is contained in:
commit
f674179109
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/hashicorp/terraform",
|
"ImportPath": "github.com/hashicorp/terraform",
|
||||||
"GoVersion": "go1.6",
|
"GoVersion": "go1.6",
|
||||||
"GodepVersion": "v62",
|
"GodepVersion": "v67",
|
||||||
"Packages": [
|
"Packages": [
|
||||||
"./..."
|
"./..."
|
||||||
],
|
],
|
||||||
|
@ -471,6 +471,11 @@
|
||||||
"Comment": "v1.1.15",
|
"Comment": "v1.1.15",
|
||||||
"Rev": "2cc71659118a868dc7544a7ef0808eb42d487011"
|
"Rev": "2cc71659118a868dc7544a7ef0808eb42d487011"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/service/emr/emriface",
|
||||||
|
"Comment": "v1.1.23",
|
||||||
|
"Rev": "2cc71659118a868dc7544a7ef0808eb42d487011"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/aws/aws-sdk-go/service/firehose",
|
"ImportPath": "github.com/aws/aws-sdk-go/service/firehose",
|
||||||
"Comment": "v1.1.23",
|
"Comment": "v1.1.23",
|
||||||
|
@ -1094,198 +1099,198 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud",
|
"ImportPath": "github.com/rackspace/gophercloud",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes",
|
||||||
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/flavors",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/flavors",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/images",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/images",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/servers",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/compute/v2/servers",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tenants",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tenants",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tokens",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tokens",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v3/tokens",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v3/tokens",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/networks",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/networks",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/ports",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/ports",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/subnets",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/networking/v2/subnets",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/openstack/utils",
|
"ImportPath": "github.com/rackspace/gophercloud/openstack/utils",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/pagination",
|
"ImportPath": "github.com/rackspace/gophercloud/pagination",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/testhelper",
|
"ImportPath": "github.com/rackspace/gophercloud/testhelper",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud/testhelper/client",
|
"ImportPath": "github.com/rackspace/gophercloud/testhelper/client",
|
||||||
"Comment": "v1.0.0-884-gc54bbac",
|
"Comment": "v1.0.0-905-gadc2065",
|
||||||
"Rev": "c54bbac81d19eb4df3ad167764dbb6ff2e7194de"
|
"Rev": "adc206589ed49d18cecc9890ab93534704b04702"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/ryanuber/columnize",
|
|
||||||
"Comment": "v2.0.1-8-g983d3a5",
|
|
||||||
"Rev": "983d3a5fab1bf04d1b412465d2d9f8430e2e917e"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rainycape/unidecode",
|
"ImportPath": "github.com/rainycape/unidecode",
|
||||||
|
|
|
@ -103,6 +103,13 @@ func (c *Config) blockStorageV1Client(region string) (*gophercloud.ServiceClient
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) blockStorageV2Client(region string) (*gophercloud.ServiceClient, error) {
|
||||||
|
return openstack.NewBlockStorageV2(c.osClient, gophercloud.EndpointOpts{
|
||||||
|
Region: region,
|
||||||
|
Availability: c.getEndpointType(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) computeV2Client(region string) (*gophercloud.ServiceClient, error) {
|
func (c *Config) computeV2Client(region string) (*gophercloud.ServiceClient, error) {
|
||||||
return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{
|
return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{
|
||||||
Region: region,
|
Region: region,
|
||||||
|
|
|
@ -92,6 +92,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
|
|
||||||
ResourcesMap: map[string]*schema.Resource{
|
ResourcesMap: map[string]*schema.Resource{
|
||||||
"openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(),
|
"openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(),
|
||||||
|
"openstack_blockstorage_volume_v2": resourceBlockStorageVolumeV2(),
|
||||||
"openstack_compute_instance_v2": resourceComputeInstanceV2(),
|
"openstack_compute_instance_v2": resourceComputeInstanceV2(),
|
||||||
"openstack_compute_keypair_v2": resourceComputeKeypairV2(),
|
"openstack_compute_keypair_v2": resourceComputeKeypairV2(),
|
||||||
"openstack_compute_secgroup_v2": resourceComputeSecGroupV2(),
|
"openstack_compute_secgroup_v2": resourceComputeSecGroupV2(),
|
||||||
|
|
|
@ -0,0 +1,341 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceBlockStorageVolumeV2() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceBlockStorageVolumeV2Create,
|
||||||
|
Read: resourceBlockStorageVolumeV2Read,
|
||||||
|
Update: resourceBlockStorageVolumeV2Update,
|
||||||
|
Delete: resourceBlockStorageVolumeV2Delete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"region": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
|
||||||
|
},
|
||||||
|
"size": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: false,
|
||||||
|
},
|
||||||
|
"description": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: false,
|
||||||
|
},
|
||||||
|
"availability_zone": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"metadata": &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: false,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"snapshot_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"source_vol_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"image_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"volume_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"consistency_group_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"source_replica": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"attachment": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"instance_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"device": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: resourceVolumeV2AttachmentHash,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceBlockStorageVolumeV2Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
blockStorageClient, err := config.blockStorageV2Client(d.Get("region").(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
createOpts := &volumes.CreateOpts{
|
||||||
|
AvailabilityZone: d.Get("availability_zone").(string),
|
||||||
|
ConsistencyGroupID: d.Get("consistency_group_id").(string),
|
||||||
|
Description: d.Get("description").(string),
|
||||||
|
ImageID: d.Get("image_id").(string),
|
||||||
|
Metadata: resourceContainerMetadataV2(d),
|
||||||
|
Name: d.Get("name").(string),
|
||||||
|
Size: d.Get("size").(int),
|
||||||
|
SnapshotID: d.Get("snapshot_id").(string),
|
||||||
|
SourceReplica: d.Get("source_replica").(string),
|
||||||
|
SourceVolID: d.Get("source_vol_id").(string),
|
||||||
|
VolumeType: d.Get("volume_type").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
||||||
|
v, err := volumes.Create(blockStorageClient, createOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack volume: %s", err)
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Volume ID: %s", v.ID)
|
||||||
|
|
||||||
|
// Store the ID now
|
||||||
|
d.SetId(v.ID)
|
||||||
|
|
||||||
|
// Wait for the volume to become available.
|
||||||
|
log.Printf(
|
||||||
|
"[DEBUG] Waiting for volume (%s) to become available",
|
||||||
|
v.ID)
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"downloading", "creating"},
|
||||||
|
Target: []string{"available"},
|
||||||
|
Refresh: VolumeV2StateRefreshFunc(blockStorageClient, v.ID),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for volume (%s) to become ready: %s",
|
||||||
|
v.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceBlockStorageVolumeV2Read(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceBlockStorageVolumeV2Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
blockStorageClient, err := config.blockStorageV2Client(d.Get("region").(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := volumes.Get(blockStorageClient, d.Id()).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return CheckDeleted(d, err, "volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Retreived volume %s: %+v", d.Id(), v)
|
||||||
|
|
||||||
|
d.Set("size", v.Size)
|
||||||
|
d.Set("description", v.Description)
|
||||||
|
d.Set("availability_zone", v.AvailabilityZone)
|
||||||
|
d.Set("name", v.Name)
|
||||||
|
d.Set("snapshot_id", v.SnapshotID)
|
||||||
|
d.Set("source_vol_id", v.SourceVolID)
|
||||||
|
d.Set("volume_type", v.VolumeType)
|
||||||
|
d.Set("metadata", v.Metadata)
|
||||||
|
|
||||||
|
if len(v.Attachments) > 0 {
|
||||||
|
attachments := make([]map[string]interface{}, len(v.Attachments))
|
||||||
|
for i, attachment := range v.Attachments {
|
||||||
|
attachments[i] = make(map[string]interface{})
|
||||||
|
attachments[i]["id"] = attachment["id"]
|
||||||
|
attachments[i]["instance_id"] = attachment["server_id"]
|
||||||
|
attachments[i]["device"] = attachment["device"]
|
||||||
|
log.Printf("[DEBUG] attachment: %v", attachment)
|
||||||
|
}
|
||||||
|
d.Set("attachment", attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceBlockStorageVolumeV2Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
blockStorageClient, err := config.blockStorageV2Client(d.Get("region").(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOpts := volumes.UpdateOpts{
|
||||||
|
Name: d.Get("name").(string),
|
||||||
|
Description: d.Get("description").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("metadata") {
|
||||||
|
updateOpts.Metadata = resourceVolumeMetadataV2(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = volumes.Update(blockStorageClient, d.Id(), updateOpts).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error updating OpenStack volume: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceBlockStorageVolumeV2Read(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceBlockStorageVolumeV2Delete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
blockStorageClient, err := config.blockStorageV2Client(d.Get("region").(string))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := volumes.Get(blockStorageClient, d.Id()).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return CheckDeleted(d, err, "volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure this volume is detached from all instances before deleting
|
||||||
|
if len(v.Attachments) > 0 {
|
||||||
|
log.Printf("[DEBUG] detaching volumes")
|
||||||
|
if computeClient, err := config.computeV2Client(d.Get("region").(string)); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
for _, volumeAttachment := range v.Attachments {
|
||||||
|
log.Printf("[DEBUG] Attachment: %v", volumeAttachment)
|
||||||
|
if err := volumeattach.Delete(computeClient, volumeAttachment["server_id"].(string), volumeAttachment["id"].(string)).ExtractErr(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"in-use", "attaching", "detaching"},
|
||||||
|
Target: []string{"available"},
|
||||||
|
Refresh: VolumeV2StateRefreshFunc(blockStorageClient, d.Id()),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for volume (%s) to become available: %s",
|
||||||
|
d.Id(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's possible that this volume was used as a boot device and is currently
|
||||||
|
// in a "deleting" state from when the instance was terminated.
|
||||||
|
// If this is true, just move on. It'll eventually delete.
|
||||||
|
if v.Status != "deleting" {
|
||||||
|
if err := volumes.Delete(blockStorageClient, d.Id()).ExtractErr(); err != nil {
|
||||||
|
return CheckDeleted(d, err, "volume")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the volume to delete before moving on.
|
||||||
|
log.Printf("[DEBUG] Waiting for volume (%s) to delete", d.Id())
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"deleting", "downloading", "available"},
|
||||||
|
Target: []string{"deleted"},
|
||||||
|
Refresh: VolumeV2StateRefreshFunc(blockStorageClient, d.Id()),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for volume (%s) to delete: %s",
|
||||||
|
d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceVolumeMetadataV2(d *schema.ResourceData) map[string]string {
|
||||||
|
m := make(map[string]string)
|
||||||
|
for key, val := range d.Get("metadata").(map[string]interface{}) {
|
||||||
|
m[key] = val.(string)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeV2StateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
|
||||||
|
// an OpenStack volume.
|
||||||
|
func VolumeV2StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
v, err := volumes.Get(client, volumeID).Extract()
|
||||||
|
if err != nil {
|
||||||
|
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
||||||
|
if !ok {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if errCode.Actual == 404 {
|
||||||
|
return v, "deleted", nil
|
||||||
|
}
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, v.Status, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceVolumeV2AttachmentHash(v interface{}) int {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
if m["instance_id"] != nil {
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", m["instance_id"].(string)))
|
||||||
|
}
|
||||||
|
return hashcode.String(buf.String())
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccBlockStorageV2Volume_basic(t *testing.T) {
|
||||||
|
var volume volumes.Volume
|
||||||
|
|
||||||
|
var testAccBlockStorageV2Volume_basic = fmt.Sprintf(`
|
||||||
|
resource "openstack_blockstorage_volume_v2" "volume_1" {
|
||||||
|
name = "volume_1"
|
||||||
|
description = "first test volume"
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
size = 1
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var testAccBlockStorageV2Volume_update = fmt.Sprintf(`
|
||||||
|
resource "openstack_blockstorage_volume_v2" "volume_1" {
|
||||||
|
name = "volume_1-updated"
|
||||||
|
description = "first test volume"
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
size = 1
|
||||||
|
}`)
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckBlockStorageV2VolumeDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccBlockStorageV2Volume_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckBlockStorageV2VolumeExists(t, "openstack_blockstorage_volume_v2.volume_1", &volume),
|
||||||
|
resource.TestCheckResourceAttr("openstack_blockstorage_volume_v2.volume_1", "name", "volume_1"),
|
||||||
|
testAccCheckBlockStorageV2VolumeMetadata(&volume, "foo", "bar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccBlockStorageV2Volume_update,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckBlockStorageV2VolumeExists(t, "openstack_blockstorage_volume_v2.volume_1", &volume),
|
||||||
|
resource.TestCheckResourceAttr("openstack_blockstorage_volume_v2.volume_1", "name", "volume_1-updated"),
|
||||||
|
testAccCheckBlockStorageV2VolumeMetadata(&volume, "foo", "bar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccBlockStorageV2Volume_bootable(t *testing.T) {
|
||||||
|
var volume volumes.Volume
|
||||||
|
|
||||||
|
var testAccBlockStorageV2Volume_bootable = fmt.Sprintf(`
|
||||||
|
resource "openstack_blockstorage_volume_v2" "volume_1" {
|
||||||
|
name = "volume_1-bootable"
|
||||||
|
size = 5
|
||||||
|
image_id = "%s"
|
||||||
|
}`,
|
||||||
|
os.Getenv("OS_IMAGE_ID"))
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckBlockStorageV2VolumeDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccBlockStorageV2Volume_bootable,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckBlockStorageV2VolumeExists(t, "openstack_blockstorage_volume_v2.volume_1", &volume),
|
||||||
|
resource.TestCheckResourceAttr("openstack_blockstorage_volume_v2.volume_1", "name", "volume_1-bootable"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckBlockStorageV2VolumeDestroy(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
blockStorageClient, err := config.blockStorageV2Client(OS_REGION_NAME)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "openstack_blockstorage_volume_v2" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := volumes.Get(blockStorageClient, rs.Primary.ID).Extract()
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Volume still exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckBlockStorageV2VolumeExists(t *testing.T, n string, volume *volumes.Volume) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
blockStorageClient, err := config.blockStorageV2Client(OS_REGION_NAME)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := volumes.Get(blockStorageClient, rs.Primary.ID).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if found.ID != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Volume not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
*volume = *found
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckBlockStorageV2VolumeDoesNotExist(t *testing.T, n string, volume *volumes.Volume) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
blockStorageClient, err := config.blockStorageV2Client(OS_REGION_NAME)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = volumes.Get(blockStorageClient, volume.ID).Extract()
|
||||||
|
if err != nil {
|
||||||
|
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if errCode.Actual == 404 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Volume still exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckBlockStorageV2VolumeMetadata(
|
||||||
|
volume *volumes.Volume, k string, v string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if volume.Metadata == nil {
|
||||||
|
return fmt.Errorf("No metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range volume.Metadata {
|
||||||
|
if k != key {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == value {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Bad value for %s: %s", k, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Metadata not found: %s", k)
|
||||||
|
}
|
||||||
|
}
|
5
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/doc.go
generated
vendored
Normal file
5
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Package volumes provides information and interaction with volumes in the
|
||||||
|
// OpenStack Block Storage service. A volume is a detachable block storage
|
||||||
|
// device, akin to a USB hard drive. It can only be attached to one instance at
|
||||||
|
// a time.
|
||||||
|
package volumes
|
204
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/fixtures.go
generated
vendored
Normal file
204
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/fixtures.go
generated
vendored
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
fake "github.com/rackspace/gophercloud/testhelper/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MockListResponse(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/volumes/detail", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "GET")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"volumes": [
|
||||||
|
{
|
||||||
|
"volume_type": "lvmdriver-1",
|
||||||
|
"created_at": "2015-09-17T03:35:03.000000",
|
||||||
|
"bootable": "false",
|
||||||
|
"name": "vol-001",
|
||||||
|
"os-vol-mig-status-attr:name_id": null,
|
||||||
|
"consistencygroup_id": null,
|
||||||
|
"source_volid": null,
|
||||||
|
"os-volume-replication:driver_data": null,
|
||||||
|
"multiattach": false,
|
||||||
|
"snapshot_id": null,
|
||||||
|
"replication_status": "disabled",
|
||||||
|
"os-volume-replication:extended_status": null,
|
||||||
|
"encrypted": false,
|
||||||
|
"os-vol-host-attr:host": null,
|
||||||
|
"availability_zone": "nova",
|
||||||
|
"attachments": [
|
||||||
|
{
|
||||||
|
"attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa",
|
||||||
|
"id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf",
|
||||||
|
"volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||||
|
"server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
|
||||||
|
"host_name": "stack",
|
||||||
|
"device": "/dev/vdc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||||
|
"size": 75,
|
||||||
|
"user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
|
||||||
|
"os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
|
||||||
|
"os-vol-mig-status-attr:migstat": null,
|
||||||
|
"metadata": {"foo": "bar"},
|
||||||
|
"status": "available",
|
||||||
|
"description": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"volume_type": "lvmdriver-1",
|
||||||
|
"created_at": "2015-09-17T03:32:29.000000",
|
||||||
|
"bootable": "false",
|
||||||
|
"name": "vol-002",
|
||||||
|
"os-vol-mig-status-attr:name_id": null,
|
||||||
|
"consistencygroup_id": null,
|
||||||
|
"source_volid": null,
|
||||||
|
"os-volume-replication:driver_data": null,
|
||||||
|
"multiattach": false,
|
||||||
|
"snapshot_id": null,
|
||||||
|
"replication_status": "disabled",
|
||||||
|
"os-volume-replication:extended_status": null,
|
||||||
|
"encrypted": false,
|
||||||
|
"os-vol-host-attr:host": null,
|
||||||
|
"availability_zone": "nova",
|
||||||
|
"attachments": [],
|
||||||
|
"id": "96c3bda7-c82a-4f50-be73-ca7621794835",
|
||||||
|
"size": 75,
|
||||||
|
"user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
|
||||||
|
"os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
|
||||||
|
"os-vol-mig-status-attr:migstat": null,
|
||||||
|
"metadata": {},
|
||||||
|
"status": "available",
|
||||||
|
"description": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockGetResponse(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "GET")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"volume": {
|
||||||
|
"volume_type": "lvmdriver-1",
|
||||||
|
"created_at": "2015-09-17T03:32:29.000000",
|
||||||
|
"bootable": "false",
|
||||||
|
"name": "vol-001",
|
||||||
|
"os-vol-mig-status-attr:name_id": null,
|
||||||
|
"consistencygroup_id": null,
|
||||||
|
"source_volid": null,
|
||||||
|
"os-volume-replication:driver_data": null,
|
||||||
|
"multiattach": false,
|
||||||
|
"snapshot_id": null,
|
||||||
|
"replication_status": "disabled",
|
||||||
|
"os-volume-replication:extended_status": null,
|
||||||
|
"encrypted": false,
|
||||||
|
"os-vol-host-attr:host": null,
|
||||||
|
"availability_zone": "nova",
|
||||||
|
"attachments": [{
|
||||||
|
"attachment_id": "dbce64e3-f3b9-4423-a44f-a2b15deffa1b",
|
||||||
|
"id": "3eafc6f5-ed74-456d-90fb-f253f594dbae",
|
||||||
|
"volume_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||||
|
"server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f",
|
||||||
|
"host_name": "stack",
|
||||||
|
"device": "/dev/vdd"
|
||||||
|
}],
|
||||||
|
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||||
|
"size": 75,
|
||||||
|
"user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
|
||||||
|
"os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459",
|
||||||
|
"os-vol-mig-status-attr:migstat": null,
|
||||||
|
"metadata": {},
|
||||||
|
"status": "available",
|
||||||
|
"description": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockCreateResponse(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "POST")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
th.TestHeader(t, r, "Content-Type", "application/json")
|
||||||
|
th.TestHeader(t, r, "Accept", "application/json")
|
||||||
|
th.TestJSONRequest(t, r, `
|
||||||
|
{
|
||||||
|
"volume": {
|
||||||
|
"name": "vol-001",
|
||||||
|
"size": 75
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"volume": {
|
||||||
|
"size": 75,
|
||||||
|
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||||
|
"metadata": {},
|
||||||
|
"created_at": "2015-09-17T03:32:29.044216",
|
||||||
|
"encrypted": false,
|
||||||
|
"bootable": "false",
|
||||||
|
"availability_zone": "nova",
|
||||||
|
"attachments": [],
|
||||||
|
"user_id": "ff1ce52c03ab433aaba9108c2e3ef541",
|
||||||
|
"status": "creating",
|
||||||
|
"description": null,
|
||||||
|
"volume_type": "lvmdriver-1",
|
||||||
|
"name": "vol-001",
|
||||||
|
"replication_status": "disabled",
|
||||||
|
"consistencygroup_id": null,
|
||||||
|
"source_volid": null,
|
||||||
|
"snapshot_id": null,
|
||||||
|
"multiattach": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockDeleteResponse(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "DELETE")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func MockUpdateResponse(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "PUT")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"volume": {
|
||||||
|
"name": "vol-002"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
}
|
251
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/requests.go
generated
vendored
Normal file
251
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Create request.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToVolumeCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts contains options for creating a Volume. This object is passed to
|
||||||
|
// the volumes.Create function. For more information about these parameters,
|
||||||
|
// see the Volume object.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// The availability zone [OPTIONAL]
|
||||||
|
AvailabilityZone string
|
||||||
|
// ConsistencyGroupID is the ID of a consistency group [OPTINAL]
|
||||||
|
ConsistencyGroupID string
|
||||||
|
// The volume description [OPTIONAL]
|
||||||
|
Description string
|
||||||
|
// One or more metadata key and value pairs to associate with the volume [OPTIONAL]
|
||||||
|
Metadata map[string]string
|
||||||
|
// The volume name [OPTIONAL]
|
||||||
|
Name string
|
||||||
|
// The size of the volume, in gibibytes (GiB) [REQUIRED]
|
||||||
|
Size int
|
||||||
|
// the ID of the existing volume snapshot [OPTIONAL]
|
||||||
|
SnapshotID string
|
||||||
|
// SourceReplica is a UUID of an existing volume to replicate with [OPTIONAL]
|
||||||
|
SourceReplica string
|
||||||
|
// the ID of the existing volume [OPTIONAL]
|
||||||
|
SourceVolID string
|
||||||
|
// The ID of the image from which you want to create the volume.
|
||||||
|
// Required to create a bootable volume.
|
||||||
|
ImageID string
|
||||||
|
// The associated volume type [OPTIONAL]
|
||||||
|
VolumeType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeCreateMap assembles a request body based on the contents of a
|
||||||
|
// CreateOpts.
|
||||||
|
func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
|
||||||
|
v := make(map[string]interface{})
|
||||||
|
|
||||||
|
if opts.Size == 0 {
|
||||||
|
return nil, fmt.Errorf("Required CreateOpts field 'Size' not set.")
|
||||||
|
}
|
||||||
|
v["size"] = opts.Size
|
||||||
|
|
||||||
|
if opts.AvailabilityZone != "" {
|
||||||
|
v["availability_zone"] = opts.AvailabilityZone
|
||||||
|
}
|
||||||
|
if opts.ConsistencyGroupID != "" {
|
||||||
|
v["consistencygroup_id"] = opts.ConsistencyGroupID
|
||||||
|
}
|
||||||
|
if opts.Description != "" {
|
||||||
|
v["description"] = opts.Description
|
||||||
|
}
|
||||||
|
if opts.ImageID != "" {
|
||||||
|
v["imageRef"] = opts.ImageID
|
||||||
|
}
|
||||||
|
if opts.Metadata != nil {
|
||||||
|
v["metadata"] = opts.Metadata
|
||||||
|
}
|
||||||
|
if opts.Name != "" {
|
||||||
|
v["name"] = opts.Name
|
||||||
|
}
|
||||||
|
if opts.SourceReplica != "" {
|
||||||
|
v["source_replica"] = opts.SourceReplica
|
||||||
|
}
|
||||||
|
if opts.SourceVolID != "" {
|
||||||
|
v["source_volid"] = opts.SourceVolID
|
||||||
|
}
|
||||||
|
if opts.SnapshotID != "" {
|
||||||
|
v["snapshot_id"] = opts.SnapshotID
|
||||||
|
}
|
||||||
|
if opts.VolumeType != "" {
|
||||||
|
v["volume_type"] = opts.VolumeType
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"volume": v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new Volume based on the values in CreateOpts. To extract
|
||||||
|
// the Volume object from the response, call the Extract method on the
|
||||||
|
// CreateResult.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||||
|
var res CreateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToVolumeCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will delete the existing Volume with the provided ID.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
||||||
|
var res DeleteResult
|
||||||
|
_, res.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the Volume with the provided ID. To extract the Volume object
|
||||||
|
// from the response, call the Extract method on the GetResult.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
|
var res GetResult
|
||||||
|
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOptsBuilder allows extensions to add additional parameters to the List
|
||||||
|
// request.
|
||||||
|
type ListOptsBuilder interface {
|
||||||
|
ToVolumeListQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
|
||||||
|
// function.
|
||||||
|
type ListOpts struct {
|
||||||
|
// admin-only option. Set it to true to see all tenant volumes.
|
||||||
|
AllTenants bool `q:"all_tenants"`
|
||||||
|
// List only volumes that contain Metadata.
|
||||||
|
Metadata map[string]string `q:"metadata"`
|
||||||
|
// List only volumes that have Name as the display name.
|
||||||
|
Name string `q:"name"`
|
||||||
|
// List only volumes that have a status of Status.
|
||||||
|
Status string `q:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeListQuery formats a ListOpts into a query string.
|
||||||
|
func (opts ListOpts) ToVolumeListQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return q.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns Volumes optionally limited by the conditions provided in ListOpts.
|
||||||
|
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
|
url := listURL(client)
|
||||||
|
if opts != nil {
|
||||||
|
query, err := opts.ToVolumeListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
}
|
||||||
|
createPage := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return ListResult{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, url, createPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Update request.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToVolumeUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts contain options for updating an existing Volume. This object is passed
|
||||||
|
// to the volumes.Update function. For more information about the parameters, see
|
||||||
|
// the Volume object.
|
||||||
|
type UpdateOpts struct {
|
||||||
|
// OPTIONAL
|
||||||
|
Name string
|
||||||
|
// OPTIONAL
|
||||||
|
Description string
|
||||||
|
// OPTIONAL
|
||||||
|
Metadata map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeUpdateMap assembles a request body based on the contents of an
|
||||||
|
// UpdateOpts.
|
||||||
|
func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
|
||||||
|
v := make(map[string]interface{})
|
||||||
|
|
||||||
|
if opts.Description != "" {
|
||||||
|
v["description"] = opts.Description
|
||||||
|
}
|
||||||
|
if opts.Metadata != nil {
|
||||||
|
v["metadata"] = opts.Metadata
|
||||||
|
}
|
||||||
|
if opts.Name != "" {
|
||||||
|
v["name"] = opts.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"volume": v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update will update the Volume with provided information. To extract the updated
|
||||||
|
// Volume from the response, call the Extract method on the UpdateResult.
|
||||||
|
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
|
||||||
|
var res UpdateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToVolumeUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Put(updateURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a server's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
volumeCount := 0
|
||||||
|
volumeID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A volume name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
volumeList, err := ExtractVolumes(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range volumeList {
|
||||||
|
if s.Name == name {
|
||||||
|
volumeCount++
|
||||||
|
volumeID = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch volumeCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find volume: %s", name)
|
||||||
|
case 1:
|
||||||
|
return volumeID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d volumes matching %s", volumeCount, name)
|
||||||
|
}
|
||||||
|
}
|
137
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/results.go
generated
vendored
Normal file
137
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/results.go
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Volume contains all the information associated with an OpenStack Volume.
|
||||||
|
type Volume struct {
|
||||||
|
// Instances onto which the volume is attached.
|
||||||
|
Attachments []map[string]interface{} `mapstructure:"attachments"`
|
||||||
|
|
||||||
|
// AvailabilityZone is which availability zone the volume is in.
|
||||||
|
AvailabilityZone string `mapstructure:"availability_zone"`
|
||||||
|
|
||||||
|
// Indicates whether this is a bootable volume.
|
||||||
|
Bootable string `mapstructure:"bootable"`
|
||||||
|
|
||||||
|
// ConsistencyGroupID is the consistency group ID.
|
||||||
|
ConsistencyGroupID string `mapstructure:"consistencygroup_id"`
|
||||||
|
|
||||||
|
// The date when this volume was created.
|
||||||
|
CreatedAt string `mapstructure:"created_at"`
|
||||||
|
|
||||||
|
// Human-readable description for the volume.
|
||||||
|
Description string `mapstructure:"description"`
|
||||||
|
|
||||||
|
// Encrypted denotes if the volume is encrypted.
|
||||||
|
Encrypted bool `mapstructure:"encrypted"`
|
||||||
|
|
||||||
|
// Human-readable display name for the volume.
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
|
||||||
|
// The type of volume to create, either SATA or SSD.
|
||||||
|
VolumeType string `mapstructure:"volume_type"`
|
||||||
|
|
||||||
|
// ReplicationDriverData contains data about the replication driver.
|
||||||
|
ReplicationDriverData string `mapstructure:"os-volume-replication:driver_data"`
|
||||||
|
|
||||||
|
// ReplicationExtendedStatus contains extended status about replication.
|
||||||
|
ReplicationExtendedStatus string `mapstructure:"os-volume-replication:extended_status"`
|
||||||
|
|
||||||
|
// ReplicationStatus is the status of replication.
|
||||||
|
ReplicationStatus string `mapstructure:"replication_status"`
|
||||||
|
|
||||||
|
// The ID of the snapshot from which the volume was created
|
||||||
|
SnapshotID string `mapstructure:"snapshot_id"`
|
||||||
|
|
||||||
|
// The ID of another block storage volume from which the current volume was created
|
||||||
|
SourceVolID string `mapstructure:"source_volid"`
|
||||||
|
|
||||||
|
// Current status of the volume.
|
||||||
|
Status string `mapstructure:"status"`
|
||||||
|
|
||||||
|
// TenantID is the id of the project that owns the volume.
|
||||||
|
TenantID string `mapstructure:"os-vol-tenant-attr:tenant_id"`
|
||||||
|
|
||||||
|
// Arbitrary key-value pairs defined by the user.
|
||||||
|
Metadata map[string]string `mapstructure:"metadata"`
|
||||||
|
|
||||||
|
// Multiattach denotes if the volume is multi-attach capable.
|
||||||
|
Multiattach bool `mapstructure:"multiattach"`
|
||||||
|
|
||||||
|
// Unique identifier for the volume.
|
||||||
|
ID string `mapstructure:"id"`
|
||||||
|
|
||||||
|
// Size of the volume in GB.
|
||||||
|
Size int `mapstructure:"size"`
|
||||||
|
|
||||||
|
// UserID is the id of the user who created the volume.
|
||||||
|
UserID string `mapstructure:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult contains the response body and error from a Create request.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult contains the response body and error from a Get request.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult contains the response body and error from a Delete request.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListResult is a pagination.pager that is returned from a call to the List function.
|
||||||
|
type ListResult struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if a ListResult contains no Volumes.
|
||||||
|
func (r ListResult) IsEmpty() (bool, error) {
|
||||||
|
volumes, err := ExtractVolumes(r)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(volumes) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
|
||||||
|
func ExtractVolumes(page pagination.Page) ([]Volume, error) {
|
||||||
|
var response struct {
|
||||||
|
Volumes []Volume `json:"volumes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(page.(ListResult).Body, &response)
|
||||||
|
return response.Volumes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult contains the response body and error from an Update request.
|
||||||
|
type UpdateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will get the Volume object out of the commonResult object.
|
||||||
|
func (r commonResult) Extract() (*Volume, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res struct {
|
||||||
|
Volume *Volume `json:"volume"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &res)
|
||||||
|
|
||||||
|
return res.Volume, err
|
||||||
|
}
|
23
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/urls.go
generated
vendored
Normal file
23
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func createURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("volumes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("volumes", "detail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL("volumes", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(c, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(c, id)
|
||||||
|
}
|
22
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/util.go
generated
vendored
Normal file
22
vendor/github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes/util.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitForStatus will continually poll the resource, checking for a particular
|
||||||
|
// status. It will do this for the amount of seconds defined.
|
||||||
|
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
||||||
|
return gophercloud.WaitFor(secs, func() (bool, error) {
|
||||||
|
current, err := Get(c, id).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.Status == status {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -281,6 +281,29 @@ func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoi
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
|
||||||
|
func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("volume")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force using v2 API
|
||||||
|
if strings.Contains(url, "/v1") {
|
||||||
|
url = strings.Replace(url, "/v1", "/v2", -1)
|
||||||
|
}
|
||||||
|
if !strings.Contains(url, "/v2") {
|
||||||
|
return nil, fmt.Errorf("Block Storage v2 endpoint not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gophercloud.ServiceClient{
|
||||||
|
ProviderClient: client,
|
||||||
|
Endpoint: url,
|
||||||
|
ResourceBase: url,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
|
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
|
||||||
// CDN service.
|
// CDN service.
|
||||||
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
|
|
@ -51,6 +51,8 @@ type Image struct {
|
||||||
Status string
|
Status string
|
||||||
|
|
||||||
Updated string
|
Updated string
|
||||||
|
|
||||||
|
Metadata map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImagePage contains a single page of results from a List operation.
|
// ImagePage contains a single page of results from a List operation.
|
||||||
|
|
|
@ -138,6 +138,11 @@ func (p Pager) AllPages() (Page, error) {
|
||||||
// that type.
|
// that type.
|
||||||
pageType := reflect.TypeOf(testPage)
|
pageType := reflect.TypeOf(testPage)
|
||||||
|
|
||||||
|
// if it's a single page, just return the testPage (first page)
|
||||||
|
if _, found := pageType.FieldByName("SinglePageBase"); found {
|
||||||
|
return testPage, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Switch on the page body type. Recognized types are `map[string]interface{}`,
|
// Switch on the page body type. Recognized types are `map[string]interface{}`,
|
||||||
// `[]byte`, and `[]interface{}`.
|
// `[]byte`, and `[]interface{}`.
|
||||||
switch testPage.GetBody().(type) {
|
switch testPage.GetBody().(type) {
|
||||||
|
@ -153,7 +158,14 @@ func (p Pager) AllPages() (Page, error) {
|
||||||
key = k
|
key = k
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
switch keyType := b[key].(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
pagesSlice = append(pagesSlice, keyType)
|
||||||
|
case []interface{}:
|
||||||
pagesSlice = append(pagesSlice, b[key].([]interface{})...)
|
pagesSlice = append(pagesSlice, b[key].([]interface{})...)
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("Unsupported page body type: %+v", keyType)
|
||||||
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
layout: "openstack"
|
||||||
|
page_title: "OpenStack: openstack_blockstorage_volume_v2"
|
||||||
|
sidebar_current: "docs-openstack-resource-blockstorage-volume-v2"
|
||||||
|
description: |-
|
||||||
|
Manages a V2 volume resource within OpenStack.
|
||||||
|
---
|
||||||
|
|
||||||
|
# openstack\_blockstorage\_volume_v2
|
||||||
|
|
||||||
|
Manages a V2 volume resource within OpenStack.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "openstack_blockstorage_volume_v2" "volume_1" {
|
||||||
|
region = "RegionOne"
|
||||||
|
name = "volume_1"
|
||||||
|
description = "first test volume"
|
||||||
|
size = 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `region` - (Required) The region in which to create the volume. If
|
||||||
|
omitted, the `OS_REGION_NAME` environment variable is used. Changing this
|
||||||
|
creates a new volume.
|
||||||
|
|
||||||
|
* `size` - (Required) The size of the volume to create (in gigabytes). Changing
|
||||||
|
this creates a new volume.
|
||||||
|
|
||||||
|
* `availability_zone` - (Optional) The availability zone for the volume.
|
||||||
|
Changing this creates a new volume.
|
||||||
|
|
||||||
|
* `consistency_group_id` - (Optional) The consistency group to place the volume
|
||||||
|
in.
|
||||||
|
|
||||||
|
* `description` - (Optional) A description of the volume. Changing this updates
|
||||||
|
the volume's description.
|
||||||
|
|
||||||
|
* `image_id` - (Optional) The image ID from which to create the volume.
|
||||||
|
Changing this creates a new volume.
|
||||||
|
|
||||||
|
* `metadata` - (Optional) Metadata key/value pairs to associate with the volume.
|
||||||
|
Changing this updates the existing volume metadata.
|
||||||
|
|
||||||
|
* `name` - (Optional) A unique name for the volume. Changing this updates the
|
||||||
|
volume's name.
|
||||||
|
|
||||||
|
* `snapshot_id` - (Optional) The snapshot ID from which to create the volume.
|
||||||
|
Changing this creates a new volume.
|
||||||
|
|
||||||
|
* `source_replica` - (Optional) The volume ID to replicate with.
|
||||||
|
|
||||||
|
* `source_vol_id` - (Optional) The volume ID from which to create the volume.
|
||||||
|
Changing this creates a new volume.
|
||||||
|
|
||||||
|
* `volume_type` - (Optional) The type of volume to create.
|
||||||
|
Changing this creates a new volume.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `region` - See Argument Reference above.
|
||||||
|
* `size` - See Argument Reference above.
|
||||||
|
* `name` - See Argument Reference above.
|
||||||
|
* `description` - See Argument Reference above.
|
||||||
|
* `availability_zone` - See Argument Reference above.
|
||||||
|
* `image_id` - See Argument Reference above.
|
||||||
|
* `source_vol_id` - See Argument Reference above.
|
||||||
|
* `snapshot_id` - See Argument Reference above.
|
||||||
|
* `metadata` - See Argument Reference above.
|
||||||
|
* `volume_type` - See Argument Reference above.
|
||||||
|
* `attachment` - If a volume is attached to an instance, this attribute will
|
||||||
|
display the Attachment ID, Instance ID, and the Device as the Instance
|
||||||
|
sees it.
|
|
@ -16,6 +16,9 @@
|
||||||
<li<%= sidebar_current("docs-openstack-resource-blockstorage-volume-v1") %>>
|
<li<%= sidebar_current("docs-openstack-resource-blockstorage-volume-v1") %>>
|
||||||
<a href="/docs/providers/openstack/r/blockstorage_volume_v1.html">openstack_blockstorage_volume_v1</a>
|
<a href="/docs/providers/openstack/r/blockstorage_volume_v1.html">openstack_blockstorage_volume_v1</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-openstack-resource-blockstorage-volume-v2") %>>
|
||||||
|
<a href="/docs/providers/openstack/r/blockstorage_volume_v2.html">openstack_blockstorage_volume_v2</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue