This is an automated email from the ASF dual-hosted git repository.
pearl11594 pushed a commit to branch fix-acl-ports
in repository
https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git
The following commit(s) were added to refs/heads/fix-acl-ports by this push:
new 6b1239c add support to call updateNetworkAclItem when a rule is
modified rather than re-creating
6b1239c is described below
commit 6b1239c9dbbef7b0989e0b352d95b6863159220e
Author: Pearl Dsilva <[email protected]>
AuthorDate: Wed Oct 8 17:27:39 2025 -0400
add support to call updateNetworkAclItem when a rule is modified rather
than re-creating
---
cloudstack/resource_cloudstack_network_acl_rule.go | 433 ++++++++++++++-------
1 file changed, 297 insertions(+), 136 deletions(-)
diff --git a/cloudstack/resource_cloudstack_network_acl_rule.go
b/cloudstack/resource_cloudstack_network_acl_rule.go
index be6085f..1bc17ce 100644
--- a/cloudstack/resource_cloudstack_network_acl_rule.go
+++ b/cloudstack/resource_cloudstack_network_acl_rule.go
@@ -23,6 +23,7 @@ import (
"context"
"fmt"
"log"
+ "sort"
"strconv"
"strings"
"sync"
@@ -58,7 +59,7 @@ func resourceCloudStackNetworkACLRule() *schema.Resource {
},
"rule": {
- Type: schema.TypeSet,
+ Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@@ -75,10 +76,9 @@ func resourceCloudStackNetworkACLRule() *schema.Resource {
},
"cidr_list": {
- Type:
schema.TypeSet,
+ Type:
schema.TypeList,
Required: true,
Elem:
&schema.Schema{Type: schema.TypeString},
- Set:
schema.HashString,
},
"protocol": {
@@ -155,12 +155,12 @@ func resourceCloudStackNetworkACLRuleCreate(d
*schema.ResourceData, meta interfa
}
// Create all rules that are configured
- if nrs := d.Get("rule").(*schema.Set); nrs.Len() > 0 {
- // Create an empty rule set to hold all newly created rules
- rules :=
resourceCloudStackNetworkACLRule().Schema["rule"].ZeroValue().(*schema.Set)
+ if nrs := d.Get("rule").([]interface{}); len(nrs) > 0 {
+ // Create an empty rule list to hold all newly created rules
+ rules := make([]interface{}, 0)
- log.Printf("[DEBUG] Processing %d rules", nrs.Len())
- err := createNetworkACLRules(d, meta, rules, nrs)
+ log.Printf("[DEBUG] Processing %d rules", len(nrs))
+ err := createNetworkACLRules(d, meta, &rules, nrs)
if err != nil {
log.Printf("[ERROR] Failed to create network ACL rules:
%v", err)
return err
@@ -184,15 +184,15 @@ func resourceCloudStackNetworkACLRuleCreate(d
*schema.ResourceData, meta interfa
return resourceCloudStackNetworkACLRuleRead(d, meta)
}
-func createNetworkACLRules(d *schema.ResourceData, meta interface{}, rules
*schema.Set, nrs *schema.Set) error {
- log.Printf("[DEBUG] Creating %d network ACL rules", nrs.Len())
+func createNetworkACLRules(d *schema.ResourceData, meta interface{}, rules
*[]interface{}, nrs []interface{}) error {
+ log.Printf("[DEBUG] Creating %d network ACL rules", len(nrs))
var errs *multierror.Error
var wg sync.WaitGroup
- wg.Add(nrs.Len())
+ wg.Add(len(nrs))
sem := make(chan struct{}, d.Get("parallelism").(int))
- for i, rule := range nrs.List() {
+ for i, rule := range nrs {
// Put in a tiny sleep here to avoid DoS'ing the API
time.Sleep(500 * time.Millisecond)
@@ -208,8 +208,8 @@ func createNetworkACLRules(d *schema.ResourceData, meta
interface{}, rules *sche
log.Printf("[ERROR] Failed to create rule #%d:
%v", index+1, err)
errs = multierror.Append(errs, fmt.Errorf("rule
#%d: %v", index+1, err))
} else if len(rule["uuids"].(map[string]interface{})) >
0 {
- log.Printf("[DEBUG] Successfully created rule
#%d, adding to rules set", index+1)
- rules.Add(rule)
+ log.Printf("[DEBUG] Successfully created rule
#%d, adding to rules list", index+1)
+ *rules = append(*rules, rule)
} else {
log.Printf("[WARN] Rule #%d created but has no
UUIDs", index+1)
}
@@ -261,7 +261,7 @@ func createNetworkACLRule(d *schema.ResourceData, meta
interface{}, rule map[str
// Set the CIDR list
var cidrList []string
- for _, cidr := range rule["cidr_list"].(*schema.Set).List() {
+ for _, cidr := range rule["cidr_list"].([]interface{}) {
cidrList = append(cidrList, cidr.(string))
}
p.SetCidrlist(cidrList)
@@ -422,12 +422,12 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
}
log.Printf("[DEBUG] Loaded %d rules into ruleMap", len(ruleMap))
- // Create an empty schema.Set to hold all rules
- rules :=
resourceCloudStackNetworkACLRule().Schema["rule"].ZeroValue().(*schema.Set)
+ // Create an empty rule list to hold all rules
+ var rules []interface{}
// Read all rules that are configured
- if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
- for _, rule := range rs.List() {
+ if rs := d.Get("rule").([]interface{}); len(rs) > 0 {
+ for _, rule := range rs {
rule := rule.(map[string]interface{})
uuids := rule["uuids"].(map[string]interface{})
log.Printf("[DEBUG] Processing rule with protocol=%s,
uuids=%+v", rule["protocol"].(string), uuids)
@@ -450,10 +450,10 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
// Delete the known rule so only unknown rules
remain in the ruleMap
delete(ruleMap, id.(string))
- // Create a set with all CIDR's
- cidrs := &schema.Set{F: schema.HashString}
+ // Create a list with all CIDR's
+ var cidrs []interface{}
for _, cidr := range strings.Split(r.Cidrlist,
",") {
- cidrs.Add(cidr)
+ cidrs = append(cidrs, cidr)
}
// Update the values
@@ -463,7 +463,7 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
rule["icmp_code"] = r.Icmpcode
rule["traffic_type"] =
strings.ToLower(r.Traffictype)
rule["cidr_list"] = cidrs
- rules.Add(rule)
+ rules = append(rules, rule)
log.Printf("[DEBUG] Added ICMP rule to state:
%+v", rule)
}
@@ -485,10 +485,10 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
// Delete the known rule so only unknown rules
remain in the ruleMap
delete(ruleMap, id.(string))
- // Create a set with all CIDR's
- cidrs := &schema.Set{F: schema.HashString}
+ // Create a list with all CIDR's
+ var cidrs []interface{}
for _, cidr := range strings.Split(r.Cidrlist,
",") {
- cidrs.Add(cidr)
+ cidrs = append(cidrs, cidr)
}
// Update the values
@@ -496,7 +496,7 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
rule["protocol"] = r.Protocol
rule["traffic_type"] =
strings.ToLower(r.Traffictype)
rule["cidr_list"] = cidrs
- rules.Add(rule)
+ rules = append(rules, rule)
log.Printf("[DEBUG] Added ALL rule to state:
%+v", rule)
}
@@ -505,12 +505,12 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
ps, hasPortsSet := rule["ports"].(*schema.Set)
portStr, hasPort := rule["port"].(string)
- if hasPortsSet && ps != nil && ps.Len() > 0 {
+ if hasPortsSet && ps.Len() > 0 {
// Handle deprecated ports field
(multiple ports)
log.Printf("[DEBUG] Processing %d ports
for TCP/UDP rule (deprecated field)", ps.Len())
- // Create an empty schema.Set to hold
all ports
- ports := &schema.Set{F:
schema.HashString}
+ // Create an empty list to hold all
ports
+ var ports []interface{}
// Loop through all ports and retrieve
their info
for _, port := range ps.List() {
@@ -531,10 +531,10 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
// Delete the known rule so
only unknown rules remain in the ruleMap
delete(ruleMap, id.(string))
- // Create a set with all CIDR's
- cidrs := &schema.Set{F:
schema.HashString}
+ // Create a list with all CIDR's
+ var cidrs []interface{}
for _, cidr := range
strings.Split(r.Cidrlist, ",") {
- cidrs.Add(cidr)
+ cidrs = append(cidrs,
cidr)
}
// Update the values
@@ -542,17 +542,16 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
rule["protocol"] = r.Protocol
rule["traffic_type"] =
strings.ToLower(r.Traffictype)
rule["cidr_list"] = cidrs
- ports.Add(port)
+ ports = append(ports, port)
log.Printf("[DEBUG] Added port
%s to TCP/UDP rule", port.(string))
}
- // Add this rule to the rules set with
ports
- rule["ports"] = ports
- rules.Add(rule)
+ // Add this rule to the rules list with
ports
+ rule["ports"] =
schema.NewSet(schema.HashString, ports)
+ rules = append(rules, rule)
log.Printf("[DEBUG] Added TCP/UDP rule
with deprecated ports to state: %+v", rule)
} else if hasPort && portStr != "" {
- // Handle new port field (single port)
log.Printf("[DEBUG] Processing single
port for TCP/UDP rule: %s", portStr)
id, ok := uuids[portStr]
@@ -561,7 +560,6 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
continue
}
- // Get the rule
r, ok := ruleMap[id.(string)]
if !ok {
log.Printf("[DEBUG] TCP/UDP
rule for port %s with ID %s not found, removing UUID", portStr, id.(string))
@@ -572,10 +570,10 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
// Delete the known rule so only
unknown rules remain in the ruleMap
delete(ruleMap, id.(string))
- // Create a set with all CIDR's
- cidrs := &schema.Set{F:
schema.HashString}
+ // Create a list with all CIDR's
+ var cidrs []interface{}
for _, cidr := range
strings.Split(r.Cidrlist, ",") {
- cidrs.Add(cidr)
+ cidrs = append(cidrs, cidr)
}
// Update the values
@@ -584,11 +582,10 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
rule["traffic_type"] =
strings.ToLower(r.Traffictype)
rule["cidr_list"] = cidrs
rule["port"] = portStr
- rules.Add(rule)
+ rules = append(rules, rule)
log.Printf("[DEBUG] Added TCP/UDP rule
with single port to state: %+v", rule)
} else {
- // Handle rule with no port (all ports)
log.Printf("[DEBUG] Processing TCP/UDP
rule with no port specified")
id, ok := uuids["all_ports"]
@@ -597,7 +594,6 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
continue
}
- // Get the rule
r, ok := ruleMap[id.(string)]
if !ok {
log.Printf("[DEBUG] TCP/UDP
rule for all_ports with ID %s not found, removing UUID", id.(string))
@@ -605,13 +601,12 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
continue
}
- // Delete the known rule so only
unknown rules remain in the ruleMap
delete(ruleMap, id.(string))
- // Create a set with all CIDR's
- cidrs := &schema.Set{F:
schema.HashString}
+ // Create a list with all CIDR's
+ var cidrs []interface{}
for _, cidr := range
strings.Split(r.Cidrlist, ",") {
- cidrs.Add(cidr)
+ cidrs = append(cidrs, cidr)
}
// Update the values
@@ -619,7 +614,7 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
rule["protocol"] = r.Protocol
rule["traffic_type"] =
strings.ToLower(r.Traffictype)
rule["cidr_list"] = cidrs
- rules.Add(rule)
+ rules = append(rules, rule)
log.Printf("[DEBUG] Added TCP/UDP rule
with no port to state: %+v", rule)
}
}
@@ -630,10 +625,9 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
managed := d.Get("managed").(bool)
if managed && len(ruleMap) > 0 {
for uuid := range ruleMap {
- // We need to create and add a dummy value to a
schema.Set as the
+ // We need to create and add a dummy value to a list as
the
// cidr_list is a required field and thus needs a value
- cidrs := &schema.Set{F: schema.HashString}
- cidrs.Add(uuid)
+ cidrs := []interface{}{uuid}
// Make a dummy rule to hold the unknown UUID
rule := map[string]interface{}{
@@ -642,14 +636,14 @@ func resourceCloudStackNetworkACLRuleRead(d
*schema.ResourceData, meta interface
"uuids": map[string]interface{}{uuid: uuid},
}
- // Add the dummy rule to the rules set
- rules.Add(rule)
+ // Add the dummy rule to the rules list
+ rules = append(rules, rule)
log.Printf("[DEBUG] Added managed dummy rule for UUID
%s", uuid)
}
}
- if rules.Len() > 0 {
- log.Printf("[DEBUG] Setting %d rules in state", rules.Len())
+ if len(rules) > 0 {
+ log.Printf("[DEBUG] Setting %d rules in state", len(rules))
if err := d.Set("rule", rules); err != nil {
log.Printf("[ERROR] Failed to set rule attribute: %v",
err)
return err
@@ -669,40 +663,16 @@ func resourceCloudStackNetworkACLRuleUpdate(d
*schema.ResourceData, meta interfa
return err
}
- // Check if the rule set as a whole has changed
+ // Check if the rule list has changed
if d.HasChange("rule") {
o, n := d.GetChange("rule")
- ors := o.(*schema.Set).Difference(n.(*schema.Set))
- nrs := n.(*schema.Set).Difference(o.(*schema.Set))
-
- // We need to start with a rule set containing all the rules we
- // already have and want to keep. Any rules that are not deleted
- // correctly and any newly created rules, will be added to this
- // set to make sure we end up in a consistent state
- rules := o.(*schema.Set).Intersection(n.(*schema.Set))
-
- // First loop through all the new rules and create (before
destroy) them
- if nrs.Len() > 0 {
- err := createNetworkACLRules(d, meta, rules, nrs)
-
- // We need to update this first to preserve the correct
state
- d.Set("rule", rules)
-
- if err != nil {
- return err
- }
- }
-
- // Then loop through all the old rules and delete them
- if ors.Len() > 0 {
- err := deleteNetworkACLRules(d, meta, rules, ors)
+ oldRules := o.([]interface{})
+ newRules := n.([]interface{})
- // We need to update this first to preserve the correct
state
- d.Set("rule", rules)
-
- if err != nil {
- return err
- }
+ log.Printf("[DEBUG] Rule list changed, performing efficient
updates")
+ err := updateNetworkACLRules(d, meta, oldRules, newRules)
+ if err != nil {
+ return err
}
}
@@ -710,59 +680,19 @@ func resourceCloudStackNetworkACLRuleUpdate(d
*schema.ResourceData, meta interfa
}
func resourceCloudStackNetworkACLRuleDelete(d *schema.ResourceData, meta
interface{}) error {
- // Create an empty rule set to hold all rules that where
- // not deleted correctly
- rules :=
resourceCloudStackNetworkACLRule().Schema["rule"].ZeroValue().(*schema.Set)
-
// Delete all rules
- if ors := d.Get("rule").(*schema.Set); ors.Len() > 0 {
- err := deleteNetworkACLRules(d, meta, rules, ors)
-
- // We need to update this first to preserve the correct state
- d.Set("rule", rules)
-
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func deleteNetworkACLRules(d *schema.ResourceData, meta interface{}, rules
*schema.Set, ors *schema.Set) error {
- var errs *multierror.Error
-
- var wg sync.WaitGroup
- wg.Add(ors.Len())
-
- sem := make(chan struct{}, d.Get("parallelism").(int))
- for _, rule := range ors.List() {
- // Put a sleep here to avoid DoS'ing the API
- time.Sleep(500 * time.Millisecond)
-
- go func(rule map[string]interface{}) {
- defer wg.Done()
- sem <- struct{}{}
-
- // Delete a single rule
- err := deleteNetworkACLRule(d, meta, rule)
-
- // If we have at least one UUID, we need to save the
rule
- if len(rule["uuids"].(map[string]interface{})) > 0 {
- rules.Add(rule)
- }
-
+ if ors := d.Get("rule").([]interface{}); len(ors) > 0 {
+ for _, rule := range ors {
+ ruleMap := rule.(map[string]interface{})
+ err := deleteNetworkACLRule(d, meta, ruleMap)
if err != nil {
- errs = multierror.Append(errs, err)
+ log.Printf("[ERROR] Failed to delete rule: %v",
err)
+ return err
}
-
- <-sem
- }(rule.(map[string]interface{}))
+ }
}
- wg.Wait()
-
- return errs.ErrorOrNil()
+ return nil
}
func deleteNetworkACLRule(d *schema.ResourceData, meta interface{}, rule
map[string]interface{}) error {
@@ -936,3 +866,234 @@ func checkACLListExists(cs *cloudstack.CloudStackClient,
aclID string) (bool, er
log.Printf("[DEBUG] ACL list check result: count=%d", count)
return count > 0, nil
}
+
+func updateNetworkACLRules(d *schema.ResourceData, meta interface{}, oldRules,
newRules []interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ log.Printf("[DEBUG] Updating ACL rules: %d old rules, %d new rules",
len(oldRules), len(newRules))
+
+ oldRuleMap := make(map[string]map[string]interface{})
+ newRuleMap := make(map[string]map[string]interface{})
+
+ for _, rule := range oldRules {
+ ruleMap := rule.(map[string]interface{})
+ key := createRuleKey(ruleMap)
+ oldRuleMap[key] = ruleMap
+ log.Printf("[DEBUG] Old rule key: %s", key)
+ }
+
+ for _, rule := range newRules {
+ ruleMap := rule.(map[string]interface{})
+ key := createRuleKey(ruleMap)
+ newRuleMap[key] = ruleMap
+ log.Printf("[DEBUG] New rule key: %s", key)
+ }
+
+ for key, oldRule := range oldRuleMap {
+ if _, exists := newRuleMap[key]; !exists {
+ log.Printf("[DEBUG] Deleting rule: %s", key)
+ err := deleteNetworkACLRule(d, meta, oldRule)
+ if err != nil {
+ return fmt.Errorf("failed to delete rule %s:
%v", key, err)
+ }
+ }
+ }
+
+ var rulesToCreate []interface{}
+ for key, newRule := range newRuleMap {
+ if _, exists := oldRuleMap[key]; !exists {
+ log.Printf("[DEBUG] Creating new rule: %s", key)
+ rulesToCreate = append(rulesToCreate, newRule)
+ }
+ }
+
+ if len(rulesToCreate) > 0 {
+ var createdRules []interface{}
+ err := createNetworkACLRules(d, meta, &createdRules,
rulesToCreate)
+ if err != nil {
+ return fmt.Errorf("failed to create new rules: %v", err)
+ }
+ }
+
+ for key, newRule := range newRuleMap {
+ if oldRule, exists := oldRuleMap[key]; exists {
+ if ruleNeedsUpdate(oldRule, newRule) {
+ log.Printf("[DEBUG] Updating rule: %s", key)
+ err := updateNetworkACLRule(cs, oldRule,
newRule)
+ if err != nil {
+ return fmt.Errorf("failed to update
rule %s: %v", key, err)
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func createRuleKey(rule map[string]interface{}) string {
+ protocol := rule["protocol"].(string)
+ trafficType := rule["traffic_type"].(string)
+
+ if protocol == "icmp" {
+ icmpType := rule["icmp_type"].(int)
+ icmpCode := rule["icmp_code"].(int)
+ return fmt.Sprintf("%s-%s-icmp-%d-%d", protocol, trafficType,
icmpType, icmpCode)
+ }
+
+ if protocol == "all" {
+ return fmt.Sprintf("%s-%s-all", protocol, trafficType)
+ }
+
+ if protocol == "tcp" || protocol == "udp" {
+ portStr, hasPort := rule["port"].(string)
+ if hasPort && portStr != "" {
+ return fmt.Sprintf("%s-%s-port-%s", protocol,
trafficType, portStr)
+ } else {
+ return fmt.Sprintf("%s-%s-noport", protocol,
trafficType)
+ }
+ }
+
+ // For numeric protocols
+ return fmt.Sprintf("%s-%s", protocol, trafficType)
+}
+
+func ruleNeedsUpdate(oldRule, newRule map[string]interface{}) bool {
+ if oldRule["action"].(string) != newRule["action"].(string) {
+ log.Printf("[DEBUG] Action changed: %s -> %s",
oldRule["action"].(string), newRule["action"].(string))
+ return true
+ }
+
+ if oldRule["protocol"].(string) != newRule["protocol"].(string) {
+ log.Printf("[DEBUG] Protocol changed: %s -> %s",
oldRule["protocol"].(string), newRule["protocol"].(string))
+ return true
+ }
+
+ if oldRule["traffic_type"].(string) != newRule["traffic_type"].(string)
{
+ log.Printf("[DEBUG] Traffic type changed: %s -> %s",
oldRule["traffic_type"].(string), newRule["traffic_type"].(string))
+ return true
+ }
+
+ oldDesc, oldHasDesc := oldRule["description"].(string)
+ newDesc, newHasDesc := newRule["description"].(string)
+ if oldHasDesc != newHasDesc || (oldHasDesc && newHasDesc && oldDesc !=
newDesc) {
+ log.Printf("[DEBUG] Description changed: %s -> %s", oldDesc,
newDesc)
+ return true
+ }
+
+ protocol := newRule["protocol"].(string)
+ switch protocol {
+ case "icmp":
+ if oldRule["icmp_type"].(int) != newRule["icmp_type"].(int) {
+ log.Printf("[DEBUG] ICMP type changed: %d -> %d",
oldRule["icmp_type"].(int), newRule["icmp_type"].(int))
+ return true
+ }
+ if oldRule["icmp_code"].(int) != newRule["icmp_code"].(int) {
+ log.Printf("[DEBUG] ICMP code changed: %d -> %d",
oldRule["icmp_code"].(int), newRule["icmp_code"].(int))
+ return true
+ }
+ case "tcp", "udp":
+ oldPort, oldHasPort := oldRule["port"].(string)
+ newPort, newHasPort := newRule["port"].(string)
+ if oldHasPort != newHasPort || (oldHasPort && newHasPort &&
oldPort != newPort) {
+ log.Printf("[DEBUG] Port changed: %s -> %s", oldPort,
newPort)
+ return true
+ }
+ }
+
+ oldCidrs := oldRule["cidr_list"].([]interface{})
+ newCidrs := newRule["cidr_list"].([]interface{})
+ if len(oldCidrs) != len(newCidrs) {
+ log.Printf("[DEBUG] CIDR list length changed: %d -> %d",
len(oldCidrs), len(newCidrs))
+ return true
+ }
+
+ oldCidrStrs := make([]string, len(oldCidrs))
+ newCidrStrs := make([]string, len(newCidrs))
+ for i, cidr := range oldCidrs {
+ oldCidrStrs[i] = cidr.(string)
+ }
+ for i, cidr := range newCidrs {
+ newCidrStrs[i] = cidr.(string)
+ }
+
+ sort.Strings(oldCidrStrs)
+ sort.Strings(newCidrStrs)
+
+ for i, oldCidr := range oldCidrStrs {
+ if oldCidr != newCidrStrs[i] {
+ log.Printf("[DEBUG] CIDR changed at index %d: %s ->
%s", i, oldCidr, newCidrStrs[i])
+ return true
+ }
+ }
+
+ return false
+}
+
+func updateNetworkACLRule(cs *cloudstack.CloudStackClient, oldRule, newRule
map[string]interface{}) error {
+ uuids := oldRule["uuids"].(map[string]interface{})
+
+ for key, uuid := range uuids {
+ if key == "%" {
+ continue
+ }
+
+ log.Printf("[DEBUG] Updating ACL rule with UUID: %s",
uuid.(string))
+ p := cs.NetworkACL.NewUpdateNetworkACLItemParams(uuid.(string))
+
+ p.SetAction(newRule["action"].(string))
+
+ var cidrList []string
+ for _, cidr := range newRule["cidr_list"].([]interface{}) {
+ cidrList = append(cidrList, cidr.(string))
+ }
+ p.SetCidrlist(cidrList)
+
+ if desc, ok := newRule["description"].(string); ok && desc !=
"" {
+ p.SetReason(desc)
+ }
+
+ p.SetProtocol(newRule["protocol"].(string))
+
+ p.SetTraffictype(newRule["traffic_type"].(string))
+
+ protocol := newRule["protocol"].(string)
+ switch protocol {
+ case "icmp":
+ if icmpType, ok := newRule["icmp_type"].(int); ok {
+ p.SetIcmptype(icmpType)
+ log.Printf("[DEBUG] Set icmp_type=%d", icmpType)
+ }
+ if icmpCode, ok := newRule["icmp_code"].(int); ok {
+ p.SetIcmpcode(icmpCode)
+ log.Printf("[DEBUG] Set icmp_code=%d", icmpCode)
+ }
+ case "tcp", "udp":
+ if portStr, hasPort := newRule["port"].(string);
hasPort && portStr != "" {
+ m := splitPorts.FindStringSubmatch(portStr)
+ if m != nil {
+ startPort, err := strconv.Atoi(m[1])
+ if err == nil {
+ endPort := startPort
+ if m[2] != "" {
+ if ep, err :=
strconv.Atoi(m[2]); err == nil {
+ endPort = ep
+ }
+ }
+ p.SetStartport(startPort)
+ p.SetEndport(endPort)
+ log.Printf("[DEBUG] Set port
start=%d, end=%d", startPort, endPort)
+ }
+ }
+ }
+ }
+
+ _, err := cs.NetworkACL.UpdateNetworkACLItem(p)
+ if err != nil {
+ log.Printf("[ERROR] Failed to update ACL rule %s: %v",
uuid.(string), err)
+ return err
+ }
+
+ log.Printf("[DEBUG] Successfully updated ACL rule %s",
uuid.(string))
+ }
+
+ return nil
+}