Skip to content

Commit 5e18d6f

Browse files
authored
Merge pull request #131 from DimensionDataResearch/development/v2.0
Static route, IP address reservation and bug fixing
2 parents 4cf2f9e + f266664 commit 5e18d6f

23 files changed

+744
-363
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ AccTest.log
3636
#.bak
3737
**/.idea
3838
*.old
39-
*.OLD
39+
*.OLD

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: go
22

33
go:
4-
- "1.10"
4+
- "1.12.1"
55

66
branches:
77
only:
@@ -14,7 +14,7 @@ install:
1414
- go get github.com/pkg/errors
1515
- go get github.com/hashicorp/terraform
1616
- pushd $GOPATH/src/github.com/hashicorp/terraform
17-
- git checkout v0.11.11
17+
- git checkout v0.11.13
1818
- popd
1919

2020
script:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PROVIDER_NAME = ddcloud
22

3-
VERSION = 2.1.2
3+
VERSION = 2.2.2
44
VERSION_INFO_FILE = ./$(PROVIDER_NAME)/version-info.go
55

66
BIN_DIRECTORY = _bin

ddcloud/datasource_vlan.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ func dataSourceVLAN() *schema.Resource {
4747
Computed: true,
4848
Description: "The VLAN's IPv6 prefix length.",
4949
},
50+
resourceKeyVLANIPv6GatewayAddress: &schema.Schema{
51+
Type: schema.TypeString,
52+
Computed: true,
53+
Description: "The VLAN's IPv6 Gateway Address",
54+
},
55+
resourceKeyVLANIPv4GatewayAddress: &schema.Schema{
56+
Type: schema.TypeString,
57+
Computed: true,
58+
Description: "The VLAN's IPv4 Gateway Address",
59+
},
5060
},
5161
}
5262
}
@@ -71,6 +81,9 @@ func dataSourceVLANRead(data *schema.ResourceData, provider interface{}) error {
7181
data.Set(resourceKeyVLANIPv4PrefixSize, vlan.IPv4Range.PrefixSize)
7282
data.Set(resourceKeyVLANIPv6BaseAddress, vlan.IPv6Range.BaseAddress)
7383
data.Set(resourceKeyVLANIPv6PrefixSize, vlan.IPv6Range.PrefixSize)
84+
data.Set(resourceKeyVLANIPv4GatewayAddress, vlan.IPv4GatewayAddress)
85+
data.Set(resourceKeyVLANIPv6GatewayAddress, vlan.IPv6GatewayAddress)
86+
7487
} else {
7588
return fmt.Errorf("failed to find VLAN '%s' in network domain '%s'", name, networkDomainID)
7689
}

ddcloud/provider.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ func Provider() terraform.ResourceProvider {
2121
// Provider settings schema
2222
Schema: map[string]*schema.Schema{
2323
"region": &schema.Schema{
24-
Type: schema.TypeString,
25-
Optional: true,
26-
Default: "",
27-
Description: "The region code that identifies the target end-point for the Dimension Data CloudControl API.",
24+
Type: schema.TypeString,
25+
Optional: true,
26+
Default: "",
27+
Description: "The region code that identifies the target end-point for the Dimension Data CloudControl API.",
2828
ConflictsWith: []string{"cloudcontrol_endpoint"},
2929
},
3030
"cloudcontrol_endpoint": &schema.Schema{
31-
Type: schema.TypeString,
32-
Optional: true,
33-
Default: "",
34-
Description: "The base URL of a custom target end-point for the Dimension Data CloudControl API.",
31+
Type: schema.TypeString,
32+
Optional: true,
33+
Default: "",
34+
Description: "The base URL of a custom target end-point for the Dimension Data CloudControl API.",
3535
ConflictsWith: []string{"region"},
3636
},
3737
"username": &schema.Schema{

ddcloud/resource_address.go

Lines changed: 132 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package ddcloud
22

33
import (
4+
"fmt"
5+
"github.com/DimensionDataResearch/dd-cloud-compute-terraform/retry"
6+
"github.com/DimensionDataResearch/go-dd-cloud-compute/compute"
47
"github.com/hashicorp/terraform/helper/schema"
58
"github.com/pkg/errors"
69
"log"
@@ -14,6 +17,7 @@ const (
1417
resourceKeyAddressPrefixSize = "prefix_size"
1518
resourceKeyAddressNetworkDomainID = "networkdomain"
1619
resourceKeyAddressListID = "addresslist_id"
20+
resourceKeyAddressAddressListName = "addresslist_name"
1721
)
1822

1923
func resourceAddress() *schema.Resource {
@@ -31,11 +35,11 @@ func resourceAddress() *schema.Resource {
3135
Required: true,
3236
Description: "The Id of the network domain in which the address applies",
3337
},
34-
resourceKeyAddressListID: &schema.Schema{
38+
resourceKeyAddressAddressListName: &schema.Schema{
3539
Type: schema.TypeString,
3640
ForceNew: true,
3741
Required: true,
38-
Description: "The Id of the address list",
42+
Description: "The Name of the address list",
3943
},
4044
resourceKeyAddressBegin: &schema.Schema{
4145
Type: schema.TypeString,
@@ -64,9 +68,17 @@ func resourceAddress() *schema.Resource {
6468
// Check if an address list resource exists.
6569
func resourceAddressExists(data *schema.ResourceData, provider interface{}) (bool, error) {
6670
log.Printf("resourceAddressExists")
67-
addressListId := data.Get(resourceKeyAddressListID).(string)
68-
6971
client := provider.(*providerState).Client()
72+
networkDomainId := data.Get(resourceKeyAddressNetworkDomainID).(string)
73+
addressListName := data.Get(resourceKeyAddressAddressListName).(string)
74+
addrList, _ := client.GetIPAddressListByName(addressListName, networkDomainId)
75+
76+
var addressListId string
77+
if addrList == nil {
78+
return false, nil
79+
} else {
80+
addressListId = addrList.ID
81+
}
7082

7183
begin, okBegin := data.GetOk(resourceKeyAddressBegin)
7284
network, okNetwork := data.GetOk(resourceKeyAddressNetwork)
@@ -92,7 +104,19 @@ func resourceAddressCreate(data *schema.ResourceData, provider interface{}) erro
92104
var network string
93105
var prefixSize int
94106

95-
addressListId := data.Get(resourceKeyAddressListID).(string)
107+
providerState := provider.(*providerState)
108+
client := providerState.Client()
109+
110+
addressListName := data.Get(resourceKeyAddressAddressListName).(string)
111+
networkDomainId := data.Get(resourceKeyAddressNetworkDomainID).(string)
112+
addrList, err := client.GetIPAddressListByName(addressListName, networkDomainId)
113+
114+
var addressListId string
115+
if err != nil {
116+
return err
117+
} else {
118+
addressListId = addrList.ID
119+
}
96120

97121
valBegin, beginOk := data.GetOk(resourceKeyAddressBegin)
98122
if beginOk {
@@ -124,8 +148,26 @@ func resourceAddressCreate(data *schema.ResourceData, provider interface{}) erro
124148
}
125149
}
126150

127-
client := provider.(*providerState).Client()
128-
_, err := client.AddAddress(addressListId, begin, end, network, prefixSize)
151+
operationDescription := fmt.Sprintf("Add Address to FW Address List'")
152+
153+
err = providerState.RetryAction(operationDescription, func(context retry.Context) {
154+
// CloudControl has issues if more than one asynchronous operation is initated at a time (returns UNEXPECTED_ERROR).
155+
asyncLock := providerState.AcquireAsyncOperationLock("Create Address begin:'%s' end:%s network:%s prefix:%d",
156+
valBegin, valEnd, valNetwork, valPrefix)
157+
defer asyncLock.Release()
158+
159+
_, deployError := client.AddAddress(addressListId, begin, end, network, prefixSize)
160+
161+
if deployError != nil {
162+
if compute.IsResourceBusyError(deployError) {
163+
context.Retry()
164+
} else {
165+
context.Fail(deployError)
166+
}
167+
}
168+
169+
asyncLock.Release()
170+
})
129171

130172
if err != nil {
131173
return err
@@ -147,12 +189,22 @@ func resourceAddressCreate(data *schema.ResourceData, provider interface{}) erro
147189
func resourceAddressRead(data *schema.ResourceData, provider interface{}) error {
148190
log.Printf("resourceAddressRead")
149191

150-
addressListId := data.Get(resourceKeyAddressListID).(string)
151192
begin, _ := data.GetOk(resourceKeyAddressBegin)
152193
network, _ := data.GetOk(resourceKeyAddressNetwork)
153194

154195
client := provider.(*providerState).Client()
155196

197+
networkDomainId := data.Get(resourceKeyAddressNetworkDomainID).(string)
198+
addressListName := data.Get(resourceKeyAddressAddressListName).(string)
199+
addrList, err := client.GetIPAddressListByName(addressListName, networkDomainId)
200+
201+
var addressListId string
202+
if err != nil {
203+
return err
204+
} else {
205+
addressListId = addrList.ID
206+
}
207+
156208
// Check if address exists in cloud
157209
addr, addrOk := client.GetAddressOk(addressListId, begin.(string), network.(string))
158210
if !addrOk {
@@ -192,7 +244,19 @@ func resourceAddressUpdate(data *schema.ResourceData, provider interface{}) erro
192244
// Enable partial state mode
193245
// data.Partial(true)
194246

195-
addressListId := data.Get(resourceKeyAddressListID).(string)
247+
providerState := provider.(*providerState)
248+
client := providerState.Client()
249+
250+
networkDomainId := data.Get(resourceKeyAddressNetworkDomainID).(string)
251+
addressListName := data.Get(resourceKeyAddressAddressListName).(string)
252+
addrList, err := client.GetIPAddressListByName(addressListName, networkDomainId)
253+
254+
var addressListId string
255+
if err != nil {
256+
return err
257+
} else {
258+
addressListId = addrList.ID
259+
}
196260

197261
valBegin, beginOk := data.GetOk(resourceKeyAddressBegin)
198262
if beginOk {
@@ -214,8 +278,6 @@ func resourceAddressUpdate(data *schema.ResourceData, provider interface{}) erro
214278
prefixSize = valPrefix.(int)
215279
}
216280

217-
client := provider.(*providerState).Client()
218-
219281
oldBegin, _ := data.GetChange(resourceKeyAddressBegin)
220282
oldNetwork, _ := data.GetChange(resourceKeyAddressNetwork)
221283

@@ -227,33 +289,49 @@ func resourceAddressUpdate(data *schema.ResourceData, provider interface{}) erro
227289
ipAddress = oldNetwork.(string)
228290
}
229291

230-
// Update step 1: Remove old address
231-
_, errOld := client.DeleteAddress(addressListId, ipAddress)
232-
if errOld != nil {
233-
return errOld
234-
}
292+
operationDescription := fmt.Sprintf("Delete Address in FW Address List'")
293+
294+
err = providerState.RetryAction(operationDescription, func(context retry.Context) {
295+
// CloudControl has issues if more than one asynchronous operation is initated at a time (returns UNEXPECTED_ERROR).
296+
asyncLock := providerState.AcquireAsyncOperationLock("Delete Address '%s'", valBegin)
297+
defer asyncLock.Release()
298+
299+
// Update step 1: Remove old address
300+
_, deployErr := client.DeleteAddress(addressListId, ipAddress)
301+
if deployErr != nil {
302+
return
303+
}
235304

236-
// Update step 2: Add new address
237-
newAddress, err := client.AddAddress(addressListId, begin, end, network, prefixSize)
305+
// Update step 2: Add new address
306+
newAddress, deployErr := client.AddAddress(addressListId, begin, end, network, prefixSize)
307+
308+
if deployErr != nil {
309+
return
310+
}
311+
312+
log.Printf("Updated address: %s", newAddress.Begin)
313+
314+
// We succeeded, disable partial mode. This causes Terraform to save
315+
// all fields again.
316+
// data.Partial(false)
317+
318+
asyncLock.Release()
319+
})
238320

239321
if err != nil {
240322
return err
241323
}
242324

243-
log.Printf("Updated address: %s", newAddress.Begin)
244-
245-
// We succeeded, disable partial mode. This causes Terraform to save
246-
// all fields again.
247-
// data.Partial(false)
248-
249325
return resourceAddressRead(data, provider)
250326
}
251327

252328
// Delete an address list resource.
253329
func resourceAddressDelete(data *schema.ResourceData, provider interface{}) error {
254330
log.Printf("resourceAddressDelete")
255331

256-
addressListId := data.Get(resourceKeyAddressListID).(string)
332+
providerState := provider.(*providerState)
333+
client := providerState.Client()
334+
257335
begin := data.Get(resourceKeyAddressBegin).(string)
258336
network := data.Get(resourceKeyAddressNetwork).(string)
259337

@@ -264,8 +342,35 @@ func resourceAddressDelete(data *schema.ResourceData, provider interface{}) erro
264342
ip = network
265343
}
266344

267-
client := provider.(*providerState).Client()
268-
_, err := client.DeleteAddress(addressListId, ip)
345+
networkDomainId := data.Get(resourceKeyAddressNetworkDomainID).(string)
346+
addressListName := data.Get(resourceKeyAddressAddressListName).(string)
347+
addrList, err := client.GetIPAddressListByName(addressListName, networkDomainId)
348+
349+
var addressListId string
350+
if err != nil {
351+
return err
352+
} else {
353+
addressListId = addrList.ID
354+
}
355+
356+
operationDescription := fmt.Sprintf("Delete Address in FW Address List'")
357+
358+
err = providerState.RetryAction(operationDescription, func(context retry.Context) {
359+
// CloudControl has issues if more than one asynchronous operation is initated at a time (returns UNEXPECTED_ERROR).
360+
asyncLock := providerState.AcquireAsyncOperationLock("Delete Address '%s'", begin)
361+
defer asyncLock.Release()
362+
_, deployError := client.DeleteAddress(addressListId, ip)
363+
364+
if deployError != nil {
365+
if compute.IsResourceBusyError(deployError) {
366+
context.Retry()
367+
} else {
368+
context.Fail(deployError)
369+
}
370+
}
371+
372+
asyncLock.Release()
373+
})
269374

270375
if err != nil {
271376
return err

0 commit comments

Comments
 (0)