Skip to content

Commit d8cdc7a

Browse files
authored
Add openstack_blockstorage_qos_association_v3 (terraform-provider-openstack#1331)
Add new blockstorage_v3 resource for associating a qos with a volume type. Fix terraform-provider-openstack#1212
1 parent a5a4552 commit d8cdc7a

6 files changed

Lines changed: 396 additions & 0 deletions
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package openstack
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
)
10+
11+
func TestAccBlockStorageV3QosAssociation_importBasic(t *testing.T) {
12+
resourceName := "openstack_blockstorage_qos_association_v3.qos_association"
13+
14+
var qosName = fmt.Sprintf("ACCPTTEST-%s", acctest.RandString(5))
15+
var vtName = fmt.Sprintf("ACCPTTEST-%s", acctest.RandString(5))
16+
17+
resource.Test(t, resource.TestCase{
18+
PreCheck: func() {
19+
testAccPreCheck(t)
20+
testAccPreCheckAdminOnly(t)
21+
},
22+
ProviderFactories: testAccProviders,
23+
CheckDestroy: testAccCheckBlockstorageV3QosAssociationDestroy,
24+
Steps: []resource.TestStep{
25+
{
26+
Config: testAccBlockstorageV3QosAssociationBasic(qosName, vtName),
27+
},
28+
29+
{
30+
ResourceName: resourceName,
31+
ImportState: true,
32+
ImportStateVerify: true,
33+
},
34+
},
35+
})
36+
}

‎openstack/provider.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ func Provider() *schema.Provider {
299299
},
300300

301301
ResourcesMap: map[string]*schema.Resource{
302+
"openstack_blockstorage_qos_association_v3": resourceBlockStorageQosAssociationV3(),
302303
"openstack_blockstorage_qos_v3": resourceBlockStorageQosV3(),
303304
"openstack_blockstorage_quotaset_v2": resourceBlockStorageQuotasetV2(),
304305
"openstack_blockstorage_quotaset_v3": resourceBlockStorageQuotasetV3(),
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package openstack
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"strings"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
12+
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos"
13+
)
14+
15+
func resourceBlockStorageQosAssociationV3() *schema.Resource {
16+
return &schema.Resource{
17+
CreateContext: resourceBlockStorageQosAssociationV3Create,
18+
ReadContext: resourceBlockStorageQosAssociationV3Read,
19+
DeleteContext: resourceBlockStorageQosAssociationV3Delete,
20+
Importer: &schema.ResourceImporter{
21+
StateContext: schema.ImportStatePassthroughContext,
22+
},
23+
24+
Schema: map[string]*schema.Schema{
25+
"region": {
26+
Type: schema.TypeString,
27+
Optional: true,
28+
Computed: true,
29+
ForceNew: true,
30+
},
31+
32+
"qos_id": {
33+
Type: schema.TypeString,
34+
Required: true,
35+
ForceNew: true,
36+
},
37+
38+
"volume_type_id": {
39+
Type: schema.TypeString,
40+
Required: true,
41+
ForceNew: true,
42+
},
43+
},
44+
}
45+
}
46+
47+
func resourceBlockStorageQosAssociationV3Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
48+
config := meta.(*Config)
49+
blockStorageClient, err := config.BlockStorageV3Client(GetRegion(d, config))
50+
if err != nil {
51+
return diag.Errorf("Error creating OpenStack block storage client: %s", err)
52+
}
53+
54+
qosID := d.Get("qos_id").(string)
55+
vtID := d.Get("volume_type_id").(string)
56+
associateOpts := qos.AssociateOpts{
57+
VolumeTypeID: vtID,
58+
}
59+
60+
id := fmt.Sprintf("%s/%s", qosID, vtID)
61+
log.Printf("[DEBUG] openstack_blockstorage_qos_association_v3 create options: %#v", associateOpts)
62+
err = qos.Associate(blockStorageClient, qosID, associateOpts).ExtractErr()
63+
if err != nil {
64+
return diag.Errorf("Error creating openstack_blockstorage_qos_association_v3 %s: %s", id, err)
65+
}
66+
67+
d.SetId(id)
68+
69+
return resourceBlockStorageQosAssociationV3Read(ctx, d, meta)
70+
}
71+
72+
func resourceBlockStorageQosAssociationV3Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
73+
config := meta.(*Config)
74+
blockStorageClient, err := config.BlockStorageV3Client(GetRegion(d, config))
75+
if err != nil {
76+
return diag.Errorf("Error creating OpenStack block storage client: %s", err)
77+
}
78+
79+
qosID, vtID, err := parseQosAssociationID(d.Id())
80+
if err != nil {
81+
return diag.Errorf("Error parsing ID of openstack_blockstorage_qos_association_v3 %s: %s", d.Id(), err)
82+
}
83+
84+
allPages, err := qos.ListAssociations(blockStorageClient, qosID).AllPages()
85+
if err != nil {
86+
return diag.Errorf("Error retrieving associations openstack_blockstorage_qos_association_v3 for qos: %s", qosID)
87+
}
88+
89+
allAssociations, err := qos.ExtractAssociations(allPages)
90+
if err != nil {
91+
return diag.Errorf("Error extracting associations openstack_blockstorage_qos_association_v3 for qos: %s", qosID)
92+
}
93+
94+
found := false
95+
for _, association := range allAssociations {
96+
if association.ID == vtID {
97+
found = true
98+
break
99+
}
100+
}
101+
102+
if !found {
103+
return diag.Errorf("Error getting qos association openstack_blockstorage_qos_association_v3 for qos: %s and vt: %s", qosID, vtID)
104+
}
105+
106+
d.Set("region", GetRegion(d, config))
107+
d.Set("qos_id", qosID)
108+
d.Set("volume_type_id", vtID)
109+
110+
return nil
111+
}
112+
113+
func resourceBlockStorageQosAssociationV3Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
114+
config := meta.(*Config)
115+
blockStorageClient, err := config.BlockStorageV3Client(GetRegion(d, config))
116+
if err != nil {
117+
return diag.Errorf("Error creating OpenStack block storage client: %s", err)
118+
}
119+
120+
qosID, vtID, err := parseQosAssociationID(d.Id())
121+
if err != nil {
122+
return diag.Errorf("Error parsing ID of openstack_blockstorage_qos_association_v3 %s: %s", d.Id(), err)
123+
}
124+
125+
disassociateOpts := qos.DisassociateOpts{
126+
VolumeTypeID: vtID,
127+
}
128+
129+
err = qos.Disassociate(blockStorageClient, qosID, disassociateOpts).ExtractErr()
130+
if err != nil {
131+
return diag.FromErr(CheckDeleted(d, err, "Error deleting openstack_blockstorage_qos_association_v3"))
132+
}
133+
134+
return nil
135+
}
136+
137+
func parseQosAssociationID(id string) (string, string, error) {
138+
idParts := strings.Split(id, "/")
139+
if len(idParts) < 2 {
140+
return "", "", fmt.Errorf("Unable to determine qos association ID %s", id)
141+
}
142+
143+
return idParts[0], idParts[1], nil
144+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package openstack
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
10+
11+
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos"
12+
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes"
13+
)
14+
15+
func TestAccBlockstorageV3QosAssociation_basic(t *testing.T) {
16+
var qosTest qos.QoS
17+
var qosName = fmt.Sprintf("ACCPTTEST-%s", acctest.RandString(5))
18+
19+
var vt volumetypes.VolumeType
20+
var vtName = fmt.Sprintf("ACCPTTEST-%s", acctest.RandString(5))
21+
22+
resource.Test(t, resource.TestCase{
23+
PreCheck: func() {
24+
testAccPreCheck(t)
25+
testAccPreCheckAdminOnly(t)
26+
},
27+
ProviderFactories: testAccProviders,
28+
CheckDestroy: testAccCheckBlockstorageV3QosAssociationDestroy,
29+
Steps: []resource.TestStep{
30+
{
31+
Config: testAccBlockstorageV3QosAssociationBasic(qosName, vtName),
32+
Check: resource.ComposeTestCheckFunc(
33+
testAccCheckBlockStorageQosV3Exists("openstack_blockstorage_qos_v3.qos", &qosTest),
34+
testAccCheckBlockStorageVolumeTypeV3Exists("openstack_blockstorage_volume_type_v3.volume_type_1", &vt),
35+
testAccCheckBlockstorageV3QosAssociationExists("openstack_blockstorage_qos_association_v3.qos_association"),
36+
resource.TestCheckResourceAttrPtr(
37+
"openstack_blockstorage_qos_association_v3.qos_association", "qos_id", &qosTest.ID),
38+
resource.TestCheckResourceAttrPtr(
39+
"openstack_blockstorage_qos_association_v3.qos_association", "volume_type_id", &vt.ID),
40+
),
41+
},
42+
},
43+
})
44+
}
45+
46+
func testAccCheckBlockstorageV3QosAssociationDestroy(s *terraform.State) error {
47+
config := testAccProvider.Meta().(*Config)
48+
blockStorageClient, err := config.BlockStorageV3Client(osRegionName)
49+
if err != nil {
50+
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
51+
}
52+
53+
for _, rs := range s.RootModule().Resources {
54+
if rs.Type != "openstack_blockstorage_qos_association_v3" {
55+
continue
56+
}
57+
58+
qosID, vtID, err := parseQosAssociationID(rs.Primary.ID)
59+
if err != nil {
60+
return err
61+
}
62+
63+
allPages, err := qos.ListAssociations(blockStorageClient, qosID).AllPages()
64+
if err == nil {
65+
allAssociations, err := qos.ExtractAssociations(allPages)
66+
if err == nil {
67+
for _, association := range allAssociations {
68+
if association.ID == vtID {
69+
return fmt.Errorf("Qos association still exists")
70+
}
71+
}
72+
}
73+
}
74+
}
75+
76+
return nil
77+
}
78+
79+
func testAccCheckBlockstorageV3QosAssociationExists(n string) resource.TestCheckFunc {
80+
return func(s *terraform.State) error {
81+
rs, ok := s.RootModule().Resources[n]
82+
if !ok {
83+
return fmt.Errorf("Not found: %s", n)
84+
}
85+
86+
if rs.Primary.ID == "" {
87+
return fmt.Errorf("No ID is set")
88+
}
89+
90+
config := testAccProvider.Meta().(*Config)
91+
blockStorageClient, err := config.BlockStorageV3Client(osRegionName)
92+
if err != nil {
93+
return fmt.Errorf("Error creating OpenStack block storage client: %s", err)
94+
}
95+
96+
qosID, vtID, err := parseQosAssociationID(rs.Primary.ID)
97+
if err != nil {
98+
return err
99+
}
100+
101+
allPages, err := qos.ListAssociations(blockStorageClient, qosID).AllPages()
102+
if err != nil {
103+
return fmt.Errorf("Error retrieving associations for qos: %s", qosID)
104+
}
105+
106+
allAssociations, err := qos.ExtractAssociations(allPages)
107+
if err != nil {
108+
return fmt.Errorf("Error extracting associations for qos: %s", qosID)
109+
}
110+
111+
found := false
112+
for _, association := range allAssociations {
113+
if association.ID == vtID {
114+
found = true
115+
break
116+
}
117+
}
118+
119+
if !found {
120+
return fmt.Errorf("Qos association not found for qosID/vtID: %s", rs.Primary.ID)
121+
}
122+
123+
return nil
124+
}
125+
}
126+
127+
func testAccBlockstorageV3QosAssociationBasic(qosName, vtName string) string {
128+
return fmt.Sprintf(`
129+
resource "openstack_blockstorage_qos_v3" "qos" {
130+
name = "%s"
131+
consumer = "front-end"
132+
specs = {
133+
read_iops_sec = "20000"
134+
}
135+
}
136+
137+
resource "openstack_blockstorage_volume_type_v3" "volume_type_1" {
138+
name = "%s"
139+
}
140+
141+
resource "openstack_blockstorage_qos_association_v3" "qos_association" {
142+
qos_id = "${openstack_blockstorage_qos_v3.qos.id}"
143+
volume_type_id = "${openstack_blockstorage_volume_type_v3.volume_type_1.id}"
144+
}
145+
`, qosName, vtName)
146+
}

0 commit comments

Comments
 (0)