Skip to content

Commit 7f6e9b7

Browse files
committed
Improvement: ZSTAC-75840. Support static IP configuration via network_interfaces and restore compatibility with legacy l3_network_uuids field
1 parent 8afe930 commit 7f6e9b7

File tree

5 files changed

+204
-22
lines changed

5 files changed

+204
-22
lines changed

docs/index.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,27 @@ data "zstack_instance_offers" "offer" {
6262
# - `never_stop`: If set to `true`, the virtual machine will never be stopped.
6363
# - `root_disk`: Configuration for the root disk of the virtual machine.
6464
resource "zstack_instance" "example_vm" {
65-
name = "moexample-v"
66-
image_uuid = data.zstack_images.centos.images[0].uuid
67-
l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid]
65+
name = "moexample-v"
66+
image_uuid = data.zstack_images.centos.images[0].uuid
67+
# l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid]
6868
description = "Example VM instance"
6969
instance_offering_uuid = data.zstack_instance_offers.offer.instance_offers[0].uuid # Using Instance offering UUID or custom CPU and memory
7070
# memory_size = 1024000000
7171
# cpu_num = 1
72+
73+
network_interfaces = [
74+
{
75+
l3_network_uuid = data.zstack_l3networks.networks.l3networks.0.uuid
76+
# default_l3 = true
77+
static_ip = "172.30.3.154"
78+
},
79+
{
80+
l3_network_uuid = data.zstack_l3networks.vpc_net.l3networks.0.uuid
81+
default_l3 = true
82+
static_ip = "192.168.2.20"
83+
}
84+
]
85+
7286
never_stop = true
7387
root_disk = {
7488
size = 123456788

docs/resources/instance.md

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,23 @@ data "zstack_instance_offers" "offer" {
2525
}
2626
2727
resource "zstack_instance" "example_vm" {
28-
name = "example-v"
29-
image_uuid = data.zstack_images.centos.images[0].uuid
30-
l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid]
31-
description = "jumper server"
28+
name = "example-v"
29+
image_uuid = data.zstack_images.centos.images[0].uuid
30+
# l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid] #Deprecated
31+
description = "jumper server"
3232
# instance_offering_uuid = data.zstack_instance_offers.offer.instance_offers[0].uuid #using Instance offering uuid or custom cpu and memory
33+
network_interfaces = [
34+
{
35+
l3_network_uuid = data.zstack_l3networks.networks.l3networks.0.uuid
36+
# default_l3 = true
37+
static_ip = "172.30.3.154"
38+
},
39+
{
40+
l3_network_uuid = data.zstack_l3networks.vpc_net.l3networks.0.uuid
41+
default_l3 = true
42+
static_ip = "192.168.2.20"
43+
}
44+
]
3345
memory_size = 4096
3446
cpu_num = 4
3547
expunge = true
@@ -46,7 +58,6 @@ output "zstack_instance" {
4658
### Required
4759

4860
- `image_uuid` (String) The UUID of the image used to create the VM instance.
49-
- `l3_network_uuids` (List of String) A list of UUIDs for the L3 networks associated with the VM instance.
5061
- `name` (String) The name of the VM instance.
5162

5263
### Optional
@@ -60,9 +71,11 @@ output "zstack_instance" {
6071
- `gpu_devices` (Attributes List) A list of GPU devices assigned to the VM instance. (see [below for nested schema](#nestedatt--gpu_devices))
6172
- `host_uuid` (String) The UUID of the host where the VM instance is running.
6273
- `instance_offering_uuid` (String) The UUID of the instance offering used by the VM. Required if using instance offering uuid to create instances. Mutually exclusive with `cpu_num` and `memory_size`.
74+
- `l3_network_uuids` (List of String) Deprecated. Use `network_interfaces` instead. A list of UUIDs for the L3 networks associated with the VM instance.
6375
- `marketplace` (Boolean) Indicates whether the VM instance is a marketplace instance.
6476
- `memory_size` (Number) The memory size allocated to the VM instance in megabytes (MB). When used together with `cpu_num`, the `instance_offering_uuid` is not required.
65-
- `networks` (Attributes List) The network configurations associated with the VM instance. (see [below for nested schema](#nestedatt--networks))
77+
- `network_interfaces` (Attributes List) Defines network interfaces attached to the VM. Each NIC corresponds to an L3 network, and optionally configures a static IP. (see [below for nested schema](#nestedatt--network_interfaces))
78+
- `networks` (Attributes List) Deprecated. Use `network_interfaces` instead. The network configurations associated with the VM instance. (see [below for nested schema](#nestedatt--networks))
6679
- `never_stop` (Boolean) Whether the VM instance should never stop automatically.
6780
- `root_disk` (Attributes) The configuration for the root disk of the VM instance. (see [below for nested schema](#nestedatt--root_disk))
6881
- `strategy` (String) The deployment strategy for the VM instance.
@@ -108,6 +121,19 @@ Optional:
108121
- `uuid` (String) The UUID of the GPU device.
109122

110123

124+
<a id="nestedatt--network_interfaces"></a>
125+
### Nested Schema for `network_interfaces`
126+
127+
Required:
128+
129+
- `l3_network_uuid` (String) The UUID of the L3 network for this NIC.
130+
131+
Optional:
132+
133+
- `default_l3` (Boolean) Whether this NIC is the default route NIC.
134+
- `static_ip` (String) Static IP address to assign. The format will be converted to system tag `staticIp::<l3_uuid>::<ip>`.
135+
136+
111137
<a id="nestedatt--networks"></a>
112138
### Nested Schema for `networks`
113139

examples/provider/accesskey/provider.tf

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,27 @@ data "zstack_instance_offers" "offer" {
4040
# - `never_stop`: If set to `true`, the virtual machine will never be stopped.
4141
# - `root_disk`: Configuration for the root disk of the virtual machine.
4242
resource "zstack_instance" "example_vm" {
43-
name = "moexample-v"
44-
image_uuid = data.zstack_images.centos.images[0].uuid
45-
l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid]
43+
name = "moexample-v"
44+
image_uuid = data.zstack_images.centos.images[0].uuid
45+
# l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid]
4646
description = "Example VM instance"
4747
instance_offering_uuid = data.zstack_instance_offers.offer.instance_offers[0].uuid # Using Instance offering UUID or custom CPU and memory
4848
# memory_size = 1024000000
4949
# cpu_num = 1
50+
51+
network_interfaces = [
52+
{
53+
l3_network_uuid = data.zstack_l3networks.networks.l3networks.0.uuid
54+
# default_l3 = true
55+
static_ip = "172.30.3.154"
56+
},
57+
{
58+
l3_network_uuid = data.zstack_l3networks.vpc_net.l3networks.0.uuid
59+
default_l3 = true
60+
static_ip = "192.168.2.20"
61+
}
62+
]
63+
5064
never_stop = true
5165
root_disk = {
5266
size = 123456788

examples/resources/instance/resource.tf

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,23 @@ data "zstack_instance_offers" "offer" {
1111
}
1212

1313
resource "zstack_instance" "example_vm" {
14-
name = "example-v"
15-
image_uuid = data.zstack_images.centos.images[0].uuid
16-
l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid]
17-
description = "jumper server"
14+
name = "example-v"
15+
image_uuid = data.zstack_images.centos.images[0].uuid
16+
# l3_network_uuids = [data.zstack_l3networks.l3networks.l3networks[0].uuid] #Deprecated
17+
description = "jumper server"
1818
# instance_offering_uuid = data.zstack_instance_offers.offer.instance_offers[0].uuid #using Instance offering uuid or custom cpu and memory
19+
network_interfaces = [
20+
{
21+
l3_network_uuid = data.zstack_l3networks.networks.l3networks.0.uuid
22+
# default_l3 = true
23+
static_ip = "172.30.3.154"
24+
},
25+
{
26+
l3_network_uuid = data.zstack_l3networks.vpc_net.l3networks.0.uuid
27+
default_l3 = true
28+
static_ip = "192.168.2.20"
29+
}
30+
]
1931
memory_size = 4096
2032
cpu_num = 4
2133
expunge = true

zstack/provider/resource_zstack_instance.go

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ type vmInstanceDataSourceModel struct {
7070
Name types.String `tfsdk:"name"`
7171
ImageUuid types.String `tfsdk:"image_uuid"`
7272
L3NetworkUuids types.List `tfsdk:"l3_network_uuids"`
73+
NetworkInterfaces types.List `tfsdk:"network_interfaces"`
7374
RootDisk types.Object `tfsdk:"root_disk"`
7475
DataDisks types.List `tfsdk:"data_disks"`
7576
Networks types.List `tfsdk:"networks"`
@@ -97,6 +98,12 @@ type NicsModel struct {
9798
Gateway types.String `tfsdk:"gateway"`
9899
}
99100

101+
type NetworkInterfaceModel struct {
102+
L3NetworkUuid types.String `tfsdk:"l3_network_uuid"`
103+
DefaultL3 types.Bool `tfsdk:"default_l3"`
104+
StaticIp types.String `tfsdk:"static_ip"`
105+
}
106+
100107
func InstanceResource() resource.Resource {
101108
return &vmResource{}
102109
}
@@ -140,6 +147,26 @@ func (r *vmResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp
140147
Required: true,
141148
Description: "The name of the VM instance.",
142149
},
150+
"network_interfaces": schema.ListNestedAttribute{
151+
Optional: true,
152+
Description: "Defines network interfaces attached to the VM. Each NIC corresponds to an L3 network, and optionally configures a static IP.",
153+
NestedObject: schema.NestedAttributeObject{
154+
Attributes: map[string]schema.Attribute{
155+
"l3_network_uuid": schema.StringAttribute{
156+
Required: true,
157+
Description: "The UUID of the L3 network for this NIC.",
158+
},
159+
"default_l3": schema.BoolAttribute{
160+
Optional: true,
161+
Description: "Whether this NIC is the default route NIC.",
162+
},
163+
"static_ip": schema.StringAttribute{
164+
Optional: true,
165+
Description: "Static IP address to assign. The format will be converted to system tag `staticIp::<l3_uuid>::<ip>`.",
166+
},
167+
},
168+
},
169+
},
143170
"vm_nics": schema.ListNestedAttribute{
144171
NestedObject: schema.NestedAttributeObject{
145172
Attributes: map[string]schema.Attribute{
@@ -175,8 +202,8 @@ func (r *vmResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp
175202
},
176203
"l3_network_uuids": schema.ListAttribute{
177204
ElementType: types.StringType,
178-
Required: true,
179-
Description: "A list of UUIDs for the L3 networks associated with the VM instance.",
205+
Optional: true,
206+
Description: "Deprecated. Use `network_interfaces` instead. A list of UUIDs for the L3 networks associated with the VM instance.",
180207
},
181208
"networks": schema.ListNestedAttribute{
182209
NestedObject: schema.NestedAttributeObject{
@@ -188,7 +215,7 @@ func (r *vmResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp
188215
},
189216
},
190217
Optional: true,
191-
Description: "The network configurations associated with the VM instance.",
218+
Description: "Deprecated. Use `network_interfaces` instead. The network configurations associated with the VM instance.",
192219
},
193220
"root_disk": schema.SingleNestedAttribute{
194221
Attributes: map[string]schema.Attribute{
@@ -362,6 +389,9 @@ func (r *vmResource) Create(ctx context.Context, req resource.CreateRequest, res
362389
var dataVolumeSystemTagsOnIndex []string
363390
var dataDiskSystemTags []string
364391

392+
var networkInterfaces []NetworkInterfaceModel
393+
var defaultL3Uuid string
394+
365395
// SET ROOT DISK
366396
if !plan.RootDisk.IsNull() {
367397
diags = plan.RootDisk.As(ctx, &rootDiskPlan, basetypes.ObjectAsOptions{})
@@ -444,21 +474,55 @@ func (r *vmResource) Create(ctx context.Context, req resource.CreateRequest, res
444474

445475
// SET NETWORK
446476

447-
if !plan.L3NetworkUuids.IsNull() && len(plan.L3NetworkUuids.Elements()) > 0 {
477+
if !plan.NetworkInterfaces.IsNull() && len(plan.NetworkInterfaces.Elements()) > 0 {
478+
plan.NetworkInterfaces.ElementsAs(ctx, &networkInterfaces, false)
479+
480+
for _, nic := range networkInterfaces {
481+
l3uuid := nic.L3NetworkUuid.ValueString()
482+
l3NetworkUuids = append(l3NetworkUuids, l3uuid)
483+
484+
if nic.DefaultL3.ValueBool() {
485+
defaultL3Uuid = l3uuid
486+
}
487+
488+
if !nic.StaticIp.IsNull() && nic.StaticIp.ValueString() != "" {
489+
systemTags = append(systemTags, fmt.Sprintf("staticIp::%s::%s", l3uuid, nic.StaticIp.ValueString()))
490+
}
491+
}
492+
} else if !plan.L3NetworkUuids.IsNull() && len(plan.L3NetworkUuids.Elements()) > 0 {
448493
plan.L3NetworkUuids.ElementsAs(ctx, &l3NetworkUuids, false)
494+
defaultL3Uuid = l3NetworkUuids[0]
449495
} else if !plan.Networks.IsNull() && len(plan.Networks.Elements()) > 0 {
450496
var networks []networkModel
451497
plan.Networks.ElementsAs(ctx, &networks, false)
452498
for _, network := range networks {
453499
l3NetworkUuids = append(l3NetworkUuids, network.Uuid.ValueString())
454500
}
501+
defaultL3Uuid = l3NetworkUuids[0]
455502
} else {
456503
resp.Diagnostics.AddError(
457504
"Params Error",
458-
"l3NetworkUuids or networks cannot be null at the same time",
505+
"network_interfaces or l3_network_uuids cannot be null at the same time",
459506
)
460507
return
461508
}
509+
/*
510+
if !plan.L3NetworkUuids.IsNull() && len(plan.L3NetworkUuids.Elements()) > 0 {
511+
plan.L3NetworkUuids.ElementsAs(ctx, &l3NetworkUuids, false)
512+
} else if !plan.Networks.IsNull() && len(plan.Networks.Elements()) > 0 {
513+
var networks []networkModel
514+
plan.Networks.ElementsAs(ctx, &networks, false)
515+
for _, network := range networks {
516+
l3NetworkUuids = append(l3NetworkUuids, network.Uuid.ValueString())
517+
}
518+
} else {
519+
resp.Diagnostics.AddError(
520+
"Params Error",
521+
"l3NetworkUuids or networks cannot be null at the same time",
522+
)
523+
return
524+
}
525+
*/
462526

463527
// SET IMAGE
464528
image, err := r.client.GetImage(plan.ImageUuid.ValueString())
@@ -619,7 +683,7 @@ func (r *vmResource) Create(ctx context.Context, req resource.CreateRequest, res
619683
ClusterUUID: clusterUuid,
620684
HostUuid: hostUuid,
621685
Description: plan.Description.ValueString(),
622-
DefaultL3NetworkUuid: l3NetworkUuids[0],
686+
DefaultL3NetworkUuid: defaultL3Uuid,
623687
TagUuids: nil,
624688
Strategy: param.InstanceStrategy(plan.Strategy.ValueString()),
625689
MemorySize: memorySize,
@@ -645,6 +709,22 @@ func (r *vmResource) Create(ctx context.Context, req resource.CreateRequest, res
645709
plan.CPUNum = types.Int64Value(int64(instance.CPUNum))
646710
//plan.IP = types.StringValue(instance.VMNics[0].IP)
647711

712+
if len(networkInterfaces) > 0 {
713+
networkInterfaceAttrTypes := map[string]attr.Type{
714+
"l3_network_uuid": types.StringType,
715+
"default_l3": types.BoolType,
716+
"static_ip": types.StringType,
717+
}
718+
networkInterfacesList, diags := types.ListValueFrom(ctx,
719+
types.ObjectType{AttrTypes: networkInterfaceAttrTypes},
720+
networkInterfaces)
721+
resp.Diagnostics.Append(diags...)
722+
if resp.Diagnostics.HasError() {
723+
return
724+
}
725+
plan.NetworkInterfaces = networkInterfacesList
726+
}
727+
648728
var diskModelAttrTypes = map[string]attr.Type{
649729
"offering_uuid": types.StringType,
650730
"size": types.Int64Type,
@@ -735,6 +815,22 @@ func (r *vmResource) Read(ctx context.Context, req resource.ReadRequest, resp *r
735815

736816
state.VMNics, _ = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: networkModelAttrTypes}, vmNics)
737817

818+
var networkInterfaces []NetworkInterfaceModel
819+
for _, nic := range vm.VMNics {
820+
networkInterfaces = append(networkInterfaces, NetworkInterfaceModel{
821+
L3NetworkUuid: types.StringValue(nic.L3NetworkUUID),
822+
DefaultL3: types.BoolValue(nic.L3NetworkUUID == vm.DefaultL3NetworkUUID),
823+
StaticIp: types.StringNull(), // Terraform 没法读取 staticIp,保持 null
824+
})
825+
}
826+
state.NetworkInterfaces, _ = types.ListValueFrom(ctx, types.ObjectType{
827+
AttrTypes: map[string]attr.Type{
828+
"l3_network_uuid": types.StringType,
829+
"default_l3": types.BoolType,
830+
"static_ip": types.StringType,
831+
},
832+
}, networkInterfaces)
833+
738834
state.L3NetworkUuids, _ = types.ListValue(types.StringType, []attr.Value{types.StringValue(vm.DefaultL3NetworkUUID)})
739835
diags = resp.State.Set(ctx, &state)
740836
resp.Diagnostics.Append(diags...)
@@ -815,6 +911,26 @@ func (r *vmResource) Update(ctx context.Context, req resource.UpdateRequest, res
815911
// 将 vm_nics 信息设置到状态中
816912
plan.VMNics, _ = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: networkModelAttrTypes}, vmNics)
817913

914+
var networkInterfaces []NetworkInterfaceModel
915+
for _, nic := range instance.VMNics {
916+
networkInterfaces = append(networkInterfaces, NetworkInterfaceModel{
917+
L3NetworkUuid: types.StringValue(nic.L3NetworkUUID),
918+
DefaultL3: types.BoolValue(nic.L3NetworkUUID == instance.DefaultL3NetworkUUID),
919+
StaticIp: types.StringNull(), // 无法反查
920+
})
921+
}
922+
plan.NetworkInterfaces, _ = types.ListValueFrom(ctx, types.ObjectType{
923+
AttrTypes: map[string]attr.Type{
924+
"l3_network_uuid": types.StringType,
925+
"default_l3": types.BoolType,
926+
"static_ip": types.StringType,
927+
},
928+
}, networkInterfaces)
929+
930+
plan.L3NetworkUuids, _ = types.ListValue(types.StringType, []attr.Value{
931+
types.StringValue(instance.DefaultL3NetworkUUID),
932+
})
933+
818934
diags := resp.State.Set(ctx, &plan)
819935
resp.Diagnostics.Append(diags...)
820936
if resp.Diagnostics.HasError() {

0 commit comments

Comments
 (0)