From 29b5438faf7c1d09291c549516ac359952e2a87b Mon Sep 17 00:00:00 2001 From: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Date: Fri, 15 Mar 2024 21:06:30 -0400 Subject: [PATCH] chore(vm): refactoring: extract network device code from vm.go (#1127) chore(vm): refactoring: extract network code Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> --- proxmoxtf/datasource/datastores_test.go | 2 +- proxmoxtf/datasource/dns_test.go | 2 +- proxmoxtf/datasource/firewall/alias_test.go | 8 +- proxmoxtf/datasource/firewall/aliases_test.go | 6 +- proxmoxtf/datasource/firewall/ipset_test.go | 10 +- proxmoxtf/datasource/firewall/ipsets_test.go | 6 +- proxmoxtf/datasource/firewall/rule_test.go | 8 +- .../firewall/security_group_test.go | 8 +- .../firewall/security_groups_test.go | 6 +- proxmoxtf/datasource/group_test.go | 2 +- proxmoxtf/datasource/groups_test.go | 2 +- proxmoxtf/datasource/hosts_test.go | 2 +- proxmoxtf/datasource/nodes_test.go | 2 +- proxmoxtf/datasource/pool_test.go | 2 +- proxmoxtf/datasource/pools_test.go | 2 +- proxmoxtf/datasource/role_test.go | 2 +- proxmoxtf/datasource/roles_test.go | 2 +- proxmoxtf/datasource/time_test.go | 2 +- proxmoxtf/datasource/user_test.go | 2 +- proxmoxtf/datasource/users_test.go | 2 +- proxmoxtf/datasource/vm_test.go | 2 +- proxmoxtf/datasource/vms_test.go | 2 +- proxmoxtf/provider/provider_test.go | 4 +- proxmoxtf/resource/certificate_test.go | 2 +- .../cluster/firewall/security_group_test.go | 2 +- .../resource/container/container_test.go | 2 +- proxmoxtf/resource/dns_test.go | 2 +- proxmoxtf/resource/file_test.go | 2 +- proxmoxtf/resource/firewall/alias_test.go | 2 +- proxmoxtf/resource/firewall/ipset_test.go | 2 +- proxmoxtf/resource/firewall/options_test.go | 2 +- proxmoxtf/resource/firewall/rules_test.go | 2 +- proxmoxtf/resource/group_test.go | 2 +- proxmoxtf/resource/hosts_test.go | 2 +- proxmoxtf/resource/pool_test.go | 2 +- proxmoxtf/resource/role_test.go | 2 +- proxmoxtf/resource/time_test.go | 2 +- proxmoxtf/resource/user_test.go | 2 +- proxmoxtf/resource/vm/disk/schema_test.go | 53 ++ proxmoxtf/resource/vm/network/network.go | 276 +++++++ proxmoxtf/resource/vm/network/schema.go | 189 +++++ proxmoxtf/resource/vm/network/schema_test.go | 55 ++ proxmoxtf/resource/vm/validators.go | 5 - proxmoxtf/resource/vm/vm.go | 710 ++++-------------- proxmoxtf/resource/vm/vm_test.go | 137 +--- proxmoxtf/test/utils.go | 38 +- 46 files changed, 837 insertions(+), 742 deletions(-) create mode 100644 proxmoxtf/resource/vm/disk/schema_test.go create mode 100644 proxmoxtf/resource/vm/network/network.go create mode 100644 proxmoxtf/resource/vm/network/schema.go create mode 100644 proxmoxtf/resource/vm/network/schema_test.go diff --git a/proxmoxtf/datasource/datastores_test.go b/proxmoxtf/datasource/datastores_test.go index 3ea13fde..b18cdaea 100644 --- a/proxmoxtf/datasource/datastores_test.go +++ b/proxmoxtf/datasource/datastores_test.go @@ -28,7 +28,7 @@ func TestDatastoresInstantiation(t *testing.T) { func TestDatastoresSchema(t *testing.T) { t.Parallel() - s := Datastores() + s := Datastores().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentDatastoresNodeName, diff --git a/proxmoxtf/datasource/dns_test.go b/proxmoxtf/datasource/dns_test.go index f8fb75e8..b886c761 100644 --- a/proxmoxtf/datasource/dns_test.go +++ b/proxmoxtf/datasource/dns_test.go @@ -28,7 +28,7 @@ func TestDNSInstantiation(t *testing.T) { func TestDNSSchema(t *testing.T) { t.Parallel() - s := DNS() + s := DNS().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentDNSNodeName, diff --git a/proxmoxtf/datasource/firewall/alias_test.go b/proxmoxtf/datasource/firewall/alias_test.go index 3c3079d2..33685f91 100644 --- a/proxmoxtf/datasource/firewall/alias_test.go +++ b/proxmoxtf/datasource/firewall/alias_test.go @@ -25,18 +25,18 @@ func TestAliasSchemaInstantiation(t *testing.T) { func TestAliasSchema(t *testing.T) { t.Parallel() - r := schema.Resource{Schema: AliasSchema()} + s := AliasSchema() - test.AssertRequiredArguments(t, &r, []string{ + test.AssertRequiredArguments(t, s, []string{ mkAliasName, }) - test.AssertComputedAttributes(t, &r, []string{ + test.AssertComputedAttributes(t, s, []string{ mkAliasCIDR, mkAliasComment, }) - test.AssertValueTypes(t, &r, map[string]schema.ValueType{ + test.AssertValueTypes(t, s, map[string]schema.ValueType{ mkAliasName: schema.TypeString, mkAliasCIDR: schema.TypeString, mkAliasComment: schema.TypeString, diff --git a/proxmoxtf/datasource/firewall/aliases_test.go b/proxmoxtf/datasource/firewall/aliases_test.go index 81c7f9d6..978c3371 100644 --- a/proxmoxtf/datasource/firewall/aliases_test.go +++ b/proxmoxtf/datasource/firewall/aliases_test.go @@ -25,13 +25,13 @@ func TestAliasesSchemaInstantiation(t *testing.T) { func TestAliasesSchema(t *testing.T) { t.Parallel() - r := schema.Resource{Schema: AliasesSchema()} + s := AliasesSchema() - test.AssertComputedAttributes(t, &r, []string{ + test.AssertComputedAttributes(t, s, []string{ mkAliasesAliasNames, }) - test.AssertValueTypes(t, &r, map[string]schema.ValueType{ + test.AssertValueTypes(t, s, map[string]schema.ValueType{ mkAliasesAliasNames: schema.TypeList, }) } diff --git a/proxmoxtf/datasource/firewall/ipset_test.go b/proxmoxtf/datasource/firewall/ipset_test.go index 778b5a1e..560b0050 100644 --- a/proxmoxtf/datasource/firewall/ipset_test.go +++ b/proxmoxtf/datasource/firewall/ipset_test.go @@ -25,24 +25,24 @@ func TestIPSetSchemaInstantiation(t *testing.T) { func TestIPSetSchema(t *testing.T) { t.Parallel() - r := schema.Resource{Schema: IPSetSchema()} + s := IPSetSchema() - test.AssertRequiredArguments(t, &r, []string{ + test.AssertRequiredArguments(t, s, []string{ mkIPSetName, }) - test.AssertComputedAttributes(t, &r, []string{ + test.AssertComputedAttributes(t, s, []string{ mkIPSetCIDR, mkIPSetCIDRComment, }) - test.AssertValueTypes(t, &r, map[string]schema.ValueType{ + test.AssertValueTypes(t, s, map[string]schema.ValueType{ mkIPSetName: schema.TypeString, mkIPSetCIDR: schema.TypeList, mkIPSetCIDRComment: schema.TypeString, }) - cird := test.AssertNestedSchemaExistence(t, &r, mkIPSetCIDR) + cird := test.AssertNestedSchemaExistence(t, s, mkIPSetCIDR) test.AssertComputedAttributes(t, cird, []string{ mkIPSetCIDRName, diff --git a/proxmoxtf/datasource/firewall/ipsets_test.go b/proxmoxtf/datasource/firewall/ipsets_test.go index d48f0cb4..c6b4b65d 100644 --- a/proxmoxtf/datasource/firewall/ipsets_test.go +++ b/proxmoxtf/datasource/firewall/ipsets_test.go @@ -25,13 +25,13 @@ func TestIPSetsSchemaInstantiation(t *testing.T) { func TestIPSetsSchema(t *testing.T) { t.Parallel() - r := schema.Resource{Schema: IPSetsSchema()} + s := IPSetsSchema() - test.AssertComputedAttributes(t, &r, []string{ + test.AssertComputedAttributes(t, s, []string{ mkIPSetsIPSetNames, }) - test.AssertValueTypes(t, &r, map[string]schema.ValueType{ + test.AssertValueTypes(t, s, map[string]schema.ValueType{ mkIPSetsIPSetNames: schema.TypeList, }) } diff --git a/proxmoxtf/datasource/firewall/rule_test.go b/proxmoxtf/datasource/firewall/rule_test.go index 58d44fdc..927efff5 100644 --- a/proxmoxtf/datasource/firewall/rule_test.go +++ b/proxmoxtf/datasource/firewall/rule_test.go @@ -25,14 +25,14 @@ func TestRuleSchemaInstantiation(t *testing.T) { func TestRuleSchema(t *testing.T) { t.Parallel() - r := schema.Resource{Schema: RuleSchema()} + s := RuleSchema() - test.AssertRequiredArguments(t, &r, []string{ + test.AssertRequiredArguments(t, s, []string{ mkRuleAction, mkRuleType, }) - test.AssertComputedAttributes(t, &r, []string{ + test.AssertComputedAttributes(t, s, []string{ mkRuleComment, mkRuleDest, mkRuleDPort, @@ -45,7 +45,7 @@ func TestRuleSchema(t *testing.T) { mkRuleSPort, }) - test.AssertValueTypes(t, &r, map[string]schema.ValueType{ + test.AssertValueTypes(t, s, map[string]schema.ValueType{ mkRulePos: schema.TypeInt, mkRuleAction: schema.TypeString, mkRuleType: schema.TypeString, diff --git a/proxmoxtf/datasource/firewall/security_group_test.go b/proxmoxtf/datasource/firewall/security_group_test.go index d9c29011..f4c53130 100644 --- a/proxmoxtf/datasource/firewall/security_group_test.go +++ b/proxmoxtf/datasource/firewall/security_group_test.go @@ -25,18 +25,18 @@ func TestSecurityGroupSchemaInstantiation(t *testing.T) { func TestSecurityGroupSchema(t *testing.T) { t.Parallel() - r := schema.Resource{Schema: SecurityGroupSchema()} + s := SecurityGroupSchema() - test.AssertRequiredArguments(t, &r, []string{ + test.AssertRequiredArguments(t, s, []string{ mkSecurityGroupName, }) - test.AssertComputedAttributes(t, &r, []string{ + test.AssertComputedAttributes(t, s, []string{ mkSecurityGroupComment, mkRules, }) - test.AssertValueTypes(t, &r, map[string]schema.ValueType{ + test.AssertValueTypes(t, s, map[string]schema.ValueType{ mkSecurityGroupName: schema.TypeString, mkSecurityGroupComment: schema.TypeString, mkRules: schema.TypeList, diff --git a/proxmoxtf/datasource/firewall/security_groups_test.go b/proxmoxtf/datasource/firewall/security_groups_test.go index 9ed00972..44fafb88 100644 --- a/proxmoxtf/datasource/firewall/security_groups_test.go +++ b/proxmoxtf/datasource/firewall/security_groups_test.go @@ -25,13 +25,13 @@ func TestSecurityGroupsSchemaInstantiation(t *testing.T) { func TestSecurityGroupsSchema(t *testing.T) { t.Parallel() - r := schema.Resource{Schema: SecurityGroupsSchema()} + s := SecurityGroupsSchema() - test.AssertComputedAttributes(t, &r, []string{ + test.AssertComputedAttributes(t, s, []string{ mkSecurityGroupsSecurityGroupNames, }) - test.AssertValueTypes(t, &r, map[string]schema.ValueType{ + test.AssertValueTypes(t, s, map[string]schema.ValueType{ mkSecurityGroupsSecurityGroupNames: schema.TypeList, }) } diff --git a/proxmoxtf/datasource/group_test.go b/proxmoxtf/datasource/group_test.go index 66c93c67..2c30b701 100644 --- a/proxmoxtf/datasource/group_test.go +++ b/proxmoxtf/datasource/group_test.go @@ -28,7 +28,7 @@ func TestGroupInstantiation(t *testing.T) { func TestGroupSchema(t *testing.T) { t.Parallel() - s := Group() + s := Group().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentGroupID, diff --git a/proxmoxtf/datasource/groups_test.go b/proxmoxtf/datasource/groups_test.go index 529212a7..4ea3c487 100644 --- a/proxmoxtf/datasource/groups_test.go +++ b/proxmoxtf/datasource/groups_test.go @@ -28,7 +28,7 @@ func TestGroupsInstantiation(t *testing.T) { func TestGroupsSchema(t *testing.T) { t.Parallel() - s := Groups() + s := Groups().Schema test.AssertComputedAttributes(t, s, []string{ mkDataSourceVirtualEnvironmentGroupsComments, diff --git a/proxmoxtf/datasource/hosts_test.go b/proxmoxtf/datasource/hosts_test.go index f3b09292..4837c1f3 100644 --- a/proxmoxtf/datasource/hosts_test.go +++ b/proxmoxtf/datasource/hosts_test.go @@ -28,7 +28,7 @@ func TestHostsInstantiation(t *testing.T) { func TestHostsSchema(t *testing.T) { t.Parallel() - s := Hosts() + s := Hosts().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentHostsNodeName, diff --git a/proxmoxtf/datasource/nodes_test.go b/proxmoxtf/datasource/nodes_test.go index 17fe3a2d..8dfcbe41 100644 --- a/proxmoxtf/datasource/nodes_test.go +++ b/proxmoxtf/datasource/nodes_test.go @@ -28,7 +28,7 @@ func TestNodesInstantiation(t *testing.T) { func TestNodesSchema(t *testing.T) { t.Parallel() - s := Nodes() + s := Nodes().Schema test.AssertComputedAttributes(t, s, []string{ mkDataSourceVirtualEnvironmentNodesCPUCount, diff --git a/proxmoxtf/datasource/pool_test.go b/proxmoxtf/datasource/pool_test.go index a4812b9b..0ae0252b 100644 --- a/proxmoxtf/datasource/pool_test.go +++ b/proxmoxtf/datasource/pool_test.go @@ -28,7 +28,7 @@ func TestPoolInstantiation(t *testing.T) { func TestPoolSchema(t *testing.T) { t.Parallel() - s := Pool() + s := Pool().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentPoolPoolID, diff --git a/proxmoxtf/datasource/pools_test.go b/proxmoxtf/datasource/pools_test.go index 70721077..3d4eb503 100644 --- a/proxmoxtf/datasource/pools_test.go +++ b/proxmoxtf/datasource/pools_test.go @@ -28,7 +28,7 @@ func TestPoolsInstantiation(t *testing.T) { func TestPoolsSchema(t *testing.T) { t.Parallel() - s := Pools() + s := Pools().Schema test.AssertComputedAttributes(t, s, []string{ mkDataSourceVirtualEnvironmentPoolsPoolIDs, diff --git a/proxmoxtf/datasource/role_test.go b/proxmoxtf/datasource/role_test.go index 221f83f0..5861a004 100644 --- a/proxmoxtf/datasource/role_test.go +++ b/proxmoxtf/datasource/role_test.go @@ -28,7 +28,7 @@ func TestRoleInstantiation(t *testing.T) { func TestRoleSchema(t *testing.T) { t.Parallel() - s := Role() + s := Role().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentRoleID, diff --git a/proxmoxtf/datasource/roles_test.go b/proxmoxtf/datasource/roles_test.go index 272a2269..ebf779a0 100644 --- a/proxmoxtf/datasource/roles_test.go +++ b/proxmoxtf/datasource/roles_test.go @@ -28,7 +28,7 @@ func TestRolesInstantiation(t *testing.T) { func TestRolesSchema(t *testing.T) { t.Parallel() - s := Roles() + s := Roles().Schema test.AssertComputedAttributes(t, s, []string{ mkDataSourceVirtualEnvironmentRolesPrivileges, diff --git a/proxmoxtf/datasource/time_test.go b/proxmoxtf/datasource/time_test.go index f3cbbcf0..dc6168b9 100644 --- a/proxmoxtf/datasource/time_test.go +++ b/proxmoxtf/datasource/time_test.go @@ -28,7 +28,7 @@ func TestTimeInstantiation(t *testing.T) { func TestTimeSchema(t *testing.T) { t.Parallel() - s := Time() + s := Time().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentTimeNodeName, diff --git a/proxmoxtf/datasource/user_test.go b/proxmoxtf/datasource/user_test.go index c7c3ab41..5e6f70dd 100644 --- a/proxmoxtf/datasource/user_test.go +++ b/proxmoxtf/datasource/user_test.go @@ -28,7 +28,7 @@ func TestUserInstantiation(t *testing.T) { func TestUserSchema(t *testing.T) { t.Parallel() - s := User() + s := User().Schema test.AssertRequiredArguments(t, s, []string{ mkDataSourceVirtualEnvironmentUserUserID, diff --git a/proxmoxtf/datasource/users_test.go b/proxmoxtf/datasource/users_test.go index f8d91dbb..4cf28d7a 100644 --- a/proxmoxtf/datasource/users_test.go +++ b/proxmoxtf/datasource/users_test.go @@ -28,7 +28,7 @@ func TestUsersInstantiation(t *testing.T) { func TestUsersSchema(t *testing.T) { t.Parallel() - s := Users() + s := Users().Schema test.AssertComputedAttributes(t, s, []string{ mkDataSourceVirtualEnvironmentUsersComments, diff --git a/proxmoxtf/datasource/vm_test.go b/proxmoxtf/datasource/vm_test.go index 4c030050..8f1cd27c 100644 --- a/proxmoxtf/datasource/vm_test.go +++ b/proxmoxtf/datasource/vm_test.go @@ -29,7 +29,7 @@ func TestVMInstantiation(t *testing.T) { func TestVMSchema(t *testing.T) { t.Parallel() - s := VM() + s := VM().Schema test.AssertComputedAttributes(t, s, []string{ mkDataSourceVirtualEnvironmentVMName, diff --git a/proxmoxtf/datasource/vms_test.go b/proxmoxtf/datasource/vms_test.go index e30caa56..87e7d485 100644 --- a/proxmoxtf/datasource/vms_test.go +++ b/proxmoxtf/datasource/vms_test.go @@ -29,7 +29,7 @@ func TestVMsInstantiation(t *testing.T) { func TestVMsSchema(t *testing.T) { t.Parallel() - s := VMs() + s := VMs().Schema test.AssertComputedAttributes(t, s, []string{ mkDataSourceVirtualEnvironmentVMs, diff --git a/proxmoxtf/provider/provider_test.go b/proxmoxtf/provider/provider_test.go index 286a33e7..786c45bf 100644 --- a/proxmoxtf/provider/provider_test.go +++ b/proxmoxtf/provider/provider_test.go @@ -28,9 +28,7 @@ func TestProviderInstantiation(t *testing.T) { func TestProviderSchema(t *testing.T) { t.Parallel() - s := &schema.Resource{ - Schema: ProxmoxVirtualEnvironment().Schema, - } + s := ProxmoxVirtualEnvironment().Schema test.AssertOptionalArguments(t, s, []string{ mkProviderUsername, diff --git a/proxmoxtf/resource/certificate_test.go b/proxmoxtf/resource/certificate_test.go index f4fe32ce..eab26c40 100644 --- a/proxmoxtf/resource/certificate_test.go +++ b/proxmoxtf/resource/certificate_test.go @@ -28,7 +28,7 @@ func TestCertificateInstantiation(t *testing.T) { func TestCertificateSchema(t *testing.T) { t.Parallel() - s := Certificate() + s := Certificate().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentCertificateCertificate, diff --git a/proxmoxtf/resource/cluster/firewall/security_group_test.go b/proxmoxtf/resource/cluster/firewall/security_group_test.go index 53c82209..4c50a235 100644 --- a/proxmoxtf/resource/cluster/firewall/security_group_test.go +++ b/proxmoxtf/resource/cluster/firewall/security_group_test.go @@ -26,7 +26,7 @@ func TestSecurityGroupInstantiation(t *testing.T) { func TestSecurityGroupSchema(t *testing.T) { t.Parallel() - s := SecurityGroup() + s := SecurityGroup().Schema test.AssertRequiredArguments(t, s, []string{ mkSecurityGroupName, diff --git a/proxmoxtf/resource/container/container_test.go b/proxmoxtf/resource/container/container_test.go index 8a71b821..b0014de5 100644 --- a/proxmoxtf/resource/container/container_test.go +++ b/proxmoxtf/resource/container/container_test.go @@ -28,7 +28,7 @@ func TestContainerInstantiation(t *testing.T) { func TestContainerSchema(t *testing.T) { t.Parallel() - s := Container() + s := Container().Schema test.AssertRequiredArguments(t, s, []string{ mkNodeName, diff --git a/proxmoxtf/resource/dns_test.go b/proxmoxtf/resource/dns_test.go index 6eb765c5..a995c8d1 100644 --- a/proxmoxtf/resource/dns_test.go +++ b/proxmoxtf/resource/dns_test.go @@ -28,7 +28,7 @@ func TestDNSInstantiation(t *testing.T) { func TestDNSSchema(t *testing.T) { t.Parallel() - s := DNS() + s := DNS().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentDNSDomain, diff --git a/proxmoxtf/resource/file_test.go b/proxmoxtf/resource/file_test.go index 992a6aef..0c20eddb 100644 --- a/proxmoxtf/resource/file_test.go +++ b/proxmoxtf/resource/file_test.go @@ -29,7 +29,7 @@ func TestFileInstantiation(t *testing.T) { func TestFileSchema(t *testing.T) { t.Parallel() - s := File() + s := File().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentFileDatastoreID, diff --git a/proxmoxtf/resource/firewall/alias_test.go b/proxmoxtf/resource/firewall/alias_test.go index 6f34c471..d8a5d041 100644 --- a/proxmoxtf/resource/firewall/alias_test.go +++ b/proxmoxtf/resource/firewall/alias_test.go @@ -25,7 +25,7 @@ func TestAliasInstantiation(t *testing.T) { func TestAliasSchema(t *testing.T) { t.Parallel() - s := Alias() + s := Alias().Schema test.AssertRequiredArguments(t, s, []string{ mkAliasName, diff --git a/proxmoxtf/resource/firewall/ipset_test.go b/proxmoxtf/resource/firewall/ipset_test.go index 0b2a5e49..016e6392 100644 --- a/proxmoxtf/resource/firewall/ipset_test.go +++ b/proxmoxtf/resource/firewall/ipset_test.go @@ -26,7 +26,7 @@ func TestIPSetInstantiation(t *testing.T) { func TestIPSetSchema(t *testing.T) { t.Parallel() - s := IPSet() + s := IPSet().Schema test.AssertRequiredArguments(t, s, []string{ mkIPSetName, diff --git a/proxmoxtf/resource/firewall/options_test.go b/proxmoxtf/resource/firewall/options_test.go index 66a934f7..9eb42a32 100644 --- a/proxmoxtf/resource/firewall/options_test.go +++ b/proxmoxtf/resource/firewall/options_test.go @@ -25,7 +25,7 @@ func TestOptionsInstantiation(t *testing.T) { func TestOptionsSchema(t *testing.T) { t.Parallel() - s := Options() + s := Options().Schema test.AssertOptionalArguments(t, s, []string{ mkDHCP, diff --git a/proxmoxtf/resource/firewall/rules_test.go b/proxmoxtf/resource/firewall/rules_test.go index 41ec219a..350e7f7f 100644 --- a/proxmoxtf/resource/firewall/rules_test.go +++ b/proxmoxtf/resource/firewall/rules_test.go @@ -25,7 +25,7 @@ func TestRuleInstantiation(t *testing.T) { func TestRuleSchema(t *testing.T) { t.Parallel() - rules := Rules() + rules := Rules().Schema test.AssertRequiredArguments(t, rules, []string{ MkRule, diff --git a/proxmoxtf/resource/group_test.go b/proxmoxtf/resource/group_test.go index 2293ba46..996dc358 100644 --- a/proxmoxtf/resource/group_test.go +++ b/proxmoxtf/resource/group_test.go @@ -28,7 +28,7 @@ func TestGroupInstantiation(t *testing.T) { func TestGroupSchema(t *testing.T) { t.Parallel() - s := Group() + s := Group().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentGroupID, diff --git a/proxmoxtf/resource/hosts_test.go b/proxmoxtf/resource/hosts_test.go index bc650bad..46888ce2 100644 --- a/proxmoxtf/resource/hosts_test.go +++ b/proxmoxtf/resource/hosts_test.go @@ -28,7 +28,7 @@ func TestHostsInstantiation(t *testing.T) { func TestHostsSchema(t *testing.T) { t.Parallel() - s := Hosts() + s := Hosts().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentHostsEntry, diff --git a/proxmoxtf/resource/pool_test.go b/proxmoxtf/resource/pool_test.go index f952de5e..58b02a6f 100644 --- a/proxmoxtf/resource/pool_test.go +++ b/proxmoxtf/resource/pool_test.go @@ -28,7 +28,7 @@ func TestPoolInstantiation(t *testing.T) { func TestPoolSchema(t *testing.T) { t.Parallel() - s := Pool() + s := Pool().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentPoolPoolID, diff --git a/proxmoxtf/resource/role_test.go b/proxmoxtf/resource/role_test.go index 75b2016b..ed2517de 100644 --- a/proxmoxtf/resource/role_test.go +++ b/proxmoxtf/resource/role_test.go @@ -28,7 +28,7 @@ func TestRoleInstantiation(t *testing.T) { func TestRoleSchema(t *testing.T) { t.Parallel() - s := Role() + s := Role().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentRolePrivileges, diff --git a/proxmoxtf/resource/time_test.go b/proxmoxtf/resource/time_test.go index 6ee516b2..33a3be9e 100644 --- a/proxmoxtf/resource/time_test.go +++ b/proxmoxtf/resource/time_test.go @@ -28,7 +28,7 @@ func TestTimeInstantiation(t *testing.T) { func TestTimeSchema(t *testing.T) { t.Parallel() - s := Time() + s := Time().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentTimeNodeName, diff --git a/proxmoxtf/resource/user_test.go b/proxmoxtf/resource/user_test.go index d5122fd9..f4f08cc3 100644 --- a/proxmoxtf/resource/user_test.go +++ b/proxmoxtf/resource/user_test.go @@ -28,7 +28,7 @@ func TestUserInstantiation(t *testing.T) { func TestUserSchema(t *testing.T) { t.Parallel() - s := User() + s := User().Schema test.AssertRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentUserUserID, diff --git a/proxmoxtf/resource/vm/disk/schema_test.go b/proxmoxtf/resource/vm/disk/schema_test.go new file mode 100644 index 00000000..21dac165 --- /dev/null +++ b/proxmoxtf/resource/vm/disk/schema_test.go @@ -0,0 +1,53 @@ +package disk + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" +) + +func TestVMSchema(t *testing.T) { + t.Parallel() + + s := Schema() + + diskSchema := test.AssertNestedSchemaExistence(t, s, MkDisk) + + test.AssertOptionalArguments(t, diskSchema, []string{ + mkDiskDatastoreID, + mkDiskPathInDatastore, + mkDiskFileFormat, + mkDiskFileID, + mkDiskSize, + }) + + test.AssertValueTypes(t, diskSchema, map[string]schema.ValueType{ + mkDiskDatastoreID: schema.TypeString, + mkDiskPathInDatastore: schema.TypeString, + mkDiskFileFormat: schema.TypeString, + mkDiskFileID: schema.TypeString, + mkDiskSize: schema.TypeInt, + }) + + diskSpeedSchema := test.AssertNestedSchemaExistence( + t, + diskSchema, + mkDiskSpeed, + ) + + test.AssertOptionalArguments(t, diskSpeedSchema, []string{ + mkDiskSpeedRead, + mkDiskSpeedReadBurstable, + mkDiskSpeedWrite, + mkDiskSpeedWriteBurstable, + }) + + test.AssertValueTypes(t, diskSpeedSchema, map[string]schema.ValueType{ + mkDiskSpeedRead: schema.TypeInt, + mkDiskSpeedReadBurstable: schema.TypeInt, + mkDiskSpeedWrite: schema.TypeInt, + mkDiskSpeedWriteBurstable: schema.TypeInt, + }) +} diff --git a/proxmoxtf/resource/vm/network/network.go b/proxmoxtf/resource/vm/network/network.go new file mode 100644 index 00000000..8ee94bba --- /dev/null +++ b/proxmoxtf/resource/vm/network/network.go @@ -0,0 +1,276 @@ +package network + +import ( + "context" + "fmt" + "strconv" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" + "github.com/bpg/terraform-provider-proxmox/proxmox/types" +) + +// GetNetworkDeviceObjects returns a list of network devices from the resource data. +func GetNetworkDeviceObjects(d *schema.ResourceData) (vms.CustomNetworkDevices, error) { + networkDevice := d.Get(MkNetworkDevice).([]interface{}) + networkDeviceObjects := make(vms.CustomNetworkDevices, len(networkDevice)) + + for i, networkDeviceEntry := range networkDevice { + block := networkDeviceEntry.(map[string]interface{}) + + bridge := block[mkNetworkDeviceBridge].(string) + enabled := block[mkNetworkDeviceEnabled].(bool) + firewall := types.CustomBool(block[mkNetworkDeviceFirewall].(bool)) + macAddress := block[mkNetworkDeviceMACAddress].(string) + model := block[mkNetworkDeviceModel].(string) + queues := block[mkNetworkDeviceQueues].(int) + rateLimit := block[mkNetworkDeviceRateLimit].(float64) + vlanID := block[mkNetworkDeviceVLANID].(int) + trunks := block[mkNetworkDeviceTrunks].(string) + mtu := block[mkNetworkDeviceMTU].(int) + + device := vms.CustomNetworkDevice{ + Enabled: enabled, + Firewall: &firewall, + Model: model, + } + + if bridge != "" { + device.Bridge = &bridge + } + + if macAddress != "" { + device.MACAddress = &macAddress + } + + if queues != 0 { + device.Queues = &queues + } + + if rateLimit != 0 { + device.RateLimit = &rateLimit + } + + if vlanID != 0 { + device.Tag = &vlanID + } + + if trunks != "" { + splitTrunks := strings.Split(trunks, ";") + + var trunksAsInt []int + + for _, numStr := range splitTrunks { + num, err := strconv.Atoi(numStr) + if err != nil { + return nil, fmt.Errorf("error parsing trunks: %w", err) + } + + trunksAsInt = append(trunksAsInt, num) + } + + device.Trunks = trunksAsInt + } + + if mtu != 0 { + device.MTU = &mtu + } + + networkDeviceObjects[i] = device + } + + return networkDeviceObjects, nil +} + +// ReadNetworkDeviceObjects reads the network device objects from the resource data. +func ReadNetworkDeviceObjects(d *schema.ResourceData, vmConfig *vms.GetResponseData) diag.Diagnostics { + var diags diag.Diagnostics + + // Compare the network devices to those stored in the state. + currentNetworkDeviceList := d.Get(MkNetworkDevice).([]interface{}) + + macAddresses := make([]interface{}, MaxNetworkDevices) + networkDeviceLast := -1 + networkDeviceList := make([]interface{}, MaxNetworkDevices) + networkDeviceObjects := []*vms.CustomNetworkDevice{ + vmConfig.NetworkDevice0, + vmConfig.NetworkDevice1, + vmConfig.NetworkDevice2, + vmConfig.NetworkDevice3, + vmConfig.NetworkDevice4, + vmConfig.NetworkDevice5, + vmConfig.NetworkDevice6, + vmConfig.NetworkDevice7, + vmConfig.NetworkDevice8, + vmConfig.NetworkDevice9, + vmConfig.NetworkDevice10, + vmConfig.NetworkDevice11, + vmConfig.NetworkDevice12, + vmConfig.NetworkDevice13, + vmConfig.NetworkDevice14, + vmConfig.NetworkDevice15, + vmConfig.NetworkDevice16, + vmConfig.NetworkDevice17, + vmConfig.NetworkDevice18, + vmConfig.NetworkDevice19, + vmConfig.NetworkDevice20, + vmConfig.NetworkDevice21, + vmConfig.NetworkDevice22, + vmConfig.NetworkDevice23, + vmConfig.NetworkDevice24, + vmConfig.NetworkDevice25, + vmConfig.NetworkDevice26, + vmConfig.NetworkDevice27, + vmConfig.NetworkDevice28, + vmConfig.NetworkDevice29, + vmConfig.NetworkDevice30, + vmConfig.NetworkDevice31, + } + + for ni, nd := range networkDeviceObjects { + networkDevice := map[string]interface{}{} + + if nd != nil { + networkDeviceLast = ni + + if nd.Bridge != nil { + networkDevice[mkNetworkDeviceBridge] = *nd.Bridge + } else { + networkDevice[mkNetworkDeviceBridge] = "" + } + + networkDevice[mkNetworkDeviceEnabled] = nd.Enabled + + if nd.Firewall != nil { + networkDevice[mkNetworkDeviceFirewall] = *nd.Firewall + } else { + networkDevice[mkNetworkDeviceFirewall] = false + } + + if nd.MACAddress != nil { + macAddresses[ni] = *nd.MACAddress + } else { + macAddresses[ni] = "" + } + + networkDevice[mkNetworkDeviceMACAddress] = macAddresses[ni] + networkDevice[mkNetworkDeviceModel] = nd.Model + + if nd.Queues != nil { + networkDevice[mkNetworkDeviceQueues] = *nd.Queues + } else { + networkDevice[mkNetworkDeviceQueues] = 0 + } + + if nd.RateLimit != nil { + networkDevice[mkNetworkDeviceRateLimit] = *nd.RateLimit + } else { + networkDevice[mkNetworkDeviceRateLimit] = 0 + } + + if nd.Tag != nil { + networkDevice[mkNetworkDeviceVLANID] = nd.Tag + } else { + networkDevice[mkNetworkDeviceVLANID] = 0 + } + + if nd.Trunks != nil { + networkDevice[mkNetworkDeviceTrunks] = strings.Trim( + strings.Join(strings.Fields(fmt.Sprint(nd.Trunks)), ";"), "[]") + } else { + networkDevice[mkNetworkDeviceTrunks] = "" + } + + if nd.MTU != nil { + networkDevice[mkNetworkDeviceMTU] = nd.MTU + } else { + networkDevice[mkNetworkDeviceMTU] = 0 + } + } else { + macAddresses[ni] = "" + networkDevice[mkNetworkDeviceEnabled] = false + } + + networkDeviceList[ni] = networkDevice + } + + if len(currentNetworkDeviceList) > 0 || networkDeviceLast > -1 { + err := d.Set(MkNetworkDevice, networkDeviceList[:networkDeviceLast+1]) + diags = append(diags, diag.FromErr(err)...) + } + + err := d.Set(mkMACAddresses, macAddresses[0:len(currentNetworkDeviceList)]) + diags = append(diags, diag.FromErr(err)...) + + return diags +} + +// ReadNetworkValues reads the network values from the resource data. +func ReadNetworkValues( + ctx context.Context, + d *schema.ResourceData, + vmAPI *vms.Client, + started bool, + vmConfig *vms.GetResponseData, + agentTimeout time.Duration, +) diag.Diagnostics { + var diags diag.Diagnostics + + var ipv4Addresses []interface{} + + var ipv6Addresses []interface{} + + var networkInterfaceNames []interface{} + + if started { + if vmConfig.Agent != nil && vmConfig.Agent.Enabled != nil && *vmConfig.Agent.Enabled { + var macAddresses []interface{} + + networkInterfaces, err := vmAPI.WaitForNetworkInterfacesFromVMAgent(ctx, int(agentTimeout.Seconds()), 5, true) + if err == nil && networkInterfaces.Result != nil { + ipv4Addresses = make([]interface{}, len(*networkInterfaces.Result)) + ipv6Addresses = make([]interface{}, len(*networkInterfaces.Result)) + macAddresses = make([]interface{}, len(*networkInterfaces.Result)) + networkInterfaceNames = make([]interface{}, len(*networkInterfaces.Result)) + + for ri, rv := range *networkInterfaces.Result { + var rvIPv4Addresses []interface{} + + var rvIPv6Addresses []interface{} + + if rv.IPAddresses != nil { + for _, ip := range *rv.IPAddresses { + switch ip.Type { + case "ipv4": + rvIPv4Addresses = append(rvIPv4Addresses, ip.Address) + case "ipv6": + rvIPv6Addresses = append(rvIPv6Addresses, ip.Address) + } + } + } + + ipv4Addresses[ri] = rvIPv4Addresses + ipv6Addresses[ri] = rvIPv6Addresses + macAddresses[ri] = strings.ToUpper(rv.MACAddress) + networkInterfaceNames[ri] = rv.Name + } + } + + err = d.Set(mkMACAddresses, macAddresses) + diags = append(diags, diag.FromErr(err)...) + } + } + + e := d.Set(mkIPv4Addresses, ipv4Addresses) + diags = append(diags, diag.FromErr(e)...) + e = d.Set(mkIPv6Addresses, ipv6Addresses) + diags = append(diags, diag.FromErr(e)...) + e = d.Set(mkNetworkInterfaceNames, networkInterfaceNames) + diags = append(diags, diag.FromErr(e)...) + + return diags +} diff --git a/proxmoxtf/resource/vm/network/schema.go b/proxmoxtf/resource/vm/network/schema.go new file mode 100644 index 00000000..5a1515a7 --- /dev/null +++ b/proxmoxtf/resource/vm/network/schema.go @@ -0,0 +1,189 @@ +package network + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validators" +) + +const ( + // MaxNetworkDevices is the maximum number of network devices supported by the resource. + MaxNetworkDevices = 32 + + dvNetworkDeviceBridge = "vmbr0" + dvNetworkDeviceEnabled = true + dvNetworkDeviceFirewall = false + dvNetworkDeviceModel = "virtio" + dvNetworkDeviceQueues = 0 + dvNetworkDeviceRateLimit = 0 + dvNetworkDeviceVLANID = 0 + dvNetworkDeviceTrunks = "" + dvNetworkDeviceMTU = 0 + + mkIPv4Addresses = "ipv4_addresses" + mkIPv6Addresses = "ipv6_addresses" + mkMACAddresses = "mac_addresses" + + // MkNetworkDevice is the name of the network device. + MkNetworkDevice = "network_device" + mkNetworkDeviceBridge = "bridge" + mkNetworkDeviceEnabled = "enabled" + mkNetworkDeviceFirewall = "firewall" + mkNetworkDeviceMACAddress = "mac_address" + mkNetworkDeviceModel = "model" + mkNetworkDeviceQueues = "queues" + mkNetworkDeviceRateLimit = "rate_limit" + mkNetworkDeviceVLANID = "vlan_id" + mkNetworkDeviceTrunks = "trunks" + mkNetworkDeviceMTU = "mtu" + mkNetworkInterfaceNames = "network_interface_names" +) + +// Schema returns the schema for the network resource. +func Schema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + mkIPv4Addresses: { + Type: schema.TypeList, + Description: "The IPv4 addresses published by the QEMU agent", + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + mkIPv6Addresses: { + Type: schema.TypeList, + Description: "The IPv6 addresses published by the QEMU agent", + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + mkMACAddresses: { + Type: schema.TypeList, + Description: "The MAC addresses for the network interfaces", + Computed: true, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + MkNetworkDevice: { + Type: schema.TypeList, + Description: "The network devices", + Optional: true, + DefaultFunc: func() (interface{}, error) { + return make([]interface{}, 1), nil + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkNetworkDeviceBridge: { + Type: schema.TypeString, + Description: "The bridge", + Optional: true, + Default: dvNetworkDeviceBridge, + }, + mkNetworkDeviceEnabled: { + Type: schema.TypeBool, + Description: "Whether to enable the network device", + Optional: true, + Default: dvNetworkDeviceEnabled, + }, + mkNetworkDeviceFirewall: { + Type: schema.TypeBool, + Description: "Whether this interface's firewall rules should be used", + Optional: true, + Default: dvNetworkDeviceFirewall, + }, + mkNetworkDeviceMACAddress: { + Type: schema.TypeString, + Description: "The MAC address", + Optional: true, + Computed: true, + ValidateDiagFunc: validators.MACAddress(), + }, + mkNetworkDeviceModel: { + Type: schema.TypeString, + Description: "The model", + Optional: true, + Default: dvNetworkDeviceModel, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{ + "e1000", + "rtl8139", + "virtio", + "vmxnet3", + }, false)), + }, + mkNetworkDeviceQueues: { + Type: schema.TypeInt, + Description: "Number of packet queues to be used on the device", + Optional: true, + Default: dvNetworkDeviceQueues, + ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 64)), + }, + mkNetworkDeviceRateLimit: { + Type: schema.TypeFloat, + Description: "The rate limit in megabytes per second", + Optional: true, + Default: dvNetworkDeviceRateLimit, + }, + mkNetworkDeviceVLANID: { + Type: schema.TypeInt, + Description: "The VLAN identifier", + Optional: true, + Default: dvNetworkDeviceVLANID, + }, + mkNetworkDeviceTrunks: { + Type: schema.TypeString, + Optional: true, + Description: "List of VLAN trunks for the network interface", + }, + mkNetworkDeviceMTU: { + Type: schema.TypeInt, + Description: "Maximum transmission unit (MTU)", + Optional: true, + Default: dvNetworkDeviceMTU, + }, + }, + }, + MaxItems: MaxNetworkDevices, + MinItems: 0, + }, + mkNetworkInterfaceNames: { + Type: schema.TypeList, + Description: "The network interface names published by the QEMU agent", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + } +} + +// CustomizeDiff returns the custom diff functions for the network resource. +func CustomizeDiff() []schema.CustomizeDiffFunc { + return []schema.CustomizeDiffFunc{ + customdiff.ComputedIf( + mkIPv4Addresses, + func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { + return d.HasChange("started") || + d.HasChange(MkNetworkDevice) + }, + ), + customdiff.ComputedIf( + mkIPv6Addresses, + func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { + return d.HasChange("started") || + d.HasChange(MkNetworkDevice) + }, + ), + customdiff.ComputedIf( + mkNetworkInterfaceNames, + func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { + return d.HasChange("started") || + d.HasChange(MkNetworkDevice) + }, + ), + } +} diff --git a/proxmoxtf/resource/vm/network/schema_test.go b/proxmoxtf/resource/vm/network/schema_test.go new file mode 100644 index 00000000..705aca1a --- /dev/null +++ b/proxmoxtf/resource/vm/network/schema_test.go @@ -0,0 +1,55 @@ +package network + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" +) + +func TestNetworkSchema(t *testing.T) { + t.Parallel() + + s := Schema() + + test.AssertComputedAttributes(t, s, []string{ + mkIPv4Addresses, + mkIPv6Addresses, + mkMACAddresses, + mkNetworkInterfaceNames, + }) + + test.AssertValueTypes(t, s, map[string]schema.ValueType{ + mkIPv4Addresses: schema.TypeList, + mkIPv6Addresses: schema.TypeList, + mkMACAddresses: schema.TypeList, + mkNetworkInterfaceNames: schema.TypeList, + }) + + deviceSchema := test.AssertNestedSchemaExistence( + t, + s, + MkNetworkDevice, + ) + + test.AssertOptionalArguments(t, deviceSchema, []string{ + mkNetworkDeviceBridge, + mkNetworkDeviceEnabled, + mkNetworkDeviceMACAddress, + mkNetworkDeviceModel, + mkNetworkDeviceRateLimit, + mkNetworkDeviceVLANID, + mkNetworkDeviceMTU, + }) + + test.AssertValueTypes(t, deviceSchema, map[string]schema.ValueType{ + mkNetworkDeviceBridge: schema.TypeString, + mkNetworkDeviceEnabled: schema.TypeBool, + mkNetworkDeviceMACAddress: schema.TypeString, + mkNetworkDeviceModel: schema.TypeString, + mkNetworkDeviceRateLimit: schema.TypeFloat, + mkNetworkDeviceVLANID: schema.TypeInt, + mkNetworkDeviceMTU: schema.TypeInt, + }) +} diff --git a/proxmoxtf/resource/vm/validators.go b/proxmoxtf/resource/vm/validators.go index 2745d76d..61a0b0d3 100644 --- a/proxmoxtf/resource/vm/validators.go +++ b/proxmoxtf/resource/vm/validators.go @@ -144,11 +144,6 @@ func CPUTypeValidator() schema.SchemaValidateDiagFunc { )) } -// NetworkDeviceModelValidator is a schema validation function for network device models. -func NetworkDeviceModelValidator() schema.SchemaValidateDiagFunc { - return validation.ToDiagFunc(validation.StringInSlice([]string{"e1000", "rtl8139", "virtio", "vmxnet3"}, false)) -} - // QEMUAgentTypeValidator is a schema validation function for QEMU agent types. func QEMUAgentTypeValidator() schema.SchemaValidateDiagFunc { return validation.ToDiagFunc(validation.StringInSlice([]string{"isa", "virtio"}, false)) diff --git a/proxmoxtf/resource/vm/vm.go b/proxmoxtf/resource/vm/vm.go index 75c3f758..c999638b 100644 --- a/proxmoxtf/resource/vm/vm.go +++ b/proxmoxtf/resource/vm/vm.go @@ -17,6 +17,7 @@ import ( "time" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/disk" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/network" "github.com/bpg/terraform-provider-proxmox/utils" "github.com/google/go-cmp/cmp" @@ -93,48 +94,39 @@ const ( dvMemoryShared = 0 dvMigrate = false dvName = "" - dvNetworkDeviceBridge = "vmbr0" - dvNetworkDeviceEnabled = true - dvNetworkDeviceFirewall = false - dvNetworkDeviceModel = "virtio" - dvNetworkDeviceQueues = 0 - dvNetworkDeviceRateLimit = 0 - dvNetworkDeviceVLANID = 0 - dvNetworkDeviceTrunks = "" - dvNetworkDeviceMTU = 0 - dvOperatingSystemType = "other" - dvPoolID = "" - dvProtection = false - dvSerialDeviceDevice = "socket" - dvSMBIOSFamily = "" - dvSMBIOSManufacturer = "" - dvSMBIOSProduct = "" - dvSMBIOSSKU = "" - dvSMBIOSSerial = "" - dvSMBIOSVersion = "" - dvStarted = true - dvStartupOrder = -1 - dvStartupUpDelay = -1 - dvStartupDownDelay = -1 - dvTabletDevice = true - dvTemplate = false - dvTimeoutClone = 1800 - dvTimeoutCreate = 1800 - dvTimeoutMoveDisk = 1800 - dvTimeoutMigrate = 1800 - dvTimeoutReboot = 1800 - dvTimeoutShutdownVM = 1800 - dvTimeoutStartVM = 1800 - dvTimeoutStopVM = 300 - dvVGAEnabled = true - dvVGAMemory = 16 - dvVGAType = "std" - dvSCSIHardware = "virtio-scsi-pci" - dvStopOnDestroy = false - dvHookScript = "" + + dvOperatingSystemType = "other" + dvPoolID = "" + dvProtection = false + dvSerialDeviceDevice = "socket" + dvSMBIOSFamily = "" + dvSMBIOSManufacturer = "" + dvSMBIOSProduct = "" + dvSMBIOSSKU = "" + dvSMBIOSSerial = "" + dvSMBIOSVersion = "" + dvStarted = true + dvStartupOrder = -1 + dvStartupUpDelay = -1 + dvStartupDownDelay = -1 + dvTabletDevice = true + dvTemplate = false + dvTimeoutClone = 1800 + dvTimeoutCreate = 1800 + dvTimeoutMoveDisk = 1800 + dvTimeoutMigrate = 1800 + dvTimeoutReboot = 1800 + dvTimeoutShutdownVM = 1800 + dvTimeoutStartVM = 1800 + dvTimeoutStopVM = 300 + dvVGAEnabled = true + dvVGAMemory = 16 + dvVGAType = "std" + dvSCSIHardware = "virtio-scsi-pci" + dvStopOnDestroy = false + dvHookScript = "" maxResourceVirtualEnvironmentVMAudioDevices = 1 - maxResourceVirtualEnvironmentVMNetworkDevices = 32 maxResourceVirtualEnvironmentVMSerialDevices = 4 maxResourceVirtualEnvironmentVMHostPCIDevices = 8 maxResourceVirtualEnvironmentVMHostUSBDevices = 4 @@ -215,72 +207,59 @@ const ( mkInitializationVendorDataFileID = "vendor_data_file_id" mkInitializationNetworkDataFileID = "network_data_file_id" mkInitializationMetaDataFileID = "meta_data_file_id" - mkIPv4Addresses = "ipv4_addresses" - mkIPv6Addresses = "ipv6_addresses" - mkKeyboardLayout = "keyboard_layout" - mkKVMArguments = "kvm_arguments" - mkMachine = "machine" - mkMACAddresses = "mac_addresses" - mkMemory = "memory" - mkMemoryDedicated = "dedicated" - mkMemoryFloating = "floating" - mkMemoryShared = "shared" - mkMigrate = "migrate" - mkName = "name" - mkNetworkDevice = "network_device" - mkNetworkDeviceBridge = "bridge" - mkNetworkDeviceEnabled = "enabled" - mkNetworkDeviceFirewall = "firewall" - mkNetworkDeviceMACAddress = "mac_address" - mkNetworkDeviceModel = "model" - mkNetworkDeviceQueues = "queues" - mkNetworkDeviceRateLimit = "rate_limit" - mkNetworkDeviceVLANID = "vlan_id" - mkNetworkDeviceTrunks = "trunks" - mkNetworkDeviceMTU = "mtu" - mkNetworkInterfaceNames = "network_interface_names" - mkNodeName = "node_name" - mkOperatingSystem = "operating_system" - mkOperatingSystemType = "type" - mkPoolID = "pool_id" - mkProtection = "protection" - mkSerialDevice = "serial_device" - mkSerialDeviceDevice = "device" - mkSMBIOS = "smbios" - mkSMBIOSFamily = "family" - mkSMBIOSManufacturer = "manufacturer" - mkSMBIOSProduct = "product" - mkSMBIOSSKU = "sku" - mkSMBIOSSerial = "serial" - mkSMBIOSUUID = "uuid" - mkSMBIOSVersion = "version" - mkStarted = "started" - mkStartup = "startup" - mkStartupOrder = "order" - mkStartupUpDelay = "up_delay" - mkStartupDownDelay = "down_delay" - mkTabletDevice = "tablet_device" - mkTags = "tags" - mkTemplate = "template" - mkTimeoutClone = "timeout_clone" - mkTimeoutCreate = "timeout_create" - mkTimeoutMigrate = "timeout_migrate" - mkTimeoutReboot = "timeout_reboot" - mkTimeoutShutdownVM = "timeout_shutdown_vm" - mkTimeoutStartVM = "timeout_start_vm" - mkTimeoutStopVM = "timeout_stop_vm" - mkHostUSB = "usb" - mkHostUSBDevice = "host" - mkHostUSBDeviceMapping = "mapping" - mkHostUSBDeviceUSB3 = "usb3" - mkVGA = "vga" - mkVGAEnabled = "enabled" - mkVGAMemory = "memory" - mkVGAType = "type" - mkVMID = "vm_id" - mkSCSIHardware = "scsi_hardware" - mkHookScriptFileID = "hook_script_file_id" - mkStopOnDestroy = "stop_on_destroy" + + mkKeyboardLayout = "keyboard_layout" + mkKVMArguments = "kvm_arguments" + mkMachine = "machine" + mkMemory = "memory" + mkMemoryDedicated = "dedicated" + mkMemoryFloating = "floating" + mkMemoryShared = "shared" + mkMigrate = "migrate" + mkName = "name" + + mkNodeName = "node_name" + mkOperatingSystem = "operating_system" + mkOperatingSystemType = "type" + mkPoolID = "pool_id" + mkProtection = "protection" + mkSerialDevice = "serial_device" + mkSerialDeviceDevice = "device" + mkSMBIOS = "smbios" + mkSMBIOSFamily = "family" + mkSMBIOSManufacturer = "manufacturer" + mkSMBIOSProduct = "product" + mkSMBIOSSKU = "sku" + mkSMBIOSSerial = "serial" + mkSMBIOSUUID = "uuid" + mkSMBIOSVersion = "version" + mkStarted = "started" + mkStartup = "startup" + mkStartupOrder = "order" + mkStartupUpDelay = "up_delay" + mkStartupDownDelay = "down_delay" + mkTabletDevice = "tablet_device" + mkTags = "tags" + mkTemplate = "template" + mkTimeoutClone = "timeout_clone" + mkTimeoutCreate = "timeout_create" + mkTimeoutMigrate = "timeout_migrate" + mkTimeoutReboot = "timeout_reboot" + mkTimeoutShutdownVM = "timeout_shutdown_vm" + mkTimeoutStartVM = "timeout_start_vm" + mkTimeoutStopVM = "timeout_stop_vm" + mkHostUSB = "usb" + mkHostUSBDevice = "host" + mkHostUSBDeviceMapping = "mapping" + mkHostUSBDeviceUSB3 = "usb3" + mkVGA = "vga" + mkVGAEnabled = "enabled" + mkVGAMemory = "memory" + mkVGAType = "type" + mkVMID = "vm_id" + mkSCSIHardware = "scsi_hardware" + mkHookScriptFileID = "hook_script_file_id" + mkStopOnDestroy = "stop_on_destroy" ) // VM returns a resource that manages VMs. @@ -897,24 +876,6 @@ func VM() *schema.Resource { MaxItems: 1, MinItems: 0, }, - mkIPv4Addresses: { - Type: schema.TypeList, - Description: "The IPv4 addresses published by the QEMU agent", - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - mkIPv6Addresses: { - Type: schema.TypeList, - Description: "The IPv6 addresses published by the QEMU agent", - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, mkHostPCI: { Type: schema.TypeList, Description: "The Host PCI devices mapped to the VM", @@ -1013,13 +974,6 @@ func VM() *schema.Resource { Default: dvMachineType, ValidateDiagFunc: MachineTypeValidator(), }, - mkMACAddresses: { - Type: schema.TypeList, - Description: "The MAC addresses for the network interfaces", - Computed: true, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, mkMemory: { Type: schema.TypeList, Description: "The memory allocation", @@ -1073,88 +1027,6 @@ func VM() *schema.Resource { Optional: true, Default: dvName, }, - mkNetworkDevice: { - Type: schema.TypeList, - Description: "The network devices", - Optional: true, - DefaultFunc: func() (interface{}, error) { - return make([]interface{}, 1), nil - }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - mkNetworkDeviceBridge: { - Type: schema.TypeString, - Description: "The bridge", - Optional: true, - Default: dvNetworkDeviceBridge, - }, - mkNetworkDeviceEnabled: { - Type: schema.TypeBool, - Description: "Whether to enable the network device", - Optional: true, - Default: dvNetworkDeviceEnabled, - }, - mkNetworkDeviceFirewall: { - Type: schema.TypeBool, - Description: "Whether this interface's firewall rules should be used", - Optional: true, - Default: dvNetworkDeviceFirewall, - }, - mkNetworkDeviceMACAddress: { - Type: schema.TypeString, - Description: "The MAC address", - Optional: true, - Computed: true, - ValidateDiagFunc: validators.MACAddress(), - }, - mkNetworkDeviceModel: { - Type: schema.TypeString, - Description: "The model", - Optional: true, - Default: dvNetworkDeviceModel, - ValidateDiagFunc: NetworkDeviceModelValidator(), - }, - mkNetworkDeviceQueues: { - Type: schema.TypeInt, - Description: "Number of packet queues to be used on the device", - Optional: true, - Default: dvNetworkDeviceQueues, - ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 64)), - }, - mkNetworkDeviceRateLimit: { - Type: schema.TypeFloat, - Description: "The rate limit in megabytes per second", - Optional: true, - Default: dvNetworkDeviceRateLimit, - }, - mkNetworkDeviceVLANID: { - Type: schema.TypeInt, - Description: "The VLAN identifier", - Optional: true, - Default: dvNetworkDeviceVLANID, - }, - mkNetworkDeviceTrunks: { - Type: schema.TypeString, - Optional: true, - Description: "List of VLAN trunks for the network interface", - }, - mkNetworkDeviceMTU: { - Type: schema.TypeInt, - Description: "Maximum transmission unit (MTU)", - Optional: true, - Default: dvNetworkDeviceMTU, - }, - }, - }, - MaxItems: maxResourceVirtualEnvironmentVMNetworkDevices, - MinItems: 0, - }, - mkNetworkInterfaceNames: { - Type: schema.TypeList, - Description: "The network interface names published by the QEMU agent", - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, mkNodeName: { Type: schema.TypeString, Description: "The node name", @@ -1462,6 +1334,7 @@ func VM() *schema.Resource { } structure.MergeSchema(s, disk.Schema()) + structure.MergeSchema(s, network.Schema()) return &schema.Resource{ Schema: s, @@ -1470,27 +1343,7 @@ func VM() *schema.Resource { UpdateContext: vmUpdate, DeleteContext: vmDelete, CustomizeDiff: customdiff.All( - customdiff.ComputedIf( - mkIPv4Addresses, - func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { - return d.HasChange(mkStarted) || - d.HasChange(mkNetworkDevice) - }, - ), - customdiff.ComputedIf( - mkIPv6Addresses, - func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { - return d.HasChange(mkStarted) || - d.HasChange(mkNetworkDevice) - }, - ), - customdiff.ComputedIf( - mkNetworkInterfaceNames, - func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { - return d.HasChange(mkStarted) || - d.HasChange(mkNetworkDevice) - }, - ), + customdiff.All(network.CustomizeDiff()...), customdiff.ForceNewIf( mkVMID, func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { @@ -1825,7 +1678,6 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d hostUSB := d.Get(mkHostUSB).([]interface{}) keyboardLayout := d.Get(mkKeyboardLayout).(string) memory := d.Get(mkMemory).([]interface{}) - networkDevice := d.Get(mkNetworkDevice).([]interface{}) operatingSystem := d.Get(mkOperatingSystem).([]interface{}) serialDevice := d.Get(mkSerialDevice).([]interface{}) onBoot := types.CustomBool(d.Get(mkOnBoot).(bool)) @@ -2027,8 +1879,9 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d } } + networkDevice := d.Get(network.MkNetworkDevice).([]interface{}) if len(networkDevice) > 0 { - updateBody.NetworkDevices, err = vmGetNetworkDeviceObjects(d) + updateBody.NetworkDevices, err = network.GetNetworkDeviceObjects(d) if err != nil { return diag.FromErr(err) } @@ -2039,7 +1892,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d } } - for i := len(updateBody.NetworkDevices); i < maxResourceVirtualEnvironmentVMNetworkDevices; i++ { + for i := len(updateBody.NetworkDevices); i < network.MaxNetworkDevices; i++ { del = append(del, fmt.Sprintf("net%d", i)) } } @@ -2426,7 +2279,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) name := d.Get(mkName).(string) tags := d.Get(mkTags).([]interface{}) - networkDeviceObjects, err := vmGetNetworkDeviceObjects(d) + networkDeviceObjects, err := network.GetNetworkDeviceObjects(d) if err != nil { return diag.FromErr(err) } @@ -3054,77 +2907,6 @@ func vmGetHostUSBDeviceObjects(d *schema.ResourceData) vms.CustomUSBDevices { return usbDeviceObjects } -func vmGetNetworkDeviceObjects(d *schema.ResourceData) (vms.CustomNetworkDevices, error) { - networkDevice := d.Get(mkNetworkDevice).([]interface{}) - networkDeviceObjects := make(vms.CustomNetworkDevices, len(networkDevice)) - - for i, networkDeviceEntry := range networkDevice { - block := networkDeviceEntry.(map[string]interface{}) - - bridge := block[mkNetworkDeviceBridge].(string) - enabled := block[mkNetworkDeviceEnabled].(bool) - firewall := types.CustomBool(block[mkNetworkDeviceFirewall].(bool)) - macAddress := block[mkNetworkDeviceMACAddress].(string) - model := block[mkNetworkDeviceModel].(string) - queues := block[mkNetworkDeviceQueues].(int) - rateLimit := block[mkNetworkDeviceRateLimit].(float64) - vlanID := block[mkNetworkDeviceVLANID].(int) - trunks := block[mkNetworkDeviceTrunks].(string) - mtu := block[mkNetworkDeviceMTU].(int) - - device := vms.CustomNetworkDevice{ - Enabled: enabled, - Firewall: &firewall, - Model: model, - } - - if bridge != "" { - device.Bridge = &bridge - } - - if macAddress != "" { - device.MACAddress = &macAddress - } - - if queues != 0 { - device.Queues = &queues - } - - if rateLimit != 0 { - device.RateLimit = &rateLimit - } - - if vlanID != 0 { - device.Tag = &vlanID - } - - if trunks != "" { - splitTrunks := strings.Split(trunks, ";") - - var trunksAsInt []int - - for _, numStr := range splitTrunks { - num, err := strconv.Atoi(numStr) - if err != nil { - return nil, fmt.Errorf("error parsing trunks: %w", err) - } - - trunksAsInt = append(trunksAsInt, num) - } - - device.Trunks = trunksAsInt - } - - if mtu != 0 { - device.MTU = &mtu - } - - networkDeviceObjects[i] = device - } - - return networkDeviceObjects, nil -} - func vmGetSerialDeviceList(d *schema.ResourceData) vms.CustomSerialDevices { device := d.Get(mkSerialDevice).([]interface{}) list := make(vms.CustomSerialDevices, len(device)) @@ -3350,9 +3132,9 @@ func vmReadCustom( ) diag.Diagnostics { config := m.(proxmoxtf.ProviderConfiguration) - api, err := config.GetClient() - if err != nil { - return diag.FromErr(err) + api, e := config.GetClient() + if e != nil { + return diag.FromErr(e) } diags := vmReadPrimitiveValues(d, vmConfig, vmStatus) @@ -3368,7 +3150,7 @@ func vmReadCustom( d.Id(), storedVMID, vmID), }) - err = d.Set(mkVMID, vmID) + err := d.Set(mkVMID, vmID) diags = append(diags, diag.FromErr(err)...) } @@ -3418,7 +3200,7 @@ func vmReadCustom( if len(clone) > 0 { if len(currentAgent) > 0 { - err = d.Set(mkAgent, []interface{}{agent}) + err := d.Set(mkAgent, []interface{}{agent}) diags = append(diags, diag.FromErr(err)...) } } else if len(currentAgent) > 0 || @@ -3426,16 +3208,16 @@ func vmReadCustom( agent[mkAgentTimeout] != dvAgentTimeout || agent[mkAgentTrim] != dvAgentTrim || agent[mkAgentType] != dvAgentType { - err = d.Set(mkAgent, []interface{}{agent}) + err := d.Set(mkAgent, []interface{}{agent}) diags = append(diags, diag.FromErr(err)...) } } else if len(clone) > 0 { if len(currentAgent) > 0 { - err = d.Set(mkAgent, []interface{}{}) + err := d.Set(mkAgent, []interface{}{}) diags = append(diags, diag.FromErr(err)...) } } else { - err = d.Set(mkAgent, []interface{}{}) + err := d.Set(mkAgent, []interface{}{}) diags = append(diags, diag.FromErr(err)...) } } @@ -3474,7 +3256,7 @@ func vmReadCustom( } if len(clone) == 0 || len(currentAudioDevice) > 0 { - err = d.Set(mkAudioDevice, audioDevices[:audioDevicesCount]) + err := d.Set(mkAudioDevice, audioDevices[:audioDevicesCount]) diags = append(diags, diag.FromErr(err)...) } @@ -3512,11 +3294,11 @@ func vmReadCustom( cdrom[0] = cdromBlock - err = d.Set(mkCDROM, cdrom) + err := d.Set(mkCDROM, cdrom) diags = append(diags, diag.FromErr(err)...) } } else { - err = d.Set(mkCDROM, []interface{}{}) + err := d.Set(mkCDROM, []interface{}{}) diags = append(diags, diag.FromErr(err)...) } @@ -3601,7 +3383,7 @@ func vmReadCustom( if len(clone) > 0 { if len(currentCPU) > 0 { - err = d.Set(mkCPU, []interface{}{cpu}) + err := d.Set(mkCPU, []interface{}{cpu}) diags = append(diags, diag.FromErr(err)...) } } else if len(currentCPU) > 0 || @@ -3613,7 +3395,7 @@ func vmReadCustom( cpu[mkCPUSockets] != dvCPUSockets || cpu[mkCPUType] != dvCPUType || cpu[mkCPUUnits] != dvCPUUnits { - err = d.Set(mkCPU, []interface{}{cpu}) + err := d.Set(mkCPU, []interface{}{cpu}) diags = append(diags, diag.FromErr(err)...) } @@ -3636,8 +3418,8 @@ func vmReadCustom( } else { // disk format may not be returned by config API if it is default for the storage, and that may be different // from the default qcow2, so we need to read it from the storage API to make sure we have the correct value - volume, e := api.Node(nodeName).Storage(fileIDParts[0]).GetDatastoreFile(ctx, vmConfig.EFIDisk.FileVolume) - if e != nil { + volume, err := api.Node(nodeName).Storage(fileIDParts[0]).GetDatastoreFile(ctx, vmConfig.EFIDisk.FileVolume) + if err != nil { diags = append(diags, diag.FromErr(e)...) } else { efiDisk[mkEFIDiskFileFormat] = volume.FileFormat @@ -3660,7 +3442,7 @@ func vmReadCustom( if len(clone) > 0 { if len(currentEfiDisk) > 0 { - err = d.Set(mkEFIDisk, []interface{}{efiDisk}) + err := d.Set(mkEFIDisk, []interface{}{efiDisk}) diags = append(diags, diag.FromErr(err)...) } } else if len(currentEfiDisk) > 0 || @@ -3668,7 +3450,7 @@ func vmReadCustom( efiDisk[mkEFIDiskType] != dvEFIDiskType || efiDisk[mkEFIDiskPreEnrolledKeys] != dvEFIDiskPreEnrolledKeys || efiDisk[mkEFIDiskFileFormat] != dvEFIDiskFileFormat { - err = d.Set(mkEFIDisk, []interface{}{efiDisk}) + err := d.Set(mkEFIDisk, []interface{}{efiDisk}) diags = append(diags, diag.FromErr(err)...) } } @@ -3685,13 +3467,13 @@ func vmReadCustom( if len(clone) > 0 { if len(currentTPMState) > 0 { - err = d.Set(mkTPMState, []interface{}{tpmState}) + err := d.Set(mkTPMState, []interface{}{tpmState}) diags = append(diags, diag.FromErr(err)...) } } else if len(currentTPMState) > 0 || tpmState[mkTPMStateDatastoreID] != dvTPMStateDatastoreID || tpmState[mkTPMStateVersion] != dvTPMStateVersion { - err = d.Set(mkTPMState, []interface{}{tpmState}) + err := d.Set(mkTPMState, []interface{}{tpmState}) diags = append(diags, diag.FromErr(err)...) } } @@ -3755,7 +3537,7 @@ func vmReadCustom( if len(clone) == 0 || len(currentPCIList) > 0 { orderedPCIList := utils.OrderedListFromMap(pciMap) - err = d.Set(mkHostPCI, orderedPCIList) + err := d.Set(mkHostPCI, orderedPCIList) diags = append(diags, diag.FromErr(err)...) } @@ -3794,7 +3576,7 @@ func vmReadCustom( if len(clone) == 0 || len(currentUSBList) > 0 { // NOTE: reordering of devices by PVE may cause an issue here orderedUSBList := utils.OrderedListFromMap(usbMap) - err = d.Set(mkHostUSB, orderedUSBList) + err := d.Set(mkHostUSB, orderedUSBList) diags = append(diags, diag.FromErr(err)...) } @@ -4022,18 +3804,18 @@ func vmReadCustom( if len(clone) > 0 { if len(currentInitialization) > 0 { if len(initialization) > 0 { - err = d.Set(mkInitialization, []interface{}{initialization}) + err := d.Set(mkInitialization, []interface{}{initialization}) diags = append(diags, diag.FromErr(err)...) } else { - err = d.Set(mkInitialization, []interface{}{}) + err := d.Set(mkInitialization, []interface{}{}) diags = append(diags, diag.FromErr(err)...) } } } else if len(initialization) > 0 { - err = d.Set(mkInitialization, []interface{}{initialization}) + err := d.Set(mkInitialization, []interface{}{initialization}) diags = append(diags, diag.FromErr(err)...) } else { - err = d.Set(mkInitialization, []interface{}{}) + err := d.Set(mkInitialization, []interface{}{}) diags = append(diags, diag.FromErr(err)...) } @@ -4071,132 +3853,18 @@ func vmReadCustom( if len(clone) > 0 { if len(currentMemory) > 0 { - err = d.Set(mkMemory, []interface{}{memory}) + err := d.Set(mkMemory, []interface{}{memory}) diags = append(diags, diag.FromErr(err)...) } } else if len(currentMemory) > 0 || memory[mkMemoryDedicated] != dvMemoryDedicated || memory[mkMemoryFloating] != dvMemoryFloating || memory[mkMemoryShared] != dvMemoryShared { - err = d.Set(mkMemory, []interface{}{memory}) + err := d.Set(mkMemory, []interface{}{memory}) diags = append(diags, diag.FromErr(err)...) } - // Compare the network devices to those stored in the state. - currentNetworkDeviceList := d.Get(mkNetworkDevice).([]interface{}) - - macAddresses := make([]interface{}, maxResourceVirtualEnvironmentVMNetworkDevices) - networkDeviceLast := -1 - networkDeviceList := make([]interface{}, maxResourceVirtualEnvironmentVMNetworkDevices) - networkDeviceObjects := []*vms.CustomNetworkDevice{ - vmConfig.NetworkDevice0, - vmConfig.NetworkDevice1, - vmConfig.NetworkDevice2, - vmConfig.NetworkDevice3, - vmConfig.NetworkDevice4, - vmConfig.NetworkDevice5, - vmConfig.NetworkDevice6, - vmConfig.NetworkDevice7, - vmConfig.NetworkDevice8, - vmConfig.NetworkDevice9, - vmConfig.NetworkDevice10, - vmConfig.NetworkDevice11, - vmConfig.NetworkDevice12, - vmConfig.NetworkDevice13, - vmConfig.NetworkDevice14, - vmConfig.NetworkDevice15, - vmConfig.NetworkDevice16, - vmConfig.NetworkDevice17, - vmConfig.NetworkDevice18, - vmConfig.NetworkDevice19, - vmConfig.NetworkDevice20, - vmConfig.NetworkDevice21, - vmConfig.NetworkDevice22, - vmConfig.NetworkDevice23, - vmConfig.NetworkDevice24, - vmConfig.NetworkDevice25, - vmConfig.NetworkDevice26, - vmConfig.NetworkDevice27, - vmConfig.NetworkDevice28, - vmConfig.NetworkDevice29, - vmConfig.NetworkDevice30, - vmConfig.NetworkDevice31, - } - - for ni, nd := range networkDeviceObjects { - networkDevice := map[string]interface{}{} - - if nd != nil { - networkDeviceLast = ni - - if nd.Bridge != nil { - networkDevice[mkNetworkDeviceBridge] = *nd.Bridge - } else { - networkDevice[mkNetworkDeviceBridge] = "" - } - - networkDevice[mkNetworkDeviceEnabled] = nd.Enabled - - if nd.Firewall != nil { - networkDevice[mkNetworkDeviceFirewall] = *nd.Firewall - } else { - networkDevice[mkNetworkDeviceFirewall] = false - } - - if nd.MACAddress != nil { - macAddresses[ni] = *nd.MACAddress - } else { - macAddresses[ni] = "" - } - - networkDevice[mkNetworkDeviceMACAddress] = macAddresses[ni] - networkDevice[mkNetworkDeviceModel] = nd.Model - - if nd.Queues != nil { - networkDevice[mkNetworkDeviceQueues] = *nd.Queues - } else { - networkDevice[mkNetworkDeviceQueues] = 0 - } - - if nd.RateLimit != nil { - networkDevice[mkNetworkDeviceRateLimit] = *nd.RateLimit - } else { - networkDevice[mkNetworkDeviceRateLimit] = 0 - } - - if nd.Tag != nil { - networkDevice[mkNetworkDeviceVLANID] = nd.Tag - } else { - networkDevice[mkNetworkDeviceVLANID] = 0 - } - - if nd.Trunks != nil { - networkDevice[mkNetworkDeviceTrunks] = strings.Trim( - strings.Join(strings.Fields(fmt.Sprint(nd.Trunks)), ";"), "[]") - } else { - networkDevice[mkNetworkDeviceTrunks] = "" - } - - if nd.MTU != nil { - networkDevice[mkNetworkDeviceMTU] = nd.MTU - } else { - networkDevice[mkNetworkDeviceMTU] = 0 - } - } else { - macAddresses[ni] = "" - networkDevice[mkNetworkDeviceEnabled] = false - } - - networkDeviceList[ni] = networkDevice - } - - err = d.Set(mkMACAddresses, macAddresses[0:len(currentNetworkDeviceList)]) - diags = append(diags, diag.FromErr(err)...) - - if len(currentNetworkDeviceList) > 0 || networkDeviceLast > -1 { - err := d.Set(mkNetworkDevice, networkDeviceList[:networkDeviceLast+1]) - diags = append(diags, diag.FromErr(err)...) - } + diags = append(diags, network.ReadNetworkDeviceObjects(d, vmConfig)...) // Compare the operating system configuration to the one stored in the state. operatingSystem := map[string]interface{}{} @@ -4455,112 +4123,23 @@ func vmReadCustom( } } - diags = append( - diags, - vmReadNetworkValues(ctx, d, m, vmID, vmConfig)...) + vmAPI := api.Node(nodeName).VM(vmID) + started := d.Get(mkStarted).(bool) - // during import these core attributes might not be set, so set them explicitly here - d.SetId(strconv.Itoa(vmID)) - e := d.Set(mkVMID, vmID) - diags = append(diags, diag.FromErr(e)...) - e = d.Set(mkNodeName, nodeName) - diags = append(diags, diag.FromErr(e)...) - - return diags -} - -func vmReadNetworkValues( - ctx context.Context, - d *schema.ResourceData, - m interface{}, - vmID int, - vmConfig *vms.GetResponseData, -) diag.Diagnostics { - var diags diag.Diagnostics - - config := m.(proxmoxtf.ProviderConfiguration) - - api, e := config.GetClient() + agentTimeout, e := getAgentTimeout(d) if e != nil { return diag.FromErr(e) } - nodeName := d.Get(mkNodeName).(string) + diags = append( + diags, + network.ReadNetworkValues(ctx, d, vmAPI, started, vmConfig, agentTimeout)...) - vmAPI := api.Node(nodeName).VM(vmID) - - started := d.Get(mkStarted).(bool) - - var ipv4Addresses []interface{} - - var ipv6Addresses []interface{} - - var networkInterfaceNames []interface{} - - if started { - if vmConfig.Agent != nil && vmConfig.Agent.Enabled != nil && *vmConfig.Agent.Enabled { - resource := VM() - - agentBlock, err := structure.GetSchemaBlock( - resource, - d, - []string{mkAgent}, - 0, - true, - ) - if err != nil { - return diag.FromErr(err) - } - - agentTimeout, err := time.ParseDuration( - agentBlock[mkAgentTimeout].(string), - ) - if err != nil { - return diag.FromErr(err) - } - - var macAddresses []interface{} - - networkInterfaces, err := vmAPI.WaitForNetworkInterfacesFromVMAgent(ctx, int(agentTimeout.Seconds()), 5, true) - if err == nil && networkInterfaces.Result != nil { - ipv4Addresses = make([]interface{}, len(*networkInterfaces.Result)) - ipv6Addresses = make([]interface{}, len(*networkInterfaces.Result)) - macAddresses = make([]interface{}, len(*networkInterfaces.Result)) - networkInterfaceNames = make([]interface{}, len(*networkInterfaces.Result)) - - for ri, rv := range *networkInterfaces.Result { - var rvIPv4Addresses []interface{} - - var rvIPv6Addresses []interface{} - - if rv.IPAddresses != nil { - for _, ip := range *rv.IPAddresses { - switch ip.Type { - case "ipv4": - rvIPv4Addresses = append(rvIPv4Addresses, ip.Address) - case "ipv6": - rvIPv6Addresses = append(rvIPv6Addresses, ip.Address) - } - } - } - - ipv4Addresses[ri] = rvIPv4Addresses - ipv6Addresses[ri] = rvIPv6Addresses - macAddresses[ri] = strings.ToUpper(rv.MACAddress) - networkInterfaceNames[ri] = rv.Name - } - } - - err = d.Set(mkMACAddresses, macAddresses) - diags = append(diags, diag.FromErr(err)...) - } - } - - e = d.Set(mkIPv4Addresses, ipv4Addresses) + // during import these core attributes might not be set, so set them explicitly here + d.SetId(strconv.Itoa(vmID)) + e = d.Set(mkVMID, vmID) diags = append(diags, diag.FromErr(e)...) - e = d.Set(mkIPv6Addresses, ipv6Addresses) - diags = append(diags, diag.FromErr(e)...) - e = d.Set(mkNetworkInterfaceNames, networkInterfaceNames) + e = d.Set(mkNodeName, nodeName) diags = append(diags, diag.FromErr(e)...) return diags @@ -5257,8 +4836,9 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D } // Prepare the new network device configuration. - if d.HasChange(mkNetworkDevice) { - updateBody.NetworkDevices, err = vmGetNetworkDeviceObjects(d) + + if d.HasChange(network.MkNetworkDevice) { + updateBody.NetworkDevices, err = network.GetNetworkDeviceObjects(d) if err != nil { return diag.FromErr(err) } @@ -5269,7 +4849,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D } } - for i := len(updateBody.NetworkDevices); i < maxResourceVirtualEnvironmentVMNetworkDevices; i++ { + for i := len(updateBody.NetworkDevices); i < network.MaxNetworkDevices; i++ { del = append(del, fmt.Sprintf("net%d", i)) } @@ -5731,3 +5311,27 @@ func parseImportIDWithNodeName(id string) (string, string, error) { return nodeName, id, nil } + +func getAgentTimeout(d *schema.ResourceData) (time.Duration, error) { + resource := VM() + + agentBlock, err := structure.GetSchemaBlock( + resource, + d, + []string{mkAgent}, + 0, + true, + ) + if err != nil { + return 0, fmt.Errorf("failed to get agent block: %w", err) + } + + agentTimeout, err := time.ParseDuration( + agentBlock[mkAgentTimeout].(string), + ) + if err != nil { + return 0, fmt.Errorf("failed to parse agent timeout: %w", err) + } + + return agentTimeout, nil +} diff --git a/proxmoxtf/resource/vm/vm_test.go b/proxmoxtf/resource/vm/vm_test.go index d37f2fea..db39e236 100644 --- a/proxmoxtf/resource/vm/vm_test.go +++ b/proxmoxtf/resource/vm/vm_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/disk" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/vm/network" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) @@ -20,8 +21,8 @@ import ( func TestVMInstantiation(t *testing.T) { t.Parallel() - s := VM() - if s == nil { + r := VM() + if r == nil { t.Fatalf("Cannot instantiate VM") } } @@ -30,7 +31,7 @@ func TestVMInstantiation(t *testing.T) { func TestVMSchema(t *testing.T) { t.Parallel() - s := VM() + s := VM().Schema test.AssertRequiredArguments(t, s, []string{ mkNodeName, @@ -56,7 +57,7 @@ func TestVMSchema(t *testing.T) { mkMachine, mkMemory, mkName, - mkNetworkDevice, + network.MkNetworkDevice, mkOperatingSystem, mkPoolID, mkSerialDevice, @@ -67,45 +68,33 @@ func TestVMSchema(t *testing.T) { mkSCSIHardware, }) - test.AssertComputedAttributes(t, s, []string{ - mkIPv4Addresses, - mkIPv6Addresses, - mkMACAddresses, - mkNetworkInterfaceNames, - }) - test.AssertValueTypes(t, s, map[string]schema.ValueType{ - mkACPI: schema.TypeBool, - mkAgent: schema.TypeList, - mkAudioDevice: schema.TypeList, - mkBIOS: schema.TypeString, - mkBootOrder: schema.TypeList, - mkCDROM: schema.TypeList, - mkCPU: schema.TypeList, - mkDescription: schema.TypeString, - disk.MkDisk: schema.TypeList, - mkEFIDisk: schema.TypeList, - mkHostPCI: schema.TypeList, - mkHostUSB: schema.TypeList, - mkInitialization: schema.TypeList, - mkIPv4Addresses: schema.TypeList, - mkIPv6Addresses: schema.TypeList, - mkKeyboardLayout: schema.TypeString, - mkKVMArguments: schema.TypeString, - mkMachine: schema.TypeString, - mkMemory: schema.TypeList, - mkName: schema.TypeString, - mkNetworkDevice: schema.TypeList, - mkMACAddresses: schema.TypeList, - mkNetworkInterfaceNames: schema.TypeList, - mkOperatingSystem: schema.TypeList, - mkPoolID: schema.TypeString, - mkSerialDevice: schema.TypeList, - mkStarted: schema.TypeBool, - mkTabletDevice: schema.TypeBool, - mkTemplate: schema.TypeBool, - mkVMID: schema.TypeInt, - mkSCSIHardware: schema.TypeString, + mkACPI: schema.TypeBool, + mkAgent: schema.TypeList, + mkAudioDevice: schema.TypeList, + mkBIOS: schema.TypeString, + mkBootOrder: schema.TypeList, + mkCDROM: schema.TypeList, + mkCPU: schema.TypeList, + mkDescription: schema.TypeString, + disk.MkDisk: schema.TypeList, + mkEFIDisk: schema.TypeList, + mkHostPCI: schema.TypeList, + mkHostUSB: schema.TypeList, + mkInitialization: schema.TypeList, + mkKeyboardLayout: schema.TypeString, + mkKVMArguments: schema.TypeString, + mkMachine: schema.TypeString, + mkMemory: schema.TypeList, + mkName: schema.TypeString, + mkOperatingSystem: schema.TypeList, + mkPoolID: schema.TypeString, + mkSerialDevice: schema.TypeList, + mkStarted: schema.TypeBool, + mkTabletDevice: schema.TypeBool, + mkTemplate: schema.TypeBool, + mkVMID: schema.TypeInt, + mkSCSIHardware: schema.TypeString, }) agentSchema := test.AssertNestedSchemaExistence(t, s, mkAgent) @@ -188,44 +177,6 @@ func TestVMSchema(t *testing.T) { mkCPUUnits: schema.TypeInt, }) - // diskSchema := test.AssertNestedSchemaExistence(t, s, mkDisk) - // - // test.AssertOptionalArguments(t, diskSchema, []string{ - // mkDiskDatastoreID, - // mkDiskPathInDatastore, - // mkDiskFileFormat, - // mkDiskFileID, - // mkDiskSize, - // }) - // - // test.AssertValueTypes(t, diskSchema, map[string]schema.ValueType{ - // mkDiskDatastoreID: schema.TypeString, - // mkDiskPathInDatastore: schema.TypeString, - // mkDiskFileFormat: schema.TypeString, - // mkDiskFileID: schema.TypeString, - // mkDiskSize: schema.TypeInt, - // }) - // - // diskSpeedSchema := test.AssertNestedSchemaExistence( - // t, - // diskSchema, - // mkDiskSpeed, - //) - // - // test.AssertOptionalArguments(t, diskSpeedSchema, []string{ - // mkDiskSpeedRead, - // mkDiskSpeedReadBurstable, - // mkDiskSpeedWrite, - // mkDiskSpeedWriteBurstable, - // }) - // - // test.AssertValueTypes(t, diskSpeedSchema, map[string]schema.ValueType{ - // mkDiskSpeedRead: schema.TypeInt, - // mkDiskSpeedReadBurstable: schema.TypeInt, - // mkDiskSpeedWrite: schema.TypeInt, - // mkDiskSpeedWriteBurstable: schema.TypeInt, - // }) - efiDiskSchema := test.AssertNestedSchemaExistence(t, s, mkEFIDisk) test.AssertOptionalArguments(t, efiDiskSchema, []string{ @@ -390,32 +341,6 @@ func TestVMSchema(t *testing.T) { mkMemoryShared: schema.TypeInt, }) - networkDeviceSchema := test.AssertNestedSchemaExistence( - t, - s, - mkNetworkDevice, - ) - - test.AssertOptionalArguments(t, networkDeviceSchema, []string{ - mkNetworkDeviceBridge, - mkNetworkDeviceEnabled, - mkNetworkDeviceMACAddress, - mkNetworkDeviceModel, - mkNetworkDeviceRateLimit, - mkNetworkDeviceVLANID, - mkNetworkDeviceMTU, - }) - - test.AssertValueTypes(t, networkDeviceSchema, map[string]schema.ValueType{ - mkNetworkDeviceBridge: schema.TypeString, - mkNetworkDeviceEnabled: schema.TypeBool, - mkNetworkDeviceMACAddress: schema.TypeString, - mkNetworkDeviceModel: schema.TypeString, - mkNetworkDeviceRateLimit: schema.TypeFloat, - mkNetworkDeviceVLANID: schema.TypeInt, - mkNetworkDeviceMTU: schema.TypeInt, - }) - operatingSystemSchema := test.AssertNestedSchemaExistence( t, s, diff --git a/proxmoxtf/test/utils.go b/proxmoxtf/test/utils.go index ab4bb89b..9181cdf6 100644 --- a/proxmoxtf/test/utils.go +++ b/proxmoxtf/test/utils.go @@ -15,20 +15,20 @@ import ( ) // AssertComputedAttributes checks that the given schema has the given computed attributes. -func AssertComputedAttributes(t *testing.T, s *schema.Resource, keys []string) { +func AssertComputedAttributes(t *testing.T, s map[string]*schema.Schema, keys []string) { t.Helper() for _, v := range keys { - require.NotNil(t, s.Schema[v], "Error in Schema: Missing definition for \"%s\"", v) - assert.True(t, s.Schema[v].Computed, "Error in Schema: Attribute \"%s\" is not computed", v) + require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v) + assert.True(t, s[v].Computed, "Error in Schema: Attribute \"%s\" is not computed", v) } } // AssertNestedSchemaExistence checks that the given schema has a nested schema for the given key. -func AssertNestedSchemaExistence(t *testing.T, s *schema.Resource, key string) *schema.Resource { +func AssertNestedSchemaExistence(t *testing.T, s map[string]*schema.Schema, key string) map[string]*schema.Schema { t.Helper() - sh, ok := s.Schema[key].Elem.(*schema.Resource) + r, ok := s[key].Elem.(*schema.Resource) if !ok { t.Fatalf("Error in Schema: Missing nested schema for \"%s\"", key) @@ -36,45 +36,45 @@ func AssertNestedSchemaExistence(t *testing.T, s *schema.Resource, key string) * return nil } - return sh + return r.Schema } // AssertListMaxItems checks that the given schema attribute has given expected MaxItems value. -func AssertListMaxItems(t *testing.T, s *schema.Resource, key string, expectedMaxItems int) { +func AssertListMaxItems(t *testing.T, s map[string]*schema.Schema, key string, expectedMaxItems int) { t.Helper() - require.NotNil(t, s.Schema[key], "Error in Schema: Missing definition for \"%s\"", key) - assert.Equal(t, expectedMaxItems, s.Schema[key].MaxItems, + require.NotNil(t, s[key], "Error in Schema: Missing definition for \"%s\"", key) + assert.Equal(t, expectedMaxItems, s[key].MaxItems, "Error in Schema: Argument \"%s\" has \"MaxItems: %#v\", but value %#v is expected!", - key, s.Schema[key].MaxItems, expectedMaxItems) + key, s[key].MaxItems, expectedMaxItems) } // AssertOptionalArguments checks that the given schema has the given optional arguments. -func AssertOptionalArguments(t *testing.T, s *schema.Resource, keys []string) { +func AssertOptionalArguments(t *testing.T, s map[string]*schema.Schema, keys []string) { t.Helper() for _, v := range keys { - require.NotNil(t, s.Schema[v], "Error in Schema: Missing definition for \"%s\"", v) - assert.True(t, s.Schema[v].Optional, "Error in Schema: Argument \"%s\" is not optional", v) + require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v) + assert.True(t, s[v].Optional, "Error in Schema: Argument \"%s\" is not optional", v) } } // AssertRequiredArguments checks that the given schema has the given required arguments. -func AssertRequiredArguments(t *testing.T, s *schema.Resource, keys []string) { +func AssertRequiredArguments(t *testing.T, s map[string]*schema.Schema, keys []string) { t.Helper() for _, v := range keys { - require.NotNil(t, s.Schema[v], "Error in Schema: Missing definition for \"%s\"", v) - assert.True(t, s.Schema[v].Required, "Error in Schema: Argument \"%s\" is not required", v) + require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v) + assert.True(t, s[v].Required, "Error in Schema: Argument \"%s\" is not required", v) } } // AssertValueTypes checks that the given schema has the given value types for the given fields. -func AssertValueTypes(t *testing.T, s *schema.Resource, f map[string]schema.ValueType) { +func AssertValueTypes(t *testing.T, s map[string]*schema.Schema, f map[string]schema.ValueType) { t.Helper() for fn, ft := range f { - require.NotNil(t, s.Schema[fn], "Error in Schema: Missing definition for \"%s\"", fn) - assert.Equal(t, ft, s.Schema[fn].Type, "Error in Schema: Argument or attribute \"%s\" is not of type \"%v\"", fn, ft) + require.NotNil(t, s[fn], "Error in Schema: Missing definition for \"%s\"", fn) + assert.Equal(t, ft, s[fn].Type, "Error in Schema: Argument or attribute \"%s\" is not of type \"%v\"", fn, ft) } }