diff --git a/docs/resources/virtual_environment/proxmox_virtual_environment_ipset.md b/docs/resources/virtual_environment/proxmox_virtual_environment_ipset.md new file mode 100644 index 00000000..9a1a8daf --- /dev/null +++ b/docs/resources/virtual_environment/proxmox_virtual_environment_ipset.md @@ -0,0 +1,50 @@ +--- +layout: page +title: IPSet +permalink: /ressources/virtual-environment/ipset +nav_order: 12 +parent: Virtual Environment Resources +grand_parent: Resources +--- + +# Resource: IPSet + +An IPSet allows us to group multiple IP addresses, IP subnets and aliases. + +## Example Usage + +``` +resource "proxmox_virtual_environment_cluster_ipset" "ipset" { + name = "local_network" + comment = "Managed by Terraform" + + ipset { + cidr = "192.168.0.0/23" + comment = "Local network 1" + } + + ipset { + cidr = "192.168.0.1" + comment = "Server 1" + nomatch = true + } + + ipset { + cidr = "192.168.2.1" + comment = "Server 1" + } +} +``` + +## Arguments Reference + +* `name` - (Required) Alias name. +* `comment` - (Optional) Alias comment. +* `ipset` - (Optional) IP/CIDR block (multiple blocks supported). + * `cidr` - Network/IP specification in CIDR format. + * `comment` - (Optional) Arbitrary string annotation. + * `nomatch` - (Optional) Entries marked as `nomatch` are skipped as if those were not added to the set. + +## Attributes Reference + +There are no attribute references available for this resource. diff --git a/example/resource_virtual_environment_cluster_ipset.tf b/example/resource_virtual_environment_cluster_ipset.tf new file mode 100644 index 00000000..63bbcc96 --- /dev/null +++ b/example/resource_virtual_environment_cluster_ipset.tf @@ -0,0 +1,25 @@ +resource "proxmox_virtual_environment_cluster_ipset" "example" { + name = "local_network" + comment = "Managed by Terraform" + + ipset { + cidr = "192.168.0.0/23" + comment = "Local network 1" + } + + ipset { + cidr = "192.168.0.1" + comment = "Server 1" + nomatch = true + } + + ipset { + cidr = "192.168.2.1" + comment = "Server 1" + } +} + +output "resource_proxmox_virtual_environment_cluster_ipset" { + value = "${proxmox_virtual_environment_cluster_ipset.example.name}" +} + diff --git a/proxmox/virtual_environment_cluster_ipset.go b/proxmox/virtual_environment_cluster_ipset.go new file mode 100644 index 00000000..3513bede --- /dev/null +++ b/proxmox/virtual_environment_cluster_ipset.go @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package proxmox + +import ( + "errors" + "fmt" + "net/url" + "sort" +) + +// CreateIPSet create an IPSet +func (c *VirtualEnvironmentClient) CreateIPSet(d *VirtualEnvironmentClusterIPSetCreateRequestBody) error { + return c.DoRequest(hmPOST, "cluster/firewall/ipset", d, nil) +} + +// Add IP or Network to IPSet +func (c *VirtualEnvironmentClient) AddCIDRToIPSet(id string, d *VirtualEnvironmentClusterIPSetGetResponseData) error { + return c.DoRequest(hmPOST, fmt.Sprintf("cluster/firewall/ipset/%s/", url.PathEscape(id)), d, nil) +} + +// UpdateIPSet updates an IPSet. +func (c *VirtualEnvironmentClient) UpdateIPSet(d *VirtualEnvironmentClusterIPSetUpdateRequestBody) error { + return c.DoRequest(hmPOST, fmt.Sprint("cluster/firewall/ipset/"), d, nil) +} + +// DeleteIPSet delete an IPSet +func (c *VirtualEnvironmentClient) DeleteIPSet(id string) error { + return c.DoRequest(hmDELETE, fmt.Sprintf("cluster/firewall/ipset/%s", url.PathEscape(id)), nil, nil) +} + +// DeleteIPSetContent remove IP or Network from IPSet. +func (c *VirtualEnvironmentClient) DeleteIPSetContent(id string, cidr string) error { + return c.DoRequest(hmDELETE, fmt.Sprintf("cluster/firewall/ipset/%s/%s", url.PathEscape(id), url.PathEscape(cidr)), nil, nil) +} + +// GetListIPSetContent retrieve a list of IPSet content +func (c *VirtualEnvironmentClient) GetListIPSetContent(id string) ([]*VirtualEnvironmentClusterIPSetGetResponseData, error) { + resBody := &VirtualEnvironmentClusterIPSetGetResponseBody{} + err := c.DoRequest(hmGET, fmt.Sprintf("cluster/firewall/ipset/%s", url.PathEscape(id)), nil, resBody) + + if err != nil { + return nil, err + } + + if resBody.Data == nil { + return nil, errors.New("The server did not include a data object in the response") + } + + return resBody.Data, nil +} + +// GetListIPSets retrieves list of IPSets. +func (c *VirtualEnvironmentClient) GetListIPSets() (*VirtualEnvironmentClusterIPSetListResponseBody, error) { + resBody := &VirtualEnvironmentClusterIPSetListResponseBody{} + err := c.DoRequest(hmGET, "cluster/firewall/ipset", nil, resBody) + + if err != nil { + return nil, err + } + + if resBody.Data == nil { + return nil, errors.New("The server did not include a data object in the response") + } + + sort.Slice(resBody.Data, func(i, j int) bool { + return resBody.Data[i].Name < resBody.Data[j].Name + }) + + return resBody, nil +} \ No newline at end of file diff --git a/proxmox/virtual_environment_cluster_ipset_types.go b/proxmox/virtual_environment_cluster_ipset_types.go new file mode 100644 index 00000000..36edafff --- /dev/null +++ b/proxmox/virtual_environment_cluster_ipset_types.go @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/** +* Reference: https://pve.proxmox.com/pve-docs/api-viewer/#/cluster/firewall/ipset + */ + +package proxmox + +// VirtualEnvironmentClusterIPSetListResponseBody contains the data from an IPSet get response. +type VirtualEnvironmentClusterIPSetListResponseBody struct { + Data []*VirtualEnvironmentClusterIPSetCreateRequestBody `json:"data,omitempty"` +} + +// VirtualEnvironmentClusterIPSetCreateRequestBody contains the data for an IPSet create request +type VirtualEnvironmentClusterIPSetCreateRequestBody struct { + Comment string `json:"comment,omitempty" url:"comment,omitempty"` + Name string `json:"name" url:"name"` +} + +// VirtualEnvironmentClusterIPSetGetResponseBody contains the body from an IPSet get response. +type VirtualEnvironmentClusterIPSetGetResponseBody struct { + Data []*VirtualEnvironmentClusterIPSetGetResponseData `json:"data,omitempty"` +} + +// VirtualEnvironmentClusterIPSetGetResponseData contains the data from an IPSet get response. +type VirtualEnvironmentClusterIPSetGetResponseData struct { + CIDR string `json:"cidr" url:"cidr"` + NoMatch *CustomBool `json:"nomatch,omitempty" url:"nomatch,omitempty,int"` + Comment string `json:"comment,omitempty" url:"comment,omitempty"` +} + +// VirtualEnvironmentClusterIPSetUpdateRequestBody contains the data for an IPSet update request. +type VirtualEnvironmentClusterIPSetUpdateRequestBody struct { + ReName string `json:"rename,omitempty" url:"rename,omitempty"` + Comment *string `json:"comment,omitempty" url:"comment,omitempty"` + Name string `json:"name" url:"name"` +} + +// VirtualEnvironmentClusterIPSetGetResponseData contains list of IPSets from +type VirtualEnvironmentClusterIPSetListResponseData struct { + Comment *string `json:"comment,omitempty" url:"comment,omitempty"` + Name string `json:"name" url:"name"` +} + +// VirtualEnvironmentClusterIPSetContent is an array of VirtualEnvironmentClusterIPSetGetResponseData. +type VirtualEnvironmentClusterIPSetContent []VirtualEnvironmentClusterIPSetGetResponseData diff --git a/proxmoxtf/provider.go b/proxmoxtf/provider.go index 7d2b514f..eb9f3052 100644 --- a/proxmoxtf/provider.go +++ b/proxmoxtf/provider.go @@ -63,6 +63,7 @@ func Provider() *schema.Provider { "proxmox_virtual_environment_time": resourceVirtualEnvironmentTime(), "proxmox_virtual_environment_user": resourceVirtualEnvironmentUser(), "proxmox_virtual_environment_vm": resourceVirtualEnvironmentVM(), + "proxmox_virtual_environment_cluster_ipset": resourceVirtualEnvironmentClusterIPSet(), }, Schema: map[string]*schema.Schema{ mkProviderVirtualEnvironment: { diff --git a/proxmoxtf/resource_virtual_environment_cluster_ipset.go b/proxmoxtf/resource_virtual_environment_cluster_ipset.go new file mode 100644 index 00000000..52e036fd --- /dev/null +++ b/proxmoxtf/resource_virtual_environment_cluster_ipset.go @@ -0,0 +1,273 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package proxmoxtf + +import ( + "github.com/danitso/terraform-provider-proxmox/proxmox" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +const ( + dvResourceVirtualEnvironmentClusterIPSetComment = "" + dvResourceVirtualEnvironmentClusterIPSetNoMatch = false + + mkResourceVirtualEnvironmentClusterIPSet = "ipset" + mkResourceVirtualEnvironmentClusterIPSetCIDR = "cidr" + mkResourceVirtualEnvironmentClusterIPSetName = "name" + mkResourceVirtualEnvironmentClusterIPSetComment = "comment" + mkResourceVirtualEnvironmentClusterIPSetNoMatch = "nomatch" +) + +func resourceVirtualEnvironmentClusterIPSet() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkResourceVirtualEnvironmentClusterIPSetName: { + Type: schema.TypeString, + Description: "IPSet name", + Required: true, + ForceNew: false, + }, + mkResourceVirtualEnvironmentClusterIPSet: { + Type: schema.TypeList, + Description: "List of IP or Networks", + Optional: true, + ForceNew: true, + DefaultFunc: func() (interface{}, error) { + return []interface{}{}, nil + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkResourceVirtualEnvironmentClusterIPSetCIDR: { + Type: schema.TypeString, + Description: "Network/IP specification in CIDR format", + Required: true, + ForceNew: true, + }, + mkResourceVirtualEnvironmentClusterIPSetNoMatch: { + Type: schema.TypeBool, + Description: "No match this IP/CIDR", + Optional: true, + Default: dvResourceVirtualEnvironmentClusterIPSetNoMatch, + ForceNew: true, + }, + mkResourceVirtualEnvironmentClusterIPSetComment: { + Type: schema.TypeString, + Description: "IP/CIDR comment", + Optional: true, + Default: dvResourceVirtualEnvironmentClusterIPSetComment, + ForceNew: true, + }, + }, + }, + }, + mkResourceVirtualEnvironmentClusterIPSetComment: { + Type: schema.TypeString, + Description: "IPSet comment", + Optional: true, + Default: dvResourceVirtualEnvironmentClusterIPSetComment, + }, + }, + Create: resourceVirtualEnvironmentClusterIPSetCreate, + Read: resourceVirtualEnvironmentClusterIPSetRead, + Update: resourceVirtualEnvironmentClusterIPSetUpdate, + Delete: resourceVirtualEnvironmentClusterIPSetDelete, + } +} + +func resourceVirtualEnvironmentClusterIPSetCreate(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + comment := d.Get(mkResourceVirtualEnvironmentClusterIPSetComment).(string) + name := d.Get(mkResourceVirtualEnvironmentClusterIPSetName).(string) + + IPSets := d.Get(mkResourceVirtualEnvironmentClusterIPSet).([]interface{}) + IPSetsArray := make(proxmox.VirtualEnvironmentClusterIPSetContent, len(IPSets)) + + for i, v := range IPSets { + IPSetMap := v.(map[string]interface{}) + IPSetObject := proxmox.VirtualEnvironmentClusterIPSetGetResponseData{} + + cidr := IPSetMap[mkResourceVirtualEnvironmentClusterIPSetCIDR].(string) + noMatch := IPSetMap[mkResourceVirtualEnvironmentClusterIPSetNoMatch].(bool) + comment := IPSetMap[mkResourceVirtualEnvironmentClusterIPSetComment].(string) + + + IPSetObject.Comment = comment + IPSetObject.CIDR = cidr + + if noMatch { + noMatchBool := proxmox.CustomBool(true) + IPSetObject.NoMatch = &noMatchBool + } + + + IPSetsArray[i] = IPSetObject + } + + body := &proxmox.VirtualEnvironmentClusterIPSetCreateRequestBody{ + Comment: comment, + Name: name, + } + + err = veClient.CreateIPSet(body) + + if err != nil { + return err + } + + for _, v := range IPSetsArray { + err = veClient.AddCIDRToIPSet(name, &v) + + if err != nil { + return err + } + } + + d.SetId(name) + return resourceVirtualEnvironmentClusterIPSetRead(d, m) +} + +func resourceVirtualEnvironmentClusterIPSetRead(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + name := d.Id() + + allIPSets, err := veClient.GetListIPSets() + + if err != nil { + return err + } + + for _, v := range allIPSets.Data { + if v.Name == name { + err = d.Set(mkResourceVirtualEnvironmentClusterIPSetName, v.Name) + + if err != nil { + return err + } + + err = d.Set(mkResourceVirtualEnvironmentClusterIPSetComment, v.Comment) + + if err != nil { + return err + } + } + } + + + IPSet, err := veClient.GetListIPSetContent(name) + + if err != nil { + if strings.Contains(err.Error(), "HTTP 404") { + d.SetId("") + return nil + } + + return err + } + + for key, _ := range IPSet { + d.Set(mkResourceVirtualEnvironmentClusterIPSet, IPSet[key]) + } + + return nil +} + +func resourceVirtualEnvironmentClusterIPSetUpdate(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + comment := d.Get(mkResourceVirtualEnvironmentClusterIPSetComment).(string) + newName := d.Get(mkResourceVirtualEnvironmentClusterIPSetName).(string) + previousName := d.Id() + + body := &proxmox.VirtualEnvironmentClusterIPSetUpdateRequestBody{ + ReName: previousName, + Name: newName, + Comment: &comment, + } + + err = veClient.UpdateIPSet(body) + + if err != nil { + return err + } + + d.SetId(newName) + + return resourceVirtualEnvironmentClusterIPSetRead(d, m) +} + + +func resourceVirtualEnvironmentClusterIPSetDelete(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return nil + } + + name := d.Id() + + IPSetContent, err := veClient.GetListIPSetContent(name) + + if err != nil { + return err + } + + // PVE requires content of IPSet be cleared before removal + if len(IPSetContent) > 0 { + for _, IPSet := range IPSetContent { + err = veClient.DeleteIPSetContent(name, IPSet.CIDR) + if err != nil { + return err + } + } + } + + err = veClient.DeleteIPSet(name) + + if err != nil { + if strings.Contains(err.Error(), "HTTP 404") { + d.SetId("") + return nil + } + + return err + } + + d.SetId("") + + return nil +} + + + + + + + + + + + + + + diff --git a/proxmoxtf/resource_virtual_environment_cluster_ipset_test.go b/proxmoxtf/resource_virtual_environment_cluster_ipset_test.go new file mode 100644 index 00000000..75c9022a --- /dev/null +++ b/proxmoxtf/resource_virtual_environment_cluster_ipset_test.go @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package proxmoxtf + +import ( + "github.com/hashicorp/terraform/helper/schema" + "testing" +) + +// TestResourceVirtualEnvironmentIPSetInstantiation tests whether the resourceVirtualEnvironmentClusterIPSet +// instance can be instantiated. +func TestResourceVirtualEnvironmentIPSetInstantiation(t *testing.T) { + s := resourceVirtualEnvironmentClusterIPSet() + + if s == nil { + t.Fatalf("Cannot instantiate resourceVirtualEnvironmentAlias") + } +} + +// TestResourceVirtualEnvironmentIPSetSchema tests the resourceVirtualEnvironmentClusterIPSet schema. +func TestResourceVirtualEnvironmentIPSetSchema(t *testing.T) { + s := resourceVirtualEnvironmentClusterIPSet() + + testRequiredArguments(t, s, []string{ + mkResourceVirtualEnvironmentClusterIPSetName, + }) + + testOptionalArguments(t, s, []string{ + mkResourceVirtualEnvironmentClusterIPSet, + mkResourceVirtualEnvironmentClusterIPSetComment, + }) + + testValueTypes(t, s, map[string]schema.ValueType{ + mkResourceVirtualEnvironmentClusterIPSetName: schema.TypeString, + mkResourceVirtualEnvironmentClusterIPSet: schema.TypeList, + mkResourceVirtualEnvironmentClusterIPSetComment: schema.TypeString, + }) + + IPSetSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentClusterIPSet) + + testRequiredArguments(t, IPSetSchema, []string{ + mkResourceVirtualEnvironmentClusterIPSetCIDR, + }) + + testOptionalArguments(t, IPSetSchema, []string{ + mkResourceVirtualEnvironmentClusterIPSetComment, + mkResourceVirtualEnvironmentClusterIPSetNoMatch, + }) + + testValueTypes(t, IPSetSchema, map[string]schema.ValueType{ + mkResourceVirtualEnvironmentClusterIPSetCIDR: schema.TypeString, + mkResourceVirtualEnvironmentClusterIPSetComment: schema.TypeString, + mkResourceVirtualEnvironmentClusterIPSetNoMatch: schema.TypeBool, + }) + +}