Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 18 additions & 15 deletions cloudstack/resource_cloudstack_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ func resourceCloudStackNetwork() *schema.Resource {

"cidr": {
Type: schema.TypeString,
Required: true,
Optional: true,
Comment thread
Damans227 marked this conversation as resolved.
Computed: true,
Comment thread
Damans227 marked this conversation as resolved.
ForceNew: true,
},

Expand Down Expand Up @@ -190,23 +191,25 @@ func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) e
return err
}

m, err := parseCIDR(d, no.Specifyipranges)
if err != nil {
return err
}
if _, ok := d.GetOk("cidr"); ok {
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With cidr now optional, users can set gateway/startip/endip without setting cidr, but this branch will skip parseCIDR and those user-supplied values will be silently ignored (and netmask can’t be derived). Consider validating that cidr must be set whenever any of gateway, startip, or endip is configured (return a clear error), or otherwise handle those fields explicitly when cidr is absent.

Suggested change
if _, ok := d.GetOk("cidr"); ok {
_, cidrConfigured := d.GetOk("cidr")
if !cidrConfigured {
if _, ok := d.GetOk("gateway"); ok {
return fmt.Errorf("cidr must be specified when gateway is configured")
}
if _, ok := d.GetOk("startip"); ok {
return fmt.Errorf("cidr must be specified when startip is configured")
}
if _, ok := d.GetOk("endip"); ok {
return fmt.Errorf("cidr must be specified when endip is configured")
}
}
if cidrConfigured {

Copilot uses AI. Check for mistakes.
m, err := parseCIDR(d, no.Specifyipranges)
if err != nil {
Comment on lines +194 to +196
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With cidr optional, GetNetworkOfferingByID is only needed when cidr is present (to read Specifyipranges for parseCIDR). To avoid an extra API call on L2/no-cidr networks, consider moving the network-offering lookup inside the cidr conditional block.

Copilot uses AI. Check for mistakes.
return err
}
Comment on lines +194 to +198
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that cidr is optional, users can set gateway/startip/endip without setting cidr. In that case this block is skipped and those arguments are silently ignored, which can lead to confusing behavior and perpetual diffs (e.g., config sets gateway but API/state stays empty). Consider validating that gateway, startip, and endip may only be set when cidr is also set (e.g., schema RequiredWith/ConflictsWith or an explicit check in Create returning a clear error).

Copilot uses AI. Check for mistakes.

// Set the needed IP config
p.SetGateway(m["gateway"])
p.SetNetmask(m["netmask"])
// Set the needed IP config
p.SetGateway(m["gateway"])
p.SetNetmask(m["netmask"])

// Only set the start IP if we have one
if startip, ok := m["startip"]; ok {
p.SetStartip(startip)
}
// Only set the start IP if we have one
if startip, ok := m["startip"]; ok {
p.SetStartip(startip)
}

// Only set the end IP if we have one
if endip, ok := m["endip"]; ok {
p.SetEndip(endip)
// Only set the end IP if we have one
if endip, ok := m["endip"]; ok {
p.SetEndip(endip)
}
}

// Set the network domain if we have one
Expand Down
56 changes: 56 additions & 0 deletions cloudstack/resource_cloudstack_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,59 @@ resource "cloudstack_network" "foo" {
acl_id = cloudstack_network_acl.bar.id
zone = cloudstack_vpc.foo.zone
}`

func TestAccCloudStackNetwork_l2NoCidr(t *testing.T) {
var network cloudstack.Network

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudStackNetworkDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudStackNetwork_l2NoCidr,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackNetworkExists(
"cloudstack_network.l2", &network),
),
},
},
})
}

const testAccCloudStackNetwork_l2NoCidr = `
resource "cloudstack_network" "l2" {
name = "terraform-l2-network"
display_text = "terraform-l2-network"
network_offering = "DefaultL2NetworkOffering"
zone = "Sandbox-simulator"
}`

func TestAccCloudStackNetwork_isolatedNoCidr(t *testing.T) {
var network cloudstack.Network

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudStackNetworkDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudStackNetwork_isolatedNoCidr,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackNetworkExists(
"cloudstack_network.isolated_no_cidr", &network),
resource.TestCheckResourceAttrSet(
"cloudstack_network.isolated_no_cidr", "cidr"),
),
},
},
})
}

const testAccCloudStackNetwork_isolatedNoCidr = `
resource "cloudstack_network" "isolated_no_cidr" {
name = "terraform-isolated-no-cidr"
display_text = "terraform-isolated-no-cidr"
network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
zone = "Sandbox-simulator"
}`